Package org.springmodules.lucene.index.object.database

Source Code of org.springmodules.lucene.index.object.database.DefaultDatabaseIndexer$IndexingMappingQuery

/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache 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.apache.org/licenses/LICENSE-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.springmodules.lucene.index.object.database;

import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.sql.DataSource;

import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexWriter;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.object.MappingSqlQuery;
import org.springmodules.lucene.index.LuceneIndexAccessException;
import org.springmodules.lucene.index.LuceneIndexingException;
import org.springmodules.lucene.index.document.handler.DocumentHandler;
import org.springmodules.lucene.index.document.handler.database.SqlDocumentHandler;
import org.springmodules.lucene.index.document.handler.database.SqlRequest;
import org.springmodules.lucene.index.factory.IndexFactory;
import org.springmodules.lucene.index.factory.IndexWriterFactoryUtils;
import org.springmodules.lucene.index.factory.LuceneIndexWriter;
import org.springmodules.lucene.index.object.AbstractIndexer;

/**
* <b>This is the central class in the lucene database indexing package.</b>
* It simplifies the use of lucene to index a database specifying the requests
* to execute and how to index the corresponding rows.
* It helps to avoid common errors and to manage these resource in a flexible
* manner.
* It executes core Lucene workflow, leaving application code to focus on
* the way to create Lucene documents from a row for a request.
*
* <p>This class is based on the IndexFactory abstraction which is a
* factory to create IndexWriter for the configured
* Directory. For the execution and the indexation of the corresponding
* data, the indexer uses the same IndexWriter. It calls the IndexWriterFactoryUtils
* class to eventually release it. So the indexer doesn't need to always
* hold resources during the indexation of every requests and
* this avoids some locking problems on the index. You can too apply
* different strategies for managing index resources.
*
* <p>Can be used within a service implementation via direct instantiation
* with a IndexFactory reference, or get prepared in an application context
* and given to services as bean reference. Note: The IndexFactory should
* always be configured as a bean in the application context, in the first case
* given to the service directly, in the second case to the prepared template.
*
* @author Thierry Templier
* @see org.springmodules.lucene.index.object.AbstractIndexer
* @see org.springmodules.lucene.index.factory.IndexFactory
* @see org.springmodules.lucene.index.support.database.SqlDocumentHandler
* @see org.springmodules.lucene.index.object.database.DatabaseIndexingListener
* @see org.springmodules.lucene.index.factory.IndexWriterFactoryUtils#getIndexWriter(IndexFactory)
* @see org.springmodules.lucene.index.factory.IndexWriterFactoryUtils#releaseIndexWriter(IndexFactory, IndexWriter)
*/
public class DefaultDatabaseIndexer extends AbstractIndexer implements DatabaseIndexer {
  private Map requestDocumentHandlers;
  private List listeners;

  /**
   * Construct a new DatabaseIndexer, given an IndexFactory to obtain IndexWriter.
   *
   * @param indexFactory IndexFactory to obtain IndexWriter
   */
  public DefaultDatabaseIndexer(IndexFactory indexFactory) {
    setIndexFactory(indexFactory);
    requestDocumentHandlers=new HashMap();
    listeners=new ArrayList();
    registerDefautHandlers();
  }

  /**
   * This method specifies the default handlers to automatically
   * register when the indexer is instantiated.
   *
   * <p>This method is empty but you can overwrite it to specify
   * your default handlers.
   */
  protected void registerDefautHandlers() {
  }

  /**
   * This method is used to register a request to execute and an handler
   * to specify how to index the rows corresponding to the request.
   *
   * <p>The request is specify with an instance of the SqlRequest class
   * which contains the sql requests, the parameter types and values.
   * The implementation of the SqlDocumentHandler class defines a callback
   * method which will be called for every row to index its data.
   *
   * @param sqlRequest the request to execute
   * @param handler the handler to index the rows
   */
  public void registerDocumentHandler(SqlRequest sqlRequest, SqlDocumentHandler handler) {
    if( sqlRequest!=null && handler!=null ) {
      requestDocumentHandlers.put(sqlRequest, handler);
    }
  }

  /**
   * This method is used to unregister a request.
   *
   * @param sqlRequest the request to execute
   */
  public void unregisterDocumentHandler(SqlRequest sqlRequest) {
    if( sqlRequest!=null ) {
      requestDocumentHandlers.remove(sqlRequest);
    }
  }

  /**
   * This method is used to get the SqlDocumentHandler implementation
   * corresponding to SqlRequest passed as parameter.
   *
   * @param sqlRequest the request to execute
   * @return the handler to index the rows
   */
  public SqlDocumentHandler getDocumentHandler(SqlRequest sqlRequest) {
    if( sqlRequest!=null ) {
      return (SqlDocumentHandler)requestDocumentHandlers.get(sqlRequest);
    } else {
      return null;
    }
  }

  /**
   * This method returns all the registred requests and their
   * corresponding handlers.
   *
   * @return a map containing all the registred requests and their
   * corresponding handlers
   */
  public Map getDocumentHandlers() {
    return requestDocumentHandlers;
  }

  /**
   * This method is used to add a listener to be notified during the
   * indexing execution.
   *
   * @param listener the listener to add
   */
  public void addListener(DatabaseIndexingListener listener) {
    if( listener!=null ) {
      listeners.add(listener);
    }
  }

  /**
   * This method is used to remove a specified listener.
   *
   * @param listener the listener to remove
   */
  public void removeListener(DatabaseIndexingListener listener) {
    if( listener!=null ) {
      listeners.remove(listener);
    }
  }

  /**
   * This method is used to get the list of listeners to notify
   * during the indexing execution.
   *
   * @return the list of listeners to notify
   */
  public List getListeners() {
    return listeners;
  }

  /**
   * This method is used to fire the "on before request" event to
   * every listeners.
   *
   * <p>This event will be fired before the request execution and
   * the indexing of rows, even if there is an sql or indexing
   * error.
   *
   * @param request the request which will be executed
   */
  protected void fireListenersOnBeforeRequest(SqlRequest request) {
    for(Iterator i=listeners.iterator();i.hasNext();) {
      DatabaseIndexingListener listener=(DatabaseIndexingListener)i.next();
      listener.beforeIndexingRequest(request);
    }
  }

  /**
   * This method is used to fire the "on after request" event to
   * every listeners.
   *
   * <p>This event will be fired after the request execution and
   * the indexing of rows. It will not happen if there is an sql
   * or indexing error.
   *
   * @param request the request which has been executed
   */
  protected void fireListenersOnAfterRequest(SqlRequest request) {
    for(Iterator i=listeners.iterator();i.hasNext();) {
      DatabaseIndexingListener listener=(DatabaseIndexingListener)i.next();
      listener.afterIndexingRequest(request);
    }
  }

  /**
   * This method is used to fire the "on error request" event to
   * every listeners.
   *
   * <p>This event will be fired if there is an sql or indexing error.
   *
   * @param request the request which has been executed
   */
  protected void fireListenersOnErrorRequest(SqlRequest request,Exception ex) {
    for(Iterator i=listeners.iterator();i.hasNext();) {
      DatabaseIndexingListener listener=(DatabaseIndexingListener)i.next();
      listener.onErrorIndexingRequest(request,ex);
    }
  }

  /**
   * This method executes the sql request on a specified datasource with or
   * with sql parameters. It is based on the Spring JDBC framework and uses
   * a sub class of MappingSqlQuery.
   *
   * <p>It is based on the IndexingMappingQuery class which delegates its mapRow
   * method to the getDocument of a SqlDocumentHandler implementation. The
   *
   * @param dataSource the datasource to use
   * @param request the request to execute
   * @param handler the handler to use to index the rows
   * @return the list of Lucene documents add to the index
   * @see IndexingMappingQuery
   */
  private List indexResultSql(DataSource dataSource,SqlRequest request,SqlDocumentHandler handler) {
    IndexingMappingQuery query=new IndexingMappingQuery(dataSource,request,handler);
    if( request.getParams()!=null ) {
      return query.execute(request.getParams());
    } else {
      return query.execute();
    }
  }

  /**
   * This method adds a list of Lucene documents to an index using an
   * corresponding IndexWriter instance.
   * @param writer the IndexWriter instance to use
   * @param documents the list of documents to add
   */
  private void addDocumentsInIndex(LuceneIndexWriter writer, List documents) throws IOException{
    for(Iterator i=documents.iterator(); i.hasNext();) {
    Document document = (Document)i.next();
      if( document!=null ) {
        writer.addDocument(document);
      }
    }
  }

  /**
   * This method executes the request and adds the result of indexing
   * of rows in the index. It gets an IndexWriter instance from the
   * configured IndexFactory and releases it if necessary. It manages
   * too both exceptions thrown during the request execution and the
   * indexing of rows.
   *
   * <p>Before the return of the method, it optimizes too the index
   * if the value of the optimizeIndex parameter is true.
   *
   * @param dataSource the datasource to use
   * @param request the request to execute
   * @param handler the handler to use to index the rows
   * @param optimizeIndex if the index must be optimized after
   * the request indexing
   * @see IndexWriterFactoryUtils#getIndexWriter(IndexFactory)
   * @see IndexWriterFactoryUtils#releaseIndexWriter(IndexFactory, IndexWriter)
   * @see #fireListenersOnBeforeRequest(SqlRequest)
   * @see #fireListenersOnAfterRequest(SqlRequest)
   * @see #fireListenersOnErrorRequest(SqlRequest, Exception)
   */
  private void doHandleRequest(LuceneIndexWriter writer, DataSource dataSource,
                  SqlRequest request, SqlDocumentHandler handler) {
    try {
      fireListenersOnBeforeRequest(request);
      List documents = indexResultSql(dataSource, request, handler);
      if( documents!=null ) {
        addDocumentsInIndex(writer, documents);
      }
      fireListenersOnAfterRequest(request);
    } catch(DataAccessException ex) {
      logger.error("Error during indexing the request", ex);
      fireListenersOnErrorRequest(request, ex);
    } catch(IOException ex) {
      logger.error("Error during indexing the request", ex);
      fireListenersOnErrorRequest(request, ex);
    }
  }

  /**
   * This method is the entry point to index a database using the specified
   * datasource. It uses the registred requests and their corresponding handlers.
   *
   * <p>In this case, the index will not be optimized.
   * 
   * @param dataSource the datasource to use
   * @see #index(DataSource, boolean)
   */
  public void index(DataSource dataSource) {
    index(dataSource, false);
  }

  /**
   * This method is the entry point to index a database using the specified
   * datasource. It uses the registred requests and their corresponding handlers.
   *
   * <p>In this case, the index will be optimized at the end
   * if the value of the optimizeIndex parameter is true.
   *
   * <p>If there is an error during executing a request or indexing rows,
   * the other requests will be executed. However the error will notify to
   * specified listeners.
   * 
   * @param dataSource the datasource to use
   * @param optimizeIndex if the index must be optimized after
   * the request indexing
   * @see #doHandleRequest(DataSource, SqlRequest, SqlDocumentHandler, boolean)
   */
  public void index(DataSource dataSource, boolean optimizeIndex) {
    LuceneIndexWriter writer = IndexWriterFactoryUtils.getIndexWriter(getIndexFactory());
    try {
      Set requests = requestDocumentHandlers.keySet();
      for(Iterator i=requests.iterator(); i.hasNext();) {
        SqlRequest request = (SqlRequest)i.next();
        SqlDocumentHandler handler = (SqlDocumentHandler)requestDocumentHandlers.get(request);
        doHandleRequest(writer, dataSource, request, handler);
      }
      //Optimize the index
      if( optimizeIndex ) {
        writer.optimize();
      }
    } catch(Exception ex) {
      ex.printStackTrace();
      logger.error("Error during indexing the datasource", ex);
      throw new LuceneIndexAccessException("Error during indexing the datasource", ex);
    } catch(Throwable t) {
      t.printStackTrace();
    } finally {
      IndexWriterFactoryUtils.releaseIndexWriter(getIndexFactory(), writer);
    }
  }

  /**
   * This is the sub class MappingSqlQuery used to delegate the mapRow callback
   * to the getDocument of the specified SqlDocumentHandler for each result row
   * of the request.
   */
  private static class IndexingMappingQuery extends MappingSqlQuery {

    private SqlRequest request;
    private DocumentHandler handler;

    public IndexingMappingQuery(DataSource ds, SqlRequest request, DocumentHandler handler) {
      super(ds, request.getSql());
      this.request = request;
      this.handler = handler;
      int[] types = request.getTypes();
      if( types!=null ) {
        for(int cpt=0; cpt<types.length; cpt++) {
          super.declareParameter(new SqlParameter(types[cpt]));
        }
      }
      compile();
    }

    public Object mapRow(ResultSet rs, int rowNumber) throws SQLException {
      try {
        return handler.getDocument(request.getDescription(), rs);
      } catch (SQLException ex) {
        throw ex;
      } catch (Exception ex) {
        throw new LuceneIndexingException("Error during the indexing of the ResultSet.", ex);
      }
    }
  }
}
TOP

Related Classes of org.springmodules.lucene.index.object.database.DefaultDatabaseIndexer$IndexingMappingQuery

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.