diff --git a/e2e-test/pom.xml b/e2e-test/pom.xml index f12c76d11..28af17776 100644 --- a/e2e-test/pom.xml +++ b/e2e-test/pom.xml @@ -12,7 +12,6 @@ Test Project to test Search Service and Analytics Features on a complete setup of Alfresco, Share 1.30 - 1.26 1.13 3.0.19 3.3.0 diff --git a/e2e-test/src/main/java/org/alfresco/search/TestGroup.java b/e2e-test/src/main/java/org/alfresco/search/TestGroup.java index fbb90a64e..f2e88318c 100644 --- a/e2e-test/src/main/java/org/alfresco/search/TestGroup.java +++ b/e2e-test/src/main/java/org/alfresco/search/TestGroup.java @@ -23,16 +23,18 @@ public class TestGroup // Used for TestRail test annotation public static final String SEARCH = "search"; public static final String REST_API = "rest-api"; - + public static final String PREUPGRADE = "pre-upgrade"; public static final String POSTUPGRADE = "post-upgrade"; public static final String ASS_MASTER_SLAVE = "ASS_Master_Slave"; // Alfresco Search Services using master slave configurations public static final String ASS_MASTER ="ASS_Master"; // Alfresco search services using master/stand alone mode public static final String EXPLICIT_SHARDING ="Explicit_Sharding"; // Alfresco search services using sharded environment and explicit routing + public static final String ASS_SHARDING = "ASS_Sharding"; // Alfresco Search Services using Sharding + public static final String ASS_SHARDING_DB_ID_RANGE = "ASS_Sharding_DB_ID_RANGE"; // Alfresco Search Services using Sharding with DB_ID_RANGE public static final String NOT_INSIGHT_ENGINE = "Not_InsightEngine"; // When Alfresco Insight Engine 1.0 isn't running - + public static final String ACS_52n = "ACS_52n"; // Alfresco Content Services 5.2.n public static final String ACS_60n = "ACS_60n"; // Alfresco Content Services 6.0 or above public static final String ACS_61n = "ACS_61n"; // Alfresco Content Services 6.1 or above diff --git a/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/solr/admin/SolrE2eAdminTest.java b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/solr/admin/SolrE2eAdminTest.java new file mode 100644 index 000000000..0ae0abd97 --- /dev/null +++ b/e2e-test/src/test/java/org/alfresco/test/search/functional/searchServices/solr/admin/SolrE2eAdminTest.java @@ -0,0 +1,519 @@ +/* + * Copyright (C) 2020 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.test.search.functional.searchServices.solr.admin; + +import java.util.ArrayList; +import java.util.List; + +import org.alfresco.rest.core.RestResponse; +import org.alfresco.search.TestGroup; +import org.alfresco.test.search.functional.AbstractE2EFunctionalTest; +import org.springframework.context.annotation.Configuration; +import org.testng.Assert; +import org.testng.annotations.Test; + +/** + * End to end tests for SOLR Admin actions REST API, available from: + * + * http://:/solr/admin/cores?action=* + * + * @author aborroy + * + */ +@Configuration +public class SolrE2eAdminTest extends AbstractE2EFunctionalTest +{ + + + // SOLR default response status codes (returned in responseHeader.status) + private static final String SOLR_RESPONSE_STATUS_OK = "0"; + private static final String SOLR_RESPONSE_STATUS_INTERNAL_ERROR = "400"; + + // Alfresco SOLR action response status identifiers + private static final String ACTION_RESPONSE_REPORT = "report"; + + // Default Alfresco SOLR Core Names + List defaultCoreNames = new ArrayList<>(List.of("alfresco", "archive")); + + @Test(priority = 1) + public void testNodeReport() throws Exception + { + String nodeid = "200"; + RestResponse response = restClient.withParams("nodeid=" + nodeid).withSolrAdminAPI().getAction("nodeReport"); + + String status = response.getResponse().body().jsonPath().get("responseHeader.status").toString(); + Assert.assertEquals(status, SOLR_RESPONSE_STATUS_OK); + + String report = response.getResponse().body().jsonPath().get(ACTION_RESPONSE_REPORT).toString(); + Assert.assertNotNull(report); + + defaultCoreNames.forEach(core -> { + Assert.assertNotNull(response.getResponse().body().jsonPath().get(ACTION_RESPONSE_REPORT + "." + core)); + }); + } + + /** + * Node Report requires "nodeid" parameter. + * This test will fail as we are missing to pass the parameter. + * @throws Exception + */ + @Test(priority = 2) + public void testNodeReportError() throws Exception + { + RestResponse response = restClient.withSolrAdminAPI().getAction("nodeReport"); + + String status = response.getResponse().body().jsonPath().get("responseHeader.status").toString(); + Assert.assertEquals(status, SOLR_RESPONSE_STATUS_INTERNAL_ERROR); + } + + @Test(priority = 3) + public void testAclReport() throws Exception + { + String aclid = "1"; + RestResponse response = restClient.withParams("aclid=" + aclid).withSolrAdminAPI().getAction("aclReport"); + + String status = response.getResponse().body().jsonPath().get("responseHeader.status").toString(); + Assert.assertEquals(status, SOLR_RESPONSE_STATUS_OK); + + String report = response.getResponse().body().jsonPath().get(ACTION_RESPONSE_REPORT).toString(); + Assert.assertNotNull(report); + } + + /** + * Acl Report requires "aclid" parameter. + * This test will fail as we are missing to pass the parameter. + * @throws Exception + */ + @Test(priority = 4) + public void testAclReportError() throws Exception + { + RestResponse response = restClient.withSolrAdminAPI().getAction("aclReport"); + + String status = response.getResponse().body().jsonPath().get("responseHeader.status").toString(); + Assert.assertEquals(status, SOLR_RESPONSE_STATUS_INTERNAL_ERROR); + } + + @Test(priority = 5) + public void testTxReport() throws Exception + { + String coreName = "alfresco"; + String txid = "1"; + + RestResponse response = restClient.withParams("coreName=" + coreName, "txid=" + txid).withSolrAdminAPI().getAction("txReport"); + + String status = response.getResponse().body().jsonPath().get("responseHeader.status").toString(); + Assert.assertEquals(status, SOLR_RESPONSE_STATUS_OK); + + String report = response.getResponse().body().jsonPath().get(ACTION_RESPONSE_REPORT).toString(); + Assert.assertNotNull(report); + } + + /** + * Transaction report requires "txid" parameter. + * This test will fail as we are missing to pass the parameter. + * @throws Exception + */ + @Test(priority = 6) + public void testTxReportError() throws Exception + { + RestResponse response = restClient.withSolrAdminAPI().getAction("txReport"); + + String status = response.getResponse().body().jsonPath().get("responseHeader.status").toString(); + Assert.assertEquals(status, SOLR_RESPONSE_STATUS_INTERNAL_ERROR); + } + + @Test(priority = 7) + public void testAclTxReport() throws Exception + { + String acltxid = "1"; + + RestResponse response = restClient.withParams("acltxid=" + acltxid).withSolrAdminAPI().getAction("aclTxReport"); + + String status = response.getResponse().body().jsonPath().get("responseHeader.status").toString(); + Assert.assertEquals(status, SOLR_RESPONSE_STATUS_OK); + + String report = response.getResponse().body().jsonPath().get(ACTION_RESPONSE_REPORT).toString(); + Assert.assertNotNull(report); + } + + /** + * AclTx report requires "acltxid" parameter. + * This test will fail as we are missing to pass the parameter. + * @throws Exception + */ + @Test(priority = 8) + public void testAclTxReportError() throws Exception + { + RestResponse response = restClient.withSolrAdminAPI().getAction("aclTxReport"); + + String status = response.getResponse().body().jsonPath().get("responseHeader.status").toString(); + Assert.assertEquals(status, SOLR_RESPONSE_STATUS_INTERNAL_ERROR); + } + + @Test(priority = 9) + public void testReport() throws Exception + { + RestResponse response = restClient.withSolrAdminAPI().getAction(ACTION_RESPONSE_REPORT); + + String status = response.getResponse().body().jsonPath().get("responseHeader.status").toString(); + Assert.assertEquals(status, SOLR_RESPONSE_STATUS_OK); + + String report = response.getResponse().body().jsonPath().get(ACTION_RESPONSE_REPORT).toString(); + Assert.assertNotNull(report); + } + + @Test(priority = 10) + public void testSummary() throws Exception + { + String core = "alfresco"; + + RestResponse response = restClient.withParams("core=" + core).withSolrAdminAPI().getAction("summary"); + + String status = response.getResponse().body().jsonPath().get("responseHeader.status").toString(); + Assert.assertEquals(status, SOLR_RESPONSE_STATUS_OK); + + String report = response.getResponse().body().jsonPath().get("Summary").toString(); + Assert.assertNotNull(report); + } + + @Test(priority = 11) + public void testCheck() throws Exception + { + String core = "alfresco"; + + RestResponse response = restClient.withParams("core=" + core).withSolrAdminAPI().getAction("check"); + + String status = response.getResponse().body().jsonPath().get("responseHeader.status").toString(); + Assert.assertEquals(status, SOLR_RESPONSE_STATUS_OK); + + String actionStatus = response.getResponse().body().jsonPath().get("action.status").toString(); + Assert.assertEquals(actionStatus, "success"); + } + + /** + * This action only applies to DB_ID_RANGE Sharding method. + * This test verifies expected result when using another deployment + * @throws Exception + */ + @Test(priority = 12) + public void testRangeCheck() throws Exception + { + String coreName = "alfresco"; + + RestResponse response = restClient.withParams("coreName=" + coreName).withSolrAdminAPI().getAction("rangeCheck"); + + String status = response.getResponse().body().jsonPath().get("responseHeader.status").toString(); + Assert.assertEquals(status, SOLR_RESPONSE_STATUS_OK); + + String expand = response.getResponse().body().jsonPath().get("expand").toString(); + Assert.assertEquals(expand, "-1"); + } + + /** + * When using DB_ID_RANGE Sharding method, expand param is including a number of nodes to be extended. + * @throws Exception + */ + @Test(priority = 13, groups = { TestGroup.ASS_SHARDING_DB_ID_RANGE }) + public void testRangeCheckSharding() throws Exception + { + String coreName = "alfresco"; + + RestResponse response = restClient.withParams("coreName=" + coreName).withSolrAdminAPI().getAction("rangeCheck"); + + String status = response.getResponse().body().jsonPath().get("responseHeader.status").toString(); + Assert.assertEquals(status, SOLR_RESPONSE_STATUS_OK); + + String expand = response.getResponse().body().jsonPath().get("expand").toString(); + Assert.assertNotEquals(expand, "-1"); + } + + /** + * This action only applies to DB_ID_RANGE Sharding method. + * This test verifies expected result when using another deployment + * @throws Exception + */ + @Test(priority = 14) + public void testExpand() throws Exception + { + String coreName = "alfresco"; + String add = "1000"; + + RestResponse response = restClient.withParams("coreName=" + coreName, "add=" + add).withSolrAdminAPI().getAction("expand"); + + String status = response.getResponse().body().jsonPath().get("responseHeader.status").toString(); + Assert.assertEquals(status, SOLR_RESPONSE_STATUS_OK); + + // This action only applies to DB_ID_RANGE Sharding method + String expand = response.getResponse().body().jsonPath().get("expand").toString(); + Assert.assertEquals(expand, "-1"); + } + + /** + * When using DB_ID_RANGE Sharding method, expand param is including a number of nodes extended. + * @throws Exception + */ + @Test(priority = 15, groups = { TestGroup.ASS_SHARDING_DB_ID_RANGE }) + public void testExpandSharding() throws Exception + { + String coreName = "alfresco"; + String add = "1000"; + + RestResponse response = restClient.withParams("coreName=" + coreName, "add=" + add).withSolrAdminAPI().getAction("expand"); + + String status = response.getResponse().body().jsonPath().get("responseHeader.status").toString(); + Assert.assertEquals(status, SOLR_RESPONSE_STATUS_OK); + + // This action only applies to DB_ID_RANGE Sharding method + String expand = response.getResponse().body().jsonPath().get("expand").toString(); + Assert.assertNotEquals(expand, "-1"); + } + + @Test(priority = 16) + public void testPurge() throws Exception + { + String core = "alfresco"; + String txid = "1"; + + RestResponse response = restClient.withParams("core=" + core, "txid=" + txid).withSolrAdminAPI().getAction("purge"); + + String status = response.getResponse().body().jsonPath().get("responseHeader.status").toString(); + Assert.assertEquals(status, SOLR_RESPONSE_STATUS_OK); + + String actionStatus = response.getResponse().body().jsonPath().get("action.status").toString(); + Assert.assertEquals(actionStatus, "scheduled"); + } + + @Test(priority = 17) + public void testFix() throws Exception + { + String core = "alfresco"; + + RestResponse response = restClient.withParams("core=" + core).withSolrAdminAPI().getAction("fix"); + + String status = response.getResponse().body().jsonPath().get("responseHeader.status").toString(); + Assert.assertEquals(status, SOLR_RESPONSE_STATUS_OK); + + Assert.assertNotNull(response.getResponse().body().jsonPath().get("action." + core +".txToReindex")); + Assert.assertNotNull(response.getResponse().body().jsonPath().get("action." + core + ".aclChangeSetToReindex")); + + String actionStatus = response.getResponse().body().jsonPath().get("action.status").toString(); + Assert.assertEquals(actionStatus, "scheduled"); + } + + @Test(priority = 18) + public void testReindex() throws Exception + { + String core = "alfresco"; + String txid = "1"; + + RestResponse response = restClient.withParams("core=" + core, "txid=" + txid).withSolrAdminAPI().getAction("reindex"); + + String status = response.getResponse().body().jsonPath().get("responseHeader.status").toString(); + Assert.assertEquals(status, SOLR_RESPONSE_STATUS_OK); + + String actionStatus = response.getResponse().body().jsonPath().get("action.status").toString(); + Assert.assertEquals(actionStatus, "scheduled"); + } + + @Test(priority = 19) + public void testRetry() throws Exception + { + String core = "alfresco"; + + RestResponse response = restClient.withParams("core=" + core).withSolrAdminAPI().getAction("retry"); + + String status = response.getResponse().body().jsonPath().get("responseHeader.status").toString(); + Assert.assertEquals(status, SOLR_RESPONSE_STATUS_OK); + + String actionStatus = response.getResponse().body().jsonPath().get("action.status").toString(); + Assert.assertEquals(actionStatus, "scheduled"); + } + + @Test(priority = 20) + public void testIndex() throws Exception + { + String core = "alfresco"; + String txid = "1"; + + RestResponse response = restClient.withParams("core=" + core, "txid=" + txid).withSolrAdminAPI().getAction("index"); + + String status = response.getResponse().body().jsonPath().get("responseHeader.status").toString(); + Assert.assertEquals(status, SOLR_RESPONSE_STATUS_OK); + + String actionStatus = response.getResponse().body().jsonPath().get("action.status").toString(); + Assert.assertEquals(actionStatus, "scheduled"); + } + + @Test(priority = 21) + public void testLog4J() throws Exception + { + RestResponse response = restClient.withSolrAdminAPI().getAction("log4j"); + + String status = response.getResponse().body().jsonPath().get("responseHeader.status").toString(); + Assert.assertEquals(status, SOLR_RESPONSE_STATUS_OK); + + String actionStatus = response.getResponse().body().jsonPath().get("action.status").toString(); + Assert.assertEquals(actionStatus, "success"); + } + + /** + * This test will fail if it's executed twice + * @throws Exception + */ + @Test(priority = 22) + public void testNewCore() throws Exception + { + String core = "newCore"; + String storeRef = "workspace://SpacesStore"; + String template = "rerank"; + + RestResponse response = restClient.withParams("coreName=" + core, "storeRef=" + storeRef, "template=" + template) + .withSolrAdminAPI().getAction("newCore"); + + String status = response.getResponse().body().jsonPath().get("responseHeader.status").toString(); + Assert.assertEquals(status, SOLR_RESPONSE_STATUS_OK); + + String actionStatus = response.getResponse().body().jsonPath().get("action.status").toString(); + Assert.assertEquals(actionStatus, "success"); + } + + /** + * When creating a core that already exists, this action fails. + * @throws Exception + */ + @Test(priority = 23) + public void testNewCoreError() throws Exception + { + String core = "alfresco"; + String template = "rerank"; + + RestResponse response = restClient.withParams("coreName=" + core, "template=" + template) + .withSolrAdminAPI().getAction("newCore"); + + String status = response.getResponse().body().jsonPath().get("responseHeader.status").toString(); + Assert.assertEquals(status, SOLR_RESPONSE_STATUS_OK); + + String actionStatus = response.getResponse().body().jsonPath().get("action.status").toString(); + Assert.assertEquals(actionStatus, "error"); + } + + @Test(priority = 24) + public void testUpdateCore() throws Exception + { + String core = "alfresco"; + + RestResponse response = restClient.withParams("coreName=" + core).withSolrAdminAPI().getAction("updateCore"); + + String status = response.getResponse().body().jsonPath().get("responseHeader.status").toString(); + Assert.assertEquals(status, SOLR_RESPONSE_STATUS_OK); + + String actionStatus = response.getResponse().body().jsonPath().get("action.status").toString(); + Assert.assertEquals(actionStatus, "success"); + } + + /** + * When updating a core that doesn't exist, this action fails. + * @throws Exception + */ + @Test(priority = 25) + public void testUpdateCoreError() throws Exception + { + String core = "nonExistingCore"; + + RestResponse response = restClient.withParams("coreName=" + core).withSolrAdminAPI().getAction("updateCore"); + + String status = response.getResponse().body().jsonPath().get("responseHeader.status").toString(); + Assert.assertEquals(status, SOLR_RESPONSE_STATUS_OK); + + String actionStatus = response.getResponse().body().jsonPath().get("action.status").toString(); + Assert.assertEquals(actionStatus, "error"); + } + + /** + * This test updates "shared.properties" memory loading for every SOLR core. + * @throws Exception + */ + @Test(priority = 26) + public void testUpdateShared() throws Exception + { + RestResponse response = restClient.withSolrAdminAPI().getAction("updateShared"); + + String status = response.getResponse().body().jsonPath().get("responseHeader.status").toString(); + Assert.assertEquals(status, SOLR_RESPONSE_STATUS_OK); + + String actionStatus = response.getResponse().body().jsonPath().get("action.status").toString(); + Assert.assertEquals(actionStatus, "success"); + } + + /** + * This test will fail if it's executed twice + * @throws Exception + */ + @Test(priority = 27) + public void testNewDefaultCore() throws Exception + { + String core = "newDefaultCore"; + String storeRef = "workspace://SpacesStore"; + String template = "rerank"; + + RestResponse response = restClient + .withParams("coreName=" + core, "storeRef=" + storeRef, "template=" + template) + .withSolrAdminAPI().getAction("newDefaultIndex"); + + String status = response.getResponse().body().jsonPath().get("responseHeader.status").toString(); + Assert.assertEquals(status, SOLR_RESPONSE_STATUS_OK); + + String actionStatus = response.getResponse().body().jsonPath().get("action.status").toString(); + Assert.assertEquals(actionStatus, "success"); + } + + /** + * When creating a core that already exists, this action fails. + * @throws Exception + */ + @Test(priority = 28) + public void testNewDefaultCoreError() throws Exception + { + String core = "alfresco"; + String template = "rerank"; + + RestResponse response = restClient.withParams("coreName=" + core, "template=" + template) + .withSolrAdminAPI().getAction("newDefaultIndex"); + + String status = response.getResponse().body().jsonPath().get("responseHeader.status").toString(); + Assert.assertEquals(status, SOLR_RESPONSE_STATUS_OK); + + String actionStatus = response.getResponse().body().jsonPath().get("action.status").toString(); + Assert.assertEquals(actionStatus, "error"); + } + + /** + * This test has to be executed after "testNewCore" test, otherwise it will fail + */ + @Test(priority = 99, dependsOnMethods = ("testNewCore")) + public void testRemoveCore() throws Exception + { + String core = "newCore"; + String storeRef = "workspace://SpacesStore"; + + RestResponse response = restClient + .withParams("coreName=" + core, "storeRef=" + storeRef) + .withSolrAdminAPI().getAction("removeCore"); + + String status = response.getResponse().body().jsonPath().get("responseHeader.status").toString(); + Assert.assertEquals(status, SOLR_RESPONSE_STATUS_OK); + } + +} diff --git a/e2e-test/src/test/resources/SearchSuite.xml b/e2e-test/src/test/resources/SearchSuite.xml index 2d5e701e9..111a01356 100644 --- a/e2e-test/src/test/resources/SearchSuite.xml +++ b/e2e-test/src/test/resources/SearchSuite.xml @@ -20,7 +20,21 @@ + + + + + + + + + + + + + + diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoCoreAdminHandler.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoCoreAdminHandler.java index 4303ae3dd..9f0c64d97 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoCoreAdminHandler.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/AlfrescoCoreAdminHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2015 Alfresco Software Limited. + * Copyright (C) 2005-2020 Alfresco Software Limited. * * This file is part of Alfresco * @@ -61,6 +61,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -69,6 +70,8 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Properties; +import java.util.Set; +import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; @@ -123,6 +126,7 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler protected static final Logger LOGGER = LoggerFactory.getLogger(AlfrescoCoreAdminHandler.class); private static final String REPORT = "report"; + private static final String SUMMARY = "Summary"; private static final String ARG_ACLTXID = "acltxid"; static final String ARG_TXID = "txid"; private static final String ARG_ACLID = "aclid"; @@ -142,6 +146,25 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler ARCHIVE_CORE_NAME, StoreRef.STORE_REF_ARCHIVE_SPACESSTORE, VERSION_CORE_NAME, new StoreRef("workspace", "version2Store")); + /** + * Action status to be added to response + * - success: the action has been executed successfully + * - error: the action has not been executed, error message is added to the response + * - scheduled: the action will be executed as part of the Tracker Maintenance step + */ + private static final String ACTION_STATUS_SUCCESS = "success"; + private static final String ACTION_STATUS_ERROR = "error"; + private static final String ACTION_STATUS_SCHEDULED = "scheduled"; + + /** + * JSON/XML labels for the Action response + */ + private static final String ACTION_LABEL = "action"; + private static final String ACTION_STATUS_LABEL = "status"; + private static final String ACTION_ERROR_MESSAGE_LABEL = "errorMessage"; + private static final String ACTION_TX_TO_REINDEX = "txToReindex"; + private static final String ACTION_ACL_CHANGE_SET_TO_REINDEX = "aclChangeSetToReindex"; + private SolrTrackerScheduler scheduler; private TrackerRegistry trackerRegistry; private ConcurrentHashMap informationServers; @@ -194,6 +217,8 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler /** * Creates new default cores based on the "createDefaultCores" String passed in. + * + * Synchronous execution * * @param names comma delimited list of core names that will be created. * @param numShards The total number of shards. @@ -201,9 +226,19 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler * @param nodeInstance - Not sure why the core needs to know this. * @param numNodes - Not sure why the core needs to know this. * @param shardIds A comma separated list of shard ids for this core (or null). + * @return Response including the action result: + * - status: success, when the resource has been reloaded + * - status: error, when the resource has NOT been reloaded + * - errorMessage: message, if action status is "error" an error message node is included */ - private void setupNewDefaultCores(String names, int numShards, int replicationFactor, int nodeInstance, int numNodes, String shardIds) + private NamedList setupNewDefaultCores(String names, int numShards, int replicationFactor, int nodeInstance, int numNodes, String shardIds) { + + var wrapper = new Object() + { + NamedList response = new SimpleOrderedMap<>();; + }; + try { List coreNames = @@ -223,14 +258,21 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler throw new AlfrescoRuntimeException("Invalid '" + ALFRESCO_DEFAULTS + "' permitted values are " + STORE_REF_MAP.keySet()); } StoreRef storeRef = STORE_REF_MAP.get(coreName); - newCore(coreName, numShards, storeRef, DEFAULT_TEMPLATE, replicationFactor, nodeInstance, - numNodes, shardIds, null, new SolrQueryResponse()); + wrapper.response.addAll(newCore(coreName, numShards, storeRef, DEFAULT_TEMPLATE, replicationFactor, nodeInstance, + numNodes, shardIds, null)); }); } catch (Exception exception) { LOGGER.error("Failed to create default alfresco cores (workspace/archive stores)", exception); + wrapper.response.add(ACTION_STATUS_LABEL, ACTION_STATUS_ERROR); + wrapper.response.add(ACTION_ERROR_MESSAGE_LABEL, exception.getMessage()); + return wrapper.response; } + + wrapper.response.add(ACTION_STATUS_LABEL, ACTION_STATUS_SUCCESS); + return wrapper.response; + } /** @@ -280,108 +322,172 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler } } - private void initResourceBasedLogging(String resource) + /** + * Update memory loading from "log4j.properties" file for each Core + * + * Synchronous execution + * + * @param resource The name of the resource file to be reloaded + * @result Response including the action result: + * - status: success, when the resource has been reloaded + * - status: error, when the resource has NOT been reloaded + * - errorMessage: message, if action status is "error" an error message node is included + */ + private NamedList initResourceBasedLogging(String resource) { + + NamedList response = new SimpleOrderedMap<>(); + try { Class clazz = Class.forName("org.apache.log4j.PropertyConfigurator"); Method method = clazz.getMethod("configure", Properties.class); - InputStream is = openResource(coreContainer.getSolrHome(), resource); + InputStream is = openResource(coreContainer.getSolrHome() + "/../logs", resource); Properties p = new Properties(); p.load(is); method.invoke(null, p); + response.add(ACTION_STATUS_LABEL, ACTION_STATUS_SUCCESS); + } catch (ClassNotFoundException e) { - // Do nothing here + response.add(ACTION_STATUS_LABEL, ACTION_STATUS_ERROR); + response.add(ACTION_ERROR_MESSAGE_LABEL, "ClassNotFoundException: org.apache.log4j.PropertyConfigurator"); + return response; } catch (Exception e) { LOGGER.info("Failed to load " + resource, e); + response.add(ACTION_STATUS_LABEL, ACTION_STATUS_ERROR); + response.add(ACTION_ERROR_MESSAGE_LABEL, e.getMessage()); + return response; } - } + return response; + } + + @SuppressWarnings("unchecked") protected void handleCustomAction(SolrQueryRequest req, SolrQueryResponse rsp) { + SolrParams params = req.getParams(); String action = ofNullable(params.get(CoreAdminParams.ACTION)) .map(String::trim) .map(String::toUpperCase) .orElse(""); + String coreName = coreName(params); + LOGGER.info("Running action {} for core {} with params {}", action, coreName, params); + try { - switch (action) - { + switch (action) { + // Create a new Core in SOLR case "NEWCORE": case "NEWINDEX": - newCore(req, rsp); + rsp.add(ACTION_LABEL, newCore(req)); break; + // Reload an existing Core in SOLR case "UPDATECORE": case "UPDATEINDEX": - updateCore(req); + rsp.add(ACTION_LABEL, updateCore(req)); break; + // Update memory loading from "shared.properties" file for each Core case "UPDATESHARED": - updateShared(req); + rsp.add(ACTION_LABEL, updateShared(req)); break; + // Unload an existing Core from SOLR case "REMOVECORE": - removeCore(req); + rsp.add(ACTION_LABEL, removeCore(req)); break; + // Create a new Core in SOLR with default settings case "NEWDEFAULTINDEX": case "NEWDEFAULTCORE": - newDefaultCore(req, rsp); + rsp.add(ACTION_LABEL, newDefaultCore(req)); break; + // Enable check flag on a SOLR Core case "CHECK": - actionCHECK(params); + rsp.add(ACTION_LABEL, actionCHECK(coreName)); break; + // Get a report from a nodeId with the associated txId and the indexing status case "NODEREPORT": - actionNODEREPORTS(rsp, params); + rsp.add(REPORT, actionNODEREPORTS(params)); break; + // Get a report from an aclId with the count of documents associated to the ACL case "ACLREPORT": - actionACLREPORT(rsp, params); + rsp.add(REPORT, actionACLREPORT(params)); break; + // Get a report from a txId with detailed information related with the Transaction case "TXREPORT": - actionTXREPORT(rsp, params); + rsp.add(REPORT, actionTXREPORT(params)); break; + // Get a report from an aclTxId with detailed information related with nodes indexed + // for an ACL inside a Transaction case "ACLTXREPORT": - actionACLTXREPORT(rsp, params); + rsp.add(REPORT, actionACLTXREPORT(params)); break; + // Get a detailed report including storage and sizing for a Shards configured + // with Shard DB_ID_RANGE method. If SOLR is not using this configuration, + // "expand = -1" is returned case "RANGECHECK": - rangeCheck(rsp, params); + rsp.getValues().addAll(rangeCheck(params)); break; + // Expand the range for a Shard configured with DB_ID_RANGE having more than 75% + // space used. This configuration is not persisted in solrcore.properties case "EXPAND": - expand(rsp, params); + rsp.getValues().addAll(expand(params)); break; + // Get a detailed report for a core or for every core. This action accepts + // filtering based on commitTime, txid and acltxid case "REPORT": - actionREPORT(rsp, params); + rsp.add(REPORT, actionREPORT(params)); break; + // Add a nodeid, txid, acltxid or aclid to be purged on the next maintenance + // operation performed by MetadataTracker and AclTracker. + // Asynchronous. case "PURGE": - actionPURGE(params); + rsp.add(ACTION_LABEL, actionPURGE(params)); break; + // Add a nodeid, txid, acltxid, aclid or SOLR query to be reindexed on the + // next maintenance operation performed by MetadataTracker and AclTracker. + // Asynchronous. case "REINDEX": - actionREINDEX(params); + rsp.add(ACTION_LABEL, actionREINDEX(params)); break; + // Reindex every node marked as ERROR in a core or in every core. + // Asynchronous. case "RETRY": - actionRETRY(rsp, params); + rsp.add(ACTION_LABEL, actionRETRY(params)); break; + // Add a nodeid, txid, acltxid or aclid to be indexed on the next maintenance + // operation performed by MetadataTracker and AclTracker. + // Asynchronous. case "INDEX": - actionINDEX(params); + rsp.add(ACTION_LABEL, actionINDEX(params)); break; + // Find transactions and acls missing or duplicated in the cores and + // add them to be reindexed on the next maintenance operation + // performed by MetadataTracker and AclTracker. + // Asynchronous. case "FIX": - actionFIX(params); + rsp.add(ACTION_LABEL, actionFIX(params)); break; + // Get detailed report for a core or for every core including information + // related with handlers and trackers. case "SUMMARY": - actionSUMMARY(rsp, params); + rsp.add(SUMMARY, actionSUMMARY(params)); break; + // Update memory loading from "log4j.properties" file for each Core case "LOG4J": - initResourceBasedLogging( + rsp.add(ACTION_LABEL, initResourceBasedLogging( ofNullable(params.get("resource")) - .orElse("log4j-solr.properties")); + .orElse("log4j.properties"))); break; default: super.handleCustomAction(req, rsp); break; } + } catch (Exception ex) { @@ -390,18 +496,42 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler } } - private void newCore(SolrQueryRequest req, SolrQueryResponse rsp) + /** + * Create a new Core in SOLR + * + * Synchronous execution + * + * @param req Query Request including following parameters: + * - coreName, mandatory, the name of the core to be created + * - storeRef, mandatory, the storeRef for the SOLR Core (workspace://SpacesStore, archive://SpacesStore) + * - shardIds, optional, a String including a list of ShardIds separated with comma + * - numShards, optional, the number of Shards to be created + * - template, optional, the name of the SOLR template used to create the core (rerank, norerank) + * - replicationFactor, optional, number of Core replicas + * - nodeInstance, optional, number of the Node instance + * - numNodes, optional, number of Nodes + * + * @return NamedList including the action result: + * - status: success, when the core has been created + * - status: error, when the core has NOT been created + * - errorMessage: message, if action status is "error" an error message node is included + */ + private NamedList newCore(SolrQueryRequest req) { SolrParams params = req.getParams(); req.getContext(); + NamedList response = new SimpleOrderedMap<>(); + // If numCore > 1 we are creating a collection of cores for a sole node in a cluster int numShards = params.getInt("numShards", 1); String store = params.get("storeRef"); if (store == null || store.trim().length() == 0) { - return; + response.add(ACTION_STATUS_LABEL, ACTION_STATUS_ERROR); + response.add(ACTION_ERROR_MESSAGE_LABEL, "Core " + coreName(params) + " has NOT been created as storeRef param is required"); + return response; } StoreRef storeRef = new StoreRef(store); @@ -415,11 +545,33 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler String coreName = coreName(params); String shardIds = params.get("shardIds"); - newCore(coreName, numShards, storeRef, templateName, replicationFactor, nodeInstance, numNodes, shardIds, extractCustomProperties(params), rsp); + response = newCore(coreName, numShards, storeRef, templateName, replicationFactor, nodeInstance, numNodes, shardIds, extractCustomProperties(params)); + if (!Objects.equals(response.get(ACTION_STATUS_LABEL), ACTION_STATUS_ERROR)) + { + response.add(ACTION_STATUS_LABEL, ACTION_STATUS_SUCCESS); + } + return response; } - private void newDefaultCore(SolrQueryRequest req, SolrQueryResponse response) + /** + * Creates new default cores using default values. + * + * Synchronous execution + * + * @param req Query Request including following parameters: + * - coreName, mandatory, the name of the core to be created + * - storeRef, optional, the storeRef for the SOLR Core (workspace://SpacesStore, archive://SpacesStore) + * - template, optional, the name of the SOLR template used to create the core (rerank, norerank) + * @return Response including the action result: + * - status: success, when the resource has been reloaded + * - status: error, when the resource has NOT been reloaded + * - errorMessage: message, if action status is "error" an error message node is included + */ + private NamedList newDefaultCore(SolrQueryRequest req) { + + NamedList response = new SimpleOrderedMap<>(); + SolrParams params = req.getParams(); String coreName = ofNullable(coreName(params)).orElse(ALFRESCO_CORE_NAME); String templateName = @@ -429,30 +581,37 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler Properties extraProperties = extractCustomProperties(params); - newDefaultCore( + response = newDefaultCore( coreName, ofNullable(params.get("storeRef")) .map(StoreRef::new) .orElse(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE), templateName, - extraProperties, - response); + extraProperties); + if (!Objects.equals(response.get(ACTION_STATUS_LABEL), ACTION_STATUS_ERROR)) + { + response.add(ACTION_STATUS_LABEL, ACTION_STATUS_SUCCESS); + } + return response; } - private void newDefaultCore(String coreName, StoreRef storeRef, String templateName, Properties extraProperties, SolrQueryResponse rsp) + private NamedList newDefaultCore(String coreName, StoreRef storeRef, String templateName, Properties extraProperties) { - newCore(coreName, 1, storeRef, templateName, 1, 1, 1, null, extraProperties, rsp); + return newCore(coreName, 1, storeRef, templateName, 1, 1, 1, null, extraProperties); } - protected void newCore(String coreName, int numShards, StoreRef storeRef, String templateName, int replicationFactor, int nodeInstance, int numNodes, String shardIds, Properties extraProperties, SolrQueryResponse rsp) + protected NamedList newCore(String coreName, int numShards, StoreRef storeRef, String templateName, int replicationFactor, int nodeInstance, int numNodes, String shardIds, Properties extraProperties) { + + NamedList response = new SimpleOrderedMap<>(); + try { // copy core from template File solrHome = new File(coreContainer.getSolrHome()); File templates = new File(solrHome, "templates"); File template = new File(templates, templateName); - + if(numShards > 1) { String collectionName = templateName + "--" + storeRef.getProtocol() + "-" + storeRef.getIdentifier() + "--shards--"+numShards + "-x-"+replicationFactor+"--node--"+nodeInstance+"-of-"+numNodes; @@ -465,9 +624,11 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler File baseDirectory = new File(solrHome, collectionName); - if(nodeInstance == -1) + if (nodeInstance == -1) { - return; + response.add(ACTION_STATUS_LABEL, ACTION_STATUS_ERROR); + response.add(ACTION_ERROR_MESSAGE_LABEL, "Core " + coreName + " has NOT been created as nodeInstance param is required"); + return response; } List shards; @@ -480,29 +641,40 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler ExplicitShardingPolicy policy = new ExplicitShardingPolicy(numShards, replicationFactor, numNodes); if(!policy.configurationIsValid()) { - return; + response.add(ACTION_STATUS_LABEL, ACTION_STATUS_ERROR); + response.add(ACTION_ERROR_MESSAGE_LABEL, "Core " + coreName + " has NOT been created as explicit Sharding policy is not valid"); + return response; } shards = policy.getShardIdsForNode(nodeInstance); } - for(Integer shard : shards) + List coresNotCreated = new ArrayList<>(); + for (Integer shard : shards) { coreName = coreBase + shard; File newCore = new File(baseDirectory, coreName); - String solrCoreName = coreName; - if (coreName == null) + + response.addAll(createAndRegisterNewCore(extraProperties, storeRef, template, coreName, + newCore, numShards, shard, templateName)); + if (Objects.equals(response.get(ACTION_STATUS_LABEL), ACTION_STATUS_ERROR)) { - if(storeRef.equals(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE)) - { - solrCoreName = "alfresco-" + shard; - } - else if(storeRef.equals(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE)) - { - solrCoreName = "archive-" + shard; - } + coresNotCreated.add(coreName); } - createAndRegisterNewCore(rsp, extraProperties, storeRef, template, solrCoreName, newCore, numShards, shard, templateName); } + + if (coresNotCreated.size() > 0) + { + response.add(ACTION_STATUS_LABEL, ACTION_STATUS_ERROR); + response.add(ACTION_ERROR_MESSAGE_LABEL, + "Following cores have not been created: " + coresNotCreated); + return response; + } + else + { + response.add(ACTION_STATUS_LABEL, ACTION_STATUS_SUCCESS); + return response; + } + } else { @@ -511,11 +683,12 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler coreName = storeRef.getProtocol() + "-" + storeRef.getIdentifier(); } File newCore = new File(solrHome, coreName); - createAndRegisterNewCore(rsp, extraProperties, storeRef, template, coreName, newCore, 0, 0, templateName); + return createAndRegisterNewCore(extraProperties, storeRef, template, coreName, newCore, 0, 0, templateName); } } catch (IOException exception) { + LOGGER.error("I/O Failure detected while creating the new core " + "(name={}, numShard={}, storeRef={}, template={}, replication factor={}, node instance={}, num nodes={}, shard ids={})", coreName, @@ -527,6 +700,10 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler numNodes, shardIds, exception); + response.add(ACTION_STATUS_LABEL, ACTION_STATUS_ERROR); + response.add(ACTION_ERROR_MESSAGE_LABEL, + "Core " + coreName + " has NOT been created. Check the log to find out the reason."); + return response; } } @@ -549,14 +726,17 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler .collect(Collectors.toList()); } - private void createAndRegisterNewCore(SolrQueryResponse rsp, Properties extraProperties, StoreRef storeRef, File template, String coreName, File newCore, int shardCount, int shardInstance, String templateName) throws IOException + private NamedList createAndRegisterNewCore(Properties extraProperties, StoreRef storeRef, File template, String coreName, File newCore, int shardCount, int shardInstance, String templateName) throws IOException { + + NamedList response = new SimpleOrderedMap<>(); + if (coreContainer.getLoadedCoreNames().contains(coreName)) { //Core alfresco exists - LOGGER.warn(coreName + " already exists, not creating again."); - rsp.add("core", coreName); - return; + response.add(ACTION_STATUS_LABEL, ACTION_STATUS_ERROR); + response.add(ACTION_ERROR_MESSAGE_LABEL, "core " + coreName + " already exists, not creating again."); + return response; } FileUtils.copyDirectory(template, newCore, false); @@ -601,7 +781,8 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler } SolrCore core = coreContainer.create(coreName, newCore.toPath(), new HashMap<>(), false); - rsp.add("core", core.getName()); + response.add("core", core.getName()); + return response; } boolean hasAlfrescoCore(Collection cores) @@ -611,10 +792,23 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler .anyMatch(trackerRegistry::hasTrackersForCore); } - private void updateShared(SolrQueryRequest req) + /** + * Update memory loading from "shared.properties" file for each Core + * + * Synchronous execution + * + * @param req Query Request with no parameters + * @return Response including the action result: + * - status: success, when the properties has been reloaded + * - status: error, when the properties has NOT been reloaded + * - errorMessage: message, if action status is "error" an error message node is included + */ + private NamedList updateShared(SolrQueryRequest req) { SolrParams params = req.getParams(); + NamedList response = new SimpleOrderedMap<>(); + try { File config = new File(AlfrescoSolrDataModel.getResourceDirectory(), AlfrescoSolrDataModel.SHARED_PROPERTIES); @@ -623,65 +817,177 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler coreContainer.getCores().stream() .map(SolrCore::getName) .forEach(coreContainer::reload); + + response.add(ACTION_STATUS_LABEL, ACTION_STATUS_SUCCESS); + } catch (IOException e) { LOGGER.error("Failed to update Shared properties ", e); + response.add(ACTION_STATUS_LABEL, ACTION_STATUS_ERROR); + response.add(ACTION_ERROR_MESSAGE_LABEL, "Shared properties couldn't be reloaded for some core. Check the log to find out the reason."); + return response; } + + return response; + } - private void updateCore(SolrQueryRequest req) + /** + * Reload an existing Core in SOLR + * + * Synchronous execution + * + * @param req Query Request including following parameters: + * - coreName, mandatory, the name of the core to be reloaded + * + * @return Response including the action result: + * - status: success, when the core has been reloaded + * - status: error, when the core has NOT been reloaded + * - errorMessage: message, if action status is "error" an error message node is included + */ + private NamedList updateCore(SolrQueryRequest req) { + + var wrapper = new Object() + { + NamedList response = new SimpleOrderedMap<>();; + }; + ofNullable(coreName(req.getParams())) .map(String::trim) .filter(coreName -> !coreName.isEmpty()) - .ifPresent(coreName -> { + .ifPresentOrElse(coreName -> { try (SolrCore core = coreContainer.getCore(coreName)) { if (core == null) { - return; + wrapper.response.add(ACTION_STATUS_LABEL, ACTION_STATUS_ERROR); + wrapper.response.add(ACTION_ERROR_MESSAGE_LABEL, "Core " + coreName + " has NOT been updated as it doesn't exist"); } + else + { - String configLocaltion = core.getResourceLoader().getConfigDir(); - File config = new File(configLocaltion, "solrcore.properties"); - updatePropertiesFile(req.getParams(), config, null); - - coreContainer.reload(coreName); + String configLocaltion = core.getResourceLoader().getConfigDir(); + File config = new File(configLocaltion, "solrcore.properties"); + updatePropertiesFile(req.getParams(), config, null); + + coreContainer.reload(coreName); + + wrapper.response.add(ACTION_STATUS_LABEL, ACTION_STATUS_SUCCESS); + + } + } + }, + () -> { + wrapper.response.add(ACTION_STATUS_LABEL, ACTION_STATUS_ERROR); + wrapper.response.add(ACTION_ERROR_MESSAGE_LABEL, "Core has NOT been updated as coreName param is required"); }); + + return wrapper.response; + } - private void removeCore(SolrQueryRequest req) + /** + * Unload an existing Core from SOLR + * + * Synchronous execution + * + * @param req Query Request including following parameters: + * - coreName, mandatory, the name of the core to be unloaded + * - storeRef, mandatory, the storeRef for the SOLR Core (workspace://SpacesStore, archive://SpacesStore) + * + * @return Response including the action result: + * - status: success, when the core has been unloaded + * - status: error, when the core has NOT been unloaded + * - errorMessage: message, if action status is "error" an error message node is included + */ + private NamedList removeCore(SolrQueryRequest req) { String store = ""; SolrParams params = req.getParams(); + NamedList response = new SimpleOrderedMap<>(); + if (params.get("storeRef") != null) { store = params.get("storeRef"); } - if ((store == null) || (store.length() == 0)) { return; } + if ((store == null) || (store.length() == 0)) + { + response.add(ACTION_STATUS_LABEL, ACTION_STATUS_ERROR); + response.add(ACTION_ERROR_MESSAGE_LABEL, "Core " + params.get("coreName") + " has NOT been removed as storeRef param is required"); + return response; + } StoreRef storeRef = new StoreRef(store); String coreName = ofNullable(coreName(req.getParams())).orElse(storeRef.getProtocol() + "-" + storeRef.getIdentifier()); + SolrCore core = coreContainer.getCore(coreName); + + if (core == null) + { + response.add(ACTION_STATUS_LABEL, ACTION_STATUS_ERROR); + response.add(ACTION_ERROR_MESSAGE_LABEL, "Core " + params.get("coreName") + " has NOT been removed as it doesn't exist"); + return response; + } + + // Close the references to the core to unload before actually unloading it, + // otherwise this operation gets into and endless loop + while (core.getOpenCount() > 1) + { + core.close(); + } + + // remove core coreContainer.unload(coreName, true, true, true); + + response.add(ACTION_STATUS_LABEL, ACTION_STATUS_SUCCESS); + return response; + } - private void actionCHECK(SolrParams params) + /** + * Enable check flag on a SOLR Core or on every SOLR Core + * + * Synchronous execution + * + * @param req Query Request without parameters + * - coreName, optional, the name of the core to be checked + * + * @return Response including the action result: + * - status: success, when the core has been created + */ + private NamedList actionCHECK(String cname) { - String cname = coreName(params); coreNames().stream() .filter(coreName -> cname == null || coreName.equals(cname)) .map(trackerRegistry::getTrackersForCore) .flatMap(Collection::stream) .map(Tracker::getTrackerState) .forEach(state -> state.setCheck(true)); + + NamedList response = new SimpleOrderedMap<>(); + response.add(ACTION_STATUS_LABEL, ACTION_STATUS_SUCCESS); + return response; } - private void actionNODEREPORTS(SolrQueryResponse rsp, SolrParams params) throws JSONException + /** + * Get a report from a nodeId with the associated txId and the indexing status + * + * Synchronous execution + * + * @param params Query Request with following parameters: + * - nodeId, mandatory: the number of the node to build the report + * - core, The name of the SOLR Core or "null" to get the report for every core + * @return Response including the action result: + * - report: An Object with the report details + * + * @throws JSONException + */ + private NamedList actionNODEREPORTS(SolrParams params) throws JSONException { Long dbid = ofNullable(params.get(ARG_NODEID)) @@ -689,7 +995,6 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler .orElseThrow(() -> new AlfrescoRuntimeException("No dbid parameter set.")); NamedList report = new SimpleOrderedMap<>(); - rsp.add(REPORT, report); String requestedCoreName = coreName(params); @@ -702,9 +1007,23 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler report.add( coreNameAndPublisher.getFirst(), buildNodeReport(coreNameAndPublisher.getSecond(), dbid))); + return report; } - private void actionACLREPORT(SolrQueryResponse rsp, SolrParams params) throws JSONException + /** + * Get a report from an aclId with the count of documents associated to the ACL + * + * Synchronous execution + * + * @param params Query Request with following parameters: + * - aclId, mandatory, the number of the ACL to build the report + * - core, The name of the SOLR Core or "null" to get the report for every core + * @return Response including the action result: + * - report: an Object with the details of the report + * + * @throws JSONException + */ + private NamedList actionACLREPORT(SolrParams params) throws JSONException { Long aclid = ofNullable(params.get(ARG_ACLID)) @@ -712,7 +1031,6 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler .orElseThrow(() -> new AlfrescoRuntimeException("No " + ARG_ACLID + " parameter set.")); NamedList report = new SimpleOrderedMap<>(); - rsp.add(REPORT, report); String requestedCoreName = coreName(params); @@ -729,16 +1047,31 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler { addAlertMessage(report); } + + return report; + } - private void actionTXREPORT(SolrQueryResponse rsp, SolrParams params) throws JSONException + /** + * Get a report from a txId with detailed information related with the Transaction + * + * Synchronous execution + * + * @param params Query Request with following parameters: + * - txId, mandatory, the number of the Transaction to build the report + * - core, The name of the SOLR Core or "null" to get the report for every core + * @return Response including the action result: + * - report: an Object with the details of the report + * + * @throws JSONException + */ + private NamedList actionTXREPORT(SolrParams params) throws JSONException { String coreName = ofNullable(coreName(params)) .orElseThrow(() -> new AlfrescoRuntimeException("No " + CoreAdminParams.CORE + " parameter set.")); NamedList report = new SimpleOrderedMap<>(); - rsp.add(REPORT, report); if (isMasterOrStandalone(coreName)) { @@ -754,9 +1087,23 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler { addAlertMessage(report); } + return report; } - private void actionACLTXREPORT(SolrQueryResponse rsp, SolrParams params) throws JSONException + /** + * Get a report from an aclTxId with detailed information related with nodes indexed + * for an ACL inside a Transaction + * + * Synchronous execution + * + * @param params Query Request with following parameters: + * - acltxid, mandatory, the number of the ACL TX Id to build the report + * @return Response including the action result: + * - report: an Object with the details of the report + * + * @throws JSONException + */ + private NamedList actionACLTXREPORT(SolrParams params) throws JSONException { Long acltxid = ofNullable(params.get(ARG_ACLTXID)) @@ -764,7 +1111,6 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler .orElseThrow(() -> new AlfrescoRuntimeException("No " + ARG_ACLTXID + " parameter set.")); NamedList report = new SimpleOrderedMap<>(); - rsp.add(REPORT, report); String requestedCoreName = coreName(params); @@ -786,14 +1132,30 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler { addAlertMessage(report); } + return report; } - private void rangeCheck(SolrQueryResponse rsp, SolrParams params) throws IOException + /** + * Get a detailed report including storage and sizing for a Shards configured with Shard DB_ID_RANGE method. + * If SOLR is not using this configuration,"expand = -1" is returned + * + * Synchronous execution + * + * @param params + * - core, The name of the SOLR Core + * @return Response including the action result: + * - report: An Object with the report details + * + * @throws IOException + */ + private NamedList rangeCheck(SolrParams params) throws IOException { String coreName = ofNullable(coreName(params)) .orElseThrow(() -> new AlfrescoRuntimeException("No " + CoreAdminParams.CORE + " parameter set.")); + NamedList response = new SimpleOrderedMap<>(); + if (isMasterOrStandalone(coreName)) { InformationServer informationServer = informationServers.get(coreName); @@ -806,9 +1168,9 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler if(!dbidRangeRouter.getInitialized()) { - rsp.add("expand", 0); - rsp.add("exception", "DBIDRangeRouter not initialized yet."); - return; + response.add("expand", 0); + response.add("exception", "DBIDRangeRouter not initialized yet."); + return response; } long startRange = dbidRangeRouter.getStartRange(); @@ -861,35 +1223,51 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler } } - rsp.add("start", startRange); - rsp.add("end", endRange); - rsp.add("nodeCount", nodeCount); - rsp.add("minDbid", minNodeId); - rsp.add("maxDbid", maxNodeId); - rsp.add("density", Math.abs(density)); - rsp.add("expand", bestGuess); - rsp.add("expanded", dbidRangeRouter.getExpanded()); + response.add("start", startRange); + response.add("end", endRange); + response.add("nodeCount", nodeCount); + response.add("minDbid", minNodeId); + response.add("maxDbid", maxNodeId); + response.add("density", Math.abs(density)); + response.add("expand", bestGuess); + response.add("expanded", dbidRangeRouter.getExpanded()); } else { - rsp.add("expand", -1); - rsp.add("exception", "ERROR: Wrong document router type:"+docRouter.getClass().getSimpleName()); + response.add("expand", -1); + response.add("exception", "ERROR: Wrong document router type:"+docRouter.getClass().getSimpleName()); } } else { - NamedList report = new SimpleOrderedMap<>(); - rsp.add(REPORT, report); - addAlertMessage(report); + addAlertMessage(response); } + return response; } - private synchronized void expand(SolrQueryResponse rsp, SolrParams params) throws IOException + /** + * Expand the range for a Shard configured with DB_ID_RANGE having more than 75% + * space used. This configuration is not persisted in solrcore.properties + * + * Synchronous execution + * + * @param params + * - core, mandatory: The name of the SOLR Core + * - add, mandatory: the number of nodes to be added to the End Range limit + * @return Response including the action result: + * - expand: The number of the new End Range limit or -1 if the action failed + * - exception: Error message if expand is -1 + * + * @throws IOException + */ + private synchronized NamedList expand(SolrParams params) throws IOException { String coreName = ofNullable(coreName(params)) .orElseThrow(() -> new AlfrescoRuntimeException("No " + CoreAdminParams.CORE + " parameter set.")); + NamedList response = new SimpleOrderedMap<>(); + if (isMasterOrStandalone(coreName)) { InformationServer informationServer = informationServers.get(coreName); @@ -902,16 +1280,16 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler if(!dbidRangeRouter.getInitialized()) { - rsp.add("expand", -1); - rsp.add("exception", "DBIDRangeRouter not initialized yet."); - return; + response.add("expand", -1); + response.add("exception", "DBIDRangeRouter not initialized yet."); + return response; } if(dbidRangeRouter.getExpanded()) { - rsp.add("expand", -1); - rsp.add("exception", "dbid range has already been expanded."); - return; + response.add("expand", -1); + response.add("exception", "dbid range has already been expanded."); + return response; } long currentEndRange = dbidRangeRouter.getEndRange(); @@ -923,9 +1301,9 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler if(maxNodeId > safe) { - rsp.add("expand", -1); - rsp.add("exception", "Expansion cannot occur if max DBID in the index is more then 75% of range."); - return; + response.add("expand", -1); + response.add("exception", "Expansion cannot occur if max DBID in the index is more then 75% of range."); + return response; } long newEndRange = expansion+dbidRangeRouter.getEndRange(); @@ -936,33 +1314,53 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler dbidRangeRouter.setEndRange(newEndRange); dbidRangeRouter.setExpanded(true); assert newEndRange == dbidRangeRouter.getEndRange(); - rsp.add("expand", dbidRangeRouter.getEndRange()); + response.add("expand", dbidRangeRouter.getEndRange()); } catch(Throwable t) { - rsp.add("expand", -1); - rsp.add("exception", t.getMessage()); + response.add("expand", -1); + response.add("exception", t.getMessage()); LOGGER.error("exception expanding", t); + return response; } } else { - rsp.add("expand", -1); - rsp.add("exception", "Wrong document router type:" + docRouter.getClass().getSimpleName()); + response.add("expand", -1); + response.add("exception", "Wrong document router type:" + docRouter.getClass().getSimpleName()); + return response; } } else { - NamedList report = new SimpleOrderedMap<>(); - rsp.add(REPORT, report); - addAlertMessage(report); + addAlertMessage(response); } + return response; } - private void actionREPORT(SolrQueryResponse rsp, SolrParams params) throws JSONException + /** + * Get a detailed report for a core or for every core. This action accepts + * filtering based on commitTime, txid and acltxid + * + * Synchronous execution + * + * @param params Query Request with following parameters: + * - core, mandatory: The name of the SOLR Core + * - fromTime, optional: from transaction commit time to filter report results + * - toTime, optional: to transaction commit time to filter report results + * - fromTx, optional: from transaction Id to filter report results + * - toTx, optional: to transaction Id time to filter report results + * - fromAclTx, optional: from ACL transaction Id to filter report results + * - toCalTx, optional: to ACL transaction Id to filter report results + * + * @param Response including the action result: + * - report.core: multiple Objects with the details of the report ("core" is the name of the Core) + * + * @throws JSONException + */ + private NamedList actionREPORT(SolrParams params) throws JSONException { NamedList report = new SimpleOrderedMap<>(); - rsp.add(REPORT, report); Long fromTime = getSafeLong(params, "fromTime"); Long toTime = getSafeLong(params, "toTime"); @@ -995,9 +1393,25 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler { addAlertMessage(report); } + return report; } - private void actionPURGE(SolrParams params) + /** + * Add a nodeid, txid, acltxid or aclid to be purged on the next maintenance + * operation performed by MetadataTracker and AclTracker. + * + * Asynchronous execution + * + * @param params Query Request with following parameters: + * - core, mandatory: The name of the SOLR Core + * - txid, optional, the number of the Transaction to purge + * - acltxid, optional, the number of the ACL Transaction to purge + * - nodeId, optional, the number of the node to purge + * - aclid, optional, the number of the ACL to purge + * @return Response including the action result: + * - status: scheduled, as it will be executed by Trackers on the next maintenance operation + */ + private NamedList actionPURGE(SolrParams params) { Consumer purgeOnSpecificCore = coreName -> { final MetadataTracker metadataTracker = trackerRegistry.getTrackerForCore(coreName, MetadataTracker.class); @@ -1015,9 +1429,29 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler .filter(coreName -> requestedCoreName == null || coreName.equals(requestedCoreName)) .filter(this::isMasterOrStandalone) .forEach(purgeOnSpecificCore); + + NamedList response = new SimpleOrderedMap<>(); + response.add(ACTION_STATUS_LABEL, ACTION_STATUS_SCHEDULED); + return response; } - private void actionREINDEX(SolrParams params) + /** + * Add a nodeid, txid, acltxid, aclid or SOLR query to be reindexed on the + * next maintenance operation performed by MetadataTracker and AclTracker. + * + * Asynchronous execution + * + * @param params Query Request with following parameters: + * - core, mandatory: The name of the SOLR Core + * - txid, optional, the number of the Transaction to reindex + * - acltxid, optional, the number of the ACL Transaction to reindex + * - nodeId, optional, the number of the node to reindex + * - aclid, optional, the number of the ACL to reindex + * - query, optional, SOLR Query to reindex results + * @return Response including the action result: + * - action.status: scheduled, as it will be executed by Trackers on the next maintenance operation + */ + private NamedList actionREINDEX(SolrParams params) { Consumer reindexOnSpecificCore = coreName -> { final MetadataTracker metadataTracker = trackerRegistry.getTrackerForCore(coreName, MetadataTracker.class); @@ -1037,10 +1471,27 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler .filter(coreName -> requestedCoreName == null || coreName.equals(requestedCoreName)) .filter(this::isMasterOrStandalone) .forEach(reindexOnSpecificCore); + + NamedList response = new SimpleOrderedMap<>(); + response.add(ACTION_STATUS_LABEL, ACTION_STATUS_SCHEDULED); + return response; } - private void actionRETRY(SolrQueryResponse rsp, SolrParams params) + /** + * Reindex every node marked as ERROR in a core or in every core. + * + * Asynchronous execution + * + * @param params Query Request with following parameters: + * - core, optional: The name of the SOLR Core + * @param rsp Query Response including the action result: + * - action.status: scheduled, as it will be executed by Trackers on the next maintenance operation + * - core: list of Document Ids with error that are going to reindexed + */ + private NamedList actionRETRY(SolrParams params) { + NamedList response = new SimpleOrderedMap<>(); + final Consumer retryOnSpecificCore = coreName -> { MetadataTracker tracker = trackerRegistry.getTrackerForCore(coreName, MetadataTracker.class); InformationServer srv = informationServers.get(coreName); @@ -1051,13 +1502,21 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler { tracker.addNodeToReindex(nodeid); } - rsp.add(coreName, srv.getErrorDocIds()); + response.add(coreName, srv.getErrorDocIds()); } catch (Exception exception) { LOGGER.error("I/O Exception while adding Node to reindex.", exception); + response.add(ACTION_STATUS_LABEL, ACTION_STATUS_ERROR); + response.add(ACTION_ERROR_MESSAGE_LABEL, exception.getMessage()); + } }; + + if (Objects.equals(response.get(ACTION_STATUS_LABEL), ACTION_STATUS_ERROR)) + { + return response; + } String requestedCoreName = coreName(params); @@ -1065,9 +1524,27 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler .filter(coreName -> requestedCoreName == null || coreName.equals(requestedCoreName)) .filter(this::isMasterOrStandalone) .forEach(retryOnSpecificCore); + + response.add(ACTION_STATUS_LABEL, ACTION_STATUS_SCHEDULED); + return response; } - private void actionINDEX(SolrParams params) + /** + * Add a nodeid, txid, acltxid or aclid to be indexed on the next maintenance + * operation performed by MetadataTracker and AclTracker. + * + * Asynchronous execution + * + * @param params Query Request with following parameters: + * - core, optional: The name of the SOLR Core + * - txid, optional, the number of the Transaction to index + * - acltxid, optional, the number of the ACL Transaction to index + * - nodeId, optional, the number of the node to index + * - aclid, optional, the number of the ACL to index + * @return Response including the action result: + * - action.status: scheduled, as it will be executed by Trackers on the next maintenance operation + */ + private NamedList actionINDEX(SolrParams params) { Consumer indexOnSpecificCore = coreName -> { final MetadataTracker metadataTracker = trackerRegistry.getTrackerForCore(coreName, MetadataTracker.class); @@ -1085,19 +1562,47 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler .filter(coreName -> requestedCoreName == null || coreName.equals(requestedCoreName)) .filter(this::isMasterOrStandalone) .forEach(indexOnSpecificCore); + + NamedList response = new SimpleOrderedMap<>(); + response.add(ACTION_STATUS_LABEL, ACTION_STATUS_SCHEDULED); + return response; } - private void actionFIX(SolrParams params) throws JSONException + /** + * Find transactions and acls missing or duplicated in the cores and + * add them to be reindexed on the next maintenance operation + * performed by MetadataTracker and AclTracker. + * + * Asynchronous execution + * + * @param params + * - core, optional: The name of the SOLR Core + * @return Response including the action result: + * - action.status: scheduled, as it will be executed by Trackers on the next maintenance operation + * - txToReindex: list of Transaction Ids that are going to be reindexed + * - aclChangeSetToReindex: list of ACL Change Set Ids that are going to be reindexed + * @throws JSONException + */ + private NamedList actionFIX(SolrParams params) throws JSONException { String requestedCoreName = coreName(params); + var wrapper = new Object() + { + NamedList response = new SimpleOrderedMap<>();; + }; coreNames().stream() .filter(coreName -> requestedCoreName == null || coreName.equals(requestedCoreName)) .filter(this::isMasterOrStandalone) - .forEach(this::fixOnSpecificCore); + .forEach(coreName -> { + wrapper.response.add(coreName, fixOnSpecificCore(coreName)); + }); + + wrapper.response.add(ACTION_STATUS_LABEL, ACTION_STATUS_SCHEDULED); + return wrapper.response; } - private void fixOnSpecificCore(String coreName) + private NamedList fixOnSpecificCore(String coreName) { try { @@ -1109,8 +1614,11 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler toReindex.or(indexHealthReport.getMissingTxFromIndex()); long current = -1; // Goes through problems in the index - while ((current = toReindex.nextSetBit(current + 1)) != -1) { + Set txToReindex = new TreeSet<>(); + while ((current = toReindex.nextSetBit(current + 1)) != -1) + { metadataTracker.addTransactionToReindex(current); + txToReindex.add(current); } // Gets the Acl health and fixes any problems @@ -1121,9 +1629,18 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler toReindex.or(indexHealthReport.getMissingAclTxFromIndex()); current = -1; // Goes through the problems in the index - while ((current = toReindex.nextSetBit(current + 1)) != -1) { + Set aclChangeSetToReindex = new TreeSet<>(); + while ((current = toReindex.nextSetBit(current + 1)) != -1) + { aclTracker.addAclChangeSetToReindex(current); + aclChangeSetToReindex.add(current); } + + NamedList response = new SimpleOrderedMap<>(); + response.add(ACTION_TX_TO_REINDEX, txToReindex); + response.add(ACTION_ACL_CHANGE_SET_TO_REINDEX, aclChangeSetToReindex); + return response; + } catch(Exception exception) { @@ -1131,16 +1648,31 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler } } - void actionSUMMARY(SolrQueryResponse rsp, SolrParams params) + /** + * Get detailed report for a core or for every core including information + * related with handlers and trackers. + * + * Synchronous execution + * + * @param params Query Request with following parameters: + * - core, optional: The name of the SOLR Core + * - detail, optional, when true adds details to the report + * - hist, optional, when true adds historic details to the report + * - values, optional, when true adds values detail to the report + * - reset, optional, when true stats are reset + * @return report (Key, Value) list with the results of the report + */ + private NamedList actionSUMMARY(SolrParams params) { NamedList report = new SimpleOrderedMap<>(); - rsp.add("Summary", report); String requestedCoreName = coreName(params); coreNames().stream() .filter(coreName -> requestedCoreName == null || coreName.equals(requestedCoreName)) .forEach(coreName -> coreSummary(params, report, coreName)); + + return report; } private void coreSummary(SolrParams params, NamedList report, String coreName) @@ -1291,4 +1823,4 @@ public class AlfrescoCoreAdminHandler extends CoreAdminHandler return contentStore; } -} \ No newline at end of file +} diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/AclTracker.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/AclTracker.java index 7851920cd..3092c7d1b 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/AclTracker.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/AclTracker.java @@ -135,8 +135,13 @@ public class AclTracker extends AbstractTracker indexAcl(readers, false); } this.infoSrv.indexAclTransaction(changeSet, false); + log.info("INDEX ACTION - AclChangeSetId {} has been indexed", aclChangeSetId); requiresCommit = true; } + else + { + log.info("INDEX ACTION - AclChangeSetId {} was not found in database, it has NOT been reindexed", aclChangeSetId); + } } checkShutdown(); } @@ -160,6 +165,7 @@ public class AclTracker extends AbstractTracker //AclReaders r = readers.get(0); //System.out.println("############## READERS ID:"+r.getId()+":"+r.getReaders()); indexAcl(readers, false); + log.info("INDEX ACTION - AclId {} has been indexed", aclId); } checkShutdown(); } @@ -187,8 +193,13 @@ public class AclTracker extends AbstractTracker } this.infoSrv.indexAclTransaction(changeSet, true); + log.info("REINDEX ACTION - AclChangeSetId {} has been reindexed", aclChangeSetId); requiresCommit = true; } + else + { + log.info("REINDEX ACTION - AclChangeSetId {} was not found in database, it has NOT been reindexed", aclChangeSetId); + } } checkShutdown(); } @@ -212,6 +223,7 @@ public class AclTracker extends AbstractTracker Acl acl = new Acl(0, aclId); List readers = client.getAclReaders(Collections.singletonList(acl)); indexAcl(readers, true); + log.info("REINDEX ACTION - aclId {} has been reindexed", aclId); requiresCommit = true; } checkShutdown(); @@ -231,6 +243,7 @@ public class AclTracker extends AbstractTracker if (aclChangeSetId != null) { this.infoSrv.deleteByAclChangeSetId(aclChangeSetId); + log.info("PURGE ACTION - Purged aclChangeSetId {}", aclChangeSetId); } checkShutdown(); } @@ -245,6 +258,7 @@ public class AclTracker extends AbstractTracker if (aclId != null) { this.infoSrv.deleteByAclId(aclId); + log.info("PURGE ACTION - Purged aclId {}", aclId); } checkShutdown(); } diff --git a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/MetadataTracker.java b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/MetadataTracker.java index 3a7773dd9..dd8c92bfe 100644 --- a/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/MetadataTracker.java +++ b/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/MetadataTracker.java @@ -359,10 +359,15 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker // Index the transaction doc after the node - if this is not found then a reindex will be done. this.infoSrv.indexTransaction(info, false); + log.info("INDEX ACTION - Transaction {} has been indexed", transactionId); requiresCommit = true; trackerStats.addTxDocs(nodes.size()); } + else + { + log.info("INDEX ACTION - Transaction {} was not found in database, it has NOT been reindexed", transactionId); + } } if (docCount > batchCount) @@ -402,6 +407,7 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker node.setTxnId(Long.MAX_VALUE); this.infoSrv.indexNode(node, false); + log.info("INDEX ACTION - Node {} has been reindexed", node.getId()); requiresCommit = true; } checkShutdown(); @@ -452,6 +458,11 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker // Index the transaction doc after the node - if this is not found then a reindex will be done. this.infoSrv.indexTransaction(info, true); + log.info("REINDEX ACTION - Transaction {} has been reindexed", transactionId); + } + else + { + log.info("REINDEX ACTION - Transaction {} was not found in database, it has NOT been reindexed", transactionId); } } @@ -494,6 +505,7 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker node.setTxnId(Long.MAX_VALUE); this.infoSrv.indexNode(node, true); + log.info("REINDEX ACTION - Node {} has been reindexed", node.getId()); requiresCommit = true; } checkShutdown(); @@ -515,6 +527,7 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker if (query != null) { this.infoSrv.reindexNodeByQuery(query); + log.info("REINDEX ACTION - Nodes from query {} have been reindexed", query); requiresCommit = true; } checkShutdown(); @@ -539,6 +552,7 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker // make sure it is cleaned out so we do not miss deletes this.infoSrv.deleteByTransactionId(transactionId); requiresCommit = true; + log.info("PURGE ACTION - Purged transactionId {}", transactionId); } checkShutdown(); } @@ -559,6 +573,7 @@ public class MetadataTracker extends CoreStatePublisher implements Tracker { // make sure it is cleaned out so we do not miss deletes this.infoSrv.deleteByNodeId(nodeId); + log.info("PURGE ACTION - Purged nodeId {}", nodeId); } checkShutdown(); } diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoCoreAdminHandlerIT.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoCoreAdminHandlerIT.java index a9275c2a6..6703bdde5 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoCoreAdminHandlerIT.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoCoreAdminHandlerIT.java @@ -61,6 +61,7 @@ import org.apache.solr.common.params.CoreAdminParams; import org.apache.solr.common.params.ModifiableSolrParams; import org.apache.solr.common.params.SolrParams; import org.apache.solr.common.util.NamedList; +import org.apache.solr.common.util.SimpleOrderedMap; import org.apache.solr.core.SolrCore; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.response.SolrQueryResponse; @@ -368,7 +369,7 @@ public class AlfrescoCoreAdminHandlerIT invalidNames.forEach(spy::setupNewDefaultCores); - verify(spy, never()).newCore(any(), anyInt(), any(), any(), anyInt(), anyInt(), anyInt(), any(), any(), any()); + verify(spy, never()).newCore(any(), anyInt(), any(), any(), anyInt(), anyInt(), anyInt(), any(), any()); reset(spy); @@ -376,16 +377,17 @@ public class AlfrescoCoreAdminHandlerIT String commaSeparatedNames = String.join(",", invalidNames); spy.setupNewDefaultCores(commaSeparatedNames); - verify(spy, never()).newCore(any(), anyInt(), any(), any(), anyInt(), anyInt(), anyInt(), any(), any(), any()); + verify(spy, never()).newCore(any(), anyInt(), any(), any(), anyInt(), anyInt(), anyInt(), any(), any()); } @Test public void coreNamesAreTrimmed_oneCoreNameAtTime() { AlfrescoCoreAdminHandler spy = spy(new AlfrescoCoreAdminHandler() { @Override - protected void newCore(String coreName, int numShards, StoreRef storeRef, String templateName, int replicationFactor, int nodeInstance, int numNodes, String shardIds, Properties extraProperties, SolrQueryResponse rsp) + protected NamedList newCore(String coreName, int numShards, StoreRef storeRef, String templateName, int replicationFactor, int nodeInstance, int numNodes, String shardIds, Properties extraProperties) { // Do nothing here otherwise we cannot spy it + return new SimpleOrderedMap<>(); } }); @@ -399,18 +401,19 @@ public class AlfrescoCoreAdminHandlerIT coreNames.forEach(spy::setupNewDefaultCores); - verify(spy).newCore(eq(ARCHIVE_CORE_NAME), eq(1), eq(STORE_REF_MAP.get(ARCHIVE_CORE_NAME)), anyString(), eq(1), eq(1), eq(1), eq(null), eq(null), any()); - verify(spy).newCore(eq(ALFRESCO_CORE_NAME), eq(1), eq(STORE_REF_MAP.get(ALFRESCO_CORE_NAME)), anyString(), eq(1), eq(1), eq(1), eq(null), eq(null), any()); - verify(spy).newCore(eq(VERSION_CORE_NAME), eq(1), eq(STORE_REF_MAP.get(VERSION_CORE_NAME)), anyString(), eq(1), eq(1), eq(1), eq(null), eq(null), any()); + verify(spy).newCore(eq(ARCHIVE_CORE_NAME), eq(1), eq(STORE_REF_MAP.get(ARCHIVE_CORE_NAME)), anyString(), eq(1), eq(1), eq(1), eq(null), eq(null)); + verify(spy).newCore(eq(ALFRESCO_CORE_NAME), eq(1), eq(STORE_REF_MAP.get(ALFRESCO_CORE_NAME)), anyString(), eq(1), eq(1), eq(1), eq(null), eq(null)); + verify(spy).newCore(eq(VERSION_CORE_NAME), eq(1), eq(STORE_REF_MAP.get(VERSION_CORE_NAME)), anyString(), eq(1), eq(1), eq(1), eq(null), eq(null)); } @Test public void validAndInvalidCoreNames() { AlfrescoCoreAdminHandler spy = spy(new AlfrescoCoreAdminHandler() { @Override - protected void newCore(String coreName, int numShards, StoreRef storeRef, String templateName, int replicationFactor, int nodeInstance, int numNodes, String shardIds, Properties extraProperties, SolrQueryResponse rsp) + protected NamedList newCore(String coreName, int numShards, StoreRef storeRef, String templateName, int replicationFactor, int nodeInstance, int numNodes, String shardIds, Properties extraProperties) { // Do nothing here otherwise we cannot spy it + return new SimpleOrderedMap<>(); } }); @@ -426,8 +429,8 @@ public class AlfrescoCoreAdminHandlerIT String commaSeparatedNames = String.join(",", coreNames); spy.setupNewDefaultCores(commaSeparatedNames); - verify(spy).newCore(eq(ARCHIVE_CORE_NAME), eq(1), eq(STORE_REF_MAP.get(ARCHIVE_CORE_NAME)), anyString(), eq(1), eq(1), eq(1), eq(null), eq(null), any()); - verify(spy).newCore(eq(ALFRESCO_CORE_NAME), eq(1), eq(STORE_REF_MAP.get(ALFRESCO_CORE_NAME)), anyString(), eq(1), eq(1), eq(1), eq(null), eq(null), any()); - verify(spy).newCore(eq(VERSION_CORE_NAME), eq(1), eq(STORE_REF_MAP.get(VERSION_CORE_NAME)), anyString(), eq(1), eq(1), eq(1), eq(null), eq(null), any()); + verify(spy).newCore(eq(ARCHIVE_CORE_NAME), eq(1), eq(STORE_REF_MAP.get(ARCHIVE_CORE_NAME)), anyString(), eq(1), eq(1), eq(1), eq(null), eq(null)); + verify(spy).newCore(eq(ALFRESCO_CORE_NAME), eq(1), eq(STORE_REF_MAP.get(ALFRESCO_CORE_NAME)), anyString(), eq(1), eq(1), eq(1), eq(null), eq(null)); + verify(spy).newCore(eq(VERSION_CORE_NAME), eq(1), eq(STORE_REF_MAP.get(VERSION_CORE_NAME)), anyString(), eq(1), eq(1), eq(1), eq(null), eq(null)); } } \ No newline at end of file diff --git a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoSolrUtils.java b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoSolrUtils.java index 5d9c0d27e..d8c65469b 100644 --- a/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoSolrUtils.java +++ b/search-services/alfresco-search/src/test/java/org/alfresco/solr/AlfrescoSolrUtils.java @@ -881,14 +881,16 @@ public class AlfrescoSolrUtils TimeUnit.SECONDS.sleep(1); if(shards > 1 ) { - NamedList vals = response.getValues(); - List coreNames = vals.getAll("core"); + NamedList action = (NamedList) response.getValues().get("action"); + List coreNames = action.getAll("core"); assertEquals(shards,coreNames.size()); testingCore = getCore(coreContainer, coreNames.get(0)); } else { - assertEquals(coreName, response.getValues().get("core")); + + NamedList action = (NamedList) response.getValues().get("action"); + assertEquals(coreName, action.get("core")); //Get a reference to the new core testingCore = getCore(coreContainer, coreName); }