Compare commits

...
17 Commits
17 changed files with 413 additions and 61 deletions
+2 -2
View File
@@ -6,13 +6,13 @@
<parent> <parent>
<groupId>com.inteligr8.alfresco</groupId> <groupId>com.inteligr8.alfresco</groupId>
<artifactId>asie-platform-module-parent</artifactId> <artifactId>asie-platform-module-parent</artifactId>
<version>1.3.0</version> <version>1.3.2</version>
<relativePath>../</relativePath> <relativePath>../</relativePath>
</parent> </parent>
<groupId>com.inteligr8.alfresco</groupId> <groupId>com.inteligr8.alfresco</groupId>
<artifactId>asie-api</artifactId> <artifactId>asie-api</artifactId>
<version>1.1.0-asie2</version> <version>1.1.1-asie2</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>ASIE Jakarta RS API</name> <name>ASIE Jakarta RS API</name>
@@ -7,6 +7,7 @@ import com.inteligr8.alfresco.asie.model.core.DisableIndexingRequest;
import com.inteligr8.alfresco.asie.model.core.EnableIndexingRequest; import com.inteligr8.alfresco.asie.model.core.EnableIndexingRequest;
import com.inteligr8.alfresco.asie.model.core.FixAction; import com.inteligr8.alfresco.asie.model.core.FixAction;
import com.inteligr8.alfresco.asie.model.core.FixRequest; import com.inteligr8.alfresco.asie.model.core.FixRequest;
import com.inteligr8.alfresco.asie.model.core.IndexRequest;
import com.inteligr8.alfresco.asie.model.core.IndexingStatusAction; import com.inteligr8.alfresco.asie.model.core.IndexingStatusAction;
import com.inteligr8.alfresco.asie.model.core.NewCoreRequest; import com.inteligr8.alfresco.asie.model.core.NewCoreRequest;
import com.inteligr8.alfresco.asie.model.core.NewDefaultIndexRequest; import com.inteligr8.alfresco.asie.model.core.NewDefaultIndexRequest;
@@ -61,6 +62,10 @@ public interface CoreAdminApi extends com.inteligr8.solr.api.CoreAdminApi {
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
ActionCoreResponse<Action> purge(@BeanParam PurgeRequest request); ActionCoreResponse<Action> purge(@BeanParam PurgeRequest request);
@GET
@Produces(MediaType.APPLICATION_JSON)
ActionCoreResponse<Action> index(@BeanParam IndexRequest request);
@GET @GET
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
ActionCoreResponse<Action> reindex(@BeanParam ReindexRequest request); ActionCoreResponse<Action> reindex(@BeanParam ReindexRequest request);
@@ -0,0 +1,106 @@
package com.inteligr8.alfresco.asie.model.core;
import com.inteligr8.solr.model.JsonFormattedResponseRequest;
import jakarta.annotation.Nonnull;
import jakarta.ws.rs.DefaultValue;
import jakarta.ws.rs.QueryParam;
public class IndexRequest extends JsonFormattedResponseRequest<IndexRequest> {
private static final String ACTION = "index";
@QueryParam("action")
@DefaultValue(ACTION)
@Nonnull
private String action = ACTION;
@QueryParam("core")
private String core;
@QueryParam("txid")
private Long transactionId;
@QueryParam("acltxid")
private Long aclTransactionId;
@QueryParam("nodeId")
private Long nodeId;
@QueryParam("aclid")
private Long aclId;
public String getAction() {
return action;
}
public void setAction(String action) {
this.action = action;
}
public String getCore() {
return core;
}
public void setCore(String core) {
this.core = core;
}
public IndexRequest withCore(String core) {
this.core = core;
return this;
}
public Long getTransactionId() {
return transactionId;
}
public void setTransactionId(Long transactionId) {
this.transactionId = transactionId;
}
public IndexRequest withTransactionId(Long transactionId) {
this.transactionId = transactionId;
return this;
}
public Long getAclTransactionId() {
return aclTransactionId;
}
public void setAclTransactionId(Long aclTransactionId) {
this.aclTransactionId = aclTransactionId;
}
public IndexRequest withAclTransactionId(Long aclTransactionId) {
this.aclTransactionId = aclTransactionId;
return this;
}
public Long getNodeId() {
return nodeId;
}
public void setNodeId(Long nodeId) {
this.nodeId = nodeId;
}
public IndexRequest withNodeId(Long nodeId) {
this.nodeId = nodeId;
return this;
}
public Long getAclId() {
return aclId;
}
public void setAclId(Long aclId) {
this.aclId = aclId;
}
public IndexRequest withAclId(Long aclId) {
this.aclId = aclId;
return this;
}
}
+5 -1
View File
@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>com.inteligr8.alfresco</groupId> <groupId>com.inteligr8.alfresco</groupId>
<artifactId>asie-platform-module-parent</artifactId> <artifactId>asie-platform-module-parent</artifactId>
<version>1.3.0</version> <version>1.3.2</version>
<relativePath>../</relativePath> <relativePath>../</relativePath>
</parent> </parent>
@@ -45,21 +45,25 @@
<dependency> <dependency>
<groupId>jakarta.ws.rs</groupId> <groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId> <artifactId>jakarta.ws.rs-api</artifactId>
<version>4.0.0</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>jakarta.annotation</groupId> <groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId> <artifactId>jakarta.annotation-api</artifactId>
<version>3.0.0</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.fasterxml.jackson.datatype</groupId> <groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId> <artifactId>jackson-datatype-jsr310</artifactId>
<version>2.17.3</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.fasterxml.jackson.module</groupId> <groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jakarta-xmlbind-annotations</artifactId> <artifactId>jackson-module-jakarta-xmlbind-annotations</artifactId>
<version>2.17.2</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
</dependencies> </dependencies>
@@ -1,21 +1,33 @@
package com.inteligr8.alfresco.asie.enterprise.rest; package com.inteligr8.alfresco.asie.enterprise.rest;
import org.alfresco.repo.index.shard.ShardState; import org.alfresco.repo.index.shard.ShardState;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.extensions.webscripts.WebScriptException;
import org.springframework.extensions.webscripts.WebScriptRequest; import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.model.ShardSet; import com.inteligr8.alfresco.asie.model.ShardSet;
import com.inteligr8.alfresco.asie.rest.model.NodeShardParameterSet; import com.inteligr8.alfresco.asie.rest.model.NodeShardParameterSet;
import com.inteligr8.alfresco.asie.spi.ShardDiscoveryService;
@Component(value = "webscript.com.inteligr8.alfresco.asie.nodeShard.delete") @Component(value = "webscript.com.inteligr8.alfresco.asie.nodeShard.delete")
public class UnloadNodeShardWebScript extends AbstractUnregisterNodeWebScript<NodeShardParameterSet> { public class UnloadNodeShardWebScript extends AbstractUnregisterNodeWebScript<NodeShardParameterSet> {
@Autowired
private ShardDiscoveryService sds;
@Override @Override
protected NodeShardParameterSet createParameters(WebScriptRequest req, String nodeHostname, int nodePort) { protected NodeShardParameterSet createParameters(WebScriptRequest req, String nodeHostname, int nodePort) {
ShardSet shardSet = this.getRequiredPathParameter(req, "shardSet", ShardSet.class); String coreName = this.getRequiredPathParameter(req, "shardCore");
int shardId = this.getRequiredPathParameter(req, "shardId", Integer.class); int shardId = this.getRequiredPathParameter(req, "shardId", Integer.class);
return new NodeShardParameterSet(nodeHostname, nodePort, shardSet, shardId); try {
ShardSet shardSet = this.sds.findSetByCore(coreName);
return new NodeShardParameterSet(nodeHostname, nodePort, shardSet, shardId);
} catch (IllegalArgumentException iae) {
throw new WebScriptException(HttpStatus.BAD_REQUEST.value(), iae.getMessage());
}
} }
@Override @Override
@@ -50,12 +50,15 @@ public class ShardDiscoveryService implements com.inteligr8.alfresco.asie.spi.Sh
for (Entry<Shard, Set<ShardState>> flocShard : floc.getValue().entrySet()) { for (Entry<Shard, Set<ShardState>> flocShard : floc.getValue().entrySet()) {
for (ShardState shardState : flocShard.getValue()) { for (ShardState shardState : flocShard.getValue()) {
ShardSet shardSet = ShardSet.from(floc.getKey(), shardState); ShardSet shardSet = ShardSet.from(floc.getKey(), shardState);
if (shardSet.getCore().equals(core)) if (shardSet.getCore().equals(core)) {
this.logger.debug("Found matching shard set: {}: {}", core, shardSet);
return shardSet; return shardSet;
}
} }
} }
} }
this.logger.trace("Did not find matching shard set: {}", core);
return null; return null;
} }
@@ -96,7 +99,8 @@ public class ShardDiscoveryService implements com.inteligr8.alfresco.asie.spi.Sh
} }
} }
} }
this.logger.trace("Did not find matching node: {}:{}", nodeHostname, nodePort);
return null; return null;
} }
@@ -127,6 +131,7 @@ public class ShardDiscoveryService implements com.inteligr8.alfresco.asie.spi.Sh
ShardInstance shardInstance = shardState.getShardInstance(); ShardInstance shardInstance = shardState.getShardInstance();
if (node.equals(SolrHost.from(shardInstance))) { if (node.equals(SolrHost.from(shardInstance))) {
this.logger.trace("Found shard on node: {}: {}", node, shardInstance.getShard().getInstance());
Map<Integer, ShardInstanceState> shardStates = setShardStates.get(shardSet); Map<Integer, ShardInstanceState> shardStates = setShardStates.get(shardSet);
if (shardStates == null) if (shardStates == null)
setShardStates.put(shardSet, shardStates = new HashMap<>()); setShardStates.put(shardSet, shardStates = new HashMap<>());
@@ -152,7 +157,9 @@ public class ShardDiscoveryService implements com.inteligr8.alfresco.asie.spi.Sh
for (Entry<Floc, Map<Shard, Set<ShardState>>> floc : flocs.entrySet()) { for (Entry<Floc, Map<Shard, Set<ShardState>>> floc : flocs.entrySet()) {
if (shardMethodSet.contains(floc.getKey().getShardMethod())) { if (shardMethodSet.contains(floc.getKey().getShardMethod())) {
this.logger.trace("Found qualifying shard method: {}", floc.getKey().getShardMethod());
ShardState shardState = this.extractAnyShardState(floc.getValue()); ShardState shardState = this.extractAnyShardState(floc.getValue());
this.logger.trace("Found single shard state representing shard method: {}: {}", floc.getKey().getShardMethod(), shardState);
shardSets.add(ShardSet.from(floc.getKey(), shardState)); shardSets.add(ShardSet.from(floc.getKey(), shardState));
} }
} }
+27 -16
View File
@@ -5,7 +5,7 @@
<groupId>com.inteligr8.alfresco</groupId> <groupId>com.inteligr8.alfresco</groupId>
<artifactId>asie-platform-module-parent</artifactId> <artifactId>asie-platform-module-parent</artifactId>
<version>1.3.0</version> <version>1.3.2</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<name>ASIE Platform Module Parent</name> <name>ASIE Platform Module Parent</name>
@@ -45,13 +45,24 @@
<maven.deploy.skip>true</maven.deploy.skip> <maven.deploy.skip>true</maven.deploy.skip>
</properties> </properties>
<dependencyManagement>
<dependencies>
<!-- Provided by cxf-jaxrs-platform-module, but packaged due to shared -->
<dependency>
<groupId>com.inteligr8</groupId>
<artifactId>common-rest-client</artifactId>
<version>3.0.3-cxf</version>
</dependency>
</dependencies>
</dependencyManagement>
<build> <build>
<pluginManagement> <pluginManagement>
<plugins> <plugins>
<!-- avoids log4j dependency --> <!-- avoids log4j dependency -->
<plugin> <plugin>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version> <version>3.14.1</version>
</plugin> </plugin>
<!-- avoids struts dependency --> <!-- avoids struts dependency -->
<plugin> <plugin>
@@ -61,27 +72,27 @@
<!-- Force use of a new maven-dependency-plugin that doesn't download struts dependency --> <!-- Force use of a new maven-dependency-plugin that doesn't download struts dependency -->
<plugin> <plugin>
<artifactId>maven-dependency-plugin</artifactId> <artifactId>maven-dependency-plugin</artifactId>
<version>3.8.1</version> <version>3.9.0</version>
</plugin> </plugin>
<plugin> <plugin>
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
<version>3.4.0</version> <version>3.5.4</version>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.junit.jupiter</groupId> <groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId> <artifactId>junit-jupiter-engine</artifactId>
<version>5.11.0</version> <version>5.14.0</version>
</dependency> </dependency>
</dependencies> </dependencies>
</plugin> </plugin>
<plugin> <plugin>
<artifactId>maven-failsafe-plugin</artifactId> <artifactId>maven-failsafe-plugin</artifactId>
<version>3.4.0</version> <version>3.5.4</version>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.junit.jupiter</groupId> <groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId> <artifactId>junit-jupiter-engine</artifactId>
<version>5.11.0</version> <version>5.14.0</version>
</dependency> </dependency>
</dependencies> </dependencies>
</plugin> </plugin>
@@ -98,7 +109,7 @@
<profiles> <profiles>
<profile> <profile>
<id>ossrh-release</id> <id>central-publish</id>
<properties> <properties>
<maven.deploy.skip>true</maven.deploy.skip> <maven.deploy.skip>true</maven.deploy.skip>
</properties> </properties>
@@ -138,20 +149,20 @@
</executions> </executions>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.sonatype.plugins</groupId> <groupId>org.sonatype.central</groupId>
<artifactId>nexus-staging-maven-plugin</artifactId> <artifactId>central-publishing-maven-plugin</artifactId>
<version>1.7.0</version> <version>0.8.0</version>
<extensions>true</extensions> <extensions>true</extensions>
<configuration> <configuration>
<serverId>ossrh</serverId> <publishingServerId>central</publishingServerId>
<nexusUrl>https://s01.oss.sonatype.org/</nexusUrl> <autoPublish>true</autoPublish>
<autoReleaseAfterClose>true</autoReleaseAfterClose>
</configuration> </configuration>
<!-- for some reason this is required... -->
<executions> <executions>
<execution> <execution>
<id>ossrh-deploy</id> <id>deploy</id>
<phase>deploy</phase> <phase>deploy</phase>
<goals><goal>deploy</goal></goals> <goals><goal>publish</goal></goals>
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
+2 -3
View File
@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>com.inteligr8.alfresco</groupId> <groupId>com.inteligr8.alfresco</groupId>
<artifactId>asie-platform-module-parent</artifactId> <artifactId>asie-platform-module-parent</artifactId>
<version>1.3.0</version> <version>1.3.2</version>
<relativePath>../</relativePath> <relativePath>../</relativePath>
</parent> </parent>
@@ -36,12 +36,11 @@
<dependency> <dependency>
<groupId>com.inteligr8.alfresco</groupId> <groupId>com.inteligr8.alfresco</groupId>
<artifactId>asie-api</artifactId> <artifactId>asie-api</artifactId>
<version>1.1.0-asie2</version> <version>1.1.1-asie2</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.inteligr8</groupId> <groupId>com.inteligr8</groupId>
<artifactId>common-rest-client</artifactId> <artifactId>common-rest-client</artifactId>
<version>3.0.3-cxf</version>
</dependency> </dependency>
<!-- Needed by this module, but provided by ACS --> <!-- Needed by this module, but provided by ACS -->
@@ -7,8 +7,6 @@ import org.slf4j.LoggerFactory;
import org.springframework.extensions.webscripts.WebScriptRequest; import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.extensions.webscripts.WebScriptResponse; import org.springframework.extensions.webscripts.WebScriptResponse;
import com.inteligr8.alfresco.asie.model.ShardSet;
public abstract class AbstractAsieNodeShardWebScript extends AbstractAsieShardableWebScript { public abstract class AbstractAsieNodeShardWebScript extends AbstractAsieShardableWebScript {
private final Logger logger = LoggerFactory.getLogger(this.getClass()); private final Logger logger = LoggerFactory.getLogger(this.getClass());
@@ -22,14 +20,14 @@ public abstract class AbstractAsieNodeShardWebScript extends AbstractAsieShardab
String nodeHostname = colon < 0 ? nodeEndpoint : nodeEndpoint.substring(0, colon); String nodeHostname = colon < 0 ? nodeEndpoint : nodeEndpoint.substring(0, colon);
int nodePort = colon < 0 ? this.getApiService().getDefaultSolrPort() : Integer.parseInt(nodeEndpoint.substring(colon+1)); int nodePort = colon < 0 ? this.getApiService().getDefaultSolrPort() : Integer.parseInt(nodeEndpoint.substring(colon+1));
ShardSet shardSet = this.getRequiredPathParameter(req, "shardSet", ShardSet.class); String shardCore = this.getRequiredPathParameter(req, "shardCore", String.class);
int shardId = this.getRequiredPathParameter(req, "shardId", Integer.class); int shardId = this.getRequiredPathParameter(req, "shardId", Integer.class);
this.execute(req, res, nodeHostname, nodePort, shardSet, shardId); this.execute(req, res, nodeHostname, nodePort, shardCore, shardId);
} }
protected abstract void execute(WebScriptRequest req, WebScriptResponse res, protected abstract void execute(WebScriptRequest req, WebScriptResponse res,
String nodeHostname, int nodePort, ShardSet shardSet, int shardId) String nodeHostname, int nodePort, String shardCore, int shardId)
throws IOException; throws IOException;
} }
@@ -0,0 +1,24 @@
package com.inteligr8.alfresco.asie.rest;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.service.IndexService;
import com.inteligr8.alfresco.asie.spi.ActionCallback;
@Component(value = "webscript.com.inteligr8.alfresco.asie.indexAcsNode.post")
public class IndexAcsNodeWebScript extends AbstractAcsNodeActionWebScript {
@Autowired
private IndexService indexSerivce;
@Override
protected void executeAction(long nodeDbId, ActionCallback callback, long fullQueueTimeout, TimeUnit fullQueueUnit,
long execTimeout, TimeUnit execUnit) throws TimeoutException, InterruptedException {
this.indexSerivce.index(nodeDbId, callback, 10L, TimeUnit.SECONDS, 30L, TimeUnit.SECONDS);
}
}
@@ -32,6 +32,7 @@ public class ReconcileAcsNodesWebScript extends AbstractAsieWebScript {
public void executeAuthorized(WebScriptRequest request, WebScriptResponse response) throws IOException { public void executeAuthorized(WebScriptRequest request, WebScriptResponse response) throws IOException {
final int fromDbId = this.getRequestTemplateIntegerVariable(request, "fromDbId"); final int fromDbId = this.getRequestTemplateIntegerVariable(request, "fromDbId");
final int toDbId = this.getRequestTemplateIntegerVariable(request, "toDbId"); final int toDbId = this.getRequestTemplateIntegerVariable(request, "toDbId");
final boolean index = Boolean.TRUE.equals(this.getOptionalQueryParameter(request, "index", Boolean.class));
final boolean reindex = Boolean.TRUE.equals(this.getOptionalQueryParameter(request, "reindex", Boolean.class)); final boolean reindex = Boolean.TRUE.equals(this.getOptionalQueryParameter(request, "reindex", Boolean.class));
final boolean includeReconciled = Boolean.TRUE.equals(this.getOptionalQueryParameter(request, "includeReconciled", Boolean.class)); final boolean includeReconciled = Boolean.TRUE.equals(this.getOptionalQueryParameter(request, "includeReconciled", Boolean.class));
@@ -101,7 +102,7 @@ public class ReconcileAcsNodesWebScript extends AbstractAsieWebScript {
}; };
try { try {
this.reconcileService.reconcile(fromDbId, toDbId, null, reindex, callback, 1L, TimeUnit.HOURS, 2L, TimeUnit.MINUTES); this.reconcileService.reconcile(fromDbId, toDbId, null, index, reindex, callback, 1L, TimeUnit.HOURS, 2L, TimeUnit.MINUTES);
if (responseMap.containsKey("error")) { if (responseMap.containsKey("error")) {
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
@@ -32,6 +32,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.model.ShardInstance; import com.inteligr8.alfresco.asie.model.ShardInstance;
import com.inteligr8.alfresco.asie.spi.IndexCallback;
import com.inteligr8.alfresco.asie.spi.ReconcileCallback; import com.inteligr8.alfresco.asie.spi.ReconcileCallback;
import com.inteligr8.alfresco.asie.spi.ReindexCallback; import com.inteligr8.alfresco.asie.spi.ReindexCallback;
import com.inteligr8.alfresco.asie.util.CompositeFuture; import com.inteligr8.alfresco.asie.util.CompositeFuture;
@@ -55,6 +56,9 @@ public class AcsReconcileService implements InitializingBean, DisposableBean {
@Autowired @Autowired
private SearchService searchService; private SearchService searchService;
@Autowired
private IndexService indexService;
@Autowired @Autowired
private ReindexService reindexService; private ReindexService reindexService;
@@ -98,7 +102,8 @@ public class AcsReconcileService implements InitializingBean, DisposableBean {
* *
* @param fromDbId A node database ID, inclusive. * @param fromDbId A node database ID, inclusive.
* @param toDbId A node database ID, exclusive. * @param toDbId A node database ID, exclusive.
* @param reindexUnreconciled For nodes not found in Solr, attempt to re-index against all applicable Solr instances. * @param indexUnreconciled For nodes not found in Solr, attempt to index against all applicable Solr instances.
* @param reindexReconciled For nodes found in Solr, attempt to re-index against all applicable Solr instances.
* @param callback A callback to process multiple returned values from the re-index. * @param callback A callback to process multiple returned values from the re-index.
* @param queueTimeout A timeout for how long the calling thread should wait for space on the queue. * @param queueTimeout A timeout for how long the calling thread should wait for space on the queue.
* @param queueUnit The time units for the `queueTimeout`. * @param queueUnit The time units for the `queueTimeout`.
@@ -109,20 +114,21 @@ public class AcsReconcileService implements InitializingBean, DisposableBean {
*/ */
public void reconcile( public void reconcile(
long fromDbId, long toDbId, Integer nodesChunkSize, long fromDbId, long toDbId, Integer nodesChunkSize,
boolean reindexUnreconciled, boolean indexUnreconciled,
boolean reindexReconciled,
ReconcileCallback callback, ReconcileCallback callback,
long queueTimeout, TimeUnit queueUnit, long queueTimeout, TimeUnit queueUnit,
long execTimeout, TimeUnit execUnit) throws InterruptedException, TimeoutException { long execTimeout, TimeUnit execUnit) throws InterruptedException, TimeoutException {
if (nodesChunkSize == null) if (nodesChunkSize == null)
nodesChunkSize = this.nodesChunkSize; nodesChunkSize = this.nodesChunkSize;
if (this.logger.isTraceEnabled()) if (this.logger.isTraceEnabled())
this.logger.trace("reconcile({}, {}, {}, {}, {}, {})", fromDbId, toDbId, nodesChunkSize, reindexUnreconciled, queueUnit.toMillis(queueTimeout), execUnit.toMillis(execTimeout)); this.logger.trace("reconcile({}, {}, {}, {}, {}, {}, {})", fromDbId, toDbId, nodesChunkSize, indexUnreconciled, reindexReconciled, queueUnit.toMillis(queueTimeout), execUnit.toMillis(execTimeout));
CompositeFuture<Void> future = new CompositeFuture<>(); CompositeFuture<Void> future = new CompositeFuture<>();
for (long startDbId = fromDbId; startDbId < toDbId; startDbId += nodesChunkSize) { for (long startDbId = fromDbId; startDbId < toDbId; startDbId += nodesChunkSize) {
long endDbId = Math.min(toDbId, startDbId + nodesChunkSize); long endDbId = Math.min(toDbId, startDbId + nodesChunkSize);
future.combine(this.reconcileChunk(startDbId, endDbId, reindexUnreconciled, callback, queueTimeout, queueUnit, execTimeout, execUnit)); future.combine(this.reconcileChunk(startDbId, endDbId, indexUnreconciled, reindexReconciled, callback, queueTimeout, queueUnit, execTimeout, execUnit));
future.purge(true); future.purge(true);
} }
@@ -135,18 +141,19 @@ public class AcsReconcileService implements InitializingBean, DisposableBean {
public Future<Void> reconcile( public Future<Void> reconcile(
long fromDbId, long toDbId, Integer nodesChunkSize, long fromDbId, long toDbId, Integer nodesChunkSize,
boolean reindexUnreconciled, boolean indexUnreconciled,
boolean reindexReconciled,
ReconcileCallback callback) throws InterruptedException { ReconcileCallback callback) throws InterruptedException {
if (nodesChunkSize == null) if (nodesChunkSize == null)
nodesChunkSize = this.nodesChunkSize; nodesChunkSize = this.nodesChunkSize;
this.logger.trace("reconcile({}, {}, {}, {})", fromDbId, toDbId, nodesChunkSize, reindexUnreconciled); this.logger.trace("reconcile({}, {}, {}, {}, {})", fromDbId, toDbId, nodesChunkSize, indexUnreconciled, reindexReconciled);
CompositeFuture<Void> future = new CompositeFuture<>(); CompositeFuture<Void> future = new CompositeFuture<>();
try { try {
for (long startDbId = fromDbId; startDbId < toDbId; startDbId += nodesChunkSize) { for (long startDbId = fromDbId; startDbId < toDbId; startDbId += nodesChunkSize) {
long endDbId = Math.min(toDbId, startDbId + nodesChunkSize); long endDbId = Math.min(toDbId, startDbId + nodesChunkSize);
future.combine(this.reconcileChunk(startDbId, endDbId, reindexUnreconciled, callback, -1L, null, -1L, null)); future.combine(this.reconcileChunk(startDbId, endDbId, indexUnreconciled, reindexReconciled, callback, -1L, null, -1L, null));
future.purge(true); future.purge(true);
} }
} catch (TimeoutException te) { } catch (TimeoutException te) {
@@ -158,12 +165,13 @@ public class AcsReconcileService implements InitializingBean, DisposableBean {
protected Future<Void> reconcileChunk( protected Future<Void> reconcileChunk(
long fromDbId, long toDbId, long fromDbId, long toDbId,
boolean reindexUnreconciled, boolean indexUnreconciled,
boolean reindexReconciled,
ReconcileCallback callback, ReconcileCallback callback,
long queueTimeout, TimeUnit queueUnit, long queueTimeout, TimeUnit queueUnit,
long execTimeout, TimeUnit execUnit) throws InterruptedException, TimeoutException { long execTimeout, TimeUnit execUnit) throws InterruptedException, TimeoutException {
if (this.logger.isTraceEnabled()) if (this.logger.isTraceEnabled())
this.logger.trace("reconcileChunk({}, {}, {}, {}, {})", fromDbId, toDbId, reindexUnreconciled, queueUnit.toMillis(queueTimeout), execUnit.toMillis(execTimeout)); this.logger.trace("reconcileChunk({}, {}, {}, {}, {}, {})", fromDbId, toDbId, indexUnreconciled, reindexReconciled, queueUnit.toMillis(queueTimeout), execUnit.toMillis(execTimeout));
int dbIdCount = (int) (toDbId - fromDbId); int dbIdCount = (int) (toDbId - fromDbId);
@@ -202,22 +210,32 @@ public class AcsReconcileService implements InitializingBean, DisposableBean {
final long nodeDbId = _nodeDbId; final long nodeDbId = _nodeDbId;
this.logger.trace("Attempting to reconcile ACS node: {}", nodeDbId); this.logger.trace("Attempting to reconcile ACS node: {}", nodeDbId);
Callable<Void> callable;
final int dbIdIndex = (int) (nodeDbId - fromDbId); final int dbIdIndex = (int) (nodeDbId - fromDbId);
if (nodeRefs[dbIdIndex] != null) { if (nodeRefs[dbIdIndex] != null) {
this.logger.trace("A node in the DB is already indexed in Solr: {}: {}", nodeDbId, nodeRefs[dbIdIndex]); callable = new Callable<Void>() {
this.reconcileLogger.info("RECONCILED: {} <=> {}", nodeDbId, nodeRefs[dbIdIndex]); @Override
callback.reconciled(nodeDbId); public Void call() throws InterruptedException, TimeoutException {
continue; logger.trace("A node in the DB is already indexed in Solr: {}: {}", nodeDbId, nodeRefs[dbIdIndex]);
reconcileLogger.info("RECONCILED: {} <=> {}", nodeDbId, nodeRefs[dbIdIndex]);
callback.reconciled(nodeDbId);
if (reindexReconciled)
reindex(nodeDbId, nodeRefs[dbIdIndex], callback, execTimeout, execUnit);
return null;
}
};
} else {
callable = new Callable<Void>() {
@Override
public Void call() throws InterruptedException, TimeoutException {
reconcile(nodeDbId, indexUnreconciled, callback, execTimeout, execUnit);
return null;
}
};
} }
Callable<Void> callable = new Callable<Void>() {
@Override
public Void call() throws InterruptedException, TimeoutException {
reconcile(nodeDbId, reindexUnreconciled, callback, execTimeout, execUnit);
return null;
}
};
if (queueTimeout < 0L) { if (queueTimeout < 0L) {
future.combine(this.executor.submit(callable, -1L, null)); future.combine(this.executor.submit(callable, -1L, null));
} else { } else {
@@ -229,7 +247,7 @@ public class AcsReconcileService implements InitializingBean, DisposableBean {
} }
public void reconcile(long nodeDbId, public void reconcile(long nodeDbId,
boolean reindexUnreconciled, boolean index,
ReconcileCallback callback, ReconcileCallback callback,
long execTimeout, TimeUnit execUnit) throws InterruptedException, TimeoutException { long execTimeout, TimeUnit execUnit) throws InterruptedException, TimeoutException {
NodeRef nodeRef = this.nodeService.getNodeRef(nodeDbId); NodeRef nodeRef = this.nodeService.getNodeRef(nodeDbId);
@@ -250,16 +268,58 @@ public class AcsReconcileService implements InitializingBean, DisposableBean {
return; return;
} }
if (!reindexUnreconciled) { if (!index) {
this.logger.debug("A node in the DB is not indexed in Solr: {}: {}", nodeDbId, nodeRef); this.logger.debug("A node in the DB is not indexed in Solr: {}: {}", nodeDbId, nodeRef);
this.reconcileLogger.info("UNRECONCILED: {} <=> {}", nodeDbId, nodeRef); this.reconcileLogger.info("UNRECONCILED: {} <=> {}", nodeDbId, nodeRef);
callback.unreconciled(nodeDbId); callback.unreconciled(nodeDbId);
} else { } else {
logger.debug("A node in the DB is not indexed in Solr; attempt to reindex: {}: {}", nodeDbId, nodeRef); logger.debug("A node in the DB is not indexed in Solr; attempt to index: {}: {}", nodeDbId, nodeRef);
this.reindex(nodeDbId, nodeRef, callback, execTimeout, execUnit); this.index(nodeDbId, nodeRef, callback, execTimeout, execUnit);
} }
} }
public void index(long nodeDbId, NodeRef nodeRef,
ReconcileCallback callback,
long execTimeout, TimeUnit execUnit) throws InterruptedException, TimeoutException {
Set<ShardInstance> syncHosts = new HashSet<>();
Set<ShardInstance> asyncHosts = new HashSet<>();
Map<ShardInstance, String> errorHosts = new HashMap<>();
IndexCallback indexCallback = new IndexCallback() {
@Override
public void success(ShardInstance instance) {
reconcileLogger.info("INDEXED: {} <=> {}", nodeDbId, nodeRef);
syncHosts.add(instance);
}
@Override
public void scheduled(ShardInstance instance) {
reconcileLogger.info("INDEXING: {} <=> {}", nodeDbId, nodeRef);
asyncHosts.add(instance);
}
@Override
public void error(ShardInstance instance, String message) {
reconcileLogger.info("FAILED INDEX: {} <=> {}", nodeDbId, nodeRef);
errorHosts.put(instance, message);
}
};
try {
if (execTimeout < 0L) {
this.indexService.index(nodeDbId, indexCallback).get();
} else {
this.indexService.index(nodeDbId, indexCallback).get(execTimeout, execUnit);
}
} catch (ExecutionException ee) {
throw new RuntimeException("An unexpected exception occurred: " + ee.getMessage(), ee);
}
if (callback != null)
callback.processed(nodeDbId, syncHosts, asyncHosts, errorHosts);
}
public void reindex(long nodeDbId, NodeRef nodeRef, public void reindex(long nodeDbId, NodeRef nodeRef,
ReconcileCallback callback, ReconcileCallback callback,
long execTimeout, TimeUnit execUnit) throws InterruptedException, TimeoutException { long execTimeout, TimeUnit execUnit) throws InterruptedException, TimeoutException {
@@ -283,7 +343,7 @@ public class AcsReconcileService implements InitializingBean, DisposableBean {
@Override @Override
public void error(ShardInstance instance, String message) { public void error(ShardInstance instance, String message) {
reconcileLogger.info("UNINDEXED: {} <=> {}", nodeDbId, nodeRef); reconcileLogger.info("FAILED REINDEX: {} <=> {}", nodeDbId, nodeRef);
errorHosts.put(instance, message); errorHosts.put(instance, message);
} }
}; };
@@ -0,0 +1,43 @@
package com.inteligr8.alfresco.asie.service;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.api.CoreAdminApi;
import com.inteligr8.alfresco.asie.model.ActionCoreResponse;
import com.inteligr8.alfresco.asie.model.core.IndexRequest;
import com.inteligr8.alfresco.asie.spi.ActionCallback;
import com.inteligr8.solr.model.Action;
@Component
public class IndexService extends AbstractNodeActionService {
@Override
protected String getActionName() {
return "index";
}
@Override
protected String getThreadNamePrefix() {
return "solr-index";
}
@Override
protected ActionCoreResponse<Action> execute(CoreAdminApi api, String core, long nodeDbId) {
IndexRequest apiRequest = new IndexRequest().withCore(core).withNodeId(nodeDbId);
return api.index(apiRequest);
}
public Future<Void> index(long nodeDbId, ActionCallback callback) throws InterruptedException {
return super.action(nodeDbId, callback);
}
public void index(long nodeDbId, ActionCallback callback, long fullQueueTimeout, TimeUnit fullQueueUnit,
long execTimeout, TimeUnit execUnit) throws TimeoutException, InterruptedException {
super.action(nodeDbId, callback, fullQueueTimeout, fullQueueUnit, execTimeout, execUnit);
}
}
@@ -0,0 +1,12 @@
package com.inteligr8.alfresco.asie.spi;
import com.inteligr8.alfresco.asie.model.ShardInstance;
public interface IndexCallback extends ActionCallback {
@Override
default void unknownResult(ShardInstance instance) {
throw new IllegalStateException();
}
}
@@ -0,0 +1,63 @@
<webscript xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://bitbucket.org/!api/2.0/snippets/inteligr8/AzMgbp/80fdd26a6b3769a63cdc6b54bf1f39e378545cf7/files/snippet.txt">
<!-- Naming & Organization -->
<shortname>Index ACS Node in ASIE Indexes</shortname>
<family>Inteligr8 ASIE</family>
<description><![CDATA[
<p>Index the specified ACS node in the ASIE indexes.
This call will attempt to index the ACS node on all applicable Solr nodes.
The index operation could be synchronous or asynchronous and could fail on any Solr node.
If any Solr node failed synchronously in the execution, then expect a status code of 500.
The response body will still be identical to the 200/202 status codes.
If any Solr node is executing the index asynchronously and there are no synchronous failures, then expect a status code of 202.</p>
<p>The following path parameters are supported:</p>
<dl>
<dt>nodeId</dt>
<dd>An ACS node ID.</dd>
</dl>
<p>The following response body should be expected in most cases (200, 202, and 500 status codes):</p>
<pre>
{
"nodeDbId": number,
"success": [
"solrHostSync:8983/solr",
...
],
"scheduled": [
"solrHostAsync:8983/solr",
...
],
"error": [
"solrHostThatFailed:8983/solr": {
"message": "string"
},
...
]
}
</pre>
<p>The following status codes should be expected:</p>
<dl>
<dt>200</dt>
<dd>OK</dd>
<dt>202</dt>
<dd>Accepted</dd>
<dt>400</dt>
<dd>The path or query parameters are invalid</dd>
</dl>
]]></description>
<!-- Endpoint Configuration -->
<url>/inteligr8/asie/acs/node/{nodeId}/index</url>
<format default="json">any</format>
<!-- Security -->
<authentication>user</authentication>
<!-- Functionality -->
<cache>
<never>false</never>
<public>false</public>
</cache>
</webscript>
@@ -16,6 +16,13 @@
<dt>toDbId</dt> <dt>toDbId</dt>
<dd>A DB ID integer for the ending point of a range, exclusive.</dd> <dd>A DB ID integer for the ending point of a range, exclusive.</dd>
</dl> </dl>
<p>The following query parameters are also supported:</p>
<dl>
<dt>index</dt>
<dd>Schedule an `INDEX` action against all unreconciled nodes.</dd>
<dt>reindex</dt>
<dd>Schedule a `REINDEX` action against all reconciled nodes.</dd>
</dl>
<p>The following response body should be expected in most cases (200, 202, and 500 status codes):</p> <p>The following response body should be expected in most cases (200, 202, and 500 status codes):</p>
<pre> <pre>
{ {
@@ -58,7 +65,7 @@
]]></description> ]]></description>
<!-- Endpoint Configuration --> <!-- Endpoint Configuration -->
<url>/inteligr8/asie/acs/nodes/{fromDbId}/{toDbId}/reconcile?reindex={reindex?}</url> <url>/inteligr8/asie/acs/nodes/{fromDbId}/{toDbId}/reconcile?index={index?}&amp;reindex={reindex?}</url>
<format default="json">any</format> <format default="json">any</format>
<!-- Security --> <!-- Security -->
+1 -1
View File
@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>com.inteligr8.alfresco</groupId> <groupId>com.inteligr8.alfresco</groupId>
<artifactId>asie-platform-module-parent</artifactId> <artifactId>asie-platform-module-parent</artifactId>
<version>1.3.0</version> <version>1.3.2</version>
<relativePath>../</relativePath> <relativePath>../</relativePath>
</parent> </parent>