org.eclipse.smila.search.index.IndexManager.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.smila.search.index.IndexManager.java

Source

/***********************************************************************************************************************
 * Copyright (c) 2008 empolis GmbH and brox IT Solutions GmbH. All rights reserved. This program and the accompanying
 * materials are made available under the terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors: August Georg Schmidt (brox IT Solutions GmbH) - initial API and implementation
 **********************************************************************************************************************/
package org.eclipse.smila.search.index;

// tools

// data dictionary

// utils
import java.util.Hashtable;
import java.util.Iterator;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.smila.search.datadictionary.DataDictionaryController;
import org.eclipse.smila.search.datadictionary.DataDictionaryException;
import org.eclipse.smila.search.datadictionary.messages.datadictionary.DConnection;
import org.eclipse.smila.search.datadictionary.messages.datadictionary.DIndex;
import org.eclipse.smila.search.plugin.Plugin;
import org.eclipse.smila.search.plugin.PluginFactory;

/**
 * The Class IndexManager.
 * 
 * @author August Georg Schmidt (BROX)
 */
public abstract class IndexManager {

    /**
     * Garbage collector for unused index connections.
     */
    private static IndexCleaner s_cleaner = new IndexCleaner();

    /**
     * The s_index usages.
     */
    private static Hashtable<String, IndexUsage> s_indexUsages = new Hashtable<String, IndexUsage>(0);

    /**
     * Instantiates a new index manager.
     */
    private IndexManager() {
    }

    /**
     * Returns the instance of a requested IndexConnection by the IndexName. This method implements a pooling mechanism
     * for these Object ensuring that: - onyl one IndexConnection is used at a time - there no more than the max. # of
     * IndexConections per Index alive.
     * 
     * @param indexName
     *          Index name.
     * 
     * @return Index connection.
     * 
     * @throws IndexException
     *           Unable to get instance of index connection.
     */
    public static IndexConnection getInstance(final String indexName) throws IndexException {
        final Log log = LogFactory.getLog(IndexManager.class);

        if (s_cleaner != null) {
            ; // remove compiler warning for cleaning thread.
        }
        IndexConnection indexConnection = null;
        DIndex dIndex = null;
        try {
            dIndex = DataDictionaryController.getIndex(indexName);
        } catch (final DataDictionaryException e) {
            throw new IndexException(e.getMessage());
        }
        if (dIndex == null) {
            throw new IndexException("index not in data dictionary [" + indexName + "]");
        }

        final DConnection dConnection = dIndex.getConnection();
        IndexConnectionUsage indexConUsage = null;
        IndexUsage iu = null;

        while (indexConnection == null) {

            iu = getIndexUsage(indexName);

            synchronized (iu) {
                // check if iu in SINGLE_USAGE
                if (iu._usage != Usage.Multi) {
                    throw new IndexSingleUseException("index is not in multi use mode [" + indexName + "]");
                }

                // try to find iu that is not at work
                final Iterator<IndexConnectionUsage> it = iu._indexConnectionUsages.iterator();
                while (it.hasNext()) {
                    indexConUsage = it.next();
                    if (!indexConUsage._atWork) {
                        indexConnection = indexConUsage._indexConnection;
                        indexConUsage._atWork = true;
                        break;
                    }
                }

                // no available iu exist, create new if not exceeds max
                if (indexConnection == null) {
                    if (dConnection.getMaxConnections() > iu._indexConnectionUsages.size()) {
                        indexConUsage = new IndexConnectionUsage();
                        indexConUsage._atWork = true;
                        indexConUsage._idleSince = System.currentTimeMillis();
                        final Plugin plugin = PluginFactory.getPlugin();
                        indexConUsage._indexConnection = plugin.getIndexAccess().getIndexConnection(indexName);
                        indexConnection = indexConUsage._indexConnection;
                        iu._indexConnectionUsages.add(indexConUsage);
                    }
                }
            } // sync iu

            if (indexConnection == null) {
                try {
                    Thread.sleep(5);
                } catch (final Exception e) {
                    log.error("SLEEP!", e);
                }
            }
        }
        if (log.isDebugEnabled()) {
            log.debug("get index [" + indexConnection + "]");
        }
        return indexConnection;
    }

    /**
     * Release index connection. This method does not destroy a index connection.
     * {@see releaseInstance(IndexConnection, boolean}
     * 
     * @param indexConnection
     *          Index connection to release.
     */
    public static void releaseInstance(final IndexConnection indexConnection) {
        releaseInstance(indexConnection, false);
    }

    /**
     * Release index connection.
     * 
     * @param indexConnection
     *          Index connection to release.
     * @param destroy
     *          Whether to destroy the index connection.
     */
    public static void releaseInstance(final IndexConnection indexConnection, final boolean destroy) {
        final Log log = LogFactory.getLog(IndexManager.class);
        IndexConnectionUsage indexConUsage = null;
        final IndexUsage iu = getIndexUsage(indexConnection.getName());

        synchronized (iu) {
            final Iterator<IndexConnectionUsage> it = iu._indexConnectionUsages.iterator();
            while (it.hasNext()) {
                indexConUsage = it.next();
                if (indexConUsage._indexConnection == indexConnection) {
                    indexConUsage._idleSince = System.currentTimeMillis();
                    indexConUsage._atWork = false;
                    if (log.isDebugEnabled()) {
                        log.debug("index released [" + indexConnection + "]");
                    }
                    if (destroy) {
                        it.remove();
                        indexConUsage._indexConnection.close();
                        if ((iu._indexConnectionUsages.size() == 0) && (iu._usage != Usage.None)) {
                            s_indexUsages.remove(indexConnection.getName());
                        }
                        indexConUsage._indexConnection = null;
                        indexConUsage = null;
                        if (log.isDebugEnabled()) {
                            log.debug("index destroyed [" + indexConnection + "]");
                        }
                    }
                    break;
                }
            }
            iu._idleSince = System.currentTimeMillis();
        } // sync iu
    }

    /**
     * Do garbage collection on index connections.
     */
    protected static void doGarbageCollection() {
        synchronized (s_indexUsages) {
            final Iterator<IndexUsage> it = s_indexUsages.values().iterator();
            while (it.hasNext()) {
                final IndexUsage iu = it.next();
                final Iterator<IndexConnectionUsage> icus = iu._indexConnectionUsages.iterator();
                while (icus.hasNext()) {
                    IndexConnectionUsage icu = icus.next();
                    if (!icu._atWork) {
                        if ((System.currentTimeMillis() - icu._idleSince) > icu._timeOut) {
                            icus.remove();
                            icu._indexConnection.close();
                            icu = null;
                        }
                    }
                }
                if ((iu._indexConnectionUsages.size() == 0) && (iu._usage != Usage.None)) {
                    if ((System.currentTimeMillis() - iu._idleSince) > IndexUsage.TIMEOUT) {
                        it.remove();
                    }
                }
            }
        }
    }

    /**
     * Causes the IndexUsage for the given index to be removed from indexUsages.
     * 
     * @param indexName
     *          Index name.
     * 
     * @throws IndexException
     *           Unable to delete index usage.
     */
    static void deleteIndexUsage(final String indexName) throws IndexException {

        synchronized (s_indexUsages) {
            final IndexUsage iu = getIndexUsage(indexName);

            if (iu == null) {
                throw new IndexException("index does not exist [" + indexName + "]");
            }
            // added iu synchronization
            synchronized (iu) {
                if (iu._usage != Usage.None) {
                    throw new IndexException("index in use [" + indexName + "]");
                }
                s_indexUsages.remove(indexName);
            }
        }

    }

    /**
     * Deny all access to index.
     * 
     * @param indexName
     *          Index name.
     * 
     * @throws IndexException
     *           Unable to deny access to index.
     */
    static void noUse(final String indexName) throws IndexException {
        IndexUsage iu = null;

        iu = getIndexUsage(indexName);

        synchronized (iu) {
            // TODO: add correct solution of racing condition problem noUse, singleUse, etc
            if (Usage.None == iu._usage) {
                throw new IndexException(String.format("Index [%s] already in noUse state!", indexName));
            }
            iu._usage = Usage.None;
            final Iterator<IndexConnectionUsage> iter = iu._indexConnectionUsages.iterator();
            while (iter.hasNext()) {
                final IndexConnectionUsage icu = iter.next();
                final IndexConnection ic = icu._indexConnection;
                // releaseInstance(ic, true); WON'T WORK ==> fail-fast Iterator
                ic.close();
                iter.remove();
            } // while
        } // sync iu
    }

    /**
     * Get current index usage.
     * 
     * @param indexName
     *          Index name.
     * 
     * @return Index usage.
     */
    private static IndexUsage getIndexUsage(final String indexName) {
        synchronized (s_indexUsages) {
            IndexUsage iu = null;
            iu = s_indexUsages.get(indexName);
            if (iu == null) {
                iu = new IndexUsage();
                iu._indexName = indexName;
                s_indexUsages.put(indexName, iu);
            }
            return iu;
        }
    }

    /**
     * Put index in single use mode.
     * 
     * @param indexConnection
     *          Current index connection (only this connection may work.).
     * 
     * @throws IndexException
     *           Unable to set index to single use.
     */
    private static void singleUse(final IndexConnection indexConnection) throws IndexException {

        IndexUsage iu = null;

        iu = getIndexUsage(indexConnection.getName());

        synchronized (iu) {
            // TODO: add correct solution of racing condition problem noUse, singleUse, etc
            if (Usage.Multi != iu._usage) {
                throw new IndexException(
                        String.format("Index [%s] in [%s] state!", indexConnection.getName(), iu._usage));
            }
            iu._usage = Usage.Single;
            final Iterator<IndexConnectionUsage> iter = iu._indexConnectionUsages.iterator();
            while (iter.hasNext()) {
                final IndexConnectionUsage icu = iter.next();
                final IndexConnection ic = icu._indexConnection;
                if (ic != indexConnection) {
                    // releaseInstance(ic, true); WON'T WORK ==> fail-fast Iterator
                    ic.close();
                    iter.remove();
                }
            } // while
        } // sync iu
    }

    /**
     * Puts an index in multi-use mode. Indexes which are in multi-use mode can be opened by several search and/or
     * indexing processes at the same time. This method must be called whenever an index has been accessed in single-use
     * or no-use mode, since it is the only way to unlock an index, besides restarting the AnyFinder Engine.
     * 
     * @param indexName
     *          The name of the index to be put in multi-use mode
     * 
     * @throws IndexException
     *           Unable to set index in multi use mode.
     * 
     * @see noUse(String)
     * @see singleUse(String)
     */
    static void multiUse(final String indexName) throws IndexException {
        synchronized (s_indexUsages) {
            getIndexUsage(indexName)._usage = Usage.Multi;
        }
    }

    /**
     * Start burst mode for index connection.
     * 
     * @param indexConnection
     *          Current index connection.
     * 
     * @throws IndexException
     *           Unable to start burst mode.
     */
    public static void startBurstmode(final IndexConnection indexConnection) throws IndexException {
        try {
            singleUse(indexConnection);
            indexConnection.startBurstmode();
        } catch (final IndexException e) {
            throw e;
        } catch (final Exception e) {
            throw new IndexException("unable to start burstmode [" + indexConnection.getName() + "]", e);
        }
    }

    /**
     * Stop burst mode for index connection.
     * 
     * @param indexConnection
     *          Current index connection.
     * 
     * @throws IndexException
     *           Unable to stop burst mode.
     */
    public static void stopBurstmode(final IndexConnection indexConnection) throws IndexException {
        try {
            indexConnection.stopBurstmode();
        } catch (final Exception e) {
            throw new IndexException("unable to stop burstmode [" + indexConnection.getName() + "]", e);
        } finally {
            multiUse(indexConnection.getName());
        }
    }

    /**
     * Shutdown all connections.
     * 
     * @throws IndexException
     *           Unable to shutdown all connections.
     */
    public static void shutdown() throws IndexException {
        synchronized (s_indexUsages) {
            final Iterator<IndexUsage> it = s_indexUsages.values().iterator();
            while (it.hasNext()) {
                final IndexUsage iu = it.next();
                final Iterator<IndexConnectionUsage> icus = iu._indexConnectionUsages.iterator();
                while (icus.hasNext()) {
                    IndexConnectionUsage icu = icus.next();
                    icus.remove();
                    icu._indexConnection.close();
                    icu = null;
                }
            }
        }
    }

    /**
     * Checks if is index busy.
     * 
     * @param indexName
     *          the index name
     * 
     * @return true, if is index busy
     * 
     * @throws IndexException
     *           the index exception
     */
    public static boolean isIndexBusy(final String indexName) throws IndexException {
        // TODO: check this implementation. I do not understand the reason for "alreadyUsed"
        // reply: it's only a patch for isNew
        boolean alreadyUsed = true;
        synchronized (s_indexUsages) {
            alreadyUsed = s_indexUsages.containsKey(indexName);
        }
        final IndexUsage iu = getIndexUsage(indexName);
        synchronized (iu) {
            // TODO: valid isNew for IU
            if (alreadyUsed && ((System.currentTimeMillis() - iu._idleSince) < IndexUsage.TIMEOUT / 2)) {
                return true;
            }
            return false;
        }
    }

}