/**********************************************************************************
* $URL: https://source.sakaiproject.org/svn/search/trunk/search-impl/impl/src/java/org/sakaiproject/search/indexer/impl/SearchBuilderQueueManager.java $
* $Id: SearchBuilderQueueManager.java 105078 2012-02-24 23:00:38Z ottenhoff@longsight.com $
***********************************************************************************
*
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008 The Sakai Foundation
*
* Licensed under the Educational Community License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.opensource.org/licenses/ECL-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
**********************************************************************************/
package org.sakaiproject.search.indexer.impl;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.sakaiproject.db.cover.SqlService;
import org.sakaiproject.javax.PagingPosition;
import org.sakaiproject.search.api.EntityContentProducer;
import org.sakaiproject.search.api.SearchIndexBuilder;
import org.sakaiproject.search.indexer.api.IndexUpdateTransactionListener;
import org.sakaiproject.search.model.SearchBuilderItem;
import org.sakaiproject.search.model.impl.SearchBuilderItemImpl;
import org.sakaiproject.search.transaction.api.IndexItemsTransaction;
import org.sakaiproject.search.transaction.api.IndexTransaction;
import org.sakaiproject.search.transaction.api.IndexTransactionException;
import org.sakaiproject.search.transaction.api.TransactionSequence;
import org.sakaiproject.site.api.Site;
import org.sakaiproject.site.api.ToolConfiguration;
import org.sakaiproject.site.api.SiteService.SelectionType;
import org.sakaiproject.site.api.SiteService.SortType;
import org.sakaiproject.site.api.SiteService;
import org.sakaiproject.thread_local.api.ThreadLocalManager;
/**
* This class manages the Search Build Queue, it retrieves the
*
* @author ieb Unit test
* @see org.sakaiproject.search.indexer.impl.test.TransactionalIndexWorkerTest
*/
public class SearchBuilderQueueManager implements IndexUpdateTransactionListener
{
private static final Log log = LogFactory.getLog(SearchBuilderQueueManager.class);
private static final String SEARCH_BUILDER_ITEM_FIELDS = " name, context, searchaction, searchstate, version, itemscope, id "; //$NON-NLS-1$
private static final String SEARCH_BUILDER_ITEM_T = "searchbuilderitem"; //$NON-NLS-1$
private static final String SEARCH_BUILDER_ITEM_FIELDS_PARAMS = " ?, ?, ?, ?, ?, ?, ? "; //$NON-NLS-1$
private static final String SEARCH_BUILDER_ITEM_FIELDS_UPDATE = " name = ?, context = ?, searchaction = ?, searchstate = ?, version = ?, itemscope = ? where id = ? "; //$NON-NLS-1$
public static final String BATCH_SIZE = "batch-size";
/**
* dependency
*/
private SearchIndexBuilder searchIndexBuilder;
/**
* dependency
*/
private DataSource datasource;
private int nodeLock;
private TransactionSequence sequence;
private SiteService siteService;
private ThreadLocalManager threadLocalManager;
public void setThreadLocalManager(ThreadLocalManager threadLocalManager) {
this.threadLocalManager = threadLocalManager;
}
/** Configuration: to run the ddl on init or not. */
protected boolean autoDdl = false;
public void init()
{
try
{
if (autoDdl)
{
SqlService.getInstance().ddl(this.getClass().getClassLoader(),
"sakai_search_parallel");
}
}
catch (Exception ex)
{
log.error("Perform additional SQL setup", ex);
}
nodeLock = (int) sequence.getNextId();
}
public void destroy()
{
}
/*
* (non-Javadoc)
*
* @see org.sakaiproject.search.indexer.api.TransactionListener#prepare(org.sakaiproject.search.indexer.api.IndexUpdateTransaction)
*/
public void prepare(IndexTransaction transaction)
{
// At the moment I dot think that we need to do anything here, we could
// brign the work of the
// commit phase in here leaving the final commit to the last method,
// but that would mean taking the connection over more than one cycle.
}
/*
* (non-Javadoc)
*
* @see org.sakaiproject.search.component.service.index.transactional.api.TransactionListener#commit(org.sakaiproject.search.component.service.index.transactional.api.IndexUpdateTransaction)
*/
public void commit(IndexTransaction transaction)
{
Connection connection = null;
try
{
connection = datasource.getConnection();
commitPendingAndUnLock(((IndexItemsTransaction) transaction).getItems(),
connection);
connection.commit();
}
catch (Exception ex)
{
try
{
if (connection != null) connection.rollback();
}
catch (Exception ex2)
{
log.warn("error during rollback in commit", ex2);
}
}
finally
{
try
{
if (connection != null) connection.close();
}
catch (Exception ex2)
{
log.warn("error closing connection in commit", ex2);
}
}
}
/*
* (non-Javadoc)
*
* @see org.sakaiproject.search.component.service.index.transactional.api.TransactionListener#rollback(org.sakaiproject.search.component.service.index.transactional.api.IndexUpdateTransaction)
*/
public void rollback(IndexTransaction transaction)
{
Connection connection = null;
try
{
connection = datasource.getConnection();
rollbackPendingAndUnLock(((IndexItemsTransaction) transaction).getItems(),
connection);
connection.commit();
}
catch (Exception ex)
{
try
{
if (connection != null) connection.rollback();
}
catch (Exception ex2)
{
log.debug("Exception during rollback", ex2);
}
}
finally
{
try
{
if (connection != null) connection.close();
}
catch (Exception ex2)
{
log.debug("Exception closing connection", ex2);
}
}
}
/*
* (non-Javadoc)
*
* @see org.sakaiproject.search.component.service.index.transactional.api.TransactionListener#open(org.sakaiproject.search.component.service.index.transactional.api.IndexUpdateTransaction)
*/
public void open(IndexTransaction transaction) throws IndexTransactionException
{
Connection connection = null;
try
{
connection = datasource.getConnection();
Integer bs = (Integer) transaction.get(BATCH_SIZE);
int batchSize = 100;
if (bs != null)
{
batchSize = bs.intValue();
}
List<SearchBuilderItem> items = findPendingAndLock(batchSize, connection);
log.debug("Adding " + items.size()
+ " items to indexing queue: batch size was " + batchSize);
((IndexItemsTransaction) transaction).setItems(items);
connection.commit();
}
catch (IndexTransactionException itex)
{
log.info("Rethrowing " + itex.getMessage());
throw itex;
}
catch (Exception ex)
{
log.info("Failed to Open Transaction ", ex);
try
{
connection.rollback();
}
catch (Exception ex2)
{
log.debug("Exception during rollback", ex2);
}
throw new IndexTransactionException("Failed to open transaction ", ex);
}
finally
{
try
{
if (connection != null) connection.close();
}
catch (Exception ex2)
{
log.debug("Exception closing connection", ex2);
}
}
}
/**
* @see org.sakaiproject.search.transaction.api.TransactionListener#close(org.sakaiproject.search.transaction.api.IndexTransaction)
*/
public void close(IndexTransaction transaction) throws IndexTransactionException
{
}
private List<SearchBuilderItem> findPendingAndLock(int batchSize,
Connection connection) throws SQLException
{
// Pending is the first 100 items
// State == PENDING
// Action != Unknown
long start = System.currentTimeMillis();
try
{
log.debug("TXFind pending with " + connection); //$NON-NLS-1$
SearchBuilderItem masterItem = getMasterItem(connection);
try
{
Integer masterAction = getMasterAction(masterItem);
log.debug(" Master Item is " + masterItem.getName() + ":" //$NON-NLS-1$ //$NON-NLS-2$
+ masterItem.getSearchaction() + ":" //$NON-NLS-1$
+ masterItem.getSearchstate() + "::" //$NON-NLS-1$
+ masterItem.getVersion());
if (SearchBuilderItem.ACTION_REFRESH.equals(masterAction))
{
log.debug(" Master Action is " + masterAction); //$NON-NLS-1$
log.debug(" REFRESH = " + SearchBuilderItem.ACTION_REFRESH); //$NON-NLS-1$
log.debug(" RELOAD = " + SearchBuilderItem.ACTION_REBUILD); //$NON-NLS-1$
// get a complete list of all items, before the master
// action version
// if there are none, update the master action action to
// completed
// and return a blank list
refreshIndex(connection, masterItem);
}
else if (SearchBuilderItem.ACTION_REBUILD.equals(masterAction))
{
rebuildIndex(connection, masterItem);
}
else
{
// get all site masters and perform the required action.
List<SearchBuilderItem> siteMasters = getSiteMasterItems(connection);
for (Iterator<SearchBuilderItem> i = siteMasters.iterator(); i.hasNext();)
{
SearchBuilderItem siteMaster = (SearchBuilderItem) i.next();
try
{
Integer action = getSiteMasterAction(siteMaster);
if (SearchBuilderItem.ACTION_REBUILD.equals(action))
{
rebuildIndex(connection, siteMaster);
}
else if (SearchBuilderItem.ACTION_REFRESH.equals(action))
{
refreshIndex(connection, siteMaster);
}
}
finally
{
// any value > 1000 is a lock
if (siteMaster.getLock() == nodeLock )
{
List<SearchBuilderItem> l = new ArrayList<SearchBuilderItem>();
l.add(siteMaster);
commitPendingAndUnLock(l, connection);
}
}
}
}
}
finally
{
if (masterItem.getLock() == nodeLock )
{
List<SearchBuilderItem> l = new ArrayList<SearchBuilderItem>();
l.add(masterItem);
commitPendingAndUnLock(l, connection);
}
}
PreparedStatement pst = null;
PreparedStatement lockedPst = null;
ResultSet rst = null;
try
{
pst = connection.prepareStatement("select " //$NON-NLS-1$
+ SEARCH_BUILDER_ITEM_FIELDS + " from " //$NON-NLS-1$
+ SEARCH_BUILDER_ITEM_T + " where searchstate = ? and " //$NON-NLS-1$
+ " itemscope = ? order by version "); //$NON-NLS-1$
lockedPst = connection.prepareStatement("update " //$NON-NLS-1$
+ SEARCH_BUILDER_ITEM_T + " set searchstate = ? " //$NON-NLS-1$
+ " where id = ? and searchstate = ? "); //$NON-NLS-1$
pst.clearParameters();
pst.setInt(1, SearchBuilderItem.STATE_PENDING.intValue());
pst.setInt(2, SearchBuilderItem.ITEM.intValue());
rst = pst.executeQuery();
List<SearchBuilderItem> a = new ArrayList<SearchBuilderItem>();
while (rst.next() && a.size() < batchSize)
{
SearchBuilderItemImpl sbi = new SearchBuilderItemImpl();
populateSearchBuilderItem(rst, sbi);
if (!SearchBuilderItem.ACTION_UNKNOWN.equals(sbi.getSearchaction()))
{
lockedPst.clearParameters();
lockedPst.setInt(1, nodeLock);
lockedPst.setString(2, sbi.getId());
lockedPst.setInt(3, SearchBuilderItem.STATE_PENDING.intValue());
if (lockedPst.executeUpdate() == 1)
{
sbi.setSearchstate(SearchBuilderItem.STATE_LOCKED);
sbi.setLock(nodeLock);
a.add(sbi);
}
connection.commit();
}
}
return a;
}
finally
{
try
{
if (rst != null) rst.close();
}
catch (Exception ex)
{
log.warn("Error closing result set", ex);
}
try
{
if (pst != null) pst.close();
}
catch (Exception ex)
{
log.warn("Error closing statement", ex);
}
}
}
finally
{
long finish = System.currentTimeMillis();
log.debug(" findPending took " + (finish - start) + " ms"); //$NON-NLS-1$ //$NON-NLS-2$
}
}
/**
* @param runtimeToDo
* @param connection
* @return
* @throws SQLException
*/
private void commitPendingAndUnLock(List<SearchBuilderItem> runtimeToDo,
Connection connection) throws SQLException
{
PreparedStatement unLockPst = null;
PreparedStatement deletePst = null;
try
{
unLockPst = connection.prepareStatement("update " //$NON-NLS-1$
+ SEARCH_BUILDER_ITEM_T + " set searchstate = ? " //$NON-NLS-1$
+ " where id = ? and searchstate = ? "); //$NON-NLS-1$
deletePst = connection.prepareStatement(" delete from " //$NON-NLS-1$
+ SEARCH_BUILDER_ITEM_T + " where id = ? "); //$NON-NLS-1$
for (Iterator<SearchBuilderItem> isbi = runtimeToDo.iterator(); isbi
.hasNext();)
{
SearchBuilderItem sbi = isbi.next();
if (SearchBuilderItem.ACTION_DELETE.equals(sbi.getSearchaction()))
{
deletePst.clearParameters();
deletePst.setString(1, sbi.getId());
if (deletePst.executeUpdate() != 1)
{
log.warn("Failed to delete " + sbi.getName() + " ");
}
else
{
log.debug("Delete " + sbi.getName() + " ");
}
connection.commit();
}
else
{
unLockPst.clearParameters();
unLockPst.setInt(1, SearchBuilderItem.STATE_COMPLETED.intValue());
unLockPst.setString(2, sbi.getId());
unLockPst.setInt(3, sbi.getLock());
if (unLockPst.executeUpdate() != 1)
{
log.warn("Failed to mark " + sbi + " as completed ");
}
else
{
log.debug("Marked " + sbi.getName() + " as completed ");
}
sbi.setSearchstate(SearchBuilderItem.STATE_COMPLETED);
connection.commit();
}
}
}
finally
{
try
{
unLockPst.close();
}
catch (Exception ex)
{
log.warn("Error unlocking pst", ex);
}
try
{
deletePst.close();
}
catch (Exception ex)
{
log.warn("Error deleting pst", ex);
}
}
}
/**
* @param runtimeToDo
* @param connection
* @throws SQLException
*/
private void rollbackPendingAndUnLock(List<SearchBuilderItem> runtimeToDo,
Connection connection) throws SQLException
{
PreparedStatement unLockPst = null;
try
{
unLockPst = connection.prepareStatement("update " //$NON-NLS-1$
+ SEARCH_BUILDER_ITEM_T + " set searchstate = ? " //$NON-NLS-1$
+ " where id = ? and searchstate = ? "); //$NON-NLS-1$
for (Iterator<SearchBuilderItem> isbi = runtimeToDo.iterator(); isbi
.hasNext();)
{
SearchBuilderItem sbi = isbi.next();
unLockPst.clearParameters();
if (SearchBuilderItem.STATE_FAILED.equals(sbi.getSearchstate()))
{
sbi.setSearchstate(SearchBuilderItem.STATE_FAILED);
unLockPst.setInt(1, SearchBuilderItem.STATE_FAILED.intValue());
}
else
{
sbi.setSearchstate(SearchBuilderItem.STATE_PENDING);
unLockPst.setInt(1, SearchBuilderItem.STATE_PENDING.intValue());
}
unLockPst.setString(2, sbi.getId());
unLockPst.setInt(3, sbi.getSearchstate());
if (unLockPst.executeUpdate() == 1)
{
log.warn("Failed to mark " + sbi.getName() + " as pending ");
}
connection.commit();
}
}
finally
{
try
{
unLockPst.close();
}
catch (Exception ex)
{
log.warn("Error unlocking pst", ex);
}
}
}
/**
* get the Instance Master
*
* @return
* @throws SQLException
*/
private SearchBuilderItem getMasterItem(Connection connection) throws SQLException
{
log.debug("get Master Items with " + connection); //$NON-NLS-1$
PreparedStatement pst = null;
PreparedStatement lockMaster = null;
ResultSet rst = null;
try
{
lockMaster = connection.prepareStatement("update " + SEARCH_BUILDER_ITEM_T
+ " set searchstate = ? where itemscope = ? and searchstate = ? ");
lockMaster.clearParameters();
lockMaster.setInt(1, nodeLock);
lockMaster.setInt(2, SearchBuilderItem.ITEM_GLOBAL_MASTER.intValue());
lockMaster.setInt(3, SearchBuilderItem.STATE_PENDING.intValue());
lockMaster.executeUpdate();
pst = connection
.prepareStatement("select " //$NON-NLS-1$
+ SEARCH_BUILDER_ITEM_FIELDS
+ " from " //$NON-NLS-1$
+ SEARCH_BUILDER_ITEM_T
+ " where itemscope = ? and searchstate = ? "); //$NON-NLS-1$
pst.clearParameters();
pst.setInt(1, SearchBuilderItem.ITEM_GLOBAL_MASTER.intValue());
pst.setInt(2, nodeLock);
rst = pst.executeQuery();
SearchBuilderItemImpl sbi = new SearchBuilderItemImpl();
if (rst.next())
{
populateSearchBuilderItem(rst, sbi);
sbi.setLock(nodeLock);
log.info("Locked Master item to this node "+sbi);
rst.close();
connection.commit();
}
else
{
rst.close();
connection.rollback();
sbi.setName(SearchBuilderItem.INDEX_MASTER);
sbi.setContext(SearchBuilderItem.GLOBAL_CONTEXT);
sbi.setSearchaction(SearchBuilderItem.ACTION_UNKNOWN);
sbi.setSearchstate(SearchBuilderItem.STATE_UNKNOWN);
sbi.setItemscope(SearchBuilderItem.ITEM_GLOBAL_MASTER);
}
return sbi;
}
finally
{
try
{
if (rst != null) rst.close();
}
catch (Exception ex)
{
log.warn("Error closing rst", ex);
}
try
{
if (pst != null) pst.close();
}
catch (Exception ex)
{
log.warn("Error closing pst", ex);
}
try
{
lockMaster.close();
}
catch (Exception ex)
{
log.warn("Error closing lockMaster", ex);
}
}
}
private List<SearchBuilderItem> getSiteMasterItems(Connection connection)
throws SQLException
{
PreparedStatement pst = null;
PreparedStatement lockMaster = null;
ResultSet rst = null;
try
{
lockMaster = connection.prepareStatement("update " + SEARCH_BUILDER_ITEM_T
+ " set searchstate = ? where itemscope = ? and searchstate = ? ");
lockMaster.clearParameters();
lockMaster.setInt(1, nodeLock);
lockMaster.setInt(2, SearchBuilderItem.ITEM_SITE_MASTER.intValue());
lockMaster.setInt(3, SearchBuilderItem.STATE_PENDING.intValue());
lockMaster.executeUpdate();
pst = connection.prepareStatement("select " //$NON-NLS-1$
+ SEARCH_BUILDER_ITEM_FIELDS
+ " from " //$NON-NLS-1$
+ SEARCH_BUILDER_ITEM_T
+ " where itemscope = ? and searchstate = ? "); //$NON-NLS-1$
pst.clearParameters();
pst.setInt(1, SearchBuilderItem.ITEM_SITE_MASTER.intValue());
pst.setInt(2, nodeLock);
rst = pst.executeQuery();
List<SearchBuilderItem> a = new ArrayList<SearchBuilderItem>();
while (rst.next())
{
SearchBuilderItemImpl sbi = new SearchBuilderItemImpl();
populateSearchBuilderItem(rst, sbi);
a.add(sbi);
}
if (a.size() > 0)
{
connection.commit();
}
else
{
connection.rollback();
}
return a;
}
finally
{
try
{
rst.close();
}
catch (Exception ex)
{
log.warn("Error closing rst", ex);
}
try
{
pst.close();
}
catch (Exception ex)
{
log.warn("Error closing pst", ex);
}
try
{
lockMaster.close();
}
catch (Exception ex)
{
log.warn("Error closing lockMaster", ex);
}
}
}
/**
* get the master action of known master item
*
* @param master
* @return
*/
private Integer getMasterAction(SearchBuilderItem master)
{
if (master.getName().equals(SearchBuilderItem.GLOBAL_MASTER))
{
if (master.getLock() == nodeLock)
{
return master.getSearchaction();
}
}
return SearchBuilderItem.STATE_UNKNOWN;
}
/**
* get the action for the site master
*
* @param siteMaster
* @return
*/
private Integer getSiteMasterAction(SearchBuilderItem siteMaster)
{
if (siteMaster.getName().startsWith(SearchBuilderItem.INDEX_MASTER)
&& !SearchBuilderItem.GLOBAL_CONTEXT.equals(siteMaster.getContext()))
{
if (siteMaster.getLock() == nodeLock )
{
return siteMaster.getSearchaction();
}
}
return SearchBuilderItem.ACTION_UNKNOWN;
}
private void populateSearchBuilderItem(ResultSet rst, SearchBuilderItemImpl sbi)
throws SQLException
{
sbi.setName(rst.getString(1));
sbi.setContext(rst.getString(2));
sbi.setSearchaction(Integer.valueOf(rst.getInt(3)));
sbi.setSearchstate(Integer.valueOf(rst.getInt(4)));
sbi.setVersion(rst.getDate(5));
sbi.setItemscope(Integer.valueOf(rst.getInt(6)));
sbi.setId(rst.getString(7));
}
private void rebuildIndex(Connection connection, SearchBuilderItem controlItem)
throws SQLException
{
// delete all and return the master action only
// the caller will then rebuild the index from scratch
log
.debug("DELETE ALL RECORDS =========================================================="); //$NON-NLS-1$
Statement stm = null;
try
{
stm = connection.createStatement();
if (SearchBuilderItem.GLOBAL_CONTEXT.equals(controlItem.getContext()))
{
stm.execute("delete from searchbuilderitem where itemscope = "
+ SearchBuilderItem.ITEM
+ " or itemscope = " + SearchBuilderItem.ITEM_SITE_MASTER); //$NON-NLS-1$
}
else
{
stm.execute("delete from searchbuilderitem where context = '" //$NON-NLS-1$
+ controlItem.getContext() + "' and name <> '" //$NON-NLS-1$
+ controlItem.getName() + "' "); //$NON-NLS-1$
}
log
.debug("DONE DELETE ALL RECORDS ==========================================================="); //$NON-NLS-1$
connection.commit();
log
.debug("ADD ALL RECORDS ==========================================================="); //$NON-NLS-1$
List<String> contextList = getAllContentexts(controlItem);
//These will never change
List<EntityContentProducer> contentProducers = searchIndexBuilder.getContentProducers();
//how long and how often should we sleep?
long sleepTime = 1000*60;
long sleepInterval = 1000;
long count = 0;
long totalCount = 0;
//Iterate through each site
for (Iterator<String> c = contextList.iterator(); c.hasNext();)
{
//SAK-17117 before we do this clear threadLocal
//get the security advisor stack otherwise later calls will fail
Object obj = threadLocalManager.get("SakaiSecurity.advisor.stack");
threadLocalManager.clear();
threadLocalManager.set("SakaiSecurity.advisor.stack", obj);
if (count == sleepInterval) {
log.info("sleeping to stop GC craziness");
log.info("done " + totalCount + "/" + contextList.size());
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
count = 0;
totalCount ++;
} else {
count ++;
totalCount ++;
}
String siteContext = (String) c.next();
log.info("Rebuild for " + siteContext + " (" + totalCount + "/" + contextList.size() +")"); //$NON-NLS-1$
for (Iterator<EntityContentProducer> i = contentProducers.iterator(); i
.hasNext();)
{
EntityContentProducer ecp = (EntityContentProducer) i.next();
Iterator<String> contentIterator = null;
try
{
contentIterator = ecp.getSiteContentIterator(siteContext);
if (log.isDebugEnabled())
log.debug("Using ECP " + ecp); //$NON-NLS-1$
int added = 0;
for (; contentIterator.hasNext();)
{
String resourceName = (String) contentIterator.next();
if (log.isDebugEnabled())
log.debug("Checking " + resourceName); //$NON-NLS-1$
if (resourceName == null || resourceName.length() > 255)
{
log
.warn("Entity Reference Longer than 255 characters, ignored: Reference=" //$NON-NLS-1$
+ resourceName);
continue;
}
SearchBuilderItem sbi = new SearchBuilderItemImpl();
sbi.setName(resourceName);
sbi.setSearchaction(SearchBuilderItem.ACTION_ADD);
sbi.setSearchstate(SearchBuilderItem.STATE_PENDING);
sbi.setId(UUID.randomUUID().toString());
sbi.setVersion(new Date(System.currentTimeMillis()));
sbi.setItemscope(SearchBuilderItem.ITEM);
String context = null;
try
{
context = ecp.getSiteId(resourceName);
}
catch (Exception ex)
{
log.debug("No context for resource " + resourceName //$NON-NLS-1$
+ " defaulting to none"); //$NON-NLS-1$
}
if (context == null || context.length() == 0)
{
context = "none"; //$NON-NLS-1$
}
sbi.setContext(context);
try
{
updateOrSave(connection, sbi);
}
catch (SQLException sqlex)
{
log.error("Failed to update " + sqlex.getMessage()); //$NON-NLS-1$
}
connection.commit();
}
if (log.isDebugEnabled())
log.debug(" Added " + added); //$NON-NLS-1$
}
catch (Exception ex)
{
log.warn("Failed to index site " + siteContext
+ " site has not been indexed", ex);
}
}
}
log
.debug("DONE ADD ALL RECORDS ==========================================================="); //$NON-NLS-1$
connection.commit();
}
finally
{
try
{
stm.close();
}
catch (Exception ex)
{
log.warn("Error closing stm", ex);
}
}
}
private List<String> getAllContentexts(SearchBuilderItem controlItem) {
List<String> contextList = new ArrayList<String>();
if (SearchBuilderItem.GLOBAL_CONTEXT.equals(controlItem.getContext()))
{
int first = 1;
int increment = 1000;
int last = increment;
boolean doAnother = true;
while (doAnother) {
List<Site> sites = siteService.getSites(SelectionType.ANY, null, null,
null, SortType.NONE, new PagingPosition(first, last));
for (Iterator<Site> i = sites.iterator(); i.hasNext();)
{
Site s = (Site) i.next();
if (!siteService.isSpecialSite(s.getId())
|| siteService.isUserSite(s.getId()))
{
if (searchIndexBuilder.isOnlyIndexSearchToolSites())
{
ToolConfiguration t = s.getToolForCommonId("sakai.search"); //$NON-NLS-1$
if (t != null)
{
contextList.add(s.getId());
}
}
else
{
contextList.add(s.getId());
}
}
}
if (sites.size() < increment) {
doAnother = false;
} else {
first = last +1;
last = last + increment;
}
}
}
else
{
contextList.add(controlItem.getContext());
}
return contextList;
}
private void refreshIndex(Connection connection, SearchBuilderItem controlItem)
throws SQLException
{
// delete all and return the master action only
// the caller will then rebuild the index from scratch
log
.debug("UPDATE ALL RECORDS =========================================================="); //$NON-NLS-1$
Statement stm = null;
try
{
stm = connection.createStatement();
if (SearchBuilderItem.GLOBAL_CONTEXT.equals(controlItem.getContext()))
{
stm.execute("update searchbuilderitem set searchstate = " //$NON-NLS-1$
+ SearchBuilderItem.STATE_PENDING
+ " where itemscope = " + SearchBuilderItem.ITEM); //$NON-NLS-1$
}
else
{
stm
.execute("update searchbuilderitem set searchstate = " //$NON-NLS-1$
+ SearchBuilderItem.STATE_PENDING
+ " where itemscope = " + SearchBuilderItem.ITEM_SITE_MASTER + " and context = '" + controlItem.getContext() //$NON-NLS-1$
+ "' and name <> '" + controlItem.getName() + "'"); //$NON-NLS-1$ //$NON-NLS-2$
}
connection.commit();
}
finally
{
try
{
stm.close();
}
catch (Exception ex)
{
log.warn("Error closing stm", ex);
};
}
}
private void updateOrSave(Connection connection, SearchBuilderItem sbi)
throws SQLException
{
PreparedStatement pst = null;
try
{
try
{
save(connection, sbi);
}
catch (SQLException sqlex)
{
pst = connection.prepareStatement("update " //$NON-NLS-1$
+ SEARCH_BUILDER_ITEM_T + " set " //$NON-NLS-1$
+ SEARCH_BUILDER_ITEM_FIELDS_UPDATE);
populateStatement(pst, sbi);
pst.executeUpdate();
}
}
catch (SQLException ex)
{
log.warn("Failed ", ex); //$NON-NLS-1$
throw ex;
}
finally
{
try
{
if (pst != null) pst.close();
}
catch (Exception ex)
{
log.warn("Error closing pst", ex);
}
}
}
private void save(Connection connection, SearchBuilderItem sbi) throws SQLException
{
PreparedStatement pst = null;
try
{
pst = connection.prepareStatement(" insert into " //$NON-NLS-1$
+ SEARCH_BUILDER_ITEM_T + " ( " //$NON-NLS-1$
+ SEARCH_BUILDER_ITEM_FIELDS + " ) values ( " //$NON-NLS-1$
+ SEARCH_BUILDER_ITEM_FIELDS_PARAMS + " ) "); //$NON-NLS-1$
pst.clearParameters();
populateStatement(pst, sbi);
pst.executeUpdate();
}
finally
{
try
{
pst.close();
}
catch (Exception ex)
{
log.warn("Error closing pst", ex);
}
}
}
private int populateStatement(PreparedStatement pst, SearchBuilderItem sbi)
throws SQLException
{
pst.setString(1, sbi.getName());
pst.setString(2, sbi.getContext());
pst.setInt(3, sbi.getSearchaction().intValue());
pst.setInt(4, sbi.getSearchstate().intValue());
pst.setDate(5, new Date(sbi.getVersion().getTime()));
pst.setInt(6, sbi.getItemscope().intValue());
pst.setString(7, sbi.getId());
return 7;
}
/**
* @return the searchIndexBuilder
*/
public SearchIndexBuilder getSearchIndexBuilder()
{
return searchIndexBuilder;
}
/**
* @param searchIndexBuilder
* the searchIndexBuilder to set
*/
public void setSearchIndexBuilder(SearchIndexBuilder searchIndexBuilder)
{
this.searchIndexBuilder = searchIndexBuilder;
}
/**
* @return the datasource
*/
public DataSource getDatasource()
{
return datasource;
}
/**
* @param datasource
* the datasource to set
*/
public void setDatasource(DataSource datasource)
{
this.datasource = datasource;
}
public void setSiteService(SiteService siteService) {
this.siteService = siteService;
}
/**
* @return the sequence
*/
public TransactionSequence getSequence()
{
return sequence;
}
/**
* @param sequence the sequence to set
*/
public void setSequence(TransactionSequence sequence)
{
this.sequence = sequence;
}
/**
* Configuration: to run the ddl on init or not.
*
* @param value
* the auto ddl value.
*/
public void setAutoDdl(String value)
{
autoDdl = Boolean.valueOf(value).booleanValue();
}
/**
*/
public String getAutoDdl()
{
return String.valueOf(autoDdl);
}
}