[infinispan-commits] Infinispan SVN: r502 - branches/ISPN-32/query/src/main/java/org/infinispan/query.

infinispan-commits at lists.jboss.org infinispan-commits at lists.jboss.org
Mon Jun 29 07:14:02 EDT 2009


Author: navssurtani
Date: 2009-06-29 07:14:01 -0400 (Mon, 29 Jun 2009)
New Revision: 502

Added:
   branches/ISPN-32/query/src/main/java/org/infinispan/query/CacheEntityId.java
   branches/ISPN-32/query/src/main/java/org/infinispan/query/CacheEntityLoader.java
   branches/ISPN-32/query/src/main/java/org/infinispan/query/CacheQuery.java
   branches/ISPN-32/query/src/main/java/org/infinispan/query/CacheQueryImpl.java
   branches/ISPN-32/query/src/main/java/org/infinispan/query/EntityId.java
   branches/ISPN-32/query/src/main/java/org/infinispan/query/IndexSearcherCloser.java
   branches/ISPN-32/query/src/main/java/org/infinispan/query/InvalidFqnException.java
   branches/ISPN-32/query/src/main/java/org/infinispan/query/InvalidKeyException.java
   branches/ISPN-32/query/src/main/java/org/infinispan/query/LazyQueryResultIterator.java
   branches/ISPN-32/query/src/main/java/org/infinispan/query/QueryResultIterator.java
   branches/ISPN-32/query/src/main/java/org/infinispan/query/QueryResultIteratorImpl.java
   branches/ISPN-32/query/src/main/java/org/infinispan/query/SearchableCache.java
   branches/ISPN-32/query/src/main/java/org/infinispan/query/SearchableCacheConfiguration.java
   branches/ISPN-32/query/src/main/java/org/infinispan/query/SearchableCacheFactory.java
   branches/ISPN-32/query/src/main/java/org/infinispan/query/SearchableCacheImpl.java
   branches/ISPN-32/query/src/main/java/org/infinispan/query/SearchableCoreListener.java
   branches/ISPN-32/query/src/main/java/org/infinispan/query/SearchableCoreRemover.java
   branches/ISPN-32/query/src/main/java/org/infinispan/query/TransactionalEventTransactionContext.java
   branches/ISPN-32/query/src/main/java/org/infinispan/query/Transformer.java
   branches/ISPN-32/query/src/main/java/org/infinispan/query/Version.java
Log:
[ISPN-32] [Integrate Query API] Added classes into src/main

Added: branches/ISPN-32/query/src/main/java/org/infinispan/query/CacheEntityId.java
===================================================================
--- branches/ISPN-32/query/src/main/java/org/infinispan/query/CacheEntityId.java	                        (rev 0)
+++ branches/ISPN-32/query/src/main/java/org/infinispan/query/CacheEntityId.java	2009-06-29 11:14:01 UTC (rev 502)
@@ -0,0 +1,96 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright ${year}, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.cache.search;
+
+import org.jboss.cache.Fqn;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ *
+ * This class is used to get fqns, keys and documentId's by calling methods on {@link org.jboss.cache.search.Transformer}
+ * <p/>
+ * @author Navin Surtani (<a href="mailto:nsurtani at redhat.com">nsurtani at redhat.com</a>)
+ */
+public class CacheEntityId extends PojoEntityId
+{
+   private static final Log log = LogFactory.getLog(CacheEntityId.class);
+
+   String key;
+
+   public CacheEntityId(String documentId)
+   {
+      super(documentId);
+//      if (log.isDebugEnabled()) log.debug("Called CEI constructor using documentID constructor. DocId is " + documentId);
+   }
+
+   public CacheEntityId(Fqn fqn, String key) throws InvalidKeyException
+   {
+      super(fqn);
+//      if (log.isDebugEnabled()) log.debug("Called CEI constructor using fqn, key constructor. Fqn & key are " + fqn + key);
+      if(key == null) throw new NullPointerException("Key is null");
+      this.key = key;
+      this.documentId = Transformer.generateId(fqn, key);
+   }
+
+   /**
+    * Gets the key from the instance of CacheEntityId.
+    *
+    * @return Key from the instance of CacheEntityId.
+    */
+
+   public String getKey()
+   {
+      if (key != null) return key;
+      if (documentId != null)
+      {
+         key = Transformer.getKey(documentId);
+         return key;
+      }
+
+      throw new IllegalArgumentException("At least key or documentId must be set to call this method");
+   }
+
+   /**
+    * Gets a documentId String from an Fqn and key combination.
+    *
+    * @return documentId String.
+    */
+
+   public String getDocumentId() throws InvalidKeyException
+   {
+      if (key == null || fqn == null)
+      {
+         throw new IllegalArgumentException("Either your key or fqn is null. Please check again.");
+      }
+
+      return Transformer.generateId(fqn, key);
+   }
+
+   public Fqn getFqn()
+   {
+//    if (log.isWarnEnabled()) log.warn("getFqn() called with fqn of " + fqn + " so my documentId should be " + documentId);
+
+    return Transformer.getFqn(documentId);
+   }
+}

Added: branches/ISPN-32/query/src/main/java/org/infinispan/query/CacheEntityLoader.java
===================================================================
--- branches/ISPN-32/query/src/main/java/org/infinispan/query/CacheEntityLoader.java	                        (rev 0)
+++ branches/ISPN-32/query/src/main/java/org/infinispan/query/CacheEntityLoader.java	2009-06-29 11:14:01 UTC (rev 502)
@@ -0,0 +1,85 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright ${year}, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.cache.search;
+
+import org.jboss.cache.Cache;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * Class that is used to load objects from a list of CacheEntityId ids.
+ * <p/>
+ * @author Navin Surtani (<a href="mailto:nsurtani at redhat.com">nsurtani at redhat.com</a>)
+ */
+public class CacheEntityLoader
+{
+   Cache cache;
+   private static final Log log = LogFactory.getLog(CacheEntityLoader.class);
+
+   public CacheEntityLoader(Cache cache)
+   {
+      this.cache = cache;
+   }
+
+   /**
+    * Takes a list of entity ids and gets them from the cache.
+    * @param ids list of cache entity IDs.  Cannot be null.
+    * @return List of objects loaded from the cache.  The list returned will be exactly the same size as the ids list passed in.
+    * @throws NullPointerException if ids is null.
+    */
+   public List<Object> load(List<EntityId> ids)
+   {
+      if (ids == null) throw new NullPointerException("ids are null");
+      List<Object> retVal = new ArrayList<Object>(ids.size());
+
+      for (EntityId id: ids)
+      {
+         retVal.add( loadFromCache(id) );
+         if(log.isTraceEnabled()) log.trace("Created list of return values. Size is  " + retVal.size() );         
+      }
+      return retVal;
+   }
+
+   protected Object loadFromCache(EntityId id)
+   {
+      CacheEntityId cei = (CacheEntityId) id;
+      return cache.get(cei.getFqn(), cei.getKey());
+   }
+
+   /**
+    * Takes a list of entity ids and gets them from the cache.
+    * @param id cache entity id to look up in the cache.
+    * @return the object from the cache, or null if one cannot be found.
+    * @throws NullPointerException if ids is null.
+    */
+   public Object load(EntityId id)
+   {
+      if (id == null) throw new NullPointerException("id is null");
+      return loadFromCache(id);
+   }
+
+
+}

Added: branches/ISPN-32/query/src/main/java/org/infinispan/query/CacheQuery.java
===================================================================
--- branches/ISPN-32/query/src/main/java/org/infinispan/query/CacheQuery.java	                        (rev 0)
+++ branches/ISPN-32/query/src/main/java/org/infinispan/query/CacheQuery.java	2009-06-29 11:14:01 UTC (rev 502)
@@ -0,0 +1,147 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright ${year}, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.cache.search;
+
+import org.apache.lucene.search.Sort;
+import org.apache.lucene.search.Filter;
+import org.hibernate.search.FullTextFilter;
+
+import java.util.List;
+
+/**
+ * A cache-query is what will be returned when the createQuery() method is run. This object can have methods such
+ * as list, setFirstResult,setMaxResults, setFetchSize, getResultSize and setSort.
+ *
+ * <p/>
+ * @author Manik Surtani (<a href="mailto:manik at jboss.org">manik at jboss.org</a>)
+ * @author Navin Surtani (<a href="mailto:nsurtani at redhat.com">nsurtani at redhat.com</a>)
+ * @see org.jboss.cache.search.SearchableCache#createQuery(org.apache.lucene.search.Query) 
+ */
+public interface CacheQuery extends Iterable
+{
+   /**
+    * Returns the results of a search as a list.
+    *
+    * @return list of objects that were found from the search.
+    */
+
+   List<Object> list();
+
+   /**
+    * Returns the results of a search as a {@link QueryResultIterator} with a given
+    * integer parameter - the fetchSize.
+    *
+    * @param fetchSize integer to be given to the implementation constructor.
+    * @return a QueryResultIterator which can be used to iterate through the results that were found.
+    */
+
+   QueryResultIterator iterator(int fetchSize);
+
+   /**
+    * Returns the results of a search as a {@link QueryResultIterator}. This calls {@link CacheQuery#iterator(int fetchSize)}
+    * but uses a default fetchSize of 1. 
+    *
+    * @return a QueryResultIterator which can be used to iterate through the results that were found.
+    */
+
+   QueryResultIterator iterator();
+
+   /**
+    * Lazily loads the results from the Query as a {@link org.jboss.cache.search.QueryResultIterator} with a given
+    * integer parameter - the fetchSize.
+    *
+    * @param fetchSize integer to be passed into the lazy implementation of {@link org.jboss.cache.search.QueryResultIterator}
+    * @return a QueryResultIterator which can be used to <B>lazily</B> iterate through results.
+    */
+
+   QueryResultIterator lazyIterator(int fetchSize);
+
+   /**
+    * Calls the {@link CacheQuery#lazyIterator(int fetchSize)} method but passes in a default 1 as a parameter.
+    *
+    * @return a QueryResultIterator which can be used to <B>lazily</B> iterate through results.
+    */
+
+
+   QueryResultIterator lazyIterator();
+
+   /**
+    * Sets a result with a given index to the first result.
+    *
+    * @param index of result to be set to the first.
+    * @throws IllegalArgumentException if the index given is less than zero.
+    */
+
+   void setFirstResult(int index);
+
+   /**
+    * Sets the maximum number of results to the number passed in as a parameter.
+    *
+    * @param numResults that are to be set to the maxResults.
+    */
+
+   void setMaxResults(int numResults);
+
+   /**
+    * Gets the integer number of results.
+    *
+    * @return integer number of results.
+    */
+
+   int getResultSize();
+
+   /**
+    * Allows lucene to sort the results. Integers are sorted in descending order.
+    *
+    * @param s - lucene sort object
+    */
+
+   void setSort(Sort s);
+
+   /**
+    * Enable a given filter by its name.
+    *
+    * @param name of filter.
+    * @return a FullTextFilter object.
+    */
+   public FullTextFilter enableFullTextFilter(String name);
+
+
+   /**
+    * Disable a given filter by its name.
+    *
+    * @param name of filter.
+    */
+   public void disableFullTextFilter(String name);
+
+   /**
+    * Allows lucene to filter the results.
+    *
+    * @param f - lucene filter
+    */
+
+   public void setFilter(Filter f);
+
+
+
+}

Added: branches/ISPN-32/query/src/main/java/org/infinispan/query/CacheQueryImpl.java
===================================================================
--- branches/ISPN-32/query/src/main/java/org/infinispan/query/CacheQueryImpl.java	                        (rev 0)
+++ branches/ISPN-32/query/src/main/java/org/infinispan/query/CacheQueryImpl.java	2009-06-29 11:14:01 UTC (rev 502)
@@ -0,0 +1,801 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright ${year}, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.cache.search;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.*;
+import org.hibernate.HibernateException;
+import org.hibernate.search.FullTextFilter;
+import org.hibernate.search.SearchException;
+import org.hibernate.search.reader.ReaderProvider;
+import static org.hibernate.search.reader.ReaderProviderHelper.getIndexReaders;
+import static org.hibernate.search.util.FilterCacheModeTypeHelper.cacheInstance;
+import static org.hibernate.search.util.FilterCacheModeTypeHelper.cacheResults;
+import org.hibernate.search.engine.*;
+import org.hibernate.search.filter.ChainedFilter;
+import org.hibernate.search.filter.FilterKey;
+import org.hibernate.search.filter.StandardFilterKey;
+import org.hibernate.search.query.FullTextFilterImpl;
+import org.hibernate.search.query.QueryHits;
+import org.hibernate.search.store.DirectoryProvider;
+import org.hibernate.transform.ResultTransformer;
+import org.jboss.cache.Cache;
+import org.jboss.cache.pojo.PojoCache;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Implementation class of the CacheQuery interface.
+ * <p/>
+ *
+ * @author Navin Surtani (<a href="mailto:nsurtani at redhat.com">nsurtani at redhat.com</a>)
+ */
+public class CacheQueryImpl implements CacheQuery
+{
+   //   private Cache cache; - Removed on 11/07/2008, cache is assigned but never used. Hence removed.
+   private Sort sort;
+   private Filter filter;
+   private Map<String, FullTextFilterImpl> filterDefinitions;
+   private Integer firstResult;
+   private Integer resultSize;
+   private Integer maxResults;
+   private static final Log log = LogFactory.getLog(CacheQueryImpl.class);
+   private boolean needClassFilterClause;
+   private String[] indexProjection;
+   private ResultTransformer resultTransformer;
+   CacheEntityLoader entityLoader;
+   private Set<Class<?>> classesAndSubclasses;
+   private Set<String> idFieldNames;
+   private boolean allowFieldSelectionInProjection = true;
+
+   // Protected fields for CacheQueryPojoImpl to see.
+   protected Query luceneQuery;
+   protected SearchFactoryImplementor searchFactory;
+   protected Set<Class<?>> targetedEntities;
+   protected Cache cache;
+   protected Class[] classes;
+
+
+
+
+
+   public CacheQueryImpl(Query luceneQuery, SearchFactoryImplementor searchFactory, Cache cache, Class... classes)
+   {
+      this.luceneQuery = luceneQuery;
+      this.cache = cache;
+      entityLoader = new CacheEntityLoader(cache);
+      this.searchFactory = searchFactory;
+      this.targetedEntities = this.searchFactory.getIndexedTypesPolymorphic(classes);
+      this.classes = classes;
+   }
+
+
+   /**
+    * Takes in a lucene filter and sets it to the filter field in the class.
+    *
+    * @param f - lucene filter
+    */
+
+   public void setFilter(Filter f)
+   {
+      filter = f;
+   }
+
+
+   /**
+    * @return The result size of the query.
+    */
+   public int getResultSize()
+   {
+      if (resultSize == null)
+      {
+         //get result size without object initialization
+         IndexSearcher searcher = buildSearcher(searchFactory);
+         if (searcher == null)
+         {
+            resultSize = 0;
+         }
+         else
+         {
+            TopDocs hits;
+            try
+            {
+               hits = getQueryHits(searcher, 1).topDocs; // Lucene enforces that at least one top doc will be retrieved.
+               resultSize = hits.totalHits;
+            }
+            catch (IOException e)
+            {
+               throw new HibernateException("Unable to query Lucene index", e);
+            }
+            finally
+            {
+               //searcher cannot be null
+               try
+               {
+                  closeSearcher(searcher, searchFactory.getReaderProvider());
+                  //searchFactoryImplementor.getReaderProvider().closeReader( searcher.getIndexReader() );
+               }
+               catch (SearchException e)
+               {
+                  log.warn("Unable to properly close searcher during lucene query: " + e);
+               }
+            }
+         }
+      }
+      return this.resultSize;
+   }
+
+   private void closeSearcher(Searcher searcher, ReaderProvider readerProvider)
+   {
+      Set<IndexReader> indexReaders = getIndexReaders(searcher);
+
+      for (IndexReader indexReader : indexReaders)
+      {
+         readerProvider.closeReader(indexReader);
+      }
+   }
+
+
+   public void setSort(Sort s)
+   {
+      sort = s;
+   }
+
+
+   /**
+    * Enable a given filter by its name.
+    *
+    * @param name of filter.
+    * @return a FullTextFilter object.
+    */
+   public FullTextFilter enableFullTextFilter(String name)
+   {
+      if (filterDefinitions == null)
+      {
+         filterDefinitions = new HashMap<String, FullTextFilterImpl>();
+      }
+      FullTextFilterImpl filterDefinition = filterDefinitions.get(name);
+      if (filterDefinition != null) return filterDefinition;
+
+      filterDefinition = new FullTextFilterImpl();
+      filterDefinition.setName(name);
+      FilterDef filterDef = searchFactory.getFilterDefinition(name);
+      if (filterDef == null)
+      {
+         throw new SearchException("Unkown @FullTextFilter: " + name);
+      }
+      filterDefinitions.put(name, filterDefinition);
+      return filterDefinition;
+   }
+
+   /**
+    * Disable a given filter by its name.
+    *
+    * @param name of filter.
+    */
+   public void disableFullTextFilter(String name)
+   {
+      filterDefinitions.remove(name);
+   }
+
+   /**
+    * Sets the the result of the given integer value to the first result.
+    *
+    * @param firstResult index to be set.
+    * @throws IllegalArgumentException if the index given is less than zero.
+    */
+   public void setFirstResult(int firstResult)
+   {
+      if (firstResult < 0)
+      {
+         throw new IllegalArgumentException("'first' pagination parameter less than 0");
+      }
+      this.firstResult = firstResult;
+
+   }
+
+   public QueryResultIterator iterator() throws HibernateException
+   {
+      return iterator(1);
+   }
+
+   public QueryResultIterator iterator(int fetchSize) throws HibernateException
+   {
+      List<EntityId> ids = null;
+      IndexSearcher searcher = buildSearcher(searchFactory);
+      if (searcher == null)
+      {
+         throw new NullPointerException("IndexSearcher instance is null.");
+      }
+
+      try
+      {
+         QueryHits queryHits = getQueryHits(searcher, calculateTopDocsRetrievalSize());
+         int first = first();
+         int max = max(first, queryHits.totalHits);
+         int size = max - first + 1 < 0 ? 0 : max - first + 1;
+         ids = new ArrayList<EntityId>(size);
+
+         DocumentExtractor extractor = new DocumentExtractor(queryHits, searchFactory, indexProjection, idFieldNames, allowFieldSelectionInProjection);
+         for (int index = first; index <= max; index++)
+         {
+            String documentId = (String) extractor.extract(index).id;
+            EntityId id = createCacheEntityId(documentId);
+            ids.add(id);
+         }
+
+      }
+      catch (IOException e)
+      {
+         throw new HibernateException("Unable to query Lucene index", e);
+
+      }
+
+      finally
+      {
+
+         IndexSearcherCloser.closeSearcher(searcher, searchFactory.getReaderProvider());
+
+      }
+
+      return new QueryResultIteratorImpl(ids, entityLoader, fetchSize);
+   }
+
+   public QueryResultIterator lazyIterator()
+   {
+      return lazyIterator(1);
+   }
+
+   public QueryResultIterator lazyIterator(int fetchSize)
+   {
+      IndexSearcher searcher = buildSearcher(searchFactory);
+
+      try
+      {
+         QueryHits queryHits = getQueryHits(searcher, calculateTopDocsRetrievalSize());
+         int first = first();
+         int max = max(first, queryHits.totalHits);
+
+         DocumentExtractor extractor = new DocumentExtractor(queryHits, searchFactory, indexProjection, idFieldNames, allowFieldSelectionInProjection);
+
+         return new LazyQueryResultIterator(extractor, entityLoader, searcher, searchFactory, first, max, fetchSize);
+      }
+      catch (IOException e)
+      {
+         try
+         {
+            IndexSearcherCloser.closeSearcher(searcher, searchFactory.getReaderProvider());
+         }
+         catch (SearchException ee)
+         {
+            //we have the initial issue already
+         }
+         throw new HibernateException("Unable to query Lucene index", e);
+
+      }
+
+   }
+
+   public List<Object> list() throws HibernateException
+   {
+      IndexSearcher searcher = buildSearcher(searchFactory);
+
+      if (searcher == null) return Collections.EMPTY_LIST;
+
+
+      try
+      {
+         QueryHits queryHits = getQueryHits(searcher, calculateTopDocsRetrievalSize());
+         if (log.isTraceEnabled()) log.trace("Number of hits are " + queryHits.totalHits);
+         int first = first();
+         int max = max(first, queryHits.totalHits);
+
+         int size = max - first + 1 < 0 ? 0 : max - first + 1;
+
+         DocumentExtractor extractor = new DocumentExtractor(queryHits, searchFactory, indexProjection, idFieldNames, allowFieldSelectionInProjection);
+
+         List<EntityId> ids = new ArrayList<EntityId>(size);
+         for (int index = first; index <= max; index++)
+         {
+            String documentId = (String) extractor.extract(index).id;
+            EntityId id = createCacheEntityId(documentId);
+            ids.add(id);
+         }
+         if (entityLoader instanceof PojoEntityLoader)
+         {
+            System.out.println("entityLoader is a PEL");
+         }
+         List<Object> list = entityLoader.load(ids);
+         if (resultTransformer == null)
+         {
+            return list;
+         }
+         else
+         {
+            return resultTransformer.transformList(list);
+
+         }
+
+      }
+      catch (IOException e)
+      {
+         throw new HibernateException("Unable to query Lucene index", e);
+
+      }
+      finally
+      {
+         IndexSearcherCloser.closeSearcher(searcher, searchFactory.getReaderProvider());
+
+      }
+
+   }
+
+   protected EntityId createCacheEntityId(String docId)
+   {
+      return new CacheEntityId(docId);
+   }
+
+
+   private int max(int first, int totalHits)
+   {
+      if (maxResults == null)
+      {
+         return totalHits - 1;
+      }
+      else
+      {
+         return maxResults + first < totalHits ?
+                 first + maxResults - 1 :
+                 totalHits - 1;
+      }
+   }
+
+   private int first()
+   {
+      return firstResult != null ?
+              firstResult :
+              0;
+   }
+
+   private QueryHits getQueryHits(Searcher searcher, Integer n) throws IOException
+   {
+      org.apache.lucene.search.Query query = filterQueryByClasses(luceneQuery);
+      buildFilters();
+      QueryHits queryHits;
+      if (n == null)
+      { // try to make sure that we get the right amount of top docs
+         queryHits = new QueryHits(searcher, query, filter, sort);
+      }
+      else
+      {
+         queryHits = new QueryHits(searcher, query, filter, sort, n);
+      }
+      resultSize = queryHits.totalHits;
+      return queryHits;
+   }
+
+   private Integer calculateTopDocsRetrievalSize()
+   {
+      if (maxResults == null)
+      {
+         return null;
+      }
+      else
+      {
+         return first() + maxResults;
+      }
+   }
+
+
+   public void setMaxResults(int maxResults)
+   {
+      if (maxResults < 0)
+      {
+         throw new IllegalArgumentException("'max' pagination parameter less than 0");
+      }
+      this.maxResults = maxResults;
+   }
+
+   private IndexSearcher buildSearcher(SearchFactoryImplementor searchFactoryImplementor)
+   {
+      Map<Class<?>, DocumentBuilderIndexedEntity<?>> builders = searchFactoryImplementor.getDocumentBuildersIndexedEntities();
+      List<DirectoryProvider> directories = new ArrayList<DirectoryProvider>();
+      Set<String> idFieldNames = new HashSet<String>();
+      Similarity searcherSimilarity = null;
+      if (targetedEntities.size() == 0)
+      {
+         // empty targetedEntities array means search over all indexed enities,
+         // but we have to make sure there is at least one
+         if (builders.isEmpty())
+         {
+            throw new HibernateException(
+                    "There are no mapped entities. Don't forget to add @Indexed to at least one class."
+            );
+         }
+
+         for (DocumentBuilderIndexedEntity builder : builders.values())
+         {
+            searcherSimilarity = checkSimilarity(searcherSimilarity, builder);
+            if (builder.getIdKeywordName() != null)
+            {
+               idFieldNames.add(builder.getIdKeywordName());
+               allowFieldSelectionInProjection = allowFieldSelectionInProjection && builder.allowFieldSelectionInProjection();
+            }
+            final DirectoryProvider[] directoryProviders = builder.getDirectoryProviderSelectionStrategy()
+                    .getDirectoryProvidersForAllShards();
+            populateDirectories(directories, directoryProviders);
+         }
+         classesAndSubclasses = null;
+      }
+      else
+      {
+         Set<Class<?>> involvedClasses = new HashSet<Class<?>>(targetedEntities.size());
+         involvedClasses.addAll(targetedEntities);
+         for (Class<?> clazz : targetedEntities)
+         {
+            DocumentBuilderIndexedEntity<?> builder = builders.get(clazz);
+            if (builder != null)
+            {
+               involvedClasses.addAll(builder.getMappedSubclasses());
+            }
+         }
+
+         for (Class clazz : involvedClasses)
+         {
+            DocumentBuilderIndexedEntity builder = builders.get(clazz);
+            if (builder == null)
+            {
+               throw new HibernateException("Not a mapped entity (don't forget to add @Indexed): " + clazz);
+            }
+            if (builder.getIdKeywordName() != null)
+            {
+               idFieldNames.add(builder.getIdKeywordName());
+               allowFieldSelectionInProjection = allowFieldSelectionInProjection && builder.allowFieldSelectionInProjection();
+            }
+            final DirectoryProvider[] directoryProviders = builder.getDirectoryProviderSelectionStrategy()
+                    .getDirectoryProvidersForAllShards();
+            searcherSimilarity = checkSimilarity(searcherSimilarity, builder);
+            populateDirectories(directories, directoryProviders);
+         }
+         this.classesAndSubclasses = involvedClasses;
+      }
+      this.idFieldNames = idFieldNames;
+
+      //compute optimization needClassFilterClause
+      //if at least one DP contains one class that is not part of the targeted classesAndSubclasses we can't optimize
+      if (classesAndSubclasses != null)
+      {
+         for (DirectoryProvider dp : directories)
+         {
+            final Set<Class<?>> classesInDirectoryProvider = searchFactoryImplementor.getClassesInDirectoryProvider(
+                    dp
+            );
+            // if a DP contains only one class, we know for sure it's part of classesAndSubclasses
+            if (classesInDirectoryProvider.size() > 1)
+            {
+               //risk of needClassFilterClause
+               for (Class clazz : classesInDirectoryProvider)
+               {
+                  if (!classesAndSubclasses.contains(clazz))
+                  {
+                     this.needClassFilterClause = true;
+                     break;
+                  }
+               }
+            }
+            if (this.needClassFilterClause)
+            {
+               break;
+            }
+         }
+      }
+
+      //set up the searcher
+      final DirectoryProvider[] directoryProviders = directories.toArray(new DirectoryProvider[directories.size()]);
+      IndexSearcher is = new IndexSearcher(
+              searchFactoryImplementor.getReaderProvider().openReader(
+                      directoryProviders
+              )
+      );
+      is.setSimilarity(searcherSimilarity);
+      return is;
+   }
+
+
+   private Similarity checkSimilarity(Similarity similarity, DocumentBuilderIndexedEntity builder)
+   {
+      if (similarity == null)
+      {
+         similarity = builder.getSimilarity();
+      }
+      else if (!similarity.getClass().equals(builder.getSimilarity().getClass()))
+      {
+         throw new HibernateException("Cannot perform search on two entities with differing Similarity implementations (" + similarity.getClass().getName() + " & " + builder.getSimilarity().getClass().getName() + ")");
+      }
+
+      return similarity;
+   }
+
+   private void populateDirectories(List<DirectoryProvider> directories, DirectoryProvider[] directoryProviders)
+
+   {
+      for (DirectoryProvider provider : directoryProviders)
+      {
+         if (!directories.contains(provider))
+         {
+            directories.add(provider);
+         }
+      }
+   }
+
+
+   private org.apache.lucene.search.Query filterQueryByClasses(org.apache.lucene.search.Query luceneQuery)
+   {
+      if (!needClassFilterClause)
+      {
+         return luceneQuery;
+      }
+      else
+      {
+         //A query filter is more practical than a manual class filtering post query (esp on scrollable resultsets)
+         //it also probably minimise the memory footprint
+         BooleanQuery classFilter = new BooleanQuery();
+         //annihilate the scoring impact of DocumentBuilder.CLASS_FIELDNAME
+         classFilter.setBoost(0);
+         for (Class clazz : classesAndSubclasses)
+         {
+            Term t = new Term(DocumentBuilder.CLASS_FIELDNAME, clazz.getName());
+            TermQuery termQuery = new TermQuery(t);
+            classFilter.add(termQuery, BooleanClause.Occur.SHOULD);
+         }
+         BooleanQuery filteredQuery = new BooleanQuery();
+         filteredQuery.add(luceneQuery, BooleanClause.Occur.MUST);
+         filteredQuery.add(classFilter, BooleanClause.Occur.MUST);
+         return filteredQuery;
+      }
+   }
+
+
+   // Method changed by Navin Surtani on Dec 16th 2008. Copied out from FullTextQueryImpl from Hibernate Search code like
+   // previously done. Also copied in methods like buildLuceneFilter(), createFilter() and those methods that follow down
+   // until the end of the class.
+   private void buildFilters()
+   {
+      if (filterDefinitions == null || filterDefinitions.size() == 0)
+      {
+         return; // there is nothing to do if we don't have any filter definitions
+      }
+
+      ChainedFilter chainedFilter = new ChainedFilter();
+      for (FullTextFilterImpl fullTextFilter : filterDefinitions.values())
+      {
+         Filter filter = buildLuceneFilter(fullTextFilter);
+         chainedFilter.addFilter(filter);
+      }
+
+      if (filter != null)
+      {
+         chainedFilter.addFilter(filter);
+      }
+      filter = chainedFilter;
+   }
+
+   private Filter buildLuceneFilter(FullTextFilterImpl fullTextFilter)
+   {
+
+      /*
+      * FilterKey implementations and Filter(Factory) do not have to be threadsafe wrt their parameter injection
+      * as FilterCachingStrategy ensure a memory barrier between concurrent thread calls
+      */
+      FilterDef def = searchFactory.getFilterDefinition(fullTextFilter.getName());
+      Object instance = createFilterInstance(fullTextFilter, def);
+      FilterKey key = createFilterKey(def, instance);
+
+      // try to get the filter out of the cache
+      Filter filter = cacheInstance(def.getCacheMode()) ?
+              searchFactory.getFilterCachingStrategy().getCachedFilter(key) :
+              null;
+
+      if (filter == null)
+      {
+         filter = createFilter(def, instance);
+
+         // add filter to cache if we have to
+         if (cacheInstance(def.getCacheMode()))
+         {
+            searchFactory.getFilterCachingStrategy().addCachedFilter(key, filter);
+         }
+      }
+      return filter;
+   }
+
+   private Filter createFilter(FilterDef def, Object instance)
+   {
+      Filter filter;
+      if (def.getFactoryMethod() != null)
+      {
+         try
+         {
+            filter = (Filter) def.getFactoryMethod().invoke(instance);
+         }
+         catch (IllegalAccessException e)
+         {
+            throw new SearchException(
+                    "Unable to access @Factory method: "
+                            + def.getImpl().getName() + "." + def.getFactoryMethod().getName()
+            );
+         }
+         catch (InvocationTargetException e)
+         {
+            throw new SearchException(
+                    "Unable to access @Factory method: "
+                            + def.getImpl().getName() + "." + def.getFactoryMethod().getName()
+            );
+         }
+         catch (ClassCastException e)
+         {
+            throw new SearchException(
+                    "@Key method does not return a org.apache.lucene.search.Filter class: "
+                            + def.getImpl().getName() + "." + def.getFactoryMethod().getName()
+            );
+         }
+      }
+      else
+      {
+         try
+         {
+            filter = (Filter) instance;
+         }
+         catch (ClassCastException e)
+         {
+            throw new SearchException(
+                    "Filter implementation does not implement the Filter interface: "
+                            + def.getImpl().getName() + ". "
+                            + (def.getFactoryMethod() != null ? def.getFactoryMethod().getName() : ""), e
+            );
+         }
+      }
+
+      filter = addCachingWrapperFilter(filter, def);
+      return filter;
+   }
+
+   private Object createFilterInstance(FullTextFilterImpl fullTextFilter,
+                                       FilterDef def)
+   {
+      Object instance;
+      try
+      {
+         instance = def.getImpl().newInstance();
+      }
+      catch (InstantiationException e)
+      {
+         throw new SearchException("Unable to create @FullTextFilterDef: " + def.getImpl(), e);
+      }
+      catch (IllegalAccessException e)
+      {
+         throw new SearchException("Unable to create @FullTextFilterDef: " + def.getImpl(), e);
+      }
+      for (Map.Entry<String, Object> entry : fullTextFilter.getParameters().entrySet())
+      {
+         def.invoke(entry.getKey(), instance, entry.getValue());
+      }
+      if (cacheInstance(def.getCacheMode()) && def.getKeyMethod() == null && fullTextFilter.getParameters()
+              .size() > 0)
+      {
+         throw new SearchException("Filter with parameters and no @Key method: " + fullTextFilter.getName());
+      }
+      return instance;
+   }
+
+
+   private FilterKey createFilterKey(FilterDef def, Object instance)
+   {
+      FilterKey key = null;
+      if (!cacheInstance(def.getCacheMode()))
+      {
+         return key; // if the filter is not cached there is no key!
+      }
+
+      if (def.getKeyMethod() == null)
+      {
+         key = new FilterKey()
+         {
+            public int hashCode()
+            {
+               return getImpl().hashCode();
+            }
+
+            public boolean equals(Object obj)
+            {
+               if (!(obj instanceof FilterKey))
+               {
+                  return false;
+               }
+               FilterKey that = (FilterKey) obj;
+               return this.getImpl().equals(that.getImpl());
+            }
+         };
+      }
+      else
+      {
+         try
+         {
+            key = (FilterKey) def.getKeyMethod().invoke(instance);
+         }
+         catch (IllegalAccessException e)
+         {
+            throw new SearchException(
+                    "Unable to access @Key method: "
+                            + def.getImpl().getName() + "." + def.getKeyMethod().getName()
+            );
+         }
+         catch (InvocationTargetException e)
+         {
+            throw new SearchException(
+                    "Unable to access @Key method: "
+                            + def.getImpl().getName() + "." + def.getKeyMethod().getName()
+            );
+         }
+         catch (ClassCastException e)
+         {
+            throw new SearchException(
+                    "@Key method does not return FilterKey: "
+                            + def.getImpl().getName() + "." + def.getKeyMethod().getName()
+            );
+         }
+      }
+      key.setImpl(def.getImpl());
+
+      //Make sure Filters are isolated by filter def name
+      StandardFilterKey wrapperKey = new StandardFilterKey();
+      wrapperKey.addParameter(def.getName());
+      wrapperKey.addParameter(key);
+      return wrapperKey;
+   }
+
+   private Filter addCachingWrapperFilter(Filter filter, FilterDef def)
+   {
+      if (cacheResults(def.getCacheMode()))
+      {
+         int cachingWrapperFilterSize = searchFactory.getFilterCacheBitResultsSize();
+         filter = new org.hibernate.search.filter.CachingWrapperFilter(filter, cachingWrapperFilterSize);
+      }
+
+      return filter;
+   }
+
+
+}
+

Added: branches/ISPN-32/query/src/main/java/org/infinispan/query/EntityId.java
===================================================================
--- branches/ISPN-32/query/src/main/java/org/infinispan/query/EntityId.java	                        (rev 0)
+++ branches/ISPN-32/query/src/main/java/org/infinispan/query/EntityId.java	2009-06-29 11:14:01 UTC (rev 502)
@@ -0,0 +1,13 @@
+package org.jboss.cache.search;
+
+import org.jboss.cache.Fqn;
+
+/**
+ * @author Navin Surtani (<a href="mailto:nsurtani at redhat.com">nsurtani at redhat.com</a>)
+ */
+public interface EntityId
+{
+   Fqn getFqn();
+
+   String getDocumentId() throws InvalidKeyException;
+}

Added: branches/ISPN-32/query/src/main/java/org/infinispan/query/IndexSearcherCloser.java
===================================================================
--- branches/ISPN-32/query/src/main/java/org/infinispan/query/IndexSearcherCloser.java	                        (rev 0)
+++ branches/ISPN-32/query/src/main/java/org/infinispan/query/IndexSearcherCloser.java	2009-06-29 11:14:01 UTC (rev 502)
@@ -0,0 +1,49 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright ${year}, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.cache.search;
+
+import org.hibernate.search.engine.SearchFactoryImplementor;
+import org.hibernate.search.reader.ReaderProvider;
+import static org.hibernate.search.reader.ReaderProviderHelper.getIndexReaders;
+import org.apache.lucene.search.Searcher;
+import org.apache.lucene.index.IndexReader;
+
+import java.util.Set;
+
+/**
+ * Class with static method that is called by {@link CacheQueryImpl} and {@link org.jboss.cache.search.QueryResultIteratorImpl}
+ * <p />
+ * @author Navin Surtani (<a href="mailto:nsurtani at redhat.com">nsurtani at redhat.com</a>)
+ */
+ class IndexSearcherCloser
+{
+   static void closeSearcher(Searcher searcher, ReaderProvider readerProvider)
+   {
+      Set<IndexReader> indexReaders = getIndexReaders(searcher);
+
+      for (IndexReader indexReader : indexReaders)
+      {
+         readerProvider.closeReader(indexReader);
+      }
+   }
+}

Added: branches/ISPN-32/query/src/main/java/org/infinispan/query/InvalidFqnException.java
===================================================================
--- branches/ISPN-32/query/src/main/java/org/infinispan/query/InvalidFqnException.java	                        (rev 0)
+++ branches/ISPN-32/query/src/main/java/org/infinispan/query/InvalidFqnException.java	2009-06-29 11:14:01 UTC (rev 502)
@@ -0,0 +1,52 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright ${year}, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.cache.search;
+
+import org.jboss.cache.CacheException;
+
+/**
+ * Thrown when an invalid Fqn is passed into {@link org.jboss.cache.search.Transformer#generateId(org.jboss.cache.Fqn, String)}
+ * <p />
+ * @author Navin Surtani (<a href="mailto:nsurtani at redhat.com">nsurtani at redhat.com</a>)
+ */
+public class InvalidFqnException extends CacheException
+{
+   public InvalidFqnException()
+   {
+   }
+
+   public InvalidFqnException(String s)
+   {
+      super(s);
+   }
+
+   public InvalidFqnException(String s, Throwable throwable)
+   {
+      super(s, throwable);
+   }
+
+   public InvalidFqnException(Throwable throwable)
+   {
+      super(throwable);
+   }
+}

Added: branches/ISPN-32/query/src/main/java/org/infinispan/query/InvalidKeyException.java
===================================================================
--- branches/ISPN-32/query/src/main/java/org/infinispan/query/InvalidKeyException.java	                        (rev 0)
+++ branches/ISPN-32/query/src/main/java/org/infinispan/query/InvalidKeyException.java	2009-06-29 11:14:01 UTC (rev 502)
@@ -0,0 +1,52 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright ${year}, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.cache.search;
+
+/**
+ * * Thrown when an invalid Key is passed into {@link org.jboss.cache.search.Transformer#generateId(org.jboss.cache.Fqn, String)}
+ * <p />
+ * @author Navin Surtani (<a href="mailto:nsurtani at redhat.com">nsurtani at redhat.com</a>)
+ */
+public class InvalidKeyException extends Exception
+{
+
+   public InvalidKeyException()
+   {
+   }
+
+   public InvalidKeyException(String s)
+   {
+      super(s);
+   }
+
+   public InvalidKeyException(String s, Throwable throwable)
+   {
+      super(s, throwable);
+   }
+
+   public InvalidKeyException(Throwable throwable)
+   {
+      super(throwable);
+   }
+
+}

Added: branches/ISPN-32/query/src/main/java/org/infinispan/query/LazyQueryResultIterator.java
===================================================================
--- branches/ISPN-32/query/src/main/java/org/infinispan/query/LazyQueryResultIterator.java	                        (rev 0)
+++ branches/ISPN-32/query/src/main/java/org/infinispan/query/LazyQueryResultIterator.java	2009-06-29 11:14:01 UTC (rev 502)
@@ -0,0 +1,284 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright ${year}, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.cache.search;
+
+import org.hibernate.search.engine.DocumentExtractor;
+import org.hibernate.search.engine.SearchFactoryImplementor;
+import org.apache.lucene.search.IndexSearcher;
+
+import java.util.NoSuchElementException;
+import java.util.Arrays;
+import java.io.IOException;
+
+/**
+ * Implementation for {@link org.jboss.cache.search.QueryResultIterator}. This is what is returned when
+ * the {@link CacheQuery#lazyIterator()} method is called. This loads the results only when required and hence differs from
+ * {@link org.jboss.cache.search.QueryResultIteratorImpl} which is the other implementation of QueryResultIterator.
+ *
+ * @author Navin Surtani (<a href="mailto:nsurtani at redhat.com">nsurtani at redhat.com</a>)
+ */
+public class LazyQueryResultIterator implements QueryResultIterator
+{
+   private int index = 0;
+   private int bufferIndex = -1; // this is the index at which the buffer was populated.  Hence, the first element of the buffer is at this index in the overall result set.
+   private int max = 0;
+   private int first = 0;
+   private int fetchSize = 0;     // difference between fetchsize and index must always be the value for first. fetchSize has to be at least 1.
+   private CacheEntityLoader entityLoader;
+   private DocumentExtractor extractor;
+   private IndexSearcher searcher;
+   private SearchFactoryImplementor searchFactory;
+   Object[] buffer;
+
+   public LazyQueryResultIterator(DocumentExtractor extractor, CacheEntityLoader entityLoader,
+                                  IndexSearcher searcher, SearchFactoryImplementor searchFactory, int first, int max, int fetchSize)
+   {
+      if (fetchSize < 1)
+      {
+         throw new IllegalArgumentException("Incorrect value for fetchsize passed. Your fetchSize is less than 1");
+      }
+
+      this.extractor = extractor;
+      this.entityLoader = entityLoader;
+      index = first;
+      this.first = first;
+      this.max = max;
+      this.fetchSize = fetchSize;
+      this.searcher = searcher;
+      this.searchFactory = searchFactory;
+
+      //Create an buffer with size fetchSize (which is the size of the required buffer.
+      buffer = new Object[this.fetchSize];
+   }
+
+   public void jumpToResult(int index) throws IndexOutOfBoundsException
+   {
+      if (index < first || index > max)
+      {
+         throw new IndexOutOfBoundsException("The given index is incorrect. Please check and try again.");
+      }
+      
+      this.index = first + index;
+
+   }
+
+   public void first()
+   {
+      index = first;
+   }
+
+   public void last()
+   {
+      index = max;
+   }
+
+   public void afterFirst()
+   {
+      index = first + 1;
+   }
+
+   public void beforeLast()
+   {
+      index = max - 1;
+   }
+
+   public boolean isFirst()
+   {
+      return index == first;
+   }
+
+   public boolean isLast()
+   {
+      return index == max;
+   }
+
+   public boolean isAfterFirst()
+   {
+      return index == first + 1;
+   }
+
+   public boolean isBeforeLast()
+   {
+      return index == max - 1;
+   }
+
+   public void close()
+   {
+      IndexSearcherCloser.closeSearcher(searcher, searchFactory.getReaderProvider());
+   }
+
+   public boolean hasNext()
+   {
+      return index <= max;
+   }
+
+   public Object next()
+   {
+      if (!hasNext()) throw new IndexOutOfBoundsException("Index is out of bounds. There is no next");
+
+      Object toReturn = null;
+      int bufferSize = buffer.length;
+
+      // make sure the index we are after is in the buffer.  If it is, then index >= bufferIndex and index <= (bufferIndex + bufferSize).
+      if (bufferIndex >= 0                                       // buffer init check
+              && index >= bufferIndex                           // lower boundary
+              && index < (bufferIndex + bufferSize))          // upper boundary
+      {
+         // now we can get this from the buffer.  Sweet!
+         int indexToReturn = index - bufferIndex;
+         toReturn = buffer[indexToReturn];
+      }
+      else
+      {
+         // else we need to populate the buffer and get what we need.
+
+         try
+         {
+            String documentId = (String) extractor.extract(index).id;
+            CacheEntityId id = new CacheEntityId(documentId);
+            toReturn = entityLoader.load(id);
+
+            //Wiping bufferObjects and the bufferIndex so that there is no stale data.
+            Arrays.fill(buffer, null);
+            buffer[0] = toReturn;
+
+            // we now need to buffer item at index "index", as well as the next "fetchsize - 1" elements.  I.e., a total of fetchsize elements will be buffered.
+            // ignore loop below, in needs fixing
+            //now loop through bufferSize times to add the rest of the objects into the list.
+
+            for (int i = 1; i < bufferSize; i++)
+            {
+               String bufferDocumentId = (String) extractor.extract(index + i).id;
+               CacheEntityId bufferId = new CacheEntityId(bufferDocumentId);
+               Object toBuffer = entityLoader.load(bufferId);
+               buffer[i] = toBuffer;
+            }
+            bufferIndex = index;
+         }
+         catch (IOException e)
+         {
+            e.printStackTrace();
+         }
+      }
+
+      index++;
+      return toReturn;
+   }
+
+   public boolean hasPrevious()
+   {
+      return index >= first;
+   }
+
+   public Object previous()
+   {
+      if (!hasPrevious()) throw new IndexOutOfBoundsException("Index is out of bounds. There is no previous");
+
+      Object toReturn = null;
+      int bufferSize = buffer.length;
+
+      // make sure the index we are after is in the buffer.  If it is, then index >= bufferIndex and index <= (bufferIndex + bufferSize).
+
+      if (bufferIndex >= 0 // buffer init check
+              && index <= bufferIndex // lower boundary
+              && index >= (bufferIndex + bufferSize)) // upper boundary
+      {
+         // now we can get this from the buffer.  Sweet!
+         int indexToReturn = bufferIndex - index;        // Unlike next() we have to make sure that we are subtracting index from bufferIndex
+         toReturn = buffer[indexToReturn];
+      }
+
+      try
+      {
+         //Wiping the buffer
+         Arrays.fill(buffer, null);
+
+         String documentId = (String) extractor.extract(index).id;
+         CacheEntityId id = new CacheEntityId(documentId);
+         toReturn = entityLoader.load(id);
+
+         buffer[0] = toReturn;
+
+         //now loop through bufferSize times to add the rest of the objects into the list.
+         for (int i = 1; i < bufferSize; i++)
+         {
+            String bufferDocumentId = (String) extractor.extract(index - i).id;    //In this case it has to be index - i because previous() is called.
+            CacheEntityId bufferId = new CacheEntityId(bufferDocumentId);
+            Object toBuffer = entityLoader.load(bufferId);
+            buffer[i] = toBuffer;
+         }
+
+         bufferIndex = index;
+      }
+      catch (IOException e)
+      {
+         e.printStackTrace();
+      }
+      index--;
+      return toReturn;
+   }
+
+   public int nextIndex()
+   {
+      if (!hasNext()) throw new NoSuchElementException("Out of boundaries");
+      return index + 1;
+   }
+
+   public int previousIndex()
+   {
+      if (!hasPrevious()) throw new NoSuchElementException("Out of boundaries.");
+      return index - 1;
+   }
+
+   /**
+    * This method is not supported and should not be used. Use cache.remove() instead.
+    */
+   public void remove()
+   {
+      throw new UnsupportedOperationException("Not supported as you are trying to change something in the cache");
+   }
+
+   /**
+    * This method is not supported in and should not be called. Use cache.put() instead.
+    *
+    * @param o
+    * @throws UnsupportedOperationException
+    */
+   public void set(Object o) throws UnsupportedOperationException
+   {
+      throw new UnsupportedOperationException("Not supported as you are trying to change something in the cache");
+   }
+
+   /**
+    * This method is not supported in and should not be called. Use cache.put() instead.
+    *
+    * @param o
+    * @throws UnsupportedOperationException
+    */
+
+   public void add(Object o)
+   {
+      throw new UnsupportedOperationException("Not supported as you are trying to change something in the cache");
+   }
+
+}

Added: branches/ISPN-32/query/src/main/java/org/infinispan/query/QueryResultIterator.java
===================================================================
--- branches/ISPN-32/query/src/main/java/org/infinispan/query/QueryResultIterator.java	                        (rev 0)
+++ branches/ISPN-32/query/src/main/java/org/infinispan/query/QueryResultIterator.java	2009-06-29 11:14:01 UTC (rev 502)
@@ -0,0 +1,88 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright ${year}, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */ 
+
+package org.jboss.cache.search;
+
+import java.util.ListIterator;
+
+/**
+ * Iterates over query results
+ * <p/>
+ * @author Manik Surtani (<a href="mailto:manik at jboss.org">manik at jboss.org</a>)
+ * @author Navin Surtani (<a href="mailto:nsurtani at redhat.com">manik at jboss.org</a>)
+ */
+public interface QueryResultIterator extends ListIterator
+{
+   /**
+    * Jumps to a specific index in the iterator.
+    *
+    * @param index index to jump to.
+    * @throws IndexOutOfBoundsException if the index is out of bounds
+    */
+   void jumpToResult(int index) throws IndexOutOfBoundsException;
+
+   /**
+    * Jumps to the first result
+    */
+   void first();
+
+   /**
+    * Jumps to the last result
+    */
+   void last();
+
+   /**
+    * Jumps to the one-after-the-first result
+    */
+   void afterFirst();
+
+   /**
+    * Jumps to the one-before-the-last result
+    */
+   void beforeLast();
+
+   /**
+    * @return true if the current result is the first
+    */
+   boolean isFirst();
+
+   /**
+    * @return true if the current result is the last
+    */
+   boolean isLast();
+
+   /**
+    * @return true if the current result is one after the first
+    */
+   boolean isAfterFirst();
+
+   /**
+    * @return true if the current result is one before the last
+    */
+   boolean isBeforeLast();
+
+   /**
+    * This method must be called on your iterator once you have finished so that Lucene resources can be freed up. 
+    */
+
+   void close();
+}

Added: branches/ISPN-32/query/src/main/java/org/infinispan/query/QueryResultIteratorImpl.java
===================================================================
--- branches/ISPN-32/query/src/main/java/org/infinispan/query/QueryResultIteratorImpl.java	                        (rev 0)
+++ branches/ISPN-32/query/src/main/java/org/infinispan/query/QueryResultIteratorImpl.java	2009-06-29 11:14:01 UTC (rev 502)
@@ -0,0 +1,359 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright ${year}, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.cache.search;
+
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Arrays;
+
+/**
+ * This is the implementation class for the interface QueryResultIterator which extends ListIterator. It is what is
+ * returned when the iterator() method is run on a CacheQuery instance.
+ * <p/>
+ * <p/>
+ *
+ * @author Navin Surtani (<a href="mailto:nsurtani at redhat.com">nsurtani at redhat.com</a>)
+ */
+public class QueryResultIteratorImpl implements QueryResultIterator
+{
+   private int index = 0;
+   //private final int size;
+   private List<EntityId> idList;
+   private CacheEntityLoader entityLoader;
+   private int lowerLimit = 0;
+   private int upperLimit = 0;
+   private int fetchSize = 0;
+   private int bufferIndex = -1; // this is the index at which the buffer was populated.  Hence, the first element of the buffer is at this index in the overall result set.
+   private Object[] buffer;
+   private static final Log log = LogFactory.getLog(QueryResultIteratorImpl.class);
+
+
+   public QueryResultIteratorImpl(List<EntityId> idList, CacheEntityLoader entityLoader, int fetchSize)
+   {
+      if (fetchSize < 1)
+      {
+         throw new IllegalArgumentException("Incorrect value for fetchsize passed. Your fetchSize is less than 1");
+      }
+
+      this.idList = idList;
+      System.out.println("idList size is " + idList.size());
+      System.out.println("idList object 1 is " + idList.get(0));
+      this.entityLoader = entityLoader;
+      upperLimit = idList.size() - 1;
+      this.fetchSize = fetchSize;
+
+      buffer = new Object[this.fetchSize];
+   }
+
+   /**
+    * Jumps to a given index in the list of results.
+    *
+    * @param index to jump to
+    * @throws IndexOutOfBoundsException
+    */
+
+   public void jumpToResult(int index) throws IndexOutOfBoundsException
+   {
+      if (index > idList.size() || index < 0)
+      {
+         throw new IndexOutOfBoundsException("The index you entered is either greater than the size of the list or negative");
+      }
+      this.index = index;
+   }
+
+   /**
+    * Jumps to first element in the list.
+    */
+
+   public void first()
+   {
+      index = 0;
+   }
+
+   /**
+    * Jumps to last element in the list.
+    */
+
+   public void last()
+   {
+      index = idList.size() - 1;
+   }
+
+   /**
+    * Jumps to second element in the list.
+    */
+
+   public void afterFirst()
+   {
+      index = 1;
+   }
+
+   /**
+    * Jumps to penultimate element in the list.
+    */
+
+   public void beforeLast()
+   {
+      index = idList.size() - 2;
+   }
+
+   /**
+    * @return true if the current element is the first in the list.
+    */
+
+   public boolean isFirst()
+   {
+      return idList.get(index) == idList.get(0);
+   }
+
+   /**
+    * @return true if the current result is the last one.
+    */
+
+   public boolean isLast()
+   {
+      return index == idList.size() - 1;
+
+   }
+
+   /**
+    * @return true if the current result is one after the first.
+    */
+
+   public boolean isAfterFirst()
+   {
+      return idList.get(index) == idList.get(1);
+   }
+
+   /**
+    * @return true if the current result is one before the last
+    */
+
+   public boolean isBeforeLast()
+   {
+      return idList.get(index) == idList.get(idList.size() - 2);
+   }
+
+   public void close()
+   {
+      // This method does not need to do anything for this type of iterator as when an instace of it is
+      // created, the iterator() method in CacheQueryImpl closes everything that needs to be closed.
+   }
+
+   /**
+    * Returns true if the list has more elements when traversing the list in the forward direction.
+    *
+    * @return true if the list has more elements when traversing the list in the forward direction.
+    */
+
+   public boolean hasNext()
+   {
+      return index <= upperLimit;
+   }
+
+   /**
+    * Returns the next element in the list
+    *
+    * @return The next element in the list.
+    */
+   public Object next()
+   {
+      if (!hasNext()) throw new IndexOutOfBoundsException("Out of boundaries. There is no next");
+
+      Object toReturn;
+      int bufferSize = buffer.length;
+
+      // make sure the index we are after is in the buffer.  If it is, then index >= bufferIndex and index <= (bufferIndex + bufferSize).
+      if (bufferIndex >= 0                                       // buffer init check
+              && index >= bufferIndex                           // lower boundary
+              && index < (bufferIndex + bufferSize))          // upper boundary        //TODO: - Why does this logic work but <= not. >= works with previous() however.
+      {
+         // now we can get this from the buffer.  Sweet!
+         int indexToReturn = index - bufferIndex;
+         toReturn = buffer[indexToReturn];
+      }
+
+      else
+      {
+         // We need to populate the buffer.
+
+         toReturn = entityLoader.load(idList.get(index));
+
+         //Wiping bufferObjects and the bufferIndex so that there is no stale data.
+
+         Arrays.fill(buffer, null);
+         buffer[0] = toReturn;
+
+         // we now need to buffer item at index "index", as well as the next "fetchsize - 1" elements.  I.e., a total of fetchsize elements will be buffered.
+         //now loop through bufferSize times to add the rest of the objects into the list.
+
+         for (int i = 1; i < bufferSize; i++)
+         {
+            if (index + i > upperLimit)
+            {
+               if (log.isDebugEnabled())
+               {
+                  log.debug("Your current index + bufferSize exceeds the size of your number of hits");
+               }
+               break;
+            }
+
+            Object toBuffer = entityLoader.load(idList.get(index + i));
+            buffer[i] = toBuffer;
+         }
+         bufferIndex = index;
+
+      }
+
+      index++;
+      return toReturn;
+   }
+
+   /**
+    * Returns true if the list has more elements when traversing the list in the reverse direction.
+    *
+    * @return true if the list iterator has more elements when traversing the list in the reverse direction
+    */
+   public boolean hasPrevious()
+   {
+      return index >= lowerLimit;
+   }
+
+   /**
+    * Returns the previous element in the list.
+    *
+    * @return The previous element in the list.
+    */
+
+   public Object previous()
+   {
+      if (!hasPrevious()) throw new IndexOutOfBoundsException("Index is out of bounds. There is no previous");
+
+      Object toReturn;
+      int bufferSize = buffer.length;
+
+      // make sure the index we are after is in the buffer.  If it is, then index >= bufferIndex and index <= (bufferIndex + bufferSize).
+
+      if (bufferIndex >= 0 // buffer init check
+              && index <= bufferIndex // lower boundary
+              && index >= (bufferIndex + bufferSize)) // upper boundary
+      {
+         // now we can get this from the buffer.  Sweet!
+         int indexToReturn = bufferIndex - index;        // Unlike next() we have to make sure that we are subtracting index from bufferIndex
+         toReturn = buffer[indexToReturn];
+      }
+      else
+      {
+         toReturn = entityLoader.load(idList.get(index));
+         //Wiping bufferObjects and the bufferIndex so that there is no stale data.
+
+         Arrays.fill(buffer, null);
+         buffer[0] = toReturn;
+
+         // we now need to buffer item at index "index", as well as the next "fetchsize - 1" elements.  I.e., a total of fetchsize elements will be buffered.
+         // ignore loop below, in needs fixing
+         //now loop through bufferSize times to add the rest of the objects into the list.
+
+         for (int i = 1; i < bufferSize; i++)
+         {
+            if (index - i < lowerLimit)
+            {
+               if (log.isDebugEnabled())
+               {
+                  log.debug("Your current index - bufferSize exceeds the size of your number of hits");
+               }
+               break;
+            }
+
+            Object toBuffer = entityLoader.load(idList.get(index - i));
+            buffer[i] = toBuffer;
+         }
+         bufferIndex = index;
+
+
+      }
+      index--;
+      return toReturn;
+   }
+
+   /**
+    * Returns the index of the element that would be returned by a subsequent call to next.
+    *
+    * @return Index of next element.
+    */
+
+   public int nextIndex()
+   {
+      if (!hasNext()) throw new NoSuchElementException("Out of boundaries");
+      return index + 1;
+
+   }
+
+   /**
+    * Returns the index of the element that would be returned by a subsequent call to previous.
+    *
+    * @return Index of previous element.
+    */
+
+   public int previousIndex()
+   {
+      if (!hasPrevious()) throw new NoSuchElementException("Out of boundaries");
+      return index - 1;
+   }
+
+   /**
+    * This method is not supported and should not be used. Use cache.remove() instead.
+    */
+   public void remove()
+   {
+      throw new UnsupportedOperationException("Not supported as you are trying to change something in the cache");
+   }
+
+   /**
+    * This method is not supported in and should not be called. Use cache.put() instead.
+    *
+    * @param o
+    * @throws UnsupportedOperationException
+    */
+   public void set(Object o) throws UnsupportedOperationException
+   {
+      throw new UnsupportedOperationException("Not supported as you are trying to change something in the cache");
+   }
+
+   /**
+    * This method is not supported in and should not be called. Use cache.put() instead.
+    *
+    * @param o
+    * @throws UnsupportedOperationException
+    */
+
+   public void add(Object o)
+   {
+      throw new UnsupportedOperationException("Not supported as you are trying to change something in the cache");
+   }
+
+}

Added: branches/ISPN-32/query/src/main/java/org/infinispan/query/SearchableCache.java
===================================================================
--- branches/ISPN-32/query/src/main/java/org/infinispan/query/SearchableCache.java	                        (rev 0)
+++ branches/ISPN-32/query/src/main/java/org/infinispan/query/SearchableCache.java	2009-06-29 11:14:01 UTC (rev 502)
@@ -0,0 +1,49 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright ${year}, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.cache.search;
+
+import org.apache.lucene.search.Query;
+import org.jboss.cache.Cache;
+
+/**
+ * This will be the most used interface in JBossCache searchable. It extends Cache and therefore will have
+ * the standard get(), put() and remove() methods. The additional method is the createQuery method which people
+ * will use to build their Hibernate Search queries from a luceneQuery - Hibernate Search users will be very familiar
+ * with this.
+ *
+ * @author Navin Surtani (<a href="mailto:nsurtani at redhat.com">nsurtani at redhat.com</a>)
+ *         <p/>
+ */
+public interface SearchableCache<K, V> extends Cache<K, V>
+{
+
+
+   /**
+    * Creates a CacheQuery from a lucene query and a class array.
+    *
+    * @param classes     - array of classes to be searched from.
+    * @param luceneQuery - from {@link org.apache.lucene.search.Query}
+    * @return a CacheQuery instance from which the user can get a list/iterator object.
+    */
+   CacheQuery createQuery(Query luceneQuery, Class... classes);
+}

Added: branches/ISPN-32/query/src/main/java/org/infinispan/query/SearchableCacheConfiguration.java
===================================================================
--- branches/ISPN-32/query/src/main/java/org/infinispan/query/SearchableCacheConfiguration.java	                        (rev 0)
+++ branches/ISPN-32/query/src/main/java/org/infinispan/query/SearchableCacheConfiguration.java	2009-06-29 11:14:01 UTC (rev 502)
@@ -0,0 +1,88 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright ${year}, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */ 
+
+package org.jboss.cache.search;
+
+import org.hibernate.search.cfg.SearchConfiguration;
+import org.hibernate.annotations.common.reflection.ReflectionManager;
+
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * Class that implements {@link org.hibernate.search.cfg.SearchConfiguration} so that within JBossCache Searchable, there is no
+ * need for a Hibernate Core configuration object.
+ *
+ * @author Navin Surtani (<a href="mailto:nsurtani at redhat.com">nsurtani at redhat.com</a>)
+ */
+public class SearchableCacheConfiguration implements SearchConfiguration
+{
+   protected Map<String, Class> classes;
+   private Properties properties;
+
+   public SearchableCacheConfiguration(Class[] classArray, Properties properties)
+   {
+      // null chks
+      if (classArray == null) throw new NullPointerException("Classes provided are null");
+      this.properties = properties;
+      if (this.properties == null) this.properties = new Properties();
+      
+      classes = new HashMap<String, Class>();
+
+      // loop thru your classArray
+      // populate your Map
+
+      for (Class c: classArray)
+      {
+         String classname = c.getName();
+         classes.put(classname, c);
+      }
+   }
+
+   public Iterator getClassMappings()
+   {
+      return classes.values().iterator();
+   }
+
+   public Class getClassMapping(String name)
+   {
+      return classes.get(name);
+   }
+
+   public String getProperty(String propertyName)
+   {
+      return properties.getProperty(propertyName);
+   }
+
+   public Properties getProperties()
+   {
+      return properties;
+   }
+
+   public ReflectionManager getReflectionManager()
+   {
+      return null;
+   }
+
+}

Added: branches/ISPN-32/query/src/main/java/org/infinispan/query/SearchableCacheFactory.java
===================================================================
--- branches/ISPN-32/query/src/main/java/org/infinispan/query/SearchableCacheFactory.java	                        (rev 0)
+++ branches/ISPN-32/query/src/main/java/org/infinispan/query/SearchableCacheFactory.java	2009-06-29 11:14:01 UTC (rev 502)
@@ -0,0 +1,191 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright ${year}, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.cache.search;
+
+import org.hibernate.search.impl.SearchFactoryImpl;
+import org.hibernate.search.cfg.SearchConfiguration;
+import org.hibernate.search.engine.SearchFactoryImplementor;
+import org.jboss.cache.Cache;
+import org.jboss.cache.CacheStatus;
+import org.jboss.cache.pojo.PojoCache;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.util.Properties;
+import java.lang.reflect.Field;
+
+/**
+ * Factory class used to create the searchable-cache like so: -
+ * <p/>
+ * <p/>
+ * SearchableCache searchableCache = SearchableCacheFactory.createSearchableCache(coreCache, class[]);
+ * <p/>
+ * <p/>
+ *
+ * @author Navin Surtani (<a href="mailto:nsurtani at redhat.com">nsurtani at redhat.com</a>)
+ */
+public class SearchableCacheFactory
+{
+   private static final Log log = LogFactory.getLog(SearchableCacheFactory.class);
+
+
+   /**
+    * Creates a searchable cache from a cache object and a class array, without the properties object.
+    *
+    * @param c       - the Cache
+    * @param classes - Class array to be added
+    * @return a SearchableCache
+    */
+   public SearchableCache createSearchableCache(Cache<?, ?> c, Class... classes)
+   {
+      return createSearchableCache(c, null, classes);
+   }
+
+   /**
+    * This method creates a searchable cache as well but requires the properties object.
+    *
+    * @param c          - the Cache
+    * @param properties - java.util.properties
+    * @param classes    - a class array
+    * @return a SearchableCache
+    */
+   public SearchableCache createSearchableCache(Cache<?, ?> c, Properties properties, Class... classes)
+   {
+
+      //validate the classes first. make sure the correct annotations are put in.
+
+      validateClasses(classes);
+
+      // assume cache is already created and running.
+      // otherwise, start the cache!!
+      if (c.getCacheStatus() != CacheStatus.STARTED)
+      {
+         if (log.isInfoEnabled()) log.info("Cache not started.  Starting cache first.");
+         c.start();
+      }
+
+      if (classes.length == 0)
+      {
+         throw new IllegalArgumentException("You haven't passed in any classes to index.");
+      }
+
+      // step 1: create hibernate search searchFactory
+      SearchConfiguration cfg = new SearchableCacheConfiguration(classes, properties);
+      // set classes in the cfg
+
+      SearchFactoryImplementor searchFactory = new SearchFactoryImpl(cfg);
+
+
+      // Okay, create the core listener
+      SearchableCoreListener coreListener = new SearchableCoreListener(searchFactory);
+      c.addCacheListener(coreListener);
+
+      //Create secondary listener
+//      SearchableCoreRemover remover = new SearchableCoreRemover(searchFactory);
+//      c.addCacheListener(remover);
+
+      //Create the searchableCache and pass it on.
+      SearchableCache sc = new SearchableCacheImpl(c, searchFactory);
+      return sc;
+   }
+
+
+   /**
+    * This method creates a searchable cache as well but requires the properties object.
+    *
+    * @param pojo          - the pojoCache
+    * @param properties - java.util.properties
+    * @param classes    - a class array
+    * @return a SearchableCache
+    */
+
+   public SearchableCache createSearchableCache(PojoCache pojo, Properties properties, Class... classes)
+   {
+
+      System.out.println("create searchable cache called with pojo cache");
+      validateClasses(classes);
+
+      Cache coreCache = pojo.getCache();
+      if (coreCache.getCacheStatus() != CacheStatus.STARTED)
+      {
+         if (log.isInfoEnabled()) log.info("Cache not started.  Starting cache first.");
+         pojo.start();
+      }
+
+      if (classes.length == 0)
+      {
+         throw new IllegalArgumentException("You haven't passed in any classes to index.");
+      }
+
+      // step 1: create hibernate search searchFactory
+      SearchConfiguration cfg = new SearchableCacheConfiguration(classes, properties);
+      // set classes in the cfg
+
+      SearchFactoryImplementor searchFactory = new SearchFactoryImpl(cfg);
+
+      //Now create the pojoListener
+      SearchablePojoListener pojoListener = new SearchablePojoListener(searchFactory);
+      pojo.addListener(pojoListener);
+      pojo.getCache().addCacheListener(pojoListener);
+
+      SearchableCache sc = new SearchableCachePojoImpl(pojo, searchFactory);
+      return sc;
+   }
+
+   /**
+    * Creates a searchable cache from a cache object and a class array, without the properties object.
+    *
+    * @param pojo       - the pojoCache
+    * @param classes - Class array to be added
+    * @return a SearchableCache
+    */
+
+
+   public SearchableCache createSearchableCache(PojoCache pojo, Class... classes)
+   {
+      return createSearchableCache(pojo, null, classes);
+   }
+
+   //This is to check that both the @ProvidedId is present and the the @DocumentId is not present. This is because
+   // don't want both of these 2 annotations used at the same time.
+   private void validateClasses(Class... classes)
+   {
+      for (Class c : classes)
+      {
+         if (!c.isAnnotationPresent(org.hibernate.search.annotations.ProvidedId.class))
+         {
+            throw new IllegalArgumentException("There is no provided id on " + c.getName() + " class");
+         }
+
+         for (Field field : c.getFields())
+         {
+            if (field.getAnnotation(org.hibernate.search.annotations.DocumentId.class) != null)
+            {
+               throw new IllegalArgumentException("Please remove the documentId annotation in " + c.getName());
+            }
+         }
+      }
+
+   }
+
+}

Added: branches/ISPN-32/query/src/main/java/org/infinispan/query/SearchableCacheImpl.java
===================================================================
--- branches/ISPN-32/query/src/main/java/org/infinispan/query/SearchableCacheImpl.java	                        (rev 0)
+++ branches/ISPN-32/query/src/main/java/org/infinispan/query/SearchableCacheImpl.java	2009-06-29 11:14:01 UTC (rev 502)
@@ -0,0 +1,456 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright ${year}, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.cache.search;
+
+import org.apache.lucene.search.Query;
+import org.hibernate.search.engine.SearchFactoryImplementor;
+import org.jboss.cache.Cache;
+import org.jboss.cache.CacheException;
+import org.jboss.cache.CacheStatus;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.InvocationContext;
+import org.jboss.cache.Node;
+import org.jboss.cache.NodeNotExistsException;
+import org.jboss.cache.Region;
+import org.jboss.cache.config.Configuration;
+import org.jboss.cache.interceptors.base.CommandInterceptor;
+import org.jgroups.Address;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Implementation class for the SearchableCache interface.
+ * <p/>
+ *
+ * @author Navin Surtani (<a href="mailto:nsurtani at redhat.com">nsurtani at redhat.com</a>)
+ */
+public class SearchableCacheImpl<K, V> implements SearchableCache<K, V>
+{
+   // this is the ACTUAL cache. that does all the work.
+   // protected fields for Pojo subclass
+
+   protected Cache<K, V> cache;
+   protected SearchFactoryImplementor searchFactory;
+
+
+   public SearchableCacheImpl(Cache<K, V> cache, SearchFactoryImplementor searchFactory)
+   {
+      if (cache == null) throw new NullPointerException("Cache is null");
+      if (searchFactory == null) throw new NullPointerException("searchFactory is null");
+      this.cache = cache;
+      this.searchFactory = searchFactory;
+   }
+
+
+   /**
+    * Creates a CacheQuery object from a Lucene Query and a class array.
+    *
+    * @param luceneQuery - for lucene
+    * @param classes     array
+    * @return CacheQuery object.
+    */
+
+
+   public CacheQuery createQuery(Query luceneQuery, Class... classes)
+   {
+      return new CacheQueryImpl(luceneQuery, searchFactory, cache, classes);
+
+   }
+
+   /**
+    * Returns an org.jboss.cache.config.Configuration instance.
+    *
+    * @return org.jboss.cache.config.Configuration
+    */
+
+   public Configuration getConfiguration()
+   {
+      return cache.getConfiguration();
+   }
+
+   /**
+    * Returns an org.jboss.cache.Node instance.
+    *
+    * @return org.jboss.cache.Node<K,V>
+    */
+
+   public Node getRoot()
+   {
+      return cache.getRoot();
+   }
+
+   /**
+    * Adds a listener to the cache.
+    *
+    * @param listener
+    */
+   public void addCacheListener(Object listener)
+   {
+      cache.addCacheListener(listener);
+   }
+
+   /**
+    * Removes a listener from the cache.
+    *
+    * @param listener
+    */
+   public void removeCacheListener(Object listener)
+   {
+      cache.removeCacheListener(listener);
+   }
+
+   /**
+    * Returns a set of listeners that the cache has.
+    *
+    * @return A set of listeners.
+    */
+
+   public Set getCacheListeners()
+   {
+      return cache.getCacheListeners();
+   }
+
+   public V put(Fqn fqn, K key, V value)
+   {
+      return cache.put(fqn, key, value);
+   }
+
+
+   /**
+    * Puts something into the cache with a given Fqn, key and value.
+    *
+    * @param fqn
+    * @param key
+    * @param value
+    * @return
+    */
+
+   public V put(String fqn, K key, V value)
+   {
+      return cache.put(fqn, key, value);
+   }
+
+   public void putForExternalRead(Fqn fqn, K key, V value)
+   {
+      cache.put(fqn, key, value);
+   }
+
+   public void put(Fqn fqn, Map<? extends K, ? extends V> data)
+   {
+      cache.put(fqn, data);
+   }
+
+   public void put(String fqn, Map<? extends K, ? extends V> data)
+   {
+      cache.put(fqn, data);
+   }
+
+   public V remove(Fqn fqn, K key)
+   {
+      return cache.remove(fqn, key);
+   }
+
+   public V remove(String fqn, K key)
+   {
+      return cache.remove(fqn, key);
+   }
+
+   public boolean removeNode(Fqn fqn)
+   {
+      return cache.removeNode(fqn);
+   }
+
+   /**
+    * Convenience method that takes a string representation of an Fqn. Otherwise identical to removeNode(Fqn)
+    *
+    * @param fqn
+    * @return True if the node was removed, false if the node wasn't
+    */
+
+   public boolean removeNode(String fqn)
+   {
+      return cache.removeNode(fqn);
+   }
+
+   public Node<K, V> getNode(Fqn fqn)
+   {
+      return cache.getNode(fqn);
+   }
+
+   /**
+    * Gets a node from a String representation of a Fqn
+    *
+    * @param fqn
+    * @return
+    */
+
+   public Node getNode(String fqn)
+   {
+      return cache.getNode(fqn);
+   }
+
+   public Set<Object> getChildrenNames(Fqn fqn)
+   {
+      return cache.getChildrenNames(fqn);
+   }
+
+   public Set<String> getChildrenNames(String fqn)
+   {
+      return cache.getChildrenNames(fqn);
+   }
+
+   public boolean isLeaf(Fqn fqn)
+   {
+      return cache.isLeaf(fqn);
+   }
+
+   public boolean isLeaf(String fqn)
+   {
+      return cache.isLeaf(fqn);
+   }
+
+   public V get(Fqn fqn, K key)
+   {
+      return cache.get(fqn, key);
+   }
+
+   /**
+    * Convenience method that allows for direct access to the data in a Node.
+    *
+    * @param fqn
+    * @param key
+    * @return
+    */
+   public V get(String fqn, K key)
+   {
+      return cache.get(fqn, key);
+   }
+
+   public void evict(Fqn fqn, boolean recursive)
+   {
+      cache.evict(fqn, recursive);
+   }
+
+   public void evict(Fqn fqn)
+   {
+      cache.evict(fqn);
+   }
+
+   public Region getRegion(Fqn fqn, boolean createIfAbsent)
+   {
+      return cache.getRegion(fqn, createIfAbsent);
+   }
+
+   public boolean removeRegion(Fqn fqn)
+   {
+      return cache.removeRegion(fqn);
+   }
+
+   /**
+    * Lifecycle method that initializes configuration state, the root node, etc.
+    *
+    * @throws CacheException
+    */
+
+   public void create() throws CacheException
+   {
+      cache.create();
+   }
+
+   /**
+    * Lifecycle method that starts the cache loader, starts cache replication,
+    * starts the region manager, etc., and (if configured) warms the cache using a
+    * state transfer or cache loader preload.
+    *
+    * @throws CacheException
+    */
+
+   public void start() throws CacheException
+   {
+      cache.start();
+   }
+
+   /**
+    * Lifecycle method that stops the cache, including replication, clustering, cache loading, notifications, etc.,
+    * and clears all cache in-memory state.
+    */
+
+   public void stop()
+   {
+      cache.stop();
+   }
+
+   /**
+    * Lifecycle method that destroys the cache and removes any interceptors/configuration elements.
+    */
+
+   public void destroy()
+   {
+      cache.destroy();
+   }
+
+   /**
+    * Gets where the cache currently is its lifecycle transitions.
+    *
+    * @return the CacheStatus. Will not return null.
+    */
+
+   public CacheStatus getCacheStatus()
+   {
+      return cache.getCacheStatus();
+   }
+
+   /**
+    * The current invocation context for the current invocation and cache instance.
+    *
+    * @return the current invocation context for the current invocation and cache instance
+    */
+   public InvocationContext getInvocationContext()
+   {
+      return cache.getInvocationContext();
+   }
+
+   /**
+    * Sets the passed in InvocationContext as current.
+    *
+    * @param ctx
+    */
+
+   public void setInvocationContext(InvocationContext ctx)
+   {
+      cache.setInvocationContext(ctx);
+   }
+
+   /**
+    * Returns the local address of this cache in a cluster, or null  if running in local mode.
+    *
+    * @return Returns the local address of this cache in a cluster, or null  if running in local mode.
+    */
+   public Address getLocalAddress()
+   {
+      return cache.getLocalAddress();
+   }
+
+   /**
+    * Returns a list of members in the cluster, or null  if running in local mode.
+    *
+    * @return Returns a list of members in the cluster, or null  if running in local mode.
+    */
+
+
+   public List getMembers()
+   {
+      return cache.getMembers();
+   }
+
+   public void move(Fqn nodeToMove, Fqn newParent) throws NodeNotExistsException
+   {
+      cache.move(nodeToMove, newParent);
+   }
+
+   /**
+    * Moves a part of the cache to a different subtree. Takes Strings for convenience.
+    *
+    * @param nodeToMove
+    * @param newParent
+    * @throws NodeNotExistsException
+    */
+   public void move(String nodeToMove, String newParent) throws NodeNotExistsException
+   {
+      cache.move(nodeToMove, newParent);
+   }
+
+   /**
+    * Returns the version of the cache as a string.
+    *
+    * @return Returns the version of the cache as a string.
+    */
+   public String getVersion()
+   {
+      return cache.getVersion();
+   }
+
+   public Map<K, V> getData(Fqn fqn)
+   {
+      return cache.getData(fqn);
+   }
+
+   /**
+    * Returns a set of attribute keys for the Fqn. Takes Strings for convenience.
+    *
+    * @param fqn
+    * @return Returns a set of attribute keys for the Fqn.
+    */
+
+   public Set<K> getKeys(String fqn)
+   {
+      return cache.getKeys(fqn);
+   }
+
+   public Set<K> getKeys(Fqn fqn)
+   {
+      return cache.getKeys(fqn);
+   }
+
+   public void clearData(String fqn)
+   {
+      cache.clearData(fqn);
+   }
+
+   public void clearData(Fqn fqn)
+   {
+      cache.clearData(fqn);
+   }
+
+   public void startBatch()
+   {
+      cache.startBatch();
+   }
+
+   public void endBatch(boolean successful)
+   {
+      cache.endBatch(successful);
+   }
+
+   public void removeInterceptor(Class<? extends CommandInterceptor> interceptorType)
+   {
+      cache.removeInterceptor(interceptorType);
+   }
+
+   public void removeInterceptor(int position)
+   {
+      cache.removeInterceptor(position);
+   }
+
+   public void addInterceptor(CommandInterceptor i, Class<? extends CommandInterceptor> afterInterceptor)
+   {
+      cache.addInterceptor(i, afterInterceptor);
+   }
+
+   public void addInterceptor(CommandInterceptor i, int position)
+   {
+      cache.addInterceptor(i, position);
+   }
+}

Added: branches/ISPN-32/query/src/main/java/org/infinispan/query/SearchableCoreListener.java
===================================================================
--- branches/ISPN-32/query/src/main/java/org/infinispan/query/SearchableCoreListener.java	                        (rev 0)
+++ branches/ISPN-32/query/src/main/java/org/infinispan/query/SearchableCoreListener.java	2009-06-29 11:14:01 UTC (rev 502)
@@ -0,0 +1,211 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright ${year}, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.cache.search;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.search.backend.TransactionContext;
+import org.hibernate.search.backend.Work;
+import org.hibernate.search.backend.WorkType;
+import org.hibernate.search.engine.SearchFactoryImplementor;
+import org.jboss.cache.notifications.annotation.CacheListener;
+import org.jboss.cache.notifications.annotation.NodeModified;
+import org.jboss.cache.notifications.annotation.NodeRemoved;
+import org.jboss.cache.notifications.annotation.TransactionRegistered;
+import org.jboss.cache.notifications.event.NodeModifiedEvent;
+import org.jboss.cache.notifications.event.NodeRemovedEvent;
+import org.jboss.cache.notifications.event.TransactionRegisteredEvent;
+
+import javax.transaction.Transaction;
+import java.util.Map;
+
+/**
+ * @author Navin Surtani (<a href="mailto:nsurtani at redhat.com">nsurtani at redhat.com</a>)
+ *         <p/>
+ *         Listener class for changes made to the cache. This listener makes changes if it is a {@link org.jboss.cache.Cache} being used.
+ */
+ at CacheListener
+public class SearchableCoreListener
+{
+   private SearchFactoryImplementor searchFactory;
+   private static final Log log = LogFactory.getLog(SearchableCoreListener.class);
+
+   public SearchableCoreListener(SearchFactoryImplementor searchFactory)
+   {
+      this.searchFactory = searchFactory;
+   }
+
+   /**
+    * Takes in a NodeModifiedEvent and updates the Lucene indexes using methods on the NodeModifiedEvent class.
+    *
+    * @param event that has occured - or a node that has been changed. {@link org.jboss.cache.notifications.event.NodeModifiedEvent}
+    * @throws InvalidKeyException if an invalid key is passed in.
+    */
+
+   @NodeModified
+   public void updateLuceneIndexes(NodeModifiedEvent event) throws InvalidKeyException
+   {
+     if (log.isDebugEnabled()) log.debug("You have entered the SearchableListener");
+      if (!event.isPre())
+      {
+         if (log.isTraceEnabled()) log.trace("event.isPre is false. Going to start updating indexes");
+         switch (event.getModificationType())
+         {
+            case PUT_MAP:
+            case PUT_DATA:
+               if (log.isTraceEnabled()) log.trace("put() has been called on cache. Going to handle the data.");
+               handlePutData(event, searchFactory);
+               break;
+            case REMOVE_DATA:
+               handleDeleteData(event, searchFactory);
+               break;
+         }
+      }
+   }
+
+
+   @NodeRemoved
+   public void handleNodeRemoved(NodeRemovedEvent event) throws InvalidKeyException
+   {
+
+      log.debug("Entered the SCL. @NodeRemoved annotation found");
+      if (event.isPre())
+      {
+         // Directly going to handle the deletion of data.
+         Map dataMap = event.getData();
+         if (log.isTraceEnabled()) log.trace("Called event.getData() and saved to a Map");
+
+         TransactionContext ctx = new TransactionalEventTransactionContext(event);
+
+         for (Object key : dataMap.keySet())
+         {
+            // todo: the key may be any object ... casting may produce CCE and is breaking the cache iface contract
+            CacheEntityId cacheEntityId = new CacheEntityId(event.getFqn(), (String) key);
+            if (log.isTraceEnabled()) log.trace("Created new CacheEntityId");
+
+            String fqnString = cacheEntityId.getFqn().toString();  // Vars for logging
+            String keyString = (String) key;
+
+
+            searchFactory.getWorker().performWork(new Work(dataMap.get(key), cacheEntityId.getDocumentId(), WorkType.DELETE), ctx);
+
+            if (log.isTraceEnabled())
+            {
+               log.debug("Added your object into Lucene with Fqn " + fqnString + " and key " + keyString);
+            }
+         }
+     }
+   }
+
+   /**
+    * Transaction commencement needs to be handled explicitly
+    */
+   @TransactionRegistered
+   public void handleTransactionBoundary(TransactionRegisteredEvent event)
+   {
+      Transaction transactionId = event.getTransaction();
+
+      // TODO find some way to notify Hibernate Search that a transaction has commenced.
+
+      // According to http://www.hibernate.org/hib_docs/search/reference/en/html/search-batchindex.html#search-batchindex-indexing
+      // work is flushed automatically by HS when the transaction commits so there is nothing else to do there.  We just need an
+      // internal equivalent of fullTextSession.beginTransaction() and force it to use the transactionId provided.
+   }
+
+
+   /**
+    * If the modification type is PUT_MAP or PUT_DATA then this method will be called.
+    * Takes in the event as a parameter
+    *
+    * @param event         that has occured - or a node that has been changed. {@link org.jboss.cache.notifications.event.NodeModifiedEvent}
+    * @param searchFactory - a SearchFactoryImpl instance.
+    * @throws InvalidKeyException
+    */
+
+
+   protected void handlePutData(NodeModifiedEvent event, SearchFactoryImplementor searchFactory) throws InvalidKeyException
+   {
+      Map dataMap = event.getData();
+      if (log.isTraceEnabled()) log.trace("Called event.getData() and saved to a Map");
+
+
+      TransactionContext ctx = new TransactionalEventTransactionContext(event);
+
+      for (Object key : dataMap.keySet())
+      {
+          // todo: the key may be any object ... casting may produce CCE and is breaking the cache iface contract
+         CacheEntityId cacheEntityId = new CacheEntityId(event.getFqn(), (String) key);
+         if (log.isTraceEnabled()) log.trace("Created new CacheEntityId");
+
+         String fqnString = cacheEntityId.getFqn().toString();  // Vars for logging
+         String keyString = (String) key;
+
+
+         searchFactory.getWorker().performWork(new Work(dataMap.get(key), cacheEntityId.getDocumentId(), WorkType.DELETE), ctx);
+         searchFactory.getWorker().performWork(new Work(dataMap.get(key), cacheEntityId.getDocumentId(), WorkType.ADD), ctx);
+
+
+         if (log.isTraceEnabled())
+         {
+            log.debug("Added your object into Lucene with Fqn " + fqnString + " and key " + keyString);
+         }
+
+
+      }
+
+   }
+
+
+   /**
+    * If the modification type is DELETE_DATA then this method will be called.
+    * Takes in the event as a parameter
+    *
+    * @param event         that has occured - or a node that has been changed. {@link org.jboss.cache.notifications.event.NodeModifiedEvent}
+    * @param searchFactory - a SearchFactoryImpl instance.
+    * @throws InvalidKeyException
+    */
+   protected void handleDeleteData(NodeModifiedEvent event, SearchFactoryImplementor searchFactory) throws InvalidKeyException
+   {
+      Map dataMap = event.getData();
+
+      TransactionContext ctx = new TransactionalEventTransactionContext(event);
+
+      for (Object key : dataMap.keySet())
+      {
+         // todo: the key may be any object ... casting may produce CCE and is breaking the cache iface contract
+         CacheEntityId cacheEntityId = new CacheEntityId(event.getFqn(), (String) key);
+
+         String fqnString = cacheEntityId.getFqn().toString();
+         String keyString = (String) key;
+
+
+         searchFactory.getWorker().performWork(new Work(dataMap.get(key), cacheEntityId.getDocumentId(), WorkType.DELETE), ctx);
+         if (log.isTraceEnabled())
+            log.trace("Deleted your object from Lucene with Fqn " + fqnString + " and key " + keyString);
+
+
+      }
+
+   }
+
+}

Added: branches/ISPN-32/query/src/main/java/org/infinispan/query/SearchableCoreRemover.java
===================================================================
--- branches/ISPN-32/query/src/main/java/org/infinispan/query/SearchableCoreRemover.java	                        (rev 0)
+++ branches/ISPN-32/query/src/main/java/org/infinispan/query/SearchableCoreRemover.java	2009-06-29 11:14:01 UTC (rev 502)
@@ -0,0 +1,62 @@
+package org.jboss.cache.search;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.search.backend.TransactionContext;
+import org.hibernate.search.backend.Work;
+import org.hibernate.search.backend.WorkType;
+import org.hibernate.search.engine.SearchFactoryImplementor;
+import org.jboss.cache.notifications.annotation.CacheListener;
+import org.jboss.cache.notifications.annotation.NodeRemoved;
+import org.jboss.cache.notifications.event.NodeRemovedEvent;
+
+import java.util.Map;
+
+/**
+ * @author Navin Surtani (<a href="mailto:nsurtani at redhat.com">nsurtani at redhat.com</a>)
+ */
+ at CacheListener
+public class SearchableCoreRemover
+{
+   private SearchFactoryImplementor searchFactory;
+   private static final Log log = LogFactory.getLog(SearchableCoreListener.class);
+
+   public SearchableCoreRemover(SearchFactoryImplementor searchFactory)
+   {
+      this.searchFactory = searchFactory;
+   }
+
+
+   @NodeRemoved
+   public void logEvent(NodeRemovedEvent event) throws InvalidKeyException
+   {
+      log.debug("Entered - SearchableCoreRemover with @NodeRemoved");
+      if (event.isPre())
+      {
+         System.out.println("Event isPre()");
+
+         // Directly going to handle the deletion of data.
+         Map dataMap = event.getData();
+         if (log.isTraceEnabled()) log.trace("Called event.getData() and saved to a Map");
+
+         TransactionContext ctx = new TransactionalEventTransactionContext(event);
+
+         for (Object key : dataMap.keySet())
+         {
+            CacheEntityId cacheEntityId = new CacheEntityId(event.getFqn(), (String) key);
+            if (log.isTraceEnabled()) log.trace("Created new CacheEntityId");
+
+            String fqnString = cacheEntityId.getFqn().toString();  // Vars for logging
+            String keyString = (String) key;
+
+
+            searchFactory.getWorker().performWork(new Work(dataMap.get(key), cacheEntityId.getDocumentId(), WorkType.DELETE), ctx);
+
+            if (log.isTraceEnabled())
+            {
+               log.debug("Added your object into Lucene with Fqn " + fqnString + " and key " + keyString);
+            }
+         }
+     }
+   }
+}

Added: branches/ISPN-32/query/src/main/java/org/infinispan/query/TransactionalEventTransactionContext.java
===================================================================
--- branches/ISPN-32/query/src/main/java/org/infinispan/query/TransactionalEventTransactionContext.java	                        (rev 0)
+++ branches/ISPN-32/query/src/main/java/org/infinispan/query/TransactionalEventTransactionContext.java	2009-06-29 11:14:01 UTC (rev 502)
@@ -0,0 +1,103 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright ${year}, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */ 
+
+package org.jboss.cache.search;
+
+import org.hibernate.search.backend.TransactionContext;
+import org.jboss.cache.notifications.event.TransactionalEvent;
+
+import javax.transaction.Synchronization;
+import javax.transaction.Transaction;
+
+/**
+ * This class implements the {@link org.hibernate.search.backend.TransactionContext} interface.  It
+ * retrieves transaction context information from the {@link org.jboss.cache.notifications.event.NodeModifiedEvent} that gets passed in.
+ * <p />
+ * It is used by the {@link SearchableCoreListener} to pass transaction information to a Hibernate Search {@link org.hibernate.search.backend.Work} object.
+ * <p />
+ * @author Navin Surtani (<a href="mailto:nsurtani at redhat.com">nsurtani at redhat.com</a>)
+ * @see SearchableCoreListener
+ */
+public class TransactionalEventTransactionContext implements TransactionContext
+{
+
+   TransactionalEvent event;
+
+   /**
+    * Creates a new instance of NodeModifiedTransactionContext.
+    * <p />
+    * @param event a NodeModifiedEvent to wrap.  Should not be null.
+    * @throws NullPointerException if event is null.
+    */
+   public TransactionalEventTransactionContext(TransactionalEvent event)
+   {
+      if (event == null) throw new NullPointerException("event cannot be null");
+      this.event = event;
+   }
+
+   /**
+    * Returns a boolean value whether or not a transaction is in progress (JTA transaction and in this case *not*
+    * an org.hibernate transaction).
+    * @return true if a transaction is in progress, false otherwise.
+    */
+   public boolean isTransactionInProgress()
+   {
+      return (event.getTransaction() != null);
+   }
+
+   /**
+    * Returns a JTA transaction.
+    * @return a JTA transaction if one is available, or a null otherwise.
+    * @see org.jboss.cache.notifications.event.NodeModifiedEvent#getTransaction()
+    */
+
+   public Object getTransactionIdentifier()
+   {
+      return event.getTransaction();
+   }
+
+   /**
+    * Registers the synchronization passed in as a parameter with the ongoing transaction.
+    * <p />
+    * If there is no ongoing transaction, then this method will do nothing and simply return.
+    * <p />
+    * @param synchronization synchronization to register.  Must not be null.
+    * @throws NullPointerException if the synchronization is null.
+    */
+   public void registerSynchronization(Synchronization synchronization)
+   {
+      if (synchronization == null) throw new NullPointerException("Synchronization passed in is null!");
+
+      Transaction transaction = event.getTransaction();
+      if (transaction != null)
+      {
+         try
+         {
+            transaction.registerSynchronization(synchronization);
+         }
+         catch (Exception e)
+         {
+            throw new RuntimeException(e);
+         }
+      }
+   }
+}

Added: branches/ISPN-32/query/src/main/java/org/infinispan/query/Transformer.java
===================================================================
--- branches/ISPN-32/query/src/main/java/org/infinispan/query/Transformer.java	                        (rev 0)
+++ branches/ISPN-32/query/src/main/java/org/infinispan/query/Transformer.java	2009-06-29 11:14:01 UTC (rev 502)
@@ -0,0 +1,136 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright ${year}, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */ 
+
+package org.jboss.cache.search;
+
+import org.jboss.cache.Fqn;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+*
+* This class is one that does all the 'conversion' work between JBossCache and Hibernate Search. This is where
+* users can switch from Fqn and key to a documentId and vice versa.
+*
+* If the Fqn is in the form /a/b/c and the key - which has to be a String - is keystring; the documentId - which is also
+* a String - will be "Fqn=[/a/b/c]Key=[keystring]"
+*
+* @author Navin Surtani (<a href="mailto:nsurtani at redhat.com">nsurtani at redhat.com</a>)
+*/
+public class Transformer
+{
+   private static final Log log = LogFactory.getLog(Transformer.class);
+
+   /**
+    * Takes in the documentId string from the user and will return the key from the Fqn, key combination.
+    *
+    *
+    * @param docId - for the keystring to be obtained
+    * @return keystring.
+    */
+
+   public static String getKey(String docId)
+   {
+      //docID comes in the format "Fqn=[/a/b/c]Key=[key]"
+
+      // This will be the index of the first time the sub-String "key=[" occurs within the whole String.
+      int startIndex = docId.indexOf("Key=[");
+
+      //The index of the endIndex of the key sequence so we know when to cut out. The startIndex integer is also passed in so that
+      // there will not be an error later of endIndex < startIndex because this char occurs earlier when the Fqn is stated.
+      int endIndex = docId.indexOf("]", startIndex);
+
+      //Make the startIndex index point at the first char in the key sequence.
+      startIndex += 5;
+
+      //The resultant key that will be returned.
+      String key = docId.substring(startIndex, endIndex);
+
+      return key;
+   }
+
+   /**
+    * Takes in the documentId string from the user and will return the Fqn from the Fqn, key combination.
+    *
+    * @param docId - for the Fqn to be obtained
+    * @return Fqn from the documentId.
+    */
+
+
+   public static Fqn getFqn(String docId)
+   {
+      //docId comes in the format "Fqn=[/a/b/c]Key=[key]"
+
+
+      // This will be the index of the first time the sub-String "Fqn=[" occurs within the whole String.
+      //Adding 1 so that the index being pointed at will be the first character in the Fqn sequence.
+      int startIndex = docId.indexOf("[") + 1;
+//      if(log.isWarnEnabled()) log.warn("startIndex is " + startIndex);
+
+      //The endIndex of the Fqn sequence so that we know when to cut out the sub-String.
+      int endIndex = docId.indexOf("]");
+//      if(log.isWarnEnabled()) log.warn("endIndex is " + endIndex);
+
+
+      String fqnString = docId.substring(startIndex, endIndex);
+//      if(log.isWarnEnabled()) log.warn("fqnString is" + fqnString);
+
+
+      Fqn fqn = Fqn.fromString(fqnString);
+      return fqn;
+   }
+
+   /**
+    * Generates the documentId from an Fqn, key combination.
+    *
+    *
+    * @param fqn - standard call Fqn.fromString()
+    * @param key - cannot be an object.
+    * @return documentId String to be given to Lucene.
+    * @throws InvalidFqnException
+    */
+
+   public static String generateId(Fqn fqn, String key) throws InvalidFqnException, InvalidKeyException
+   {
+      if (key == null) throw new NullPointerException("Key passed in cannot be null!");
+
+      if (key.startsWith("Fqn")) throw new InvalidKeyException("Invalid key passed in - 'Fqn[' is not allowed");
+      if (key.startsWith("]Key=[")) throw new InvalidKeyException("Invalid key passed in - ']Key=[' is not allowed");
+
+      if (fqn == null) throw new NullPointerException("Fqn cannot be null");
+
+      if(fqn.toString().startsWith("/Fqn")) throw new InvalidFqnException("Invalid Fqn passed in.");
+      if (fqn.toString().startsWith("/]Key")) throw new InvalidFqnException("Invalid Fqn passed in.");
+      
+      StringBuilder sb = new StringBuilder();
+      sb.append("Fqn=[");
+      sb.append(fqn);
+      sb.append("]Key=[");
+      sb.append(key);
+      sb.append("]");
+
+      return sb.toString();
+
+   }
+
+
+}

Added: branches/ISPN-32/query/src/main/java/org/infinispan/query/Version.java
===================================================================
--- branches/ISPN-32/query/src/main/java/org/infinispan/query/Version.java	                        (rev 0)
+++ branches/ISPN-32/query/src/main/java/org/infinispan/query/Version.java	2009-06-29 11:14:01 UTC (rev 502)
@@ -0,0 +1,16 @@
+package org.jboss.cache.search;
+
+/**
+ * Spits out the version number.
+ *
+ * @author Navin Surtani (<a href="mailto:nsurtani at redhat.com">nsurtani at redhat.com</a>)
+ */
+public class Version
+{
+   //version string - should correspond with the version in the pom
+   private static final String version = "1.0.0-SNAPSHOT";
+   public static void main(String[] args)
+   {
+      System.out.println("\nJBoss Cache Searchable Edition\nVersion:\t" + version + "\n\n");
+   }
+}




More information about the infinispan-commits mailing list