/* * JBoss, Home of Professional Open Source. * Copyright 2011, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file 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.infinispan.query.clustered; import org.hibernate.search.query.engine.spi.DocumentExtractor; import org.infinispan.AdvancedCache; import org.infinispan.query.backend.KeyTransformationHandler; import org.infinispan.query.clustered.commandworkers.QueryExtractorUtil; import org.infinispan.query.logging.Log; import org.infinispan.util.concurrent.ConcurrentMapFactory; import org.infinispan.util.logging.LogFactory; import java.util.LinkedList; import java.util.UUID; import java.util.concurrent.ConcurrentMap; /** * Each node in the cluster has a QueryBox instance. The QueryBox keep the active lazy iterators * (actually it keeps the DocumentExtractor of the searches) on the cluster, so it can return values * for the queries in a "lazy" way. * * When a DistributedLazyIterator is created, every nodes creates a DocumentExtractor and register * it in your own QueryBox. So, the LazyIterator can fetch the values in a lazy way. * * EVICTION: Currently the QueryBox keeps the last BOX_LIMIT DocumentExtractor used... probably * there is a better way. * * @author Israel Lacerra <israeldl@gmail.com> * @since 5.1 */ public class QueryBox { // <query UUID, ISPNQuery> private final ConcurrentMap<UUID, DocumentExtractor> queries = ConcurrentMapFactory.makeConcurrentMap(); // queries UUIDs ordered (for eviction) private final LinkedList<UUID> ageOrderedQueries = new LinkedList<UUID>(); // For eviction. Probably there is a better way... private static final int BOX_LIMIT = 3000; // this id will be sent with the responses to rpcs private final UUID myId = UUID.randomUUID(); // the local cache instance private AdvancedCache<?, ?> cache; private static final Log log = LogFactory.getLog(QueryBox.class, Log.class); private KeyTransformationHandler keyTransformationHandler; /** * Get the "docIndex" value on the correct DocumentExtractor * * @param queryUuid * The queryId, so we can get the correct DocumentExtractor * @param docIndex * value index in the DocumentExtractor * @return */ public Object getValue(UUID queryUuid, int docIndex) { touch(queryUuid); DocumentExtractor extractor = queries.get(queryUuid); if (extractor == null) { throw new IllegalStateException("Query not found!"); } Object key = QueryExtractorUtil.extractKey(extractor, cache, keyTransformationHandler, docIndex); return cache.get(key); } private void touch(UUID id) { synchronized (ageOrderedQueries) { ageOrderedQueries.remove(id); ageOrderedQueries.addFirst(id); } } /** * Kill the query (DocumentExtractor) * * @param id * The id of the query */ public void kill(UUID id) { DocumentExtractor extractor = queries.remove(id); ageOrderedQueries.remove(id); if (extractor != null) extractor.close(); } /** * Register a query (DocumentExtractor), so we can lazily load the results. * * @param id * The id of the query * @param extractor * The query */ public synchronized void put(UUID id, DocumentExtractor extractor) { synchronized (ageOrderedQueries) { if (ageOrderedQueries.size() >= BOX_LIMIT) { ageOrderedQueries.removeLast(); } ageOrderedQueries.add(id); } queries.put(id, extractor); } /** * Id of this QueryBox * * @return */ public UUID getMyId() { return myId; } public void setCache(AdvancedCache<?, ?> cache) { this.cache = cache; keyTransformationHandler = KeyTransformationHandler.getInstance(cache.getAdvancedCache()); } }