org.sakaiproject.search.indexer.impl.SearchBuilderQueueManager.java Source code

Java tutorial

Introduction

Here is the source code for org.sakaiproject.search.indexer.impl.SearchBuilderQueueManager.java

Source

/**********************************************************************************
 * $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 = " //$NON-NLS-1$
                        + SearchBuilderItem.ITEM_SITE_MASTER + " and context = '" + controlItem.getContext() + "' and name <> '" + controlItem.getName() + "'"); //$NON-NLS-2$ //$NON-NLS-3$

            }
            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);
    }

}