[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