diff --git a/e2e-test/pom.xml b/e2e-test/pom.xml index 4016219ff..456a86403 100644 --- a/e2e-test/pom.xml +++ b/e2e-test/pom.xml @@ -13,7 +13,7 @@ 6.0.1.2 6.0.0.4 - 3.0.11 + 3.0.12 3.2.0-SNAPSHOT diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/lifecycle/SolrCoreLoadListener.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/lifecycle/SolrCoreLoadListener.java index 5f4e6db5f..c1ec3719d 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/lifecycle/SolrCoreLoadListener.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/lifecycle/SolrCoreLoadListener.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2016 Alfresco Software Limited. + * Copyright (C) 2005-2019 Alfresco Software Limited. * * This file is part of Alfresco * @@ -16,31 +16,291 @@ * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see . */ - package org.alfresco.solr.lifecycle; +import static java.util.Optional.ofNullable; + +import org.alfresco.opencmis.dictionary.CMISStrictDictionaryService; import org.alfresco.solr.AlfrescoCoreAdminHandler; +import org.alfresco.solr.AlfrescoSolrDataModel; +import org.alfresco.solr.SolrInformationServer; +import org.alfresco.solr.SolrKeyResourceLoader; +import org.alfresco.solr.client.SOLRAPIClient; +import org.alfresco.solr.client.SOLRAPIClientFactory; +import org.alfresco.solr.content.SolrContentStore; +import org.alfresco.solr.tracker.AclTracker; +import org.alfresco.solr.tracker.CascadeTracker; +import org.alfresco.solr.tracker.CommitTracker; +import org.alfresco.solr.tracker.ContentTracker; +import org.alfresco.solr.tracker.MetadataTracker; +import org.alfresco.solr.tracker.ModelTracker; +import org.alfresco.solr.tracker.SolrTrackerScheduler; +import org.alfresco.solr.tracker.Tracker; +import org.alfresco.solr.tracker.TrackerRegistry; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.common.util.StrUtils; import org.apache.solr.core.AbstractSolrEventListener; +import org.apache.solr.core.CloseHook; import org.apache.solr.core.CoreContainer; +import org.apache.solr.core.CoreDescriptorDecorator; +import org.apache.solr.core.PluginInfo; import org.apache.solr.core.SolrCore; +import org.apache.solr.core.SolrResourceLoader; +import org.apache.solr.handler.ReplicationHandler; +import org.apache.solr.request.SolrRequestHandler; import org.apache.solr.search.SolrIndexSearcher; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Properties; +import java.util.function.Function; +import java.util.function.Predicate; /** - * Listens for the first searcher to be created for a core and registers the trackers + * Listeners for *FIRST SEARCHER* events in order to prepare and register the SolrContentStore and the Tracking Subsystem. * * @author Gethin James + * @author Andrea Gazzarini */ -public class SolrCoreLoadListener extends AbstractSolrEventListener { +public class SolrCoreLoadListener extends AbstractSolrEventListener +{ + private static final Logger LOGGER = LoggerFactory.getLogger(SolrCoreLoadListener.class); - public SolrCoreLoadListener(SolrCore core) { + /** + * Builds a new listener instance with the given {@link SolrCore} (event source). + * + * @param core the {@link SolrCore} instance representing the event source of this listener. + */ + public SolrCoreLoadListener(SolrCore core) + { super(core); } @Override - public void newSearcher(SolrIndexSearcher newSearcher, SolrIndexSearcher currentSearcher) { + public void newSearcher(SolrIndexSearcher newSearcher, SolrIndexSearcher currentSearcher) + { CoreContainer coreContainer = getCore().getCoreContainer(); - AlfrescoCoreAdminHandler coreAdminHandler = (AlfrescoCoreAdminHandler) coreContainer.getMultiCoreHandler(); + AlfrescoCoreAdminHandler admin = (AlfrescoCoreAdminHandler) coreContainer.getMultiCoreHandler(); + SolrCore core = getCore(); + + TrackerRegistry trackerRegistry = admin.getTrackerRegistry(); + Properties coreProperties = new CoreDescriptorDecorator(core.getCoreDescriptor()).getProperties(); + + SolrResourceLoader loader = core.getLatestSchema().getResourceLoader(); + SolrKeyResourceLoader keyResourceLoader = new SolrKeyResourceLoader(loader); + SOLRAPIClientFactory clientFactory = new SOLRAPIClientFactory(); + SOLRAPIClient repositoryClient = + clientFactory.getSOLRAPIClient(coreProperties, keyResourceLoader, + AlfrescoSolrDataModel.getInstance().getDictionaryService(CMISStrictDictionaryService.DEFAULT), + AlfrescoSolrDataModel.getInstance().getNamespaceDAO()); + + SolrContentStore contentStore = new SolrContentStore(coreContainer.getSolrHome()); + SolrInformationServer informationServer = new SolrInformationServer(admin, core, repositoryClient, contentStore); + coreProperties.putAll(informationServer.getProps()); + admin.getInformationServers().put(core.getName(), informationServer); + + final SolrTrackerScheduler scheduler = admin.getScheduler(); + + // Prevents other threads from registering the ModelTracker at the same time + // Create model tracker and load all the persisted models + synchronized(SolrCoreLoadListener.class) + { + createModelTracker(core.getName(), + trackerRegistry, + coreProperties, + coreContainer.getSolrHome(), + repositoryClient, + informationServer, + scheduler); + } + + boolean trackersHaveBeenEnabled = Boolean.parseBoolean(coreProperties.getProperty("enable.alfresco.tracking", "true")); + boolean owningCoreIsSlave = isSlaveModeEnabledFor(core); + + // Guard conditions: if trackers must be disabled then immediately return, we've done here. + // Case #1: trackers have been explicitly disabled. + if (!trackersHaveBeenEnabled) + { + LOGGER.info("SearchServices Core Trackers have been explicitly disabled on core \"{}\" through \"enable.alfresco.tracking\" configuration property.", core.getName()); + return; + } + + // Case #2: we are on a slave node. + if (owningCoreIsSlave) + { + LOGGER.info("SearchServices Core Trackers have been disabled on core \"{}\" because it is a slave core.", core.getName()); + return; + } + + LOGGER.info("SearchServices Tracking Subsystem starts on core {}", core.getName()); + if (trackerRegistry.hasTrackersForCore(core.getName())) + { + LOGGER.info("Trackers for " + core.getName()+ " is already registered, shutting them down."); + shutdownTrackers(core.getName(), trackerRegistry.getTrackersForCore(core.getName()), scheduler); + trackerRegistry.removeTrackersForCore(core.getName()); + admin.getInformationServers().remove(core.getName()); + } + + final List trackers = createCoreTrackers(core.getName(), trackerRegistry, coreProperties, scheduler, repositoryClient, informationServer); + + CommitTracker commitTracker = new CommitTracker(coreProperties, repositoryClient, core.getName(), informationServer, trackers); + trackerRegistry.register(core.getName(), commitTracker); + scheduler.schedule(commitTracker, core.getName(), coreProperties); + + LOGGER.info("SearchServices Core Trackers have been correctly registered and scheduled."); + + //Add the commitTracker to the list of scheduled trackers that can be shutdown + trackers.add(commitTracker); + + core.addCloseHook(new CloseHook() + { + @Override + public void preClose(SolrCore core) + { + LOGGER.info("Tracking Subsystem shutdown procedure for core {} has been started.", core.getName()); + shutdownTrackers(core.getName(), trackers, scheduler); + } + + @Override + public void postClose(SolrCore core) + { + LOGGER.info("Shutdown procedure for core {} has been completed.", core.getName()); + } + }); - SolrCoreLoadRegistration.registerForCore(coreAdminHandler, coreContainer, getCore(), getCore().getName()); } -} + + List createCoreTrackers(String coreName, + TrackerRegistry trackerRegistry, + Properties props, + SolrTrackerScheduler scheduler, + SOLRAPIClient repositoryClient, + SolrInformationServer srv) + { + List trackers = new ArrayList<>(); + + AclTracker aclTracker = new AclTracker(props, repositoryClient, coreName, srv); + trackerRegistry.register(coreName, aclTracker); + scheduler.schedule(aclTracker, coreName, props); + + ContentTracker contentTrkr = new ContentTracker(props, repositoryClient, coreName, srv); + trackerRegistry.register(coreName, contentTrkr); + scheduler.schedule(contentTrkr, coreName, props); + + MetadataTracker metaTrkr = new MetadataTracker(props, repositoryClient, coreName, srv); + trackerRegistry.register(coreName, metaTrkr); + scheduler.schedule(metaTrkr, coreName, props); + + CascadeTracker cascadeTrkr = new CascadeTracker(props, repositoryClient, coreName, srv); + trackerRegistry.register(coreName, cascadeTrkr); + scheduler.schedule(cascadeTrkr, coreName, props); + + //The CommitTracker will acquire these locks in order + //The ContentTracker will likely have the longest runs so put it first to ensure the MetadataTracker is not paused while + //waiting for the ContentTracker to release it's lock. + //The aclTracker will likely have the shortest runs so put it last. + trackers.add(cascadeTrkr); + trackers.add(contentTrkr); + trackers.add(metaTrkr); + trackers.add(aclTracker); + return trackers; + } + + private void createModelTracker(String coreName, + TrackerRegistry trackerRegistry, + Properties props, + String solrHome, + SOLRAPIClient repositoryClient, + SolrInformationServer srv, + SolrTrackerScheduler scheduler) + { + ModelTracker mTracker = trackerRegistry.getModelTracker(); + if (mTracker == null) + { + LOGGER.debug("Creating a new Model Tracker instance."); + mTracker = new ModelTracker(solrHome, props, repositoryClient, coreName, srv); + trackerRegistry.setModelTracker(mTracker); + + LOGGER.info("Model Tracker: ensuring first model sync."); + mTracker.ensureFirstModelSync(); + + scheduler.schedule(mTracker, coreName, props); + + LOGGER.info("Model Tracker has been correctly initialised, registered and scheduled."); + } + } + + /** + * Shuts down the trackers for a core. + * + * The trackers are only deleted from the scheduler if they are the exact same instance of the Tracker class + * passed into this method. + * For example, you could have 2 cores of the same name and have the trackers registered with the scheduler BUT + * the scheduler only keys by core name. The Collections passed into this method are only removed + * from the scheduler if the instances are == (equal). See scheduler.deleteJobForTrackerInstance() + * + * Trackers are not removed from the registry because the registry only keys by core name; its possible to + * have multiple cores of the same name running. Left over trackers in the registry are cleaned up by the CoreContainer + * shutdown, that happens in the the AlfrescoCoreAdminHandler.shutdown(). + * + * @param coreName The name of the core + * @param coreTrackers A collection of trackers + * @param scheduler The scheduler + */ + void shutdownTrackers(String coreName, Collection coreTrackers, SolrTrackerScheduler scheduler) + { + try + { + LOGGER.info("Shutting down Trackers Subsystem for core \"{}\" which contains {} core trackers.", coreName, coreTrackers.size()); + + // Sets the shutdown flag on the trackers to stop them from doing any more work + coreTrackers.forEach(tracker -> tracker.setShutdown(true)); + + if (!scheduler.isShutdown()) + { + coreTrackers.forEach(tracker -> scheduler.deleteJobForTrackerInstance(coreName, tracker) ); + } + + coreTrackers.forEach(Tracker::shutdown); + } + catch (Exception e) + { + LOGGER.error("Tracking Subsystem shutdown procedure failed to shutdown trackers for core {}. See the stacktrace below for further details.", coreName, e); + } + } + + /** + * Checks if the content store belonging to the hosting Solr node must be set in read only mode. + * + * @param core the hosting {@link SolrCore} instance. + * @return true if the content store must be set in read only mode, false otherwise. + */ + boolean isSlaveModeEnabledFor(SolrCore core) + { + Predicate onlyReplicationHandler = + plugin -> "/replication".equals(plugin.name) + || plugin.className.endsWith(ReplicationHandler.class.getSimpleName()); + + Function isSlaveModeEnabled = + params -> ofNullable(params) + .map(configuration -> { + Object enable = configuration.get("enable"); + return enable == null || + (enable instanceof String ? StrUtils.parseBool((String)enable) : Boolean.TRUE.equals(enable));}) + .orElse(false); + + return core.getSolrConfig().getPluginInfos(SolrRequestHandler.class.getName()) + .stream() + .filter(PluginInfo::isEnabled) + .filter(onlyReplicationHandler) + .findFirst() + .map(plugin -> plugin.initArgs) + .map(params -> params.get("slave")) + .map(NamedList.class::cast) + .map(isSlaveModeEnabled) + .orElse(false); + } +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/lifecycle/SolrCoreLoadRegistration.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/lifecycle/SolrCoreLoadRegistration.java deleted file mode 100644 index c7f46f770..000000000 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/lifecycle/SolrCoreLoadRegistration.java +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright (C) 2005-2016 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.solr.lifecycle; - -import java.lang.invoke.MethodHandles; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Properties; - -import org.alfresco.opencmis.dictionary.CMISStrictDictionaryService; -import org.alfresco.solr.AlfrescoCoreAdminHandler; -import org.alfresco.solr.AlfrescoSolrDataModel; -import org.alfresco.solr.SolrInformationServer; -import org.alfresco.solr.SolrKeyResourceLoader; -import org.alfresco.solr.client.SOLRAPIClient; -import org.alfresco.solr.client.SOLRAPIClientFactory; -import org.alfresco.solr.content.SolrContentStore; -import org.alfresco.solr.tracker.AclTracker; -import org.alfresco.solr.tracker.CascadeTracker; -import org.alfresco.solr.tracker.CommitTracker; -import org.alfresco.solr.tracker.ContentTracker; -import org.alfresco.solr.tracker.MetadataTracker; -import org.alfresco.solr.tracker.ModelTracker; -import org.alfresco.solr.tracker.SolrTrackerScheduler; -import org.alfresco.solr.tracker.Tracker; -import org.alfresco.solr.tracker.TrackerRegistry; -import org.apache.solr.core.CloseHook; -import org.apache.solr.core.CoreContainer; -import org.apache.solr.core.CoreDescriptorDecorator; -import org.apache.solr.core.SolrCore; -import org.apache.solr.core.SolrResourceLoader; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Deals with core registration when the core is loaded. - * - * @author Gethin James - */ -public class SolrCoreLoadRegistration { - - private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - - /** - * Registers with the admin handler the information server and the trackers. - */ - public static void registerForCore(AlfrescoCoreAdminHandler adminHandler, CoreContainer coreContainer, SolrCore core, - String coreName) - { - - - TrackerRegistry trackerRegistry = adminHandler.getTrackerRegistry(); - Properties props = new CoreDescriptorDecorator(core.getCoreDescriptor()).getProperties(); - //Prepare cores - SolrResourceLoader loader = core.getLatestSchema().getResourceLoader(); - SolrKeyResourceLoader keyResourceLoader = new SolrKeyResourceLoader(loader); - SOLRAPIClientFactory clientFactory = new SOLRAPIClientFactory(); - SOLRAPIClient repositoryClient = clientFactory.getSOLRAPIClient(props, keyResourceLoader, - AlfrescoSolrDataModel.getInstance().getDictionaryService(CMISStrictDictionaryService.DEFAULT), - AlfrescoSolrDataModel.getInstance().getNamespaceDAO()); - //Start content store - SolrContentStore contentStore = new SolrContentStore(coreContainer.getSolrHome()); - SolrInformationServer srv = new SolrInformationServer(adminHandler, core, repositoryClient, contentStore); - props.putAll(srv.getProps()); - adminHandler.getInformationServers().put(coreName, srv); - - SolrTrackerScheduler scheduler = adminHandler.getScheduler(); - - // Prevents other threads from registering the ModelTracker at the same time - // Create model tracker and load all the persisted models - createModelTracker(coreName, - trackerRegistry, - props, - coreContainer.getSolrHome(), - repositoryClient, - srv, - scheduler); - - - log.info("Starting to track " + coreName); - if (Boolean.parseBoolean(props.getProperty("enable.alfresco.tracking", "false"))) - { - - if (trackerRegistry.hasTrackersForCore(coreName)) - { - log.info("Trackers for " + coreName+ " is already registered, shutting them down."); - shutdownTrackers(coreName, trackerRegistry.getTrackersForCore(coreName),scheduler); - trackerRegistry.removeTrackersForCore(coreName); - adminHandler.getInformationServers().remove(coreName); - } - - List trackers = createCoreTrackers(coreName, trackerRegistry, props, scheduler, repositoryClient, srv); - - CommitTracker commitTracker = new CommitTracker(props, repositoryClient, coreName, srv, trackers); - trackerRegistry.register(coreName, commitTracker); - scheduler.schedule(commitTracker, coreName, props); - log.info("The Trackers are now scheduled to run"); - trackers.add(commitTracker); //Add the commitTracker to the list of scheduled trackers that can be shutdown - - core.addCloseHook(new CloseHook() - { - @Override - public void preClose(SolrCore core) - { - log.info("Shutting down " + core.getName()); - SolrCoreLoadRegistration.shutdownTrackers(core.getName(), trackers, scheduler); - } - - @Override - public void postClose(SolrCore core) - { - // Nothing to be done here - } - }); - } - } - - /** - * Creates the trackers - * - * @param coreName - * @param trackerRegistry - * @param props - * @param scheduler - * @param repositoryClient - * @param srv - * @return A list of trackers - */ - private static List createCoreTrackers(String coreName, - TrackerRegistry trackerRegistry, - Properties props, - SolrTrackerScheduler scheduler, - SOLRAPIClient repositoryClient, - SolrInformationServer srv) { - List trackers = new ArrayList(); - - AclTracker aclTracker = new AclTracker(props, repositoryClient, coreName, srv); - trackerRegistry.register(coreName, aclTracker); - scheduler.schedule(aclTracker, coreName, props); - - ContentTracker contentTrkr = new ContentTracker(props, repositoryClient, coreName, srv); - trackerRegistry.register(coreName, contentTrkr); - scheduler.schedule(contentTrkr, coreName, props); - - MetadataTracker metaTrkr = new MetadataTracker(props, repositoryClient, coreName, srv); - trackerRegistry.register(coreName, metaTrkr); - scheduler.schedule(metaTrkr, coreName, props); - - CascadeTracker cascadeTrkr = new CascadeTracker(props, repositoryClient, coreName, srv); - trackerRegistry.register(coreName, cascadeTrkr); - scheduler.schedule(cascadeTrkr, coreName, props); - - //The CommitTracker will acquire these locks in order - //The ContentTracker will likely have the longest runs so put it first to ensure the MetadataTracker is not paused while - //waiting for the ContentTracker to release it's lock. - //The aclTracker will likely have the shortest runs so put it last. - trackers.add(cascadeTrkr); - trackers.add(contentTrkr); - trackers.add(metaTrkr); - trackers.add(aclTracker); - return trackers; - } - - - /** - * Create model tracker and load persisted models. - * - * @param coreName - * @param trackerRegistry - * @param props - * @param solrHome - * @param repositoryClient - * @param srv - * @param scheduler - * @return true if model tracker has been created, false if it already exists. - */ - private synchronized static void createModelTracker(String coreName, - TrackerRegistry trackerRegistry, - Properties props, - String solrHome, - SOLRAPIClient repositoryClient, - SolrInformationServer srv, - SolrTrackerScheduler scheduler) - { - ModelTracker mTracker = trackerRegistry.getModelTracker(); - if (mTracker == null) - { - log.debug("Creating ModelTracker"); - mTracker = new ModelTracker(solrHome, props, repositoryClient, - coreName, srv); - - trackerRegistry.setModelTracker(mTracker); - log.info("Ensuring first model sync."); - mTracker.ensureFirstModelSync(); - log.info("Done ensuring first model sync."); - - //Scheduling the ModelTracker. - scheduler.schedule(mTracker, coreName, props); - } - - } - - /** - * Shuts down the trackers for a core. - * - * The trackers are only deleted from the scheduler if they are the exact same instance of the Tracker class - * passed into this method. - * For example, you could have 2 cores of the same name and have the trackers registered with the scheduler BUT - * the scheduler only keys by core name. The Collections passed into this method are only removed - * from the scheduler if the instances are == (equal). See scheduler.deleteJobForTrackerInstance() - * - * Trackers are not removed from the registry because the registry only keys by core name; its possible to - * have multiple cores of the same name running. Left over trackers in the registry are cleaned up by the CoreContainer - * shutdown, that happens in the the AlfrescoCoreAdminHandler.shutdown(). - * - * @param coreName The name of the core - * @param coreTrackers A collection of trackers - * @param scheduler The scheduler - */ - public static void shutdownTrackers(String coreName, Collection coreTrackers, SolrTrackerScheduler scheduler) - { - try - { - log.info("Shutting down " + coreName + " with " + coreTrackers.size() + " trackers."); - - // Sets the shutdown flag on the trackers to stop them from doing any more work - coreTrackers.forEach(tracker -> tracker.setShutdown(true)); - - if (!scheduler.isShutdown()) - { - coreTrackers.forEach(tracker -> scheduler.deleteJobForTrackerInstance(coreName,tracker) ); - } - - coreTrackers.forEach(tracker -> tracker.shutdown()); - } - catch (Exception e) - { - log.error("Failed to shutdown trackers for core "+coreName, e); - } - } -} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/AbstractTracker.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/AbstractTracker.java index 7915ea9b3..ae944dd4b 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/AbstractTracker.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/AbstractTracker.java @@ -327,15 +327,17 @@ public abstract class AbstractTracker implements Tracker throw new IndexTrackingShutdownException(); } } - + + @Override public void setShutdown(boolean shutdown) { this.shutdown = shutdown; } + @Override public void shutdown() { - log.warn("Core "+ coreName+" shutdown called on tracker. " + getClass().getSimpleName() + " " + hashCode()); + log.warn("Core " + coreName + " shutdown called on tracker. " + getClass().getSimpleName() + " " + hashCode()); setShutdown(true); if(this.threadHandler != null) { diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ModelTracker.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ModelTracker.java index ba4e00fa9..c19c31eaa 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ModelTracker.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/ModelTracker.java @@ -115,12 +115,16 @@ public class ModelTracker extends AbstractTracker implements Tracker loadPersistedModels(); } - public boolean hasMaintenance() { + @Override + public boolean hasMaintenance() + { return false; } - public void maintenance() { - + @Override + public void maintenance() + { + // Nothing to be done here } /** @@ -203,7 +207,7 @@ public class ModelTracker extends AbstractTracker implements Tracker public void trackModels(boolean onlyFirstTime) throws AuthenticationException, IOException, JSONException { - boolean requiresWriteLock = false; + boolean requiresWriteLock; modelLock.readLock().lock(); try { @@ -271,10 +275,6 @@ public class ModelTracker extends AbstractTracker implements Tracker /** * Tracks models. Reflects changes and updates on disk copy - * - * @throws AuthenticationException - * @throws IOException - * @throws JSONException */ private void trackModelsImpl() throws AuthenticationException, IOException, JSONException { @@ -480,16 +480,10 @@ public class ModelTracker extends AbstractTracker implements Tracker } return expandedQName; - } - /** - * @param alfrescoModelDir - * @param modelName - */ private void removeMatchingModels(File alfrescoModelDir, QName modelName) { - final String prefix = modelName.toPrefixString(this.infoSrv.getNamespaceDAO()).replace(":", ".") + "."; final String postFix = ".xml"; diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/lifecycle/SolrCoreLoadListenerTest.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/lifecycle/SolrCoreLoadListenerTest.java new file mode 100644 index 000000000..8462aefc0 --- /dev/null +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/lifecycle/SolrCoreLoadListenerTest.java @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2005-2013 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.solr.lifecycle; + +import org.alfresco.solr.SolrInformationServer; +import org.alfresco.solr.client.SOLRAPIClient; +import org.alfresco.solr.tracker.AclTracker; +import org.alfresco.solr.tracker.CascadeTracker; +import org.alfresco.solr.tracker.ContentTracker; +import org.alfresco.solr.tracker.MetadataTracker; +import org.alfresco.solr.tracker.SolrTrackerScheduler; +import org.alfresco.solr.tracker.Tracker; +import org.alfresco.solr.tracker.TrackerRegistry; +import org.apache.solr.core.SolrConfig; +import org.apache.solr.core.SolrCore; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.xml.sax.InputSource; + +import java.util.List; +import java.util.Properties; + +import static java.util.Arrays.asList; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.same; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * Unit tests for the {@link SolrCoreLoadListener}. + * + * @author Andrea Gazzarini + * @since 1.5 + */ +@RunWith(MockitoJUnitRunner.class) +public class SolrCoreLoadListenerTest +{ + private SolrCoreLoadListener listener; + + @Mock + private SolrCore core; + + @Mock + private SolrTrackerScheduler scheduler; + + @Mock + private SOLRAPIClient api; + + @Mock + private SolrInformationServer informationServer; + + @Mock + private TrackerRegistry registry; + + private Properties coreProperties; + + private String coreName = "XYZ"; + + @Before + public void setUp() + { + listener = new SolrCoreLoadListener(core); + when(core.getName()).thenReturn(coreName); + + coreProperties = new Properties(); + } + + @Test + public void coreTrackersRegistrationAndScheduling() + { + List coreTrackers = listener.createCoreTrackers(core.getName(), registry, coreProperties, scheduler, api, informationServer); + + verify(registry).register(eq(coreName), any(AclTracker.class)); + verify(registry).register(eq(coreName), any(ContentTracker.class)); + verify(registry).register(eq(coreName), any(MetadataTracker.class)); + verify(registry).register(eq(coreName), any(CascadeTracker.class)); + + verify(scheduler).schedule(any(AclTracker.class), eq(coreName), same(coreProperties)); + verify(scheduler).schedule(any(ContentTracker.class), eq(coreName), same(coreProperties)); + verify(scheduler).schedule(any(MetadataTracker.class), eq(coreName), same(coreProperties)); + verify(scheduler).schedule(any(CascadeTracker.class), eq(coreName), same(coreProperties)); + + assertEquals(4, coreTrackers.size()); + } + + @Test + public void trackersShutDownProcedure() + { + List coreTrackers = + asList(mock(AclTracker.class), mock(ContentTracker.class), mock(MetadataTracker.class), mock(CascadeTracker.class)); + + listener.shutdownTrackers(coreName, coreTrackers, scheduler); + + coreTrackers.forEach(tracker -> verify(tracker).setShutdown(true)); + coreTrackers.forEach(tracker -> verify(scheduler).deleteJobForTrackerInstance(core.getName(), tracker)); + coreTrackers.forEach(tracker -> verify(tracker).shutdown()); + } + + @Test + public void noReplicationHandlerDefined_thenContentStoreIsInReadWriteMode() throws Exception + { + prepare("solrconfig_no_replication_handler_defined.xml"); + assertFalse("If no replication handler is defined, then we expect to run a RW content store.", listener.isSlaveModeEnabledFor(core)); + } + + @Test + public void emptyReplicationHandlerDefined_thenContentStoreIsInReadWriteMode() throws Exception + { + prepare("solrconfig_empty_replication_handler.xml"); + assertFalse("If an empty replication handler is defined, then we expect to run a RW content store.", listener.isSlaveModeEnabledFor(core)); + } + + @Test + public void slaveReplicationHandlerDefinedButDisabled_thenContentStoreIsInReadWriteMode() throws Exception + { + prepare("solrconfig_slave_disabled_replication_handler.xml"); + assertFalse("If a slave replication handler is defined but disabled, then we expect to run a RW content store.", listener.isSlaveModeEnabledFor(core)); + } + + @Test + public void masterReplicationHandlerDefined_thenContentStoreIsInReadWriteMode() throws Exception + { + prepare("solrconfig_master_replication_handler.xml"); + assertFalse("If a master replication handler is defined but disabled, then we expect to run a RW content store.", listener.isSlaveModeEnabledFor(core)); + } + + @Test + public void masterReplicationHandlerDefinedButDisabled_thenContentStoreIsInReadWriteMode() throws Exception + { + prepare("solrconfig_master_disabled_replication_handler.xml"); + assertFalse("If a master replication handler is defined but disabled, then we expect to run a RW content store.", listener.isSlaveModeEnabledFor(core)); + } + + @Test + public void slaveReplicationHandlerDefined_thenContentStoreIsInReadOnlyMode() throws Exception + { + prepare("solrconfig_slave_replication_handler.xml"); + assertTrue("If a slave replication handler is defined, then we expect to run a RO content store.", listener.isSlaveModeEnabledFor(core)); + } + + private void prepare(String configName) throws Exception + { + SolrConfig solrConfig = new SolrConfig(configName, new InputSource(getClass().getResourceAsStream("/test-files/" + configName))); + when(core.getSolrConfig()).thenReturn(solrConfig); + } +} \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/resources/test-files/solrconfig_empty_replication_handler.xml b/search-services/alfresco-search/src/test/resources/test-files/solrconfig_empty_replication_handler.xml new file mode 100644 index 000000000..e05ab0538 --- /dev/null +++ b/search-services/alfresco-search/src/test/resources/test-files/solrconfig_empty_replication_handler.xml @@ -0,0 +1,36 @@ + + + 6.6.5 + /tmp/test + + + ${solr.lock.type:native} + + + + + + + + + + commit + schema.xml + + + + + + false + id + lucene + + + + + Query me! + + \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/resources/test-files/solrconfig_master_disabled_replication_handler.xml b/search-services/alfresco-search/src/test/resources/test-files/solrconfig_master_disabled_replication_handler.xml new file mode 100644 index 000000000..0ed005b25 --- /dev/null +++ b/search-services/alfresco-search/src/test/resources/test-files/solrconfig_master_disabled_replication_handler.xml @@ -0,0 +1,37 @@ + + + 6.6.5 + /tmp/test + + + ${solr.lock.type:native} + + + + + + + + + + false + commit + schema.xml + + + + + + false + id + lucene + + + + + Query me! + + \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/resources/test-files/solrconfig_master_replication_handler.xml b/search-services/alfresco-search/src/test/resources/test-files/solrconfig_master_replication_handler.xml new file mode 100644 index 000000000..e05ab0538 --- /dev/null +++ b/search-services/alfresco-search/src/test/resources/test-files/solrconfig_master_replication_handler.xml @@ -0,0 +1,36 @@ + + + 6.6.5 + /tmp/test + + + ${solr.lock.type:native} + + + + + + + + + + commit + schema.xml + + + + + + false + id + lucene + + + + + Query me! + + \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/resources/test-files/solrconfig_no_replication_handler_defined.xml b/search-services/alfresco-search/src/test/resources/test-files/solrconfig_no_replication_handler_defined.xml new file mode 100644 index 000000000..0025cb3ca --- /dev/null +++ b/search-services/alfresco-search/src/test/resources/test-files/solrconfig_no_replication_handler_defined.xml @@ -0,0 +1,29 @@ + + + 6.6.5 + /tmp/test + + + ${solr.lock.type:native} + + + + + + + + + + false + id + lucene + + + + + Query me! + + \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/resources/test-files/solrconfig_slave_disabled_replication_handler.xml b/search-services/alfresco-search/src/test/resources/test-files/solrconfig_slave_disabled_replication_handler.xml new file mode 100644 index 000000000..76192edf1 --- /dev/null +++ b/search-services/alfresco-search/src/test/resources/test-files/solrconfig_slave_disabled_replication_handler.xml @@ -0,0 +1,37 @@ + + + 6.6.5 + /tmp/test + + + ${solr.lock.type:native} + + + + + + + + + + false + http://localhost:8983/solr/alfresco + 00:00:20 + + + + + + false + id + lucene + + + + + Query me! + + \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/resources/test-files/solrconfig_slave_replication_handler.xml b/search-services/alfresco-search/src/test/resources/test-files/solrconfig_slave_replication_handler.xml new file mode 100644 index 000000000..650bfe467 --- /dev/null +++ b/search-services/alfresco-search/src/test/resources/test-files/solrconfig_slave_replication_handler.xml @@ -0,0 +1,36 @@ + + + 6.6.5 + /tmp/test + + + ${solr.lock.type:native} + + + + + + + + + + http://localhost:8983/solr/alfresco + 00:00:20 + + + + + + false + id + lucene + + + + + Query me! + + \ No newline at end of file diff --git a/search-services/alfresco-solrclient-lib/pom.xml b/search-services/alfresco-solrclient-lib/pom.xml index 729d1bb30..ef23d4dc4 100644 --- a/search-services/alfresco-solrclient-lib/pom.xml +++ b/search-services/alfresco-solrclient-lib/pom.xml @@ -22,7 +22,7 @@ - 8.52 + 8.53 2.10.0