From d004bc3e90000c25df95aabf9dfe4a1bc6abc2bb Mon Sep 17 00:00:00 2001 From: Andrew Hind Date: Tue, 9 Aug 2011 10:42:23 +0000 Subject: [PATCH] SOLR bug fixes - Fix SOLR web app shutdown - Lucene search sub-system can start and self-test without being live - Fix passing faceting parameters - Model deletion on update/removal - ALF-9798 Share - doc lib - favourites fails to show with SOLR - incorrect query .... - ID now uses hidden LID field if it is a NodeRef - QNAME and other related queries generated against AUX doc git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@29624 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- config/alfresco/public-services-context.xml | 29 +- .../Search/common-search-context.xml | 29 + .../Search/lucene/index-recovery-context.xml | 4 +- .../Search/lucene/lucene-search-context.xml | 6 +- .../repo/admin/ConfigurationChecker.java | 2 +- .../MoveWCMToGroupBasedPermissionsPatch.java | 2 +- .../ResetWCMToGroupBasedPermissionsPatch.java | 2 +- .../permissions/AVMAccessControlListDAO.java | 2 +- ...hotTriggeredIndexingMethodInterceptor.java | 464 +-------------- ...riggeredIndexingMethodInterceptorImpl.java | 554 ++++++++++++++++++ .../search/impl/solr/SolrQueryHTTPClient.java | 35 +- 11 files changed, 659 insertions(+), 470 deletions(-) create mode 100644 source/java/org/alfresco/repo/search/AVMSnapShotTriggeredIndexingMethodInterceptorImpl.java diff --git a/config/alfresco/public-services-context.xml b/config/alfresco/public-services-context.xml index 0276eba0c1..abdd9130b6 100644 --- a/config/alfresco/public-services-context.xml +++ b/config/alfresco/public-services-context.xml @@ -1012,31 +1012,16 @@ - - - + + + - - + + search.avmSnapShotTriggeredIndexingMethodInterceptor - - true - - - SYNCHRONOUS - - + - SYNCHRONOUS:TYPE:STAGING - UNINDEXED:TYPE:STAGING_PREVIEW - UNINDEXED:TYPE:AUTHOR - UNINDEXED:TYPE:AUTHOR_PREVIEW - UNINDEXED:TYPE:WORKFLOW - UNINDEXED:TYPE:WORKFLOW_PREVIEW - UNINDEXED:TYPE:AUTHOR_WORKFLOW - UNINDEXED:TYPE:AUTHOR_WORKFLOW_PREVIEW - ASYNCHRONOUS:NAME:avmAsynchronousTest - SYNCHRONOUS:NAME:.* + org.alfresco.repo.search.AVMSnapShotTriggeredIndexingMethodInterceptor diff --git a/config/alfresco/subsystems/Search/common-search-context.xml b/config/alfresco/subsystems/Search/common-search-context.xml index 6ea9820784..ccc37205c1 100644 --- a/config/alfresco/subsystems/Search/common-search-context.xml +++ b/config/alfresco/subsystems/Search/common-search-context.xml @@ -31,5 +31,34 @@ + + + + + + + + + + true + + + SYNCHRONOUS + + + + SYNCHRONOUS:TYPE:STAGING + UNINDEXED:TYPE:STAGING_PREVIEW + UNINDEXED:TYPE:AUTHOR + UNINDEXED:TYPE:AUTHOR_PREVIEW + UNINDEXED:TYPE:WORKFLOW + UNINDEXED:TYPE:WORKFLOW_PREVIEW + UNINDEXED:TYPE:AUTHOR_WORKFLOW + UNINDEXED:TYPE:AUTHOR_WORKFLOW_PREVIEW + ASYNCHRONOUS:NAME:avmAsynchronousTest + SYNCHRONOUS:NAME:.* + + + diff --git a/config/alfresco/subsystems/Search/lucene/index-recovery-context.xml b/config/alfresco/subsystems/Search/lucene/index-recovery-context.xml index 3a3d359145..89f7efd08a 100644 --- a/config/alfresco/subsystems/Search/lucene/index-recovery-context.xml +++ b/config/alfresco/subsystems/Search/lucene/index-recovery-context.xml @@ -89,7 +89,7 @@ - + @@ -162,7 +162,7 @@ - + diff --git a/config/alfresco/subsystems/Search/lucene/lucene-search-context.xml b/config/alfresco/subsystems/Search/lucene/lucene-search-context.xml index 7aed4fd509..6ad7c9d2da 100644 --- a/config/alfresco/subsystems/Search/lucene/lucene-search-context.xml +++ b/config/alfresco/subsystems/Search/lucene/lucene-search-context.xml @@ -385,7 +385,7 @@ - + @@ -474,10 +474,10 @@ - + - + diff --git a/source/java/org/alfresco/repo/admin/ConfigurationChecker.java b/source/java/org/alfresco/repo/admin/ConfigurationChecker.java index 1b31610e54..965c363ce6 100644 --- a/source/java/org/alfresco/repo/admin/ConfigurationChecker.java +++ b/source/java/org/alfresco/repo/admin/ConfigurationChecker.java @@ -28,7 +28,7 @@ import org.springframework.extensions.surf.util.I18NUtil; import org.alfresco.model.ContentModel; import org.alfresco.repo.importer.ImporterBootstrap; import org.alfresco.repo.node.index.FullIndexRecoveryComponent.RecoveryMode; -import org.alfresco.repo.search.AVMSnapShotTriggeredIndexingMethodInterceptor; +import org.alfresco.repo.search.AVMSnapShotTriggeredIndexingMethodInterceptorImpl; import org.alfresco.repo.search.IndexMode; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.cmr.repository.ContentReader; diff --git a/source/java/org/alfresco/repo/admin/patch/impl/MoveWCMToGroupBasedPermissionsPatch.java b/source/java/org/alfresco/repo/admin/patch/impl/MoveWCMToGroupBasedPermissionsPatch.java index a7cdd1d25c..7a8d3e8af2 100644 --- a/source/java/org/alfresco/repo/admin/patch/impl/MoveWCMToGroupBasedPermissionsPatch.java +++ b/source/java/org/alfresco/repo/admin/patch/impl/MoveWCMToGroupBasedPermissionsPatch.java @@ -27,7 +27,7 @@ import org.alfresco.repo.admin.patch.AbstractPatch; import org.alfresco.repo.avm.AVMNodeConverter; import org.alfresco.repo.domain.PropertyValue; import org.alfresco.repo.search.AVMSnapShotTriggeredIndexingMethodInterceptor; -import org.alfresco.repo.search.AVMSnapShotTriggeredIndexingMethodInterceptor.StoreType; +import org.alfresco.repo.search.AVMSnapShotTriggeredIndexingMethodInterceptorImpl.StoreType; import org.alfresco.service.cmr.avm.AVMService; import org.alfresco.service.cmr.avm.AVMStoreDescriptor; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; diff --git a/source/java/org/alfresco/repo/admin/patch/impl/ResetWCMToGroupBasedPermissionsPatch.java b/source/java/org/alfresco/repo/admin/patch/impl/ResetWCMToGroupBasedPermissionsPatch.java index ccda25be23..fcd44ff624 100644 --- a/source/java/org/alfresco/repo/admin/patch/impl/ResetWCMToGroupBasedPermissionsPatch.java +++ b/source/java/org/alfresco/repo/admin/patch/impl/ResetWCMToGroupBasedPermissionsPatch.java @@ -38,7 +38,7 @@ import org.alfresco.repo.domain.permissions.AclEntity; import org.alfresco.repo.domain.permissions.AclUpdateEntity; import org.alfresco.repo.domain.permissions.Authority; import org.alfresco.repo.domain.permissions.Permission; -import org.alfresco.repo.search.AVMSnapShotTriggeredIndexingMethodInterceptor.StoreType; +import org.alfresco.repo.search.AVMSnapShotTriggeredIndexingMethodInterceptorImpl.StoreType; import org.alfresco.repo.security.permissions.ACEType; import org.alfresco.repo.security.permissions.ACLType; import org.alfresco.repo.security.permissions.impl.SimplePermissionReference; diff --git a/source/java/org/alfresco/repo/domain/permissions/AVMAccessControlListDAO.java b/source/java/org/alfresco/repo/domain/permissions/AVMAccessControlListDAO.java index 810e06f7f3..230cf0b3f3 100644 --- a/source/java/org/alfresco/repo/domain/permissions/AVMAccessControlListDAO.java +++ b/source/java/org/alfresco/repo/domain/permissions/AVMAccessControlListDAO.java @@ -33,7 +33,7 @@ import org.alfresco.repo.avm.util.AVMUtil; import org.alfresco.repo.domain.PropertyValue; import org.alfresco.repo.domain.avm.AVMNodeDAO; import org.alfresco.repo.domain.avm.AVMNodeEntity; -import org.alfresco.repo.search.AVMSnapShotTriggeredIndexingMethodInterceptor.StoreType; +import org.alfresco.repo.search.AVMSnapShotTriggeredIndexingMethodInterceptorImpl.StoreType; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.security.permissions.ACLType; diff --git a/source/java/org/alfresco/repo/search/AVMSnapShotTriggeredIndexingMethodInterceptor.java b/source/java/org/alfresco/repo/search/AVMSnapShotTriggeredIndexingMethodInterceptor.java index 4f391715b8..330a2cf647 100644 --- a/source/java/org/alfresco/repo/search/AVMSnapShotTriggeredIndexingMethodInterceptor.java +++ b/source/java/org/alfresco/repo/search/AVMSnapShotTriggeredIndexingMethodInterceptor.java @@ -18,37 +18,20 @@ */ package org.alfresco.repo.search; -import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import java.util.regex.Pattern; -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.repo.avm.AVMNodeConverter; -import org.alfresco.repo.domain.PropertyValue; import org.alfresco.repo.search.impl.lucene.AVMLuceneIndexer; import org.alfresco.service.cmr.avm.AVMService; -import org.alfresco.service.cmr.avm.AVMStoreDescriptor; -import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.namespace.QName; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; /** - * Method interceptor for atomic indexing of AVM entries The properties can defined how stores are indexed based on type - * (as set by Alfresco the Web site management UI) or based on the name of the store. Creates and deletes are indexed - * synchronously. Updates may be asynchronous, synchronous or ignored by the index. - * - * @author andyh + * @author Andy + * */ -public class AVMSnapShotTriggeredIndexingMethodInterceptor implements MethodInterceptor +public interface AVMSnapShotTriggeredIndexingMethodInterceptor extends MethodInterceptor { - private static Log logger = LogFactory.getLog(AVMSnapShotTriggeredIndexingMethodInterceptor.class); - - // Copy of store properties used to tag avm stores (a store propertry) public final static QName PROP_SANDBOX_STAGING_MAIN = QName.createQName(null, ".sandbox.staging.main"); @@ -66,256 +49,70 @@ public class AVMSnapShotTriggeredIndexingMethodInterceptor implements MethodInte public final static QName PROP_SANDBOX_AUTHOR_WORKFLOW_PREVIEW = QName.createQName(null, ".sandbox.author.workflow.preview"); - private AVMService avmService; - - private IndexerAndSearcher indexerAndSearcher; - - private boolean enableIndexing = true; - - private IndexMode defaultMode = IndexMode.ASYNCHRONOUS; - - private Map modeCache = new HashMap(); - - private List indexingDefinitions = new ArrayList(); - @SuppressWarnings("unchecked") - public Object invoke(MethodInvocation mi) throws Throwable - { - if (enableIndexing) - { - if (mi.getMethod().getName().equals("createSnapshot")) - { - // May cause any number of other stores to do snap shot under the covers via layering or do nothing - // So we have to watch what actually changes - - Object returnValue = mi.proceed(); - - Map snapShots = (Map) returnValue; - - // Index any stores that have moved on - for (String store : snapShots.keySet()) - { - int after = snapShots.get(store).intValue(); - indexSnapshot(store, after); - } - return returnValue; - } - else if (mi.getMethod().getName().equals("purgeStore")) - { - String store = (String) mi.getArguments()[0]; - Object returnValue = mi.proceed(); - - if (getIndexMode(store) != IndexMode.UNINDEXED) - { - AVMLuceneIndexer avmIndexer = getIndexer(store); - if (avmIndexer != null) - { - if (logger.isDebugEnabled()) - { - logger.debug("purgeStore " + store, new Exception("Stack Trace")); - } - avmIndexer.deleteIndex(store, IndexMode.SYNCHRONOUS); - } - } - return returnValue; - } - else if (mi.getMethod().getName().equals("createStore")) - { - String store = (String) mi.getArguments()[0]; - Object returnValue = mi.proceed(); - if (getIndexMode(store) != IndexMode.UNINDEXED) - { - createIndex(store); - } - return returnValue; - } - else if (mi.getMethod().getName().equals("renameStore")) - { - String from = (String) mi.getArguments()[0]; - String to = (String) mi.getArguments()[1]; - Object returnValue = mi.proceed(); - int after = avmService.getLatestSnapshotID(to); - - if (getIndexMode(from) != IndexMode.UNINDEXED) - { - AVMLuceneIndexer avmIndexer = getIndexer(from); - if (avmIndexer != null) - { - if (logger.isDebugEnabled()) - { - logger.debug("renameStore deleteIndex " + from, new Exception("Stack Trace")); - } - avmIndexer.deleteIndex(from, IndexMode.SYNCHRONOUS); - } - } - - if (getIndexMode(to) != IndexMode.UNINDEXED) - { - AVMLuceneIndexer avmIndexer = getIndexer(to); - if (avmIndexer != null) - { - if (logger.isDebugEnabled()) - { - logger.debug("renameStore createIndex " + to + "(0, " + after + ")", new Exception("Stack Trace")); - } - avmIndexer.createIndex(to, IndexMode.SYNCHRONOUS); - avmIndexer.index(to, 0, after, getIndexMode(to)); - } - } - return returnValue; - } - else - { - return mi.proceed(); - } - } - else - { - return mi.proceed(); - } - } + public abstract Object invoke(MethodInvocation mi) throws Throwable; /** * Set the AVM service * * @param avmService */ - public void setAvmService(AVMService avmService) - { - this.avmService = avmService; - } + public abstract void setAvmService(AVMService avmService); /** * Set the AVM indexer and searcher * * @param indexerAndSearcher */ - public void setIndexerAndSearcher(IndexerAndSearcher indexerAndSearcher) - { - this.indexerAndSearcher = indexerAndSearcher; - } + public abstract void setIndexerAndSearcher(IndexerAndSearcher indexerAndSearcher); /** * Enable or disable indexing * * @param enableIndexing */ - public void setEnableIndexing(boolean enableIndexing) - { - this.enableIndexing = enableIndexing; - } + public abstract void setEnableIndexing(boolean enableIndexing); /** * Set the index modes.... Strings of the form ... (ASYNCHRONOUS | SYNCHRONOUS | UNINDEXED):(NAME | TYPE):regexp * * @param definitions */ - public void setIndexingDefinitions(List definitions) - { - indexingDefinitions.clear(); - for (String def : definitions) - { - IndexingDefinition id = new IndexingDefinition(def); - indexingDefinitions.add(id); - } - } + public abstract void setIndexingDefinitions(List definitions); /** * Set the default index mode = used when there are no matches * * @param defaultMode */ - public void setDefaultMode(IndexMode defaultMode) - { - this.defaultMode = defaultMode; - } - + public abstract void setDefaultMode(IndexMode defaultMode); + /** * Is snapshot triggered indexing enabled * * @return true if indexing is enabled for AVM */ - public boolean isIndexingEnabled() - { - return enableIndexing; - } - + public abstract boolean isIndexingEnabled(); + /** * @param store * @param before * @param after */ - public void indexSnapshot(String store, int before, int after) - { - indexSnapshotImpl(store, before, after); - } - + public abstract void indexSnapshot(String store, int before, int after); + /** * @param store * @param after */ - public void indexSnapshot(String store, int after) - { - indexSnapshotImpl(store, -1, after); - } - - private void indexSnapshotImpl(String store, int before, int after) - { - if (getIndexMode(store) != IndexMode.UNINDEXED) - { - AVMLuceneIndexer avmIndexer = getIndexer(store); - if (avmIndexer != null) - { - int last = getLastIndexedSnapshot(avmIndexer, store); - - if ((last == -1) && (! hasIndexBeenCreated(store))) - { - createIndex(store); - // ALF-7845 - last = getLastIndexedSnapshot(avmIndexer, store); - } - - int from = before != -1 ? before : last; - - if (from > after) - { - if (logger.isTraceEnabled()) - { - logger.trace("skip indexSnapshotImpl " + store + " (" + (before == -1 ? "-1, " : "") + from +", " + after +")", new Exception("Stack Trace")); - } - } - else - { - if (logger.isDebugEnabled()) - { - logger.debug("indexSnapshotImpl " + store + " (" + (before == -1 ? "-1, " : "") + from +", " + after +")", new Exception("Stack Trace")); - } - avmIndexer.index(store, from, after, getIndexMode(store)); - } - } - } - } - + public abstract void indexSnapshot(String store, int after); + /** * @param store * @return - the last indexed snapshot */ - public int getLastIndexedSnapshot(String store) - { - - AVMLuceneIndexer avmIndexer = getIndexer(store); - if (avmIndexer != null) - { - return getLastIndexedSnapshot(avmIndexer, store); - } - return -1; - } - - private int getLastIndexedSnapshot(AVMLuceneIndexer avmIndexer, String store) - { - return avmIndexer.getLastIndexedSnapshot(store); - } - + public abstract int getLastIndexedSnapshot(String store); + /** * Is the snapshot applied to the index? Is there an entry for any node that was added OR have all the nodes in the * transaction been deleted as expected? @@ -324,15 +121,7 @@ public class AVMSnapShotTriggeredIndexingMethodInterceptor implements MethodInte * @param id * @return - true if applied, false if not */ - public boolean isSnapshotIndexed(String store, int id) - { - AVMLuceneIndexer avmIndexer = getIndexer(store); - if (avmIndexer != null) - { - return avmIndexer.isSnapshotIndexed(store, id); - } - return false; - } + public abstract boolean isSnapshotIndexed(String store, int id); /** * Check if the index is up to date according to its index defintion and that all asynchronous work is done. @@ -340,27 +129,7 @@ public class AVMSnapShotTriggeredIndexingMethodInterceptor implements MethodInte * @param store * @return */ - public boolean isIndexUpToDateAndSearchable(String store) - { - - switch (getIndexMode(store)) - { - case UNINDEXED: - return false; - case SYNCHRONOUS: - case ASYNCHRONOUS: - int last = avmService.getLatestSnapshotID(store); - AVMLuceneIndexer avmIndexer = getIndexer(store); - if (avmIndexer != null) - { - avmIndexer.flushPending(); - return avmIndexer.isSnapshotSearchable(store, last); - } - return false; - default: - return false; - } - } + public abstract boolean isIndexUpToDateAndSearchable(String store); /** * Check if the index is up to date according to its index defintion i it does not check that all asynchronous work @@ -369,26 +138,7 @@ public class AVMSnapShotTriggeredIndexingMethodInterceptor implements MethodInte * @param store * @return */ - public boolean isIndexUpToDate(String store) - { - switch (getIndexMode(store)) - { - case UNINDEXED: - return true; - case SYNCHRONOUS: - case ASYNCHRONOUS: - int last = avmService.getLatestSnapshotID(store); - AVMLuceneIndexer avmIndexer = getIndexer(store); - if (avmIndexer != null) - { - avmIndexer.flushPending(); - return avmIndexer.isSnapshotIndexed(store, last); - } - return false; - default: - return false; - } - } + public abstract boolean isIndexUpToDate(String store); /** * Given an avm store name determine if it is indexed and if so how. @@ -396,174 +146,14 @@ public class AVMSnapShotTriggeredIndexingMethodInterceptor implements MethodInte * @param store * @return */ - public synchronized IndexMode getIndexMode(String store) - { - IndexMode mode = modeCache.get(store); - if (mode == null) - { - for (IndexingDefinition def : indexingDefinitions) - { - if (def.definitionType == DefinitionType.NAME) - { - if (def.pattern.matcher(store).matches()) - { - mode = def.indexMode; - modeCache.put(store, mode); - break; - } - } - else - { - AVMStoreDescriptor avmStoreDescriptor = avmService.getStore(store); - Map storeProperties = null; - if (avmStoreDescriptor != null) - { - storeProperties = avmService.getStoreProperties(store); - } - String storeType = StoreType.getStoreType(store, avmStoreDescriptor, storeProperties).toString(); - if (def.pattern.matcher(storeType).matches()) - { - mode = def.indexMode; - modeCache.put(store, mode); - break; - } + public abstract IndexMode getIndexMode(String store); - } - } - } - // No definition - if (mode == null) - { - mode = defaultMode; - modeCache.put(store, mode); - } - return mode; - } + public abstract boolean hasIndexBeenCreated(String store); - private class IndexingDefinition - { - IndexMode indexMode; + public abstract void createIndex(String store); - DefinitionType definitionType; + public abstract AVMLuceneIndexer getIndexer(String store); - Pattern pattern; + public abstract void deleteIndex(String store); - IndexingDefinition(String definition) - { - String[] split = definition.split(":", 3); - if (split.length != 3) - { - throw new AlfrescoRuntimeException("Invalid index defintion. Must be of of the form IndexMode:DefinitionType:regular expression"); - } - indexMode = IndexMode.valueOf(split[0].toUpperCase()); - definitionType = DefinitionType.valueOf(split[1].toUpperCase()); - pattern = Pattern.compile(split[2]); - } - } - - private enum DefinitionType - { - NAME, TYPE; - } - - public enum StoreType - { - STAGING, STAGING_PREVIEW, AUTHOR, AUTHOR_PREVIEW, WORKFLOW, WORKFLOW_PREVIEW, AUTHOR_WORKFLOW, AUTHOR_WORKFLOW_PREVIEW, UNKNOWN; - - public static StoreType getStoreType(String name, AVMStoreDescriptor storeDescriptor, Map storeProperties) - { - // if (avmService.getStore(name) != null) - if (storeDescriptor != null) - { - // Map storeProperties = avmService.getStoreProperties(name); - if (storeProperties.containsKey(PROP_SANDBOX_STAGING_MAIN)) - { - return StoreType.STAGING; - } - else if (storeProperties.containsKey(PROP_SANDBOX_STAGING_PREVIEW)) - { - return StoreType.STAGING_PREVIEW; - } - else if (storeProperties.containsKey(PROP_SANDBOX_AUTHOR_MAIN)) - { - return StoreType.AUTHOR; - } - else if (storeProperties.containsKey(PROP_SANDBOX_AUTHOR_PREVIEW)) - { - return StoreType.AUTHOR_PREVIEW; - } - else if (storeProperties.containsKey(PROP_SANDBOX_WORKFLOW_MAIN)) - { - return StoreType.WORKFLOW; - } - else if (storeProperties.containsKey(PROP_SANDBOX_WORKFLOW_PREVIEW)) - { - return StoreType.WORKFLOW_PREVIEW; - } - else if (storeProperties.containsKey(PROP_SANDBOX_AUTHOR_WORKFLOW_MAIN)) - { - return StoreType.AUTHOR_WORKFLOW; - } - else if (storeProperties.containsKey(PROP_SANDBOX_AUTHOR_WORKFLOW_PREVIEW)) - { - return StoreType.AUTHOR_WORKFLOW_PREVIEW; - } - else - { - return StoreType.UNKNOWN; - } - } - else - { - return StoreType.UNKNOWN; - } - } - } - - public boolean hasIndexBeenCreated(String store) - { - AVMLuceneIndexer avmIndexer = getIndexer(store); - if (avmIndexer != null) - { - avmIndexer.flushPending(); - return avmIndexer.hasIndexBeenCreated(store); - } - return false; - } - - public void createIndex(String store) - { - AVMLuceneIndexer avmIndexer = getIndexer(store); - if (avmIndexer != null) - { - if (logger.isDebugEnabled()) - { - logger.debug("createIndex " + store, new Exception("Stack Trace")); - } - avmIndexer.createIndex(store, IndexMode.SYNCHRONOUS); - } - } - - public AVMLuceneIndexer getIndexer(String store) - { - StoreRef storeRef = AVMNodeConverter.ToStoreRef(store); - Indexer indexer = indexerAndSearcher.getIndexer(storeRef); - if (indexer instanceof AVMLuceneIndexer) - { - AVMLuceneIndexer avmIndexer = (AVMLuceneIndexer) indexer; - return avmIndexer; - } - return null; - } - - public void deleteIndex(String store) - { - StoreRef storeRef = AVMNodeConverter.ToStoreRef(store); - Indexer indexer = indexerAndSearcher.getIndexer(storeRef); - if (indexer instanceof AVMLuceneIndexer) - { - AVMLuceneIndexer avmIndexer = (AVMLuceneIndexer) indexer; - avmIndexer.deleteIndex(storeRef); - } - } -} +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/search/AVMSnapShotTriggeredIndexingMethodInterceptorImpl.java b/source/java/org/alfresco/repo/search/AVMSnapShotTriggeredIndexingMethodInterceptorImpl.java new file mode 100644 index 0000000000..ad18d6907a --- /dev/null +++ b/source/java/org/alfresco/repo/search/AVMSnapShotTriggeredIndexingMethodInterceptorImpl.java @@ -0,0 +1,554 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco 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 3 of the License, or + * (at your option) any later version. + * + * Alfresco 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 Alfresco. If not, see . + */ +package org.alfresco.repo.search; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.repo.avm.AVMNodeConverter; +import org.alfresco.repo.domain.PropertyValue; +import org.alfresco.repo.search.impl.lucene.AVMLuceneIndexer; +import org.alfresco.service.cmr.avm.AVMService; +import org.alfresco.service.cmr.avm.AVMStoreDescriptor; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.namespace.QName; +import org.aopalliance.intercept.MethodInvocation; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Method interceptor for atomic indexing of AVM entries The properties can defined how stores are indexed based on type + * (as set by Alfresco the Web site management UI) or based on the name of the store. Creates and deletes are indexed + * synchronously. Updates may be asynchronous, synchronous or ignored by the index. + * + * @author andyh + */ +public class AVMSnapShotTriggeredIndexingMethodInterceptorImpl implements AVMSnapShotTriggeredIndexingMethodInterceptor +{ + private static Log logger = LogFactory.getLog(AVMSnapShotTriggeredIndexingMethodInterceptorImpl.class); + + // Copy of store properties used to tag avm stores (a store propertry) + + private AVMService avmService; + + private IndexerAndSearcher indexerAndSearcher; + + private boolean enableIndexing = true; + + private IndexMode defaultMode = IndexMode.ASYNCHRONOUS; + + private Map modeCache = new HashMap(); + + private List indexingDefinitions = new ArrayList(); + + /* (non-Javadoc) + * @see org.alfresco.repo.search.AVMSnapShotTriggeredIndexingMethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation) + */ + @Override + @SuppressWarnings("unchecked") + public Object invoke(MethodInvocation mi) throws Throwable + { + if (enableIndexing) + { + if (mi.getMethod().getName().equals("createSnapshot")) + { + // May cause any number of other stores to do snap shot under the covers via layering or do nothing + // So we have to watch what actually changes + + Object returnValue = mi.proceed(); + + Map snapShots = (Map) returnValue; + + // Index any stores that have moved on + for (String store : snapShots.keySet()) + { + int after = snapShots.get(store).intValue(); + indexSnapshot(store, after); + } + return returnValue; + } + else if (mi.getMethod().getName().equals("purgeStore")) + { + String store = (String) mi.getArguments()[0]; + Object returnValue = mi.proceed(); + + if (getIndexMode(store) != IndexMode.UNINDEXED) + { + AVMLuceneIndexer avmIndexer = getIndexer(store); + if (avmIndexer != null) + { + if (logger.isDebugEnabled()) + { + logger.debug("purgeStore " + store, new Exception("Stack Trace")); + } + avmIndexer.deleteIndex(store, IndexMode.SYNCHRONOUS); + } + } + return returnValue; + } + else if (mi.getMethod().getName().equals("createStore")) + { + String store = (String) mi.getArguments()[0]; + Object returnValue = mi.proceed(); + if (getIndexMode(store) != IndexMode.UNINDEXED) + { + createIndex(store); + } + return returnValue; + } + else if (mi.getMethod().getName().equals("renameStore")) + { + String from = (String) mi.getArguments()[0]; + String to = (String) mi.getArguments()[1]; + Object returnValue = mi.proceed(); + int after = avmService.getLatestSnapshotID(to); + + if (getIndexMode(from) != IndexMode.UNINDEXED) + { + AVMLuceneIndexer avmIndexer = getIndexer(from); + if (avmIndexer != null) + { + if (logger.isDebugEnabled()) + { + logger.debug("renameStore deleteIndex " + from, new Exception("Stack Trace")); + } + avmIndexer.deleteIndex(from, IndexMode.SYNCHRONOUS); + } + } + + if (getIndexMode(to) != IndexMode.UNINDEXED) + { + AVMLuceneIndexer avmIndexer = getIndexer(to); + if (avmIndexer != null) + { + if (logger.isDebugEnabled()) + { + logger.debug("renameStore createIndex " + to + "(0, " + after + ")", new Exception("Stack Trace")); + } + avmIndexer.createIndex(to, IndexMode.SYNCHRONOUS); + avmIndexer.index(to, 0, after, getIndexMode(to)); + } + } + return returnValue; + } + else + { + return mi.proceed(); + } + } + else + { + return mi.proceed(); + } + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.AVMSnapShotTriggeredIndexingMethodInterceptor#setAvmService(org.alfresco.service.cmr.avm.AVMService) + */ + @Override + public void setAvmService(AVMService avmService) + { + this.avmService = avmService; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.AVMSnapShotTriggeredIndexingMethodInterceptor#setIndexerAndSearcher(org.alfresco.repo.search.IndexerAndSearcher) + */ + @Override + public void setIndexerAndSearcher(IndexerAndSearcher indexerAndSearcher) + { + this.indexerAndSearcher = indexerAndSearcher; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.AVMSnapShotTriggeredIndexingMethodInterceptor#setEnableIndexing(boolean) + */ + @Override + public void setEnableIndexing(boolean enableIndexing) + { + this.enableIndexing = enableIndexing; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.AVMSnapShotTriggeredIndexingMethodInterceptor#setIndexingDefinitions(java.util.List) + */ + @Override + public void setIndexingDefinitions(List definitions) + { + indexingDefinitions.clear(); + for (String def : definitions) + { + IndexingDefinition id = new IndexingDefinition(def); + indexingDefinitions.add(id); + } + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.AVMSnapShotTriggeredIndexingMethodInterceptor#setDefaultMode(org.alfresco.repo.search.IndexMode) + */ + @Override + public void setDefaultMode(IndexMode defaultMode) + { + this.defaultMode = defaultMode; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.AVMSnapShotTriggeredIndexingMethodInterceptor#isIndexingEnabled() + */ + @Override + public boolean isIndexingEnabled() + { + return enableIndexing; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.AVMSnapShotTriggeredIndexingMethodInterceptor#indexSnapshot(java.lang.String, int, int) + */ + @Override + public void indexSnapshot(String store, int before, int after) + { + indexSnapshotImpl(store, before, after); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.AVMSnapShotTriggeredIndexingMethodInterceptor#indexSnapshot(java.lang.String, int) + */ + @Override + public void indexSnapshot(String store, int after) + { + indexSnapshotImpl(store, -1, after); + } + + private void indexSnapshotImpl(String store, int before, int after) + { + if (getIndexMode(store) != IndexMode.UNINDEXED) + { + AVMLuceneIndexer avmIndexer = getIndexer(store); + if (avmIndexer != null) + { + int last = getLastIndexedSnapshot(avmIndexer, store); + + if ((last == -1) && (! hasIndexBeenCreated(store))) + { + createIndex(store); + // ALF-7845 + last = getLastIndexedSnapshot(avmIndexer, store); + } + + int from = before != -1 ? before : last; + + if (from > after) + { + if (logger.isTraceEnabled()) + { + logger.trace("skip indexSnapshotImpl " + store + " (" + (before == -1 ? "-1, " : "") + from +", " + after +")", new Exception("Stack Trace")); + } + } + else + { + if (logger.isDebugEnabled()) + { + logger.debug("indexSnapshotImpl " + store + " (" + (before == -1 ? "-1, " : "") + from +", " + after +")", new Exception("Stack Trace")); + } + avmIndexer.index(store, from, after, getIndexMode(store)); + } + } + } + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.AVMSnapShotTriggeredIndexingMethodInterceptor#getLastIndexedSnapshot(java.lang.String) + */ + @Override + public int getLastIndexedSnapshot(String store) + { + + AVMLuceneIndexer avmIndexer = getIndexer(store); + if (avmIndexer != null) + { + return getLastIndexedSnapshot(avmIndexer, store); + } + return -1; + } + + private int getLastIndexedSnapshot(AVMLuceneIndexer avmIndexer, String store) + { + return avmIndexer.getLastIndexedSnapshot(store); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.AVMSnapShotTriggeredIndexingMethodInterceptor#isSnapshotIndexed(java.lang.String, int) + */ + @Override + public boolean isSnapshotIndexed(String store, int id) + { + AVMLuceneIndexer avmIndexer = getIndexer(store); + if (avmIndexer != null) + { + return avmIndexer.isSnapshotIndexed(store, id); + } + return false; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.AVMSnapShotTriggeredIndexingMethodInterceptor#isIndexUpToDateAndSearchable(java.lang.String) + */ + @Override + public boolean isIndexUpToDateAndSearchable(String store) + { + + switch (getIndexMode(store)) + { + case UNINDEXED: + return false; + case SYNCHRONOUS: + case ASYNCHRONOUS: + int last = avmService.getLatestSnapshotID(store); + AVMLuceneIndexer avmIndexer = getIndexer(store); + if (avmIndexer != null) + { + avmIndexer.flushPending(); + return avmIndexer.isSnapshotSearchable(store, last); + } + return false; + default: + return false; + } + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.AVMSnapShotTriggeredIndexingMethodInterceptor#isIndexUpToDate(java.lang.String) + */ + @Override + public boolean isIndexUpToDate(String store) + { + switch (getIndexMode(store)) + { + case UNINDEXED: + return true; + case SYNCHRONOUS: + case ASYNCHRONOUS: + int last = avmService.getLatestSnapshotID(store); + AVMLuceneIndexer avmIndexer = getIndexer(store); + if (avmIndexer != null) + { + avmIndexer.flushPending(); + return avmIndexer.isSnapshotIndexed(store, last); + } + return false; + default: + return false; + } + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.AVMSnapShotTriggeredIndexingMethodInterceptor#getIndexMode(java.lang.String) + */ + @Override + public synchronized IndexMode getIndexMode(String store) + { + IndexMode mode = modeCache.get(store); + if (mode == null) + { + for (IndexingDefinition def : indexingDefinitions) + { + if (def.definitionType == DefinitionType.NAME) + { + if (def.pattern.matcher(store).matches()) + { + mode = def.indexMode; + modeCache.put(store, mode); + break; + } + } + else + { + AVMStoreDescriptor avmStoreDescriptor = avmService.getStore(store); + Map storeProperties = null; + if (avmStoreDescriptor != null) + { + storeProperties = avmService.getStoreProperties(store); + } + String storeType = StoreType.getStoreType(store, avmStoreDescriptor, storeProperties).toString(); + if (def.pattern.matcher(storeType).matches()) + { + mode = def.indexMode; + modeCache.put(store, mode); + break; + } + + } + } + } + // No definition + if (mode == null) + { + mode = defaultMode; + modeCache.put(store, mode); + } + return mode; + } + + private class IndexingDefinition + { + IndexMode indexMode; + + DefinitionType definitionType; + + Pattern pattern; + + IndexingDefinition(String definition) + { + String[] split = definition.split(":", 3); + if (split.length != 3) + { + throw new AlfrescoRuntimeException("Invalid index defintion. Must be of of the form IndexMode:DefinitionType:regular expression"); + } + indexMode = IndexMode.valueOf(split[0].toUpperCase()); + definitionType = DefinitionType.valueOf(split[1].toUpperCase()); + pattern = Pattern.compile(split[2]); + } + } + + private enum DefinitionType + { + NAME, TYPE; + } + + public enum StoreType + { + STAGING, STAGING_PREVIEW, AUTHOR, AUTHOR_PREVIEW, WORKFLOW, WORKFLOW_PREVIEW, AUTHOR_WORKFLOW, AUTHOR_WORKFLOW_PREVIEW, UNKNOWN; + + public static StoreType getStoreType(String name, AVMStoreDescriptor storeDescriptor, Map storeProperties) + { + // if (avmService.getStore(name) != null) + if (storeDescriptor != null) + { + // Map storeProperties = avmService.getStoreProperties(name); + if (storeProperties.containsKey(PROP_SANDBOX_STAGING_MAIN)) + { + return StoreType.STAGING; + } + else if (storeProperties.containsKey(PROP_SANDBOX_STAGING_PREVIEW)) + { + return StoreType.STAGING_PREVIEW; + } + else if (storeProperties.containsKey(PROP_SANDBOX_AUTHOR_MAIN)) + { + return StoreType.AUTHOR; + } + else if (storeProperties.containsKey(PROP_SANDBOX_AUTHOR_PREVIEW)) + { + return StoreType.AUTHOR_PREVIEW; + } + else if (storeProperties.containsKey(PROP_SANDBOX_WORKFLOW_MAIN)) + { + return StoreType.WORKFLOW; + } + else if (storeProperties.containsKey(PROP_SANDBOX_WORKFLOW_PREVIEW)) + { + return StoreType.WORKFLOW_PREVIEW; + } + else if (storeProperties.containsKey(PROP_SANDBOX_AUTHOR_WORKFLOW_MAIN)) + { + return StoreType.AUTHOR_WORKFLOW; + } + else if (storeProperties.containsKey(PROP_SANDBOX_AUTHOR_WORKFLOW_PREVIEW)) + { + return StoreType.AUTHOR_WORKFLOW_PREVIEW; + } + else + { + return StoreType.UNKNOWN; + } + } + else + { + return StoreType.UNKNOWN; + } + } + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.AVMSnapShotTriggeredIndexingMethodInterceptor#hasIndexBeenCreated(java.lang.String) + */ + @Override + public boolean hasIndexBeenCreated(String store) + { + AVMLuceneIndexer avmIndexer = getIndexer(store); + if (avmIndexer != null) + { + avmIndexer.flushPending(); + return avmIndexer.hasIndexBeenCreated(store); + } + return false; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.AVMSnapShotTriggeredIndexingMethodInterceptor#createIndex(java.lang.String) + */ + @Override + public void createIndex(String store) + { + AVMLuceneIndexer avmIndexer = getIndexer(store); + if (avmIndexer != null) + { + if (logger.isDebugEnabled()) + { + logger.debug("createIndex " + store, new Exception("Stack Trace")); + } + avmIndexer.createIndex(store, IndexMode.SYNCHRONOUS); + } + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.AVMSnapShotTriggeredIndexingMethodInterceptor#getIndexer(java.lang.String) + */ + @Override + public AVMLuceneIndexer getIndexer(String store) + { + StoreRef storeRef = AVMNodeConverter.ToStoreRef(store); + Indexer indexer = indexerAndSearcher.getIndexer(storeRef); + if (indexer instanceof AVMLuceneIndexer) + { + AVMLuceneIndexer avmIndexer = (AVMLuceneIndexer) indexer; + return avmIndexer; + } + return null; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.AVMSnapShotTriggeredIndexingMethodInterceptor#deleteIndex(java.lang.String) + */ + @Override + public void deleteIndex(String store) + { + StoreRef storeRef = AVMNodeConverter.ToStoreRef(store); + Indexer indexer = indexerAndSearcher.getIndexer(storeRef); + if (indexer instanceof AVMLuceneIndexer) + { + AVMLuceneIndexer avmIndexer = (AVMLuceneIndexer) indexer; + avmIndexer.deleteIndex(storeRef); + } + } +} diff --git a/source/java/org/alfresco/repo/search/impl/solr/SolrQueryHTTPClient.java b/source/java/org/alfresco/repo/search/impl/solr/SolrQueryHTTPClient.java index c772364518..c18eaa987c 100644 --- a/source/java/org/alfresco/repo/search/impl/solr/SolrQueryHTTPClient.java +++ b/source/java/org/alfresco/repo/search/impl/solr/SolrQueryHTTPClient.java @@ -37,6 +37,8 @@ import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; import org.alfresco.service.cmr.search.ResultSet; import org.alfresco.service.cmr.search.SearchParameters; import org.alfresco.service.cmr.search.SearchParameters.FieldFacet; +import org.alfresco.service.cmr.search.SearchParameters.FieldFacetMethod; +import org.alfresco.service.cmr.search.SearchParameters.FieldFacetSort; import org.alfresco.service.cmr.search.SearchParameters.SortDefinition; import org.alfresco.service.cmr.security.PermissionService; import org.apache.commons.codec.net.URLCodec; @@ -112,7 +114,11 @@ public class SolrQueryHTTPClient { try { - +// Simple demo +// FieldFacet ff = new FieldFacet("@"+ContentModel.PROP_NAME); +// ff.setLimit(2); +// searchParameters.addFieldFacet(ff); + URLCodec encoder = new URLCodec(); StringBuilder url = new StringBuilder(); url.append(baseUrl); @@ -208,7 +214,32 @@ public class SolrQueryHTTPClient for(FieldFacet facet : searchParameters.getFieldFacets()) { url.append("&facet.field=").append(encoder.encode(facet.getField(), "UTF-8")); - url.append("&").append(encoder.encode("f."+facet.getField()+".limit", "UTF-8")).append("=").append(encoder.encode(""+facet.getLimit(), "UTF-8")); + if(facet.getEnumMethodCacheMinDF() != 0) + { + url.append("&").append(encoder.encode("f."+facet.getField()+".facet.enum.cache.minDf", "UTF-8")).append("=").append(encoder.encode(""+facet.getEnumMethodCacheMinDF(), "UTF-8")); + } + url.append("&").append(encoder.encode("f."+facet.getField()+".facet.limit", "UTF-8")).append("=").append(encoder.encode(""+facet.getLimit(), "UTF-8")); + if(facet.getMethod() != null) + { + url.append("&").append(encoder.encode("f."+facet.getField()+".facet.method", "UTF-8")).append("=").append(encoder.encode(facet.getMethod()==FieldFacetMethod.ENUM ? "enum" : "fc", "UTF-8")); + } + if(facet.getMinCount() != 0) + { + url.append("&").append(encoder.encode("f."+facet.getField()+".facet.mincount", "UTF-8")).append("=").append(encoder.encode(""+facet.getMinCount(), "UTF-8")); + } + if(facet.getOffset() != 0) + { + url.append("&").append(encoder.encode("f."+facet.getField()+".facet.offset", "UTF-8")).append("=").append(encoder.encode(""+facet.getOffset(), "UTF-8")); + } + if(facet.getPrefix() != null) + { + url.append("&").append(encoder.encode("f."+facet.getField()+".facet.prefix", "UTF-8")).append("=").append(encoder.encode(""+facet.getPrefix(), "UTF-8")); + } + if(facet.getSort() != null) + { + url.append("&").append(encoder.encode("f."+facet.getField()+".facet.sort", "UTF-8")).append("=").append(encoder.encode(facet.getSort() == FieldFacetSort.COUNT ? "count" : "index", "UTF-8")); + } + } }