/*
 * The contents of this file are subject to the Mozilla Public License Version 1.1
 * (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.mozilla.org/MPL/>.
 * 
 * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
 * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
 * language governing rights and limitations under the License.
 * 
 * The Original Code is the Venice Web Communities System.
 * 
 * The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>,
 * for Silverwrist Design Studios.  Portions created by Eric J. Bowersox are
 * Copyright (C) 2003 Eric J. Bowersox/Silverwrist Design Studios.  All Rights Reserved.
 * 
 * Contributor(s): 
 */
package com.silverwrist.dynamo.index;

import java.lang.ref.*;
import java.util.*;
import org.apache.commons.collections.*;
import org.apache.log4j.Logger;
import org.w3c.dom.*;
import com.silverwrist.util.xml.*;
import com.silverwrist.dynamo.db.NamespaceCache;
import com.silverwrist.dynamo.except.*;
import com.silverwrist.dynamo.iface.*;
import com.silverwrist.dynamo.util.*;

public class IndexManagerObject
    implements NamedObject, ComponentInitialize, ComponentShutdown, IndexManager, IndexManagerConfig
{
  /*--------------------------------------------------------------------------------
   * Internal class which scans the cleanup queue
   *--------------------------------------------------------------------------------
   */

  private class CleanupQueueTask implements BackgroundTask
  {
    /*====================================================================
     * Constructor
     *====================================================================
     */

    CleanupQueueTask()
    {
    } // end constructor

    /*====================================================================
     * Implementations from itnerface BackgroundTask
     *====================================================================
     */

    public void run(ServiceProvider services)
    {
      Reference r = null;
      while ((r = m_cleanupqueue.poll())!=null)
      { // do the cleanup!
	DirectoryAutoCleanup dac = (DirectoryAutoCleanup)r;
	dac.cleanup();
	dac.clear();

      } // end while

    } // end run

  } // end class CleanupQueueTask

  /*--------------------------------------------------------------------------------
   * Static data members
   *--------------------------------------------------------------------------------
   */

  private static Logger logger = Logger.getLogger(IndexManagerObject.class);

  private static final String DEFAULT_ANALYZER = "org.apache.lucene.analysis.standard.StandardAnalyzer";

  /*--------------------------------------------------------------------------------
   * Attributes
   *--------------------------------------------------------------------------------
   */

  private String m_name;                       // name of this object
  private IndexManagerOps m_ops;               // database operations object
  private NamespaceCache m_ns_cache;           // namespace cache object
  private ComponentShutdown m_shut_runtime;    // shutdown runtime service
  private ComponentShutdown m_shut_init;       // shutdown init service
  private ComponentShutdown m_shut_task;       // shutdown background task
  private Hashtable m_resolvers;               // resolvers list
  private ReferenceMap m_indexcache;           // index cache
  private ReferenceQueue m_cleanupqueue;       // cleanup object queue

  /*--------------------------------------------------------------------------------
   * Constructor
   *--------------------------------------------------------------------------------
   */

  public IndexManagerObject()
  {
    m_resolvers = new Hashtable();
    m_indexcache = new ReferenceMap(ReferenceMap.HARD,ReferenceMap.SOFT);
    m_cleanupqueue = new ReferenceQueue();

  } // end constructor

  /*--------------------------------------------------------------------------------
   * Implementations from interface NamedObject
   *--------------------------------------------------------------------------------
   */

  public String getName()
  {
    return m_name;

  } // end getName

  /*--------------------------------------------------------------------------------
   * Implementations from interface ComponentInitialize
   *--------------------------------------------------------------------------------
   */

  /**
   * Initialize the component.
   *
   * @param config_root Pointer to the section of the Dynamo XML configuration file that configures this
   *                    particular component.  This is to be considered "read-only" by the component.
   * @param services An implementation of {@link com.silverwrist.dynamo.iface.ServiceProvider ServiceProvider}
   *                 which provides initialization services to the component.  This will include an implementation
   *                 of {@link com.silverwrist.dynamo.iface.ObjectProvider ObjectProvider} which may be used to
   *                 get information about other objects previously initialized by the application.
   * @exception com.silverwrist.dynamo.except.ConfigException If an error is encountered in the component
   *            configuration.
   */
  public void initialize(Element config_root, ServiceProvider services) throws ConfigException
  {
    logger.info("IndexManagerObject initializing");
    XMLLoader loader = XMLLoader.get();
    String conn_name = null;
    String nscache_name = null;
    try
    { // verify the right node name
      loader.verifyNodeName(config_root,"object");

      // get the object's name
      m_name = loader.getAttribute(config_root,"name");

      // get the database configuration connection
      DOMElementHelper config_root_h = new DOMElementHelper(config_root);
      Element elt = loader.getSubElement(config_root_h,"database");
      conn_name = loader.getAttribute(elt,"connection");
      nscache_name = loader.getAttribute(elt,"namespaces");

    } // end try
    catch (XMLLoadException e)
    { // error loading XML config data
      throw new ConfigException(e);

    } // end catch

    // Get the database connection pool and namespace cache.
    DBConnectionPool pool = GetObjectUtils.getDatabaseConnection(services,conn_name);
    m_ns_cache =
        (NamespaceCache)(GetObjectUtils.getDynamoComponent(services,NamespaceCache.class,nscache_name));

    // Get the database access object.
    m_ops = IndexManagerOps.get(pool);

    // Hook up IndexManager as a runtime service.
    SingletonServiceProvider ssp = new SingletonServiceProvider("IndexManagerObject",IndexManager.class,this);
    HookServiceProviders hooker = (HookServiceProviders)(services.queryService(HookServiceProviders.class));
    m_shut_runtime = hooker.hookRuntimeServiceProvider(ssp);

    // Hook up IndexManagerConfig as an initialization service.
    ssp = new SingletonServiceProvider("IndexManagerObject",IndexManagerConfig.class,this);
    m_shut_init = hooker.hookInitServiceProvider(ssp);

    // Set up a background task to poll the reference queue.
    BackgroundScheduler sched = (BackgroundScheduler)(services.queryService(BackgroundScheduler.class));
    m_shut_task = sched.runTaskPeriodic(new CleanupQueueTask(),5000,5000);

  } // end initialize

  /*--------------------------------------------------------------------------------
   * Implementations from interface ComponentShutdown
   *--------------------------------------------------------------------------------
   */

  public void shutdown()
  {
    m_shut_task.shutdown();
    m_shut_task = null;
    m_shut_init.shutdown();
    m_shut_init = null;
    m_shut_runtime.shutdown();
    m_shut_runtime = null;
    m_ns_cache = null;
    m_ops.dispose();
    m_ops = null;

  } // end shutdown

  /*--------------------------------------------------------------------------------
   * Implementations from interface IndexManager
   *--------------------------------------------------------------------------------
   */

  public Object resolveObjectReference(String namespace, String name, String tag) throws IndexException
  {
    return getResolver(namespace,name).resolveObject(tag);

  } // end resolveObjectReference

  public synchronized IndexService openIndex(String namespace, String name) throws DatabaseException, IndexException
  {
    PropertyKey key = new PropertyKey(m_ns_cache.namespaceNameToId(namespace),name);
    IndexServiceImpl rc = (IndexServiceImpl)(m_indexcache.get(key));
    if (rc==null)
    { // look up the index of the particular index service
      int ndx = m_ops.getIndexID(key.getNamespaceID(),key.getName());
      if (ndx==-1)
      { // the index is not present - bail out
	IndexException ie = new IndexException(IndexManagerObject.class,"IndexMessages","no.such.index");
	ie.setParameter(0,namespace);
	ie.setParameter(1,name);
	throw ie;

      } // end if

      rc = new IndexServiceImpl(this,new QualifiedNameKey(namespace,name),ndx,m_ops.getIndexOps(),
				m_cleanupqueue,false);
      m_indexcache.put(key,rc);

    } // end if

    return rc;

  } // end openIndex

  /*--------------------------------------------------------------------------------
   * Implementations from interface IndexManagerConfig
   *--------------------------------------------------------------------------------
   */

  public ComponentShutdown registerResolver(String namespace, String name, IndexedObjectResolver res)
  {
    QualifiedNameKey key = new QualifiedNameKey(namespace,name);
    m_resolvers.put(key,res);
    return new ShutdownHashtableRemove(m_resolvers,key);

  } // end registerResolver

  public void createIndex(String namespace, String name, String analyzer) throws DatabaseException, IndexException
  {
    if (analyzer==null)
      analyzer = DEFAULT_ANALYZER;
    IndexServiceImpl.createAnalyzer(analyzer);
    PropertyKey key = new PropertyKey(m_ns_cache.namespaceNameToId(namespace),name);
    synchronized (this)
    { // call down to database to create index
      int ndx = m_ops.createIndexID(key.getNamespaceID(),key.getName(),analyzer);
      if (ndx==-1)
      { // this index already exists
	IndexException ie = new IndexException(IndexManagerObject.class,"IndexMessages","index.exists");
	ie.setParameter(0,namespace);
	ie.setParameter(1,name);
	throw ie;

      } // end if

      // now create the index service object and put it in the cache
      IndexServiceImpl tmp = new IndexServiceImpl(this,new QualifiedNameKey(namespace,name),ndx,m_ops.getIndexOps(),
						  m_cleanupqueue,true);
      m_indexcache.put(key,tmp);

    } // end synchronized block

  } // end createIndex

  public void deleteIndex(String namespace, String name) throws DatabaseException, IndexException
  {
    PropertyKey key = new PropertyKey(m_ns_cache.namespaceNameToId(namespace),name);
    synchronized (this)
    { // see what the index ID is first
      int ndx = m_ops.getIndexID(key.getNamespaceID(),key.getName());
      if (ndx==-1)
      { // the index is not present - bail out
	IndexException ie = new IndexException(IndexManagerObject.class,"IndexMessages","no.such.index");
	ie.setParameter(0,namespace);
	ie.setParameter(1,name);
	throw ie;

      } // end if

      // is there an IndexServiceImpl for this index?  if so, take it down
      IndexServiceImpl tmp = (IndexServiceImpl)(m_indexcache.get(key));
      if (tmp!=null)
      { // abandon this index
	m_indexcache.remove(key);
	tmp.abandon();

      } // end if

      // now blow away the database data
      m_ops.deleteIndex(ndx);

    } // end synchronized block

  } // end deleteIndex

  /*--------------------------------------------------------------------------------
   * External operations
   *--------------------------------------------------------------------------------
   */

  IndexedObjectResolver getResolver(String namespace, String name) throws IndexException
  {
    QualifiedNameKey key = new QualifiedNameKey(namespace,name);
    IndexedObjectResolver res = (IndexedObjectResolver)(m_resolvers.get(key));
    if (res==null)
    { // the resolver could not be found
      IndexException e = new IndexException(IndexManagerObject.class,"IndexMessages","no.such.resolver");
      e.setParameter(0,namespace);
      e.setParameter(1,name);
      throw e;

    } // end if

    return res;

  } // end getResolver

} // end class IndexManagerObject

