Compare commits

...
Author SHA1 Message Date
brian.long ff068d8d3f v1.3.3 poms 2026-01-12 15:05:11 -05:00
brian.long 354ea3f337 Merge branch 'develop' into stable 2026-01-12 15:04:52 -05:00
brian.long ed2251065d added sample versions for enterprise deps 2026-01-12 15:04:40 -05:00
brian.long 1aa133876a optimizing property-based shard method 2026-01-12 15:02:10 -05:00
brian.long b737bccd1c Merge branch 'develop' into stable 2026-01-12 12:19:55 -05:00
brian.long 18ca6f09c5 move provided libs to deps 2026-01-12 12:19:44 -05:00
brian.long ed97efb4ed Merge branch 'develop' into stable 2026-01-12 12:16:03 -05:00
brian.long 59e745706b fix POM version specs 2026-01-12 12:15:52 -05:00
brian.long b6bae65e03 Merge branch 'develop' into stable 2026-01-12 12:07:53 -05:00
brian.long 52caf9b555 fix javadoc for index changes 2026-01-12 12:07:40 -05:00
brian.long fe95ff6239 set asie-api version v1.1.1-asie2 2026-01-12 12:07:26 -05:00
brian.long a1144fcad4 Merge branch 'develop' into stable 2026-01-12 11:58:42 -05:00
brian.long 560e147333 nexus-staging to central-publishing plugin; upgraded plugin versions 2026-01-12 11:58:36 -05:00
brian.long 299be529ba v1.3.2 poms 2026-01-12 11:55:16 -05:00
brian.long ff331cac48 v1.1.1-asie2 pom 2026-01-12 11:53:17 -05:00
brian.long d86c96fcb1 Merge branch 'develop' into stable 2026-01-12 11:52:20 -05:00
brian.long 29c4b4e1c4 increased debug/trace logging during shard discovery 2026-01-12 11:51:43 -05:00
brian.long 2c3fc8495d add index support to reconcile 2026-01-12 11:51:28 -05:00
brian.long bffceee21c added index endpoint 2026-01-12 11:50:19 -05:00
brian.long 294b684292 added ASIE API for index action 2026-01-12 11:29:44 -05:00
brian.long 933a786038 v1.3.1 poms 2025-04-01 16:17:27 -04:00
brian.long bb183c5e50 Merge branch 'develop' into stable 2025-04-01 16:16:54 -04:00
brian.long ba5188ff93 fix node shard unload 2025-04-01 16:16:17 -04:00
brian.long f909f3429b v1.3.0 poms 2025-03-05 16:21:19 -05:00
brian.long fe2eaa0588 v1.1.0 model poms 2025-03-05 16:21:14 -05:00
brian.long 68471be9ef Merge branch 'develop' into stable 2025-03-05 16:18:49 -05:00
brian.long 493f1f813d added reconcile/reindex/retry/fix/purge services 2025-03-05 13:28:41 -05:00
brian.long 0ed41a39e4 fixed ASIE shard model parsing 2025-02-28 17:48:43 -05:00
brian.long 3cd8c91f93 create ApiService and use it 2025-02-28 17:47:43 -05:00
brian.long 0cb566e18d v1.3.x 2025-02-28 17:46:04 -05:00
brian.long c38ed7a73a minor POM typo 2025-02-28 17:07:23 -05:00
brian.long bf9a5fca50 add ASIE unit tests 2025-02-28 17:06:56 -05:00
brian.long 40d13ac266 fix ASIE response model 2025-02-28 17:06:43 -05:00
brian.long 75e25577b7 fix Solr response model 2025-02-28 16:58:36 -05:00
brian.long 82410805db v1.2.2 poms 2025-01-09 16:08:48 -05:00
brian.long ceb8d2c690 Merge branch 'develop' into stable 2025-01-09 16:05:18 -05:00
brian.long 35bae4283d get authorities from AuthorityService 2025-01-09 11:53:48 -05:00
brian.long d537c8ec49 logging authority for debugging 2025-01-09 11:05:46 -05:00
brian.long f17556835a fix afterPropertiesSet() 2025-01-08 17:10:33 -05:00
brian.long 4531c7af8e changed admin to user auth; using configurable auth 2025-01-08 16:51:47 -05:00
brian.long 692410f535 moved ASIE custom authorization to AbstractWebScript 2025-01-08 16:47:35 -05:00
brian.long 1230a07a5a added transaction wrapper to REST declaration 2025-01-08 14:52:34 -05:00
brian.long 47835d852f wrapped attributeService in tx 2025-01-08 14:33:14 -05:00
brian.long 7535475581 refactored PersistedNode for serialization 2025-01-08 13:52:58 -05:00
brian.long 1c15853652 v1.2.1 poms 2025-01-08 11:56:21 -05:00
brian.long cf1bc8d2be Merge branch 'develop' into stable 2025-01-08 11:55:31 -05:00
brian.long 14887ca167 renamed path variables from shardSet to shardCore 2025-01-08 11:55:14 -05:00
brian.long 632900ecee Merge branch 'feature/community' into develop 2024-11-14 11:02:40 -05:00
brian.long af7c9e148e compiling/running module (untested) 2024-11-14 11:01:49 -05:00
brian.long a49ddbd373 Merge branch 'develop' into stable 2024-11-13 18:05:16 -05:00
brian.long de8e0bf2d7 update from refactoring (incomplete) 2024-11-13 18:03:22 -05:00
brian.long 006597f6fb Merge branch 'develop' into feature/community 2024-11-13 18:02:53 -05:00
brian.long 8be29fc37d substantial refactoring 2024-11-13 18:02:19 -05:00
brian.long 738e5b98f8 v1.1.1 poms 2024-11-06 13:26:28 -05:00
brian.long 9d9d17c49b Merge branch 'develop' into stable 2024-11-06 13:25:38 -05:00
brian.long 3ecbf006dd added query parsing (incomplete) 2024-11-06 13:24:54 -05:00
brian.long 07d6e63457 Merge branch 'develop' into feature/community 2024-11-06 13:24:23 -05:00
brian.long f29a0e6e3f moved shardState cache override to community 2024-11-06 13:24:13 -05:00
brian.long 4f8d6ad4a0 make ShardSet serializable 2024-11-06 13:23:42 -05:00
brian.long 22e05986b3 fix log4j logger name 2024-11-06 13:23:17 -05:00
brian.long 20d9ce299a moved unload from shared to enterprise 2024-11-06 13:22:52 -05:00
brian.long 43109e7d9c Merge branch 'develop' into stable 2024-10-31 15:17:54 -04:00
brian.long dcb37c3dc8 v1.1.0 poms 2024-10-31 14:53:06 -04:00
brian.long 68ffb56cc9 Merge branch 'develop' into stable 2024-10-31 14:50:33 -04:00
brian.long dc436e9ce0 v1.0.1 poms 2024-10-29 09:29:14 -04:00
brian.long 18c39b0fc5 fix merge 2024-10-29 09:28:21 -04:00
brian.long b7aa615103 Merge branch 'develop' into stable 2024-10-29 09:27:47 -04:00
brian.long e8f133b873 v1.0.0 poms 2024-10-28 16:26:00 -04:00
164 changed files with 7186 additions and 1642 deletions
+41 -7
View File
@@ -6,20 +6,20 @@
<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.1-SNAPSHOT</version> <version>1.3.3</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.0-SNAPSHOT-asie2</version> <version>1.1.1-asie2</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>ASIE JAX-RS API</name> <name>ASIE Jakarta RS API</name>
<description>Alfresco Search &amp; Insight Engine JAX-RS API</description> <description>Alfresco Search &amp; Insight Engine Jakarta RS API</description>
<properties> <properties>
<alfresco.platform.version>6.0.0</alfresco.platform.version> <alfresco.platform.version>23.2.0</alfresco.platform.version>
</properties> </properties>
<dependencyManagement> <dependencyManagement>
@@ -38,11 +38,45 @@
<dependency> <dependency>
<groupId>com.inteligr8</groupId> <groupId>com.inteligr8</groupId>
<artifactId>solr-api</artifactId> <artifactId>solr-api</artifactId>
<version>1.0-SNAPSHOT-solr6</version> <version>1.1.0-solr6</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.alfresco</groupId> <groupId>org.alfresco</groupId>
<artifactId>alfresco-repository</artifactId> <artifactId>alfresco-data-model</artifactId>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.17.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.inteligr8</groupId>
<artifactId>common-rest-client</artifactId>
<version>3.0.2-jersey</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-hk2</artifactId>
<version>3.1.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.11.2</version>
<scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
@@ -1,12 +1,13 @@
package com.inteligr8.alfresco.asie.api; package com.inteligr8.alfresco.asie.api;
import com.inteligr8.alfresco.asie.model.ActionResponse; import com.inteligr8.alfresco.asie.model.ActionCoreResponse;
import com.inteligr8.alfresco.asie.model.EmptyResponse; import com.inteligr8.alfresco.asie.model.EmptyResponse;
import com.inteligr8.alfresco.asie.model.core.CheckRequest; import com.inteligr8.alfresco.asie.model.core.CheckRequest;
import com.inteligr8.alfresco.asie.model.core.DisableIndexingRequest; 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.FixRequest; import com.inteligr8.alfresco.asie.model.core.FixRequest;
import com.inteligr8.alfresco.asie.model.core.FixResponseAction; 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;
@@ -14,14 +15,15 @@ import com.inteligr8.alfresco.asie.model.core.PurgeRequest;
import com.inteligr8.alfresco.asie.model.core.ReindexRequest; import com.inteligr8.alfresco.asie.model.core.ReindexRequest;
import com.inteligr8.alfresco.asie.model.core.ReportRequest; import com.inteligr8.alfresco.asie.model.core.ReportRequest;
import com.inteligr8.alfresco.asie.model.core.ReportResponse; import com.inteligr8.alfresco.asie.model.core.ReportResponse;
import com.inteligr8.alfresco.asie.model.core.RetryAction;
import com.inteligr8.alfresco.asie.model.core.RetryRequest; import com.inteligr8.alfresco.asie.model.core.RetryRequest;
import com.inteligr8.alfresco.asie.model.core.RetryResponseAction;
import com.inteligr8.alfresco.asie.model.core.SummaryRequest; import com.inteligr8.alfresco.asie.model.core.SummaryRequest;
import com.inteligr8.alfresco.asie.model.core.SummaryResponse; import com.inteligr8.alfresco.asie.model.core.SummaryResponse;
import com.inteligr8.alfresco.asie.model.core.UpdateCoreRequest; import com.inteligr8.alfresco.asie.model.core.UpdateCoreRequest;
import com.inteligr8.alfresco.asie.model.core.UpdateLog4jRequest; import com.inteligr8.alfresco.asie.model.core.UpdateLog4jRequest;
import com.inteligr8.alfresco.asie.model.core.UpdateSharedRequest; import com.inteligr8.alfresco.asie.model.core.UpdateSharedRequest;
import com.inteligr8.solr.model.ResponseAction; import com.inteligr8.solr.model.Action;
import com.inteligr8.solr.model.ActionResponse;
import jakarta.ws.rs.BeanParam; import jakarta.ws.rs.BeanParam;
import jakarta.ws.rs.GET; import jakarta.ws.rs.GET;
@@ -42,43 +44,47 @@ public interface CoreAdminApi extends com.inteligr8.solr.api.CoreAdminApi {
@GET @GET
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
ActionResponse<ResponseAction> updateCore(@BeanParam UpdateCoreRequest request); ActionResponse<Action> updateCore(@BeanParam UpdateCoreRequest request);
@GET @GET
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
ActionResponse<ResponseAction> check(@BeanParam CheckRequest request); ActionResponse<Action> check(@BeanParam CheckRequest request);
@GET @GET
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
ActionResponse<ResponseAction> updateShared(@BeanParam UpdateSharedRequest request); ActionResponse<Action> updateShared(@BeanParam UpdateSharedRequest request);
@GET @GET
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
ActionResponse<ResponseAction> updateLog4j(@BeanParam UpdateLog4jRequest request); ActionResponse<Action> updateLog4j(@BeanParam UpdateLog4jRequest request);
@GET @GET
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
ActionResponse<ResponseAction> purge(@BeanParam PurgeRequest request); ActionCoreResponse<Action> purge(@BeanParam PurgeRequest request);
@GET @GET
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
ActionResponse<ResponseAction> reindex(@BeanParam ReindexRequest request); ActionCoreResponse<Action> index(@BeanParam IndexRequest request);
@GET @GET
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
ActionResponse<RetryResponseAction> retry(@BeanParam RetryRequest request); ActionCoreResponse<Action> reindex(@BeanParam ReindexRequest request);
@GET @GET
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
ActionResponse<FixResponseAction> fix(@BeanParam FixRequest request); ActionCoreResponse<RetryAction> retry(@BeanParam RetryRequest request);
@GET @GET
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
ActionResponse<IndexingStatusAction> enableIndexing(@BeanParam EnableIndexingRequest request); ActionCoreResponse<FixAction> fix(@BeanParam FixRequest request);
@GET @GET
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
ActionResponse<IndexingStatusAction> disableIndexing(@BeanParam DisableIndexingRequest request); ActionCoreResponse<IndexingStatusAction> enableIndexing(@BeanParam EnableIndexingRequest request);
@GET
@Produces(MediaType.APPLICATION_JSON)
ActionCoreResponse<IndexingStatusAction> disableIndexing(@BeanParam DisableIndexingRequest request);
@GET @GET
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@@ -0,0 +1,18 @@
package com.inteligr8.alfresco.asie.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.inteligr8.solr.model.Action;
import com.inteligr8.solr.model.Cores;
@JsonIgnoreProperties(ignoreUnknown = true)
public class ActionCoreResponse<T extends Action> extends BaseResponse {
@JsonProperty(value = "action")
private Cores<T> cores;
public Cores<T> getCores() {
return cores;
}
}
@@ -1,22 +0,0 @@
package com.inteligr8.alfresco.asie.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;
import com.inteligr8.solr.model.ResponseAction;
@JsonIgnoreProperties(ignoreUnknown = true)
public class ActionResponse<T extends ResponseAction> extends BaseResponse {
@JsonProperty(access = Access.READ_ONLY)
private T action;
public T getAction() {
return action;
}
protected void setAction(T action) {
this.action = action;
}
}
@@ -1,41 +1,28 @@
package com.inteligr8.alfresco.asie.model; package com.inteligr8.alfresco.asie.model;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;
public class BaseResponse extends com.inteligr8.solr.model.BaseResponse { public class BaseResponse extends com.inteligr8.solr.model.BaseResponse {
@JsonProperty(value = "STATUS", access = Access.READ_ONLY) @JsonProperty(value = "STATUS")
private String reason; private String reason;
@JsonProperty(value = "exception", access = Access.READ_ONLY) @JsonProperty(value = "exception")
private String exception; private String exception;
@JsonProperty(value = "msg", access = Access.READ_ONLY) @JsonProperty(value = "msg")
private String message; private String message;
public String getReason() { public String getReason() {
return reason; return reason;
} }
protected void setReason(String reason) {
this.reason = reason;
}
public String getException() { public String getException() {
return exception; return exception;
} }
protected void setException(String exception) {
this.exception = exception;
}
public String getMessage() { public String getMessage() {
return message; return message;
} }
protected void setMessage(String message) {
this.message = message;
}
} }
@@ -0,0 +1,25 @@
package com.inteligr8.alfresco.asie.model.core;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.inteligr8.solr.model.Action;
import com.inteligr8.solr.model.TransactionStatus;
@JsonIgnoreProperties(ignoreUnknown = true)
public class FixAction extends Action {
@JsonProperty(value = "txToReindex")
private TransactionStatus transactionStatus;
@JsonProperty(value = "aclChangeSetToReindex")
private TransactionStatus aclStatus;
public TransactionStatus getTransactionStatus() {
return transactionStatus;
}
public TransactionStatus getAclStatus() {
return aclStatus;
}
}
@@ -1,34 +0,0 @@
package com.inteligr8.alfresco.asie.model.core;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;
import com.inteligr8.solr.model.ResponseAction;
import com.inteligr8.solr.model.TransactionResponseStatus;
@JsonIgnoreProperties(ignoreUnknown = true)
public class FixResponseAction extends ResponseAction {
@JsonProperty(value = "txToReindex", access = Access.READ_ONLY)
private TransactionResponseStatus transactionStatus;
@JsonProperty(value = "aclChangeSetToReindex", access = Access.READ_ONLY)
private TransactionResponseStatus aclStatus;
public TransactionResponseStatus getTransactionStatus() {
return transactionStatus;
}
protected void setTransactionStatus(TransactionResponseStatus transactionStatus) {
this.transactionStatus = transactionStatus;
}
public TransactionResponseStatus getAclStatus() {
return aclStatus;
}
protected void setAclStatus(TransactionResponseStatus aclStatus) {
this.aclStatus = aclStatus;
}
}
@@ -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;
}
}
@@ -1,25 +1,43 @@
package com.inteligr8.alfresco.asie.model.core; package com.inteligr8.alfresco.asie.model.core;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.inteligr8.solr.model.ResponseAction; import com.fasterxml.jackson.annotation.JsonProperty;
import com.inteligr8.solr.model.Action;
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class IndexingStatusAction extends ResponseAction { public class IndexingStatusAction extends Action {
private Map<String, IndexingStatusMetadata> cores; @JsonProperty(value = "ACL")
private Boolean aclIndexed;
@JsonAnyGetter
public Map<String, IndexingStatusMetadata> getCores() { @JsonProperty(value = "CONTENT")
return cores; private Boolean contentIndexed;
@JsonProperty(value = "METADATA")
private Boolean metadataIndexed;
public boolean isAclIndexed() {
return Boolean.TRUE.equals(this.aclIndexed);
} }
@JsonAnySetter public Boolean getAclIndexed() {
public void setCores(Map<String, IndexingStatusMetadata> cores) { return aclIndexed;
this.cores = cores; }
public boolean isContentIndexed() {
return Boolean.TRUE.equals(this.contentIndexed);
}
public Boolean getContentIndexed() {
return contentIndexed;
}
public boolean isMetadataIndexed() {
return Boolean.TRUE.equals(this.metadataIndexed);
}
public Boolean getMetadataIndexed() {
return metadataIndexed;
} }
} }
@@ -1,56 +0,0 @@
package com.inteligr8.alfresco.asie.model.core;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;
import com.inteligr8.solr.model.ResponseAction;
@JsonIgnoreProperties(ignoreUnknown = true)
public class IndexingStatusMetadata extends ResponseAction {
@JsonProperty(value = "ACL", access = Access.READ_ONLY)
private Boolean aclIndexed;
@JsonProperty(value = "CONTENT", access = Access.READ_ONLY)
private Boolean contentIndexed;
@JsonProperty(value = "METADATA", access = Access.READ_ONLY)
private Boolean metadataIndexed;
public boolean isAclIndexed() {
return Boolean.TRUE.equals(this.aclIndexed);
}
public Boolean getAclIndexed() {
return aclIndexed;
}
protected void setAclIndexed(Boolean aclIndexed) {
this.aclIndexed = aclIndexed;
}
public boolean isContentIndexed() {
return Boolean.TRUE.equals(this.contentIndexed);
}
public Boolean getContentIndexed() {
return contentIndexed;
}
protected void setContentIndexed(Boolean contentIndexed) {
this.contentIndexed = contentIndexed;
}
public boolean isMetadataIndexed() {
return Boolean.TRUE.equals(this.metadataIndexed);
}
public Boolean getMetadataIndexed() {
return metadataIndexed;
}
protected void setMetadataIndexed(Boolean metadataIndexed) {
this.metadataIndexed = metadataIndexed;
}
}
@@ -19,16 +19,16 @@ public class PurgeRequest extends JsonFormattedResponseRequest<PurgeRequest> {
private String core; private String core;
@QueryParam("txid") @QueryParam("txid")
private Integer transactionId; private Long transactionId;
@QueryParam("acltxid") @QueryParam("acltxid")
private Integer aclTransactionId; private Long aclTransactionId;
@QueryParam("nodeId") @QueryParam("nodeId")
private Integer nodeId; private Long nodeId;
@QueryParam("aclid") @QueryParam("aclid")
private Integer aclId; private Long aclId;
public String getAction() { public String getAction() {
return action; return action;
@@ -51,54 +51,54 @@ public class PurgeRequest extends JsonFormattedResponseRequest<PurgeRequest> {
return this; return this;
} }
public Integer getTransactionId() { public Long getTransactionId() {
return transactionId; return transactionId;
} }
public void setTransactionId(Integer transactionId) { public void setTransactionId(Long transactionId) {
this.transactionId = transactionId; this.transactionId = transactionId;
} }
public PurgeRequest withTransactionId(Integer transactionId) { public PurgeRequest withTransactionId(Long transactionId) {
this.transactionId = transactionId; this.transactionId = transactionId;
return this; return this;
} }
public Integer getAclTransactionId() { public Long getAclTransactionId() {
return aclTransactionId; return aclTransactionId;
} }
public void setAclTransactionId(Integer aclTransactionId) { public void setAclTransactionId(Long aclTransactionId) {
this.aclTransactionId = aclTransactionId; this.aclTransactionId = aclTransactionId;
} }
public PurgeRequest withAclTransactionId(Integer aclTransactionId) { public PurgeRequest withAclTransactionId(Long aclTransactionId) {
this.aclTransactionId = aclTransactionId; this.aclTransactionId = aclTransactionId;
return this; return this;
} }
public Integer getNodeId() { public Long getNodeId() {
return nodeId; return nodeId;
} }
public void setNodeId(Integer nodeId) { public void setNodeId(Long nodeId) {
this.nodeId = nodeId; this.nodeId = nodeId;
} }
public PurgeRequest withNodeId(Integer nodeId) { public PurgeRequest withNodeId(Long nodeId) {
this.nodeId = nodeId; this.nodeId = nodeId;
return this; return this;
} }
public Integer getAclId() { public Long getAclId() {
return aclId; return aclId;
} }
public void setAclId(Integer aclId) { public void setAclId(Long aclId) {
this.aclId = aclId; this.aclId = aclId;
} }
public PurgeRequest withAclId(Integer aclId) { public PurgeRequest withAclId(Long aclId) {
this.aclId = aclId; this.aclId = aclId;
return this; return this;
} }
@@ -19,16 +19,16 @@ public class ReindexRequest extends JsonFormattedResponseRequest<ReindexRequest>
private String core; private String core;
@QueryParam("txid") @QueryParam("txid")
private Integer transactionId; private Long transactionId;
@QueryParam("acltxid") @QueryParam("acltxid")
private Integer aclTransactionId; private Long aclTransactionId;
@QueryParam("nodeId") @QueryParam("nodeId")
private Integer nodeId; private Long nodeId;
@QueryParam("aclid") @QueryParam("aclid")
private Integer aclId; private Long aclId;
@QueryParam("query") @QueryParam("query")
private String query; private String query;
@@ -54,54 +54,54 @@ public class ReindexRequest extends JsonFormattedResponseRequest<ReindexRequest>
return this; return this;
} }
public Integer getTransactionId() { public Long getTransactionId() {
return transactionId; return transactionId;
} }
public void setTransactionId(Integer transactionId) { public void setTransactionId(Long transactionId) {
this.transactionId = transactionId; this.transactionId = transactionId;
} }
public ReindexRequest withTransactionId(Integer transactionId) { public ReindexRequest withTransactionId(Long transactionId) {
this.transactionId = transactionId; this.transactionId = transactionId;
return this; return this;
} }
public Integer getAclTransactionId() { public Long getAclTransactionId() {
return aclTransactionId; return aclTransactionId;
} }
public void setAclTransactionId(Integer aclTransactionId) { public void setAclTransactionId(Long aclTransactionId) {
this.aclTransactionId = aclTransactionId; this.aclTransactionId = aclTransactionId;
} }
public ReindexRequest withAclTransactionId(Integer aclTransactionId) { public ReindexRequest withAclTransactionId(Long aclTransactionId) {
this.aclTransactionId = aclTransactionId; this.aclTransactionId = aclTransactionId;
return this; return this;
} }
public Integer getNodeId() { public Long getNodeId() {
return nodeId; return nodeId;
} }
public void setNodeId(Integer nodeId) { public void setNodeId(Long nodeId) {
this.nodeId = nodeId; this.nodeId = nodeId;
} }
public ReindexRequest withNodeId(Integer nodeId) { public ReindexRequest withNodeId(Long nodeId) {
this.nodeId = nodeId; this.nodeId = nodeId;
return this; return this;
} }
public Integer getAclId() { public Long getAclId() {
return aclId; return aclId;
} }
public void setAclId(Integer aclId) { public void setAclId(Long aclId) {
this.aclId = aclId; this.aclId = aclId;
} }
public ReindexRequest withAclId(Integer aclId) { public ReindexRequest withAclId(Long aclId) {
this.aclId = aclId; this.aclId = aclId;
return this; return this;
} }
@@ -1,24 +1,9 @@
package com.inteligr8.alfresco.asie.model.core; package com.inteligr8.alfresco.asie.model.core;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.inteligr8.solr.model.Metadata;
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class Report { public class Report extends Metadata {
private Map<String, Map<String, Object>> report;
@JsonAnyGetter
public Map<String, Map<String, Object>> getReport() {
return report;
}
@JsonAnySetter
protected void setReport(Map<String, Map<String, Object>> report) {
this.report = report;
}
} }
@@ -25,10 +25,10 @@ public class ReportRequest extends JsonFormattedResponseRequest<ReportRequest> {
private Long toTime; private Long toTime;
@QueryParam("fromTx") @QueryParam("fromTx")
private Integer fromTransactionId; private Long fromTransactionId;
@QueryParam("toTx") @QueryParam("toTx")
private Integer toTransactionId; private Long toTransactionId;
public String getAction() { public String getAction() {
return action; return action;
@@ -77,28 +77,28 @@ public class ReportRequest extends JsonFormattedResponseRequest<ReportRequest> {
return this; return this;
} }
public Integer getFromTransactionId() { public Long getFromTransactionId() {
return fromTransactionId; return fromTransactionId;
} }
public void setFromTransactionId(Integer fromTransactionId) { public void setFromTransactionId(Long fromTransactionId) {
this.fromTransactionId = fromTransactionId; this.fromTransactionId = fromTransactionId;
} }
public ReportRequest fromTransactionId(Integer fromTransactionId) { public ReportRequest fromTransactionId(Long fromTransactionId) {
this.fromTransactionId = fromTransactionId; this.fromTransactionId = fromTransactionId;
return this; return this;
} }
public Integer getToTransactionId() { public Long getToTransactionId() {
return toTransactionId; return toTransactionId;
} }
public void setToTransactionId(Integer toTransactionId) { public void setToTransactionId(Long toTransactionId) {
this.toTransactionId = toTransactionId; this.toTransactionId = toTransactionId;
} }
public ReportRequest toTransactionId(Integer toTransactionId) { public ReportRequest toTransactionId(Long toTransactionId) {
this.toTransactionId = toTransactionId; this.toTransactionId = toTransactionId;
return this; return this;
} }
@@ -2,21 +2,17 @@ package com.inteligr8.alfresco.asie.model.core;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;
import com.inteligr8.alfresco.asie.model.BaseResponse; import com.inteligr8.alfresco.asie.model.BaseResponse;
import com.inteligr8.solr.model.Cores;
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class ReportResponse extends BaseResponse { public class ReportResponse extends BaseResponse {
@JsonProperty(access = Access.READ_ONLY) @JsonProperty(required = true)
private Report report; private Cores<Report> cores;
public Report getReport() { public Cores<Report> getCores() {
return report; return cores;
}
protected void setReport(Report report) {
this.report = report;
} }
} }
@@ -0,0 +1,17 @@
package com.inteligr8.alfresco.asie.model.core;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.inteligr8.solr.model.Action;
@JsonIgnoreProperties(ignoreUnknown = true)
public class RetryAction extends Action {
@JsonProperty(value = "alfresco")
private int[] nodeIds;
public int[] getNodeIds() {
return nodeIds;
}
}
@@ -1,22 +0,0 @@
package com.inteligr8.alfresco.asie.model.core;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;
import com.inteligr8.solr.model.ResponseAction;
@JsonIgnoreProperties(ignoreUnknown = true)
public class RetryResponseAction extends ResponseAction {
@JsonProperty(value = "alfresco", access = Access.READ_ONLY)
private int[] nodeIds;
public int[] getNodeIds() {
return nodeIds;
}
public void setNodeIds(int[] nodeIds) {
this.nodeIds = nodeIds;
}
}
@@ -1,24 +1,7 @@
package com.inteligr8.alfresco.asie.model.core; package com.inteligr8.alfresco.asie.model.core;
import java.util.Map; import com.inteligr8.solr.model.Metadata;
import com.fasterxml.jackson.annotation.JsonAnyGetter; public class Summary extends Metadata {
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@JsonIgnoreProperties(ignoreUnknown = true)
public class Summary {
private Map<String, Object> summary;
@JsonAnyGetter
public Map<String, Object> getSummary() {
return summary;
}
@JsonAnySetter
public void setSummary(Map<String, Object> summary) {
this.summary = summary;
}
} }
@@ -2,21 +2,17 @@ package com.inteligr8.alfresco.asie.model.core;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;
import com.inteligr8.alfresco.asie.model.BaseResponse; import com.inteligr8.alfresco.asie.model.BaseResponse;
import com.inteligr8.solr.model.Cores;
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class SummaryResponse extends BaseResponse { public class SummaryResponse extends BaseResponse {
@JsonProperty(value = "Summary", access = Access.READ_ONLY) @JsonProperty(value = "Summary", required = true)
private Summary summary; private Cores<Summary> cores;
public Summary getSummary() { public Cores<Summary> getCores() {
return summary; return cores;
}
public void setSummary(Summary summary) {
this.summary = summary;
} }
} }
@@ -0,0 +1,24 @@
package com.inteligr8.alfresco.asie;
import java.net.URL;
import com.inteligr8.alfresco.asie.api.CoreAdminApi;
import com.inteligr8.rs.ClientJerseyImpl;
public class AsieClient extends ClientJerseyImpl {
public AsieClient(String hostname) {
super(new AsieClientConfiguration().withHostname(hostname));
this.register();
}
public AsieClient(URL baseUrl) {
super(new AsieClientConfiguration().withBaseUrl(baseUrl.toString()));
this.register();
}
public CoreAdminApi getCoreAdminApi() {
return this.getApi(this.getConfig().createAuthorizationFilter(), CoreAdminApi.class);
}
}
@@ -0,0 +1,54 @@
package com.inteligr8.alfresco.asie;
import java.io.IOException;
import java.util.Arrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.inteligr8.rs.AuthorizationFilter;
import com.inteligr8.rs.ClientJerseyConfiguration;
import jakarta.ws.rs.client.ClientRequestContext;
public class AsieClientConfiguration implements ClientJerseyConfiguration {
private Logger logger = LoggerFactory.getLogger(AsieClientConfiguration.class);
private String baseUrl = "http://locahost:8983/solr";
private String searchSecret = "alfresco-secret";
@Override
public String getBaseUrl() {
return this.baseUrl;
}
public AsieClientConfiguration withBaseUrl(String baseUrl) {
this.baseUrl = baseUrl;
return this;
}
public AsieClientConfiguration withHostname(String hostname) {
this.baseUrl = "http://" + hostname + ":8983/solr";
return this;
}
public AsieClientConfiguration withSearchSecret(String searchSecret) {
this.searchSecret = searchSecret;
return this;
}
@Override
public AuthorizationFilter createAuthorizationFilter() {
if (this.searchSecret == null)
return null;
return new AuthorizationFilter() {
@Override
public void filter(ClientRequestContext requestContext) throws IOException {
logger.trace("Adding ASIE secret for authorization ...");
requestContext.getHeaders().addAll("X-Alfresco-Search-Secret", Arrays.asList(searchSecret));
}
};
}
}
@@ -0,0 +1,27 @@
package com.inteligr8.alfresco.asie;
import org.junit.jupiter.api.Assertions;
public class AssertionUtil {
public static <T> T assertNotNull(T obj) {
Assertions.assertNotNull(obj);
return obj;
}
public static <T> T assertNotNull(T obj, String message) {
Assertions.assertNotNull(obj, message);
return obj;
}
public static void assertType(Object obj, Class<?> type) {
Assertions.assertNotNull(obj);
Assertions.assertEquals(type, obj.getClass());
}
public static void assertType(Object obj, Class<?> type, String message) {
Assertions.assertNotNull(obj, message);
Assertions.assertEquals(type, obj.getClass(), message);
}
}
@@ -0,0 +1,23 @@
package com.inteligr8.alfresco.asie.api;
import java.net.MalformedURLException;
import org.junit.jupiter.api.BeforeAll;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.inteligr8.alfresco.asie.AsieClient;
public class AbstractApiUnitTest {
protected Logger logger = LoggerFactory.getLogger(this.getClass());
protected static AsieClient client;
protected static String defaultCore = "alfresco";
@BeforeAll
private static void init() throws MalformedURLException {
client = new AsieClient("localhost");
}
}
@@ -0,0 +1,34 @@
package com.inteligr8.alfresco.asie.api;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import com.inteligr8.alfresco.asie.AssertionUtil;
import com.inteligr8.alfresco.asie.model.ActionCoreResponse;
import com.inteligr8.alfresco.asie.model.core.ReindexRequest;
import com.inteligr8.solr.model.Action;
import com.inteligr8.solr.model.Action.Status;
import com.inteligr8.solr.model.Cores;
import com.inteligr8.solr.model.ResponseHeader;
public class CoreAdminReindexUnitTest extends AbstractApiUnitTest {
@Test
public void reindex() {
CoreAdminApi api = client.getCoreAdminApi();
ActionCoreResponse<Action> response = api.reindex(
new ReindexRequest()
.withCore(defaultCore));
Assertions.assertNotNull(response);
ResponseHeader responseHeader = AssertionUtil.assertNotNull(response.getResponseHeader());
Assertions.assertEquals(0, responseHeader.getStatus());
Cores<Action> cores = AssertionUtil.assertNotNull(response.getCores());
Action action = AssertionUtil.assertNotNull(cores.getByCore(defaultCore));
Assertions.assertEquals(Status.Scheduled, action.getStatus());
}
}
@@ -0,0 +1,96 @@
package com.inteligr8.alfresco.asie.api;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import com.inteligr8.alfresco.asie.AsieClient;
import com.inteligr8.alfresco.asie.AssertionUtil;
import com.inteligr8.alfresco.asie.model.core.Report;
import com.inteligr8.alfresco.asie.model.core.ReportRequest;
import com.inteligr8.alfresco.asie.model.core.ReportResponse;
import com.inteligr8.alfresco.asie.model.core.Summary;
import com.inteligr8.alfresco.asie.model.core.SummaryRequest;
import com.inteligr8.alfresco.asie.model.core.SummaryResponse;
import com.inteligr8.solr.model.CoreMetadata;
import com.inteligr8.solr.model.Cores;
import com.inteligr8.solr.model.ResponseHeader;
import com.inteligr8.solr.model.core.StatusRequest;
import com.inteligr8.solr.model.core.StatusResponse;
import jakarta.ws.rs.ProcessingException;
public class CoreAdminStatusUnitTest extends AbstractApiUnitTest {
@Test
public void noHost() {
AsieClient client = new AsieClient("doesnotexist");
CoreAdminApi api = client.getCoreAdminApi();
Assertions.assertThrows(ProcessingException.class, () -> {
api.getStatus(
new StatusRequest()
.withCore(defaultCore));
});
}
@Test
public void summary() {
CoreAdminApi api = client.getCoreAdminApi();
SummaryResponse response = api.getSummary(
new SummaryRequest()
.withCore(defaultCore));
Assertions.assertNotNull(response);
ResponseHeader responseHeader = AssertionUtil.assertNotNull(response.getResponseHeader());
Assertions.assertEquals(0, responseHeader.getStatus());
Cores<Summary> cores = AssertionUtil.assertNotNull(response.getCores());
Summary summary = AssertionUtil.assertNotNull(cores.getByCore(defaultCore));
AssertionUtil.assertType(summary.getByField("Active"), Boolean.class);
AssertionUtil.assertType(summary.getByField("Number of Searchers"), Integer.class);
AssertionUtil.assertType(summary.getByField("Last Index TX Commit Time"), Long.class);
AssertionUtil.assertType(summary.getByField("Last Index TX Commit Date"), String.class);
}
@Test
public void status() {
CoreAdminApi api = client.getCoreAdminApi();
StatusResponse response = api.getStatus(
new StatusRequest()
.withCore(defaultCore));
Assertions.assertNotNull(response);
ResponseHeader responseHeader = AssertionUtil.assertNotNull(response.getResponseHeader());
Assertions.assertEquals(0, responseHeader.getStatus());
Cores<CoreMetadata> cores = AssertionUtil.assertNotNull(response.getCores());
CoreMetadata core = AssertionUtil.assertNotNull(cores.getByCore(defaultCore));
Assertions.assertEquals(core, core.getName());
Assertions.assertNotNull(core.getStartDateTime());
}
@Test
public void report() {
CoreAdminApi api = client.getCoreAdminApi();
ReportResponse response = api.getReport(
new ReportRequest()
.withCore(defaultCore));
Assertions.assertNotNull(response);
ResponseHeader responseHeader = AssertionUtil.assertNotNull(response.getResponseHeader());
Assertions.assertEquals(0, responseHeader.getStatus());
Cores<Report> cores = AssertionUtil.assertNotNull(response.getCores());
Report report = AssertionUtil.assertNotNull(cores.getByCore(defaultCore));
AssertionUtil.assertType(report.getByField("Index error count"), Integer.class);
AssertionUtil.assertType(report.getByField("Last indexed transaction commit time"), Long.class);
AssertionUtil.assertType(report.getByField("Last indexed transaction commit date"), String.class);
}
}
@@ -0,0 +1,23 @@
rootLogger.level=trace
rootLogger.appenderRef.stdout.ref=STDOUT
logger.inteligr8-rs-request.name=jaxrs.request
logger.inteligr8-rs-request.level=trace
logger.inteligr8-rs-response.name=jaxrs.response
logger.inteligr8-rs-response.level=off
logger.this.name=com.inteligr8.alfresco.asie
logger.this.level=trace
# hide framework
logger.apache-http.name=org.apache.http
logger.apache-http.level=debug
logger.jersey.name=org.glassfish.jersey
logger.jersey.level=trace
logger.jersey-client.name=org.glassfish.jersey.client
logger.jersey-client.level=trace
appender.stdout.type=Console
appender.stdout.name=STDOUT
appender.stdout.layout.type=PatternLayout
appender.stdout.layout.pattern=%d{ABSOLUTE_MICROS} %level{length=1} %c{1}: %m%n
Binary file not shown.
+23 -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.1-SNAPSHOT</version> <version>1.2.2</version>
<relativePath>../</relativePath> <relativePath>../</relativePath>
</parent> </parent>
@@ -16,8 +16,12 @@
<name>ASIE Platform Module for ACS Community</name> <name>ASIE Platform Module for ACS Community</name>
<properties> <properties>
<alfresco.sdk.version>5.2.0</alfresco.sdk.version> <alfresco.sdk.version>4.9.0</alfresco.sdk.version>
<alfresco.platform.version>23.3.0</alfresco.platform.version> <alfresco.platform.version>23.3.0</alfresco.platform.version>
<alfresco.platform.war.version>23.3.0.98</alfresco.platform.war.version>
<tomcat-rad.version>10-2.1</tomcat-rad.version>
<beedk.rad.acs-search.enabled>true</beedk.rad.acs-search.enabled>
</properties> </properties>
<dependencyManagement> <dependencyManagement>
@@ -33,11 +37,15 @@
</dependencyManagement> </dependencyManagement>
<dependencies> <dependencies>
<dependency>
<groupId>com.inteligr8.alfresco</groupId>
<artifactId>cachext-platform-module</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency> <dependency>
<groupId>com.inteligr8.alfresco</groupId> <groupId>com.inteligr8.alfresco</groupId>
<artifactId>asie-shared</artifactId> <artifactId>asie-shared</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
<scope>provided</scope>
</dependency> </dependency>
<!-- Needed by this module, but provided by ACS --> <!-- Needed by this module, but provided by ACS -->
@@ -46,6 +54,14 @@
<artifactId>alfresco-repository</artifactId> <artifactId>alfresco-repository</artifactId>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<!-- Alfresco Modules required to use this module -->
<dependency>
<groupId>com.inteligr8.alfresco</groupId>
<artifactId>cxf-jaxrs-platform-module</artifactId>
<version>1.3.1-acs-v23.3</version>
<type>amp</type>
</dependency>
<!-- Including for testing purposes only --> <!-- Including for testing purposes only -->
<dependency> <dependency>
@@ -69,6 +85,10 @@
<extensions>true</extensions> <extensions>true</extensions>
<configuration> <configuration>
<tiles> <tiles>
<!-- Documentation: https://bitbucket.org/inteligr8/ootbee-beedk/src/stable/beedk-acs-search-rad-tile -->
<tile>com.inteligr8.ootbee:beedk-acs-search-rad-tile:[1.1.6,2.0.0)</tile>
<!-- Documentation: https://bitbucket.org/inteligr8/ootbee-beedk/src/stable/beedk-acs-platform-self-rad-tile -->
<tile>com.inteligr8.ootbee:beedk-acs-platform-self-rad-tile:[1.1.6,2.0.0)</tile>
<!-- Documentation: https://bitbucket.org/inteligr8/ootbee-beedk/src/stable/beedk-acs-platform-module-tile --> <!-- Documentation: https://bitbucket.org/inteligr8/ootbee-beedk/src/stable/beedk-acs-platform-module-tile -->
<tile>com.inteligr8.ootbee:beedk-acs-platform-module-tile:[1.1.6,2.0.0)</tile> <tile>com.inteligr8.ootbee:beedk-acs-platform-module-tile:[1.1.6,2.0.0)</tile>
</tiles> </tiles>
+4 -4
View File
@@ -1,22 +1,22 @@
#!/bin/sh #!/bin/sh
discoverArtifactId() { discoverArtifactId() {
ARTIFACT_ID=`mvn -q -Dexpression=project.artifactId -DforceStdout help:evaluate` ARTIFACT_ID=`mvn -q -Dexpression=project.artifactId -DforceStdout help:evaluate | sed 's/\x1B\[[0-9;]\{1,\}[A-Za-z]//g'`
} }
rebuild() { rebuild() {
echo "Rebuilding project ..." echo "Rebuilding project ..."
mvn process-classes mvn process-test-classes
} }
start() { start() {
echo "Rebuilding project and starting Docker containers to support rapid application development ..." echo "Rebuilding project and starting Docker containers to support rapid application development ..."
mvn -Drad process-classes mvn -Drad process-test-classes
} }
start_log() { start_log() {
echo "Rebuilding project and starting Docker containers to support rapid application development ..." echo "Rebuilding project and starting Docker containers to support rapid application development ..."
mvn -Drad -Ddocker.showLogs process-classes mvn -Drad -Ddocker.showLogs process-test-classes
} }
stop() { stop() {
@@ -0,0 +1,23 @@
package com.inteligr8.alfresco.asie;
public interface CommunityConstants extends Constants {
static final String BEAN_SHARDSETS_CACHE = "asieShardsetsCache";
static final String BEAN_NODES_CACHE = "asieNodesCache";
static final String BEAN_SHARD_NODES_CACHE = "asieShardNodesCache";
static final String BEAN_SHARDINST_STATE_CACHE = "asieShardInstanceStateCache";
static final String BEAN_NODE_DISABLE_CACHE = "asieNodeDisabledCache";
static final String BEAN_NODE_UNAVAIL_CACHE = "asieNodeUnavailableCache";
static final String BEAN_SHARDINST_DISABLE_CACHE = "asieShardInstanceDisabledCache";
static final String BEAN_SHARDINST_UNAVAIL_CACHE = "asieShardInstanceUnavailableCache";
static final String BEAN_CORE_EXPLICIT_CACHE = "asieCoreExplicitCache";
static final String ATTR_ASIE_SHARDSET = "inteligr8.asie.shardSet";
static final String ATTR_ASIE_NODE = "inteligr8.asie.node";
static final String ATTR_ASIE_SHARD_NODES = "inteligr8.asie.shard.nodes";
static final String ATTR_ASIE_SHARD_NODE = "inteligr8.asie.shard.node";
static final String ATTR_OBJECT = "object";
static final String ATTR_DISABLE = "disabled";
static final String ATTR_NODES = "nodes";
}
@@ -0,0 +1,50 @@
package com.inteligr8.alfresco.asie.compute;
import java.util.List;
import java.util.Set;
import org.alfresco.repo.search.impl.parsers.CMISLexer;
import org.alfresco.repo.search.impl.parsers.CMISParser;
import org.alfresco.service.cmr.search.SearchParameters.Operator;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.namespace.QName;
import org.antlr.runtime.ANTLRStringStream;
import org.antlr.runtime.CharStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.tree.CommonTree;
import org.antlr.runtime.tree.Tree;
import org.apache.commons.collections4.SetUtils;
import org.springframework.stereotype.Component;
@Component
public class CmisQueryInspector implements QueryInspector {
private Set<String> supportedLanguages = SetUtils.unmodifiableSet(
SearchService.LANGUAGE_CMIS_ALFRESCO,
SearchService.LANGUAGE_CMIS_STRICT,
SearchService.LANGUAGE_INDEX_CMIS,
SearchService.LANGUAGE_SOLR_CMIS);
@Override
public Set<String> getSupportedLanguages() {
return this.supportedLanguages;
}
@Override
public List<QueryValue> findRequiredPropertyValues(String query, Operator defaultOperator, QName property, DataTypeDefinition dataTypeDef) throws RecognitionException {
Tree tree = this.parseCmis(query, defaultOperator);
throw new UnsupportedOperationException();
}
protected Tree parseCmis(String cmisQuery, Operator defaultOperator) throws RecognitionException {
CharStream cs = new ANTLRStringStream(cmisQuery);
CMISLexer lexer = new CMISLexer(cs);
CommonTokenStream tokens = new CommonTokenStream(lexer);
CMISParser parser = new CMISParser(tokens);
CommonTree tree = (CommonTree) parser.query().getTree();
return tree;
}
}
@@ -0,0 +1,290 @@
package com.inteligr8.alfresco.asie.compute;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Period;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import org.alfresco.repo.search.impl.parsers.FTSLexer;
import org.alfresco.repo.search.impl.parsers.FTSParser;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.repository.AssociationRef;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.search.SearchParameters.Operator;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.antlr.runtime.ANTLRStringStream;
import org.antlr.runtime.CharStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.tree.CommonTree;
import org.antlr.runtime.tree.Tree;
import org.apache.commons.collections4.SetUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class FtsQueryInspector implements QueryInspector {
private final Logger logger = LoggerFactory.getLogger(FtsQueryInspector.class);
private final Set<String> supportedLanguages = SetUtils.unmodifiableSet(
SearchService.LANGUAGE_FTS_ALFRESCO,
SearchService.LANGUAGE_INDEX_FTS_ALFRESCO,
SearchService.LANGUAGE_SOLR_FTS_ALFRESCO,
SearchService.LANGUAGE_LUCENE);
@Autowired
private NamespaceService namespaceService;
@Override
public Set<String> getSupportedLanguages() {
return this.supportedLanguages;
}
@Override
public List<QueryValue> findRequiredPropertyValues(String ftsQuery, Operator defaultOperator, QName property, DataTypeDefinition dataTypeDef) throws RecognitionException {
Tree tree = this.parseFts(ftsQuery, defaultOperator);
tree = this.bypassSingleTermDisjunctions(tree);
if (tree == null)
return null;
Collection<Tree> trees = this.extractRequiredTerms(tree);
this.logger.trace("Found {} required terms in query: {}", trees.size(), ftsQuery);
this.filterPropertyTerms(trees, property);
this.logger.trace("Found {} required terms for property {} in query: {}", trees.size(), property, ftsQuery);
this.filterOutFuzzyTerms(trees);
this.logger.trace("Found {} required definitive terms for property {} in query: {}", trees.size(), property, ftsQuery);
List<QueryValue> values = new ArrayList<>(trees.size());
for (Tree t : trees)
values.add(this.extractValue(t, dataTypeDef));
return values;
}
protected Tree parseFts(String ftsQuery, Operator defaultOperator) throws RecognitionException {
CharStream cs = new ANTLRStringStream(ftsQuery);
FTSLexer lexer = new FTSLexer(cs);
CommonTokenStream tokens = new CommonTokenStream(lexer);
FTSParser parser = new FTSParser(tokens);
parser.setDefaultFieldConjunction(defaultOperator.equals(Operator.AND));
parser.setMode(defaultOperator.equals(Operator.AND) ? FTSParser.Mode.DEFAULT_CONJUNCTION : FTSParser.Mode.DEFAULT_DISJUNCTION);
CommonTree tree = (CommonTree) parser.ftsQuery().getTree();
return tree;
}
protected Tree bypassSingleTermDisjunctions(Tree tree) {
while ("DISJUNCTION".equals(tree.getText()) && tree.getChildCount() == 1)
tree = tree.getChild(0);
if ("DISJUNCTION".equals(tree.getText()))
return null;
return tree;
}
protected Collection<Tree> extractRequiredTerms(Tree tree) {
while ("DISJUNCTION".equals(tree.getText()) && tree.getChildCount() == 1)
tree = tree.getChild(0);
List<Tree> terms = new LinkedList<>();
switch (tree.getText()) {
case "DISJUNCTION":
break;
case "CONJUNCTION":
for (int c = 0; c < tree.getChildCount(); c++) {
Collection<Tree> subtrees = this.extractRequiredTerms(tree.getChild(c));
if (subtrees == null || subtrees.isEmpty())
continue;
terms.addAll(subtrees);
}
break;
case "DEFAULT":
terms.add(tree);
break;
default:
this.logger.warn("Unexpected/unsupported tree: {}", tree.getText());
}
return terms;
}
protected Collection<Tree> filterPropertyTerms(Collection<Tree> trees, QName property) {
if (trees.isEmpty())
return trees;
Set<String> prefixes = new HashSet<>(this.namespaceService.getPrefixes(property.getNamespaceURI()));
if (prefixes.isEmpty()) {
this.logger.warn("Unexpected/unsupported namespace: {}", property.getNamespaceURI());
trees.clear();
return trees;
}
Iterator<Tree> i = trees.iterator();
while (i.hasNext()) {
Tree tree = i.next();
if ("DEFAULT".equals(tree.getText()))
tree = tree.getChild(0);
int skip = -1;
switch (tree.getText()) {
case "TERM":
case "PHRASE":
case "EXACT_TERM":
case "EXACT_PHRASE":
skip = 1; // skip the value child
break;
case "RANGE":
skip = 4; // skip the inclusive, start, end, inclusive children
break;
default:
}
if (skip >= 0) {
Tree fieldRef = tree.getChild(skip);
if (!"FIELD_REF".equals(fieldRef.getText())) {
this.logger.warn("Unexpected/unsupported tree: {}", tree.getText());
} else if (!fieldRef.getChild(0).getText().equals(property.getLocalName())) {
this.logger.trace("Found but ignoring property: {}", fieldRef.getChild(0).getText());
} else {
Tree prefix = fieldRef.getChild(1);
if (!"PREFIX".equals(prefix.getText())) {
this.logger.warn("Unexpected/unsupported tree: {}", tree.getText());
} else if (!prefixes.contains(prefix.getChild(0).getText())) {
this.logger.trace("Found but ignoring property: {}:{}", prefix.getChild(0).getText(), property.getLocalName());
} else {
// this will skip the remove()
continue;
}
}
}
i.remove();
}
return trees;
}
protected Collection<Tree> filterOutFuzzyTerms(Collection<Tree> trees) {
if (trees.isEmpty())
return trees;
Iterator<Tree> i = trees.iterator();
while (i.hasNext()) {
Tree tree = i.next();
if ("DEFAULT".equals(tree.getText()))
tree = tree.getChild(0);
switch (tree.getText()) {
case "EXACT_TERM":
case "EXACT_PHRASE":
case "RANGE":
break;
default:
i.remove();
}
}
return trees;
}
protected QueryValue extractValue(Tree tree, DataTypeDefinition dataTypeDef) {
if ("DEFAULT".equals(tree.getText()))
tree = tree.getChild(0);
switch (tree.getText()) {
case "RANGE":
return this.extractRangeValue(tree, dataTypeDef);
default:
}
String value = this.unquote(tree.getChild(0).getText());
switch (dataTypeDef.getName().getLocalName()) {
case "boolean":
return new QuerySingleValue<Boolean>(Boolean.parseBoolean(value));
case "double":
return new QuerySingleValue<Double>(Double.parseDouble(value));
case "float":
return new QuerySingleValue<Float>(Float.parseFloat(value));
case "int":
return new QuerySingleValue<Integer>(Integer.parseInt(value));
case "long":
return new QuerySingleValue<Long>(Long.parseLong(value));
case "date":
return new QuerySingleValue<LocalDate>(this.evaluateAsDate(value));
case "datetime":
return new QuerySingleValue<LocalDateTime>(this.evaluateAsDateTime(value));
case "period":
return new QuerySingleValue<Period>(Period.parse(value));
case "qname":
return new QuerySingleValue<QName>(QName.createQName(value, this.namespaceService));
case "noderef":
return new QuerySingleValue<NodeRef>(new NodeRef(value));
case "childassocref":
return new QuerySingleValue<ChildAssociationRef>(new ChildAssociationRef(value));
case "assocref":
return new QuerySingleValue<AssociationRef>(new AssociationRef(value));
case "locale":
return new QuerySingleValue<Locale>(new Locale(value));
default:
return new QuerySingleValue<String>(value);
}
}
protected QueryRangeValue<?> extractRangeValue(Tree tree, DataTypeDefinition dataTypeDef) {
boolean includeStart = "INCLUSIVE".equals(tree.getChild(0).getText());
String start = this.unquote(tree.getChild(1).getText());
String end = this.unquote(tree.getChild(2).getText());
boolean includeEnd = "INCLUSIVE".equals(tree.getChild(3).getText());
switch (dataTypeDef.getName().getLocalName()) {
case "double":
return new QueryRangeValue<Double>(includeStart, Double.parseDouble(start), includeEnd, Double.parseDouble(end));
case "float":
return new QueryRangeValue<Float>(includeStart, Float.parseFloat(start), includeEnd, Float.parseFloat(end));
case "int":
return new QueryRangeValue<Integer>(includeStart, Integer.parseInt(start), includeEnd, Integer.parseInt(end));
case "long":
return new QueryRangeValue<Long>(includeStart, Long.parseLong(start), includeEnd, Long.parseLong(end));
case "date":
return new QueryRangeValue<LocalDate>(includeStart, this.evaluateAsDate(start), includeEnd, this.evaluateAsDate(end));
case "datetime":
return new QueryRangeValue<LocalDateTime>(includeStart, this.evaluateAsDateTime(start), includeEnd, this.evaluateAsDateTime(end));
default:
throw new UnsupportedOperationException("The data type does not make sense for range evaluation: " + dataTypeDef.getName());
}
}
protected LocalDate evaluateAsDate(String str) {
if ("now".equalsIgnoreCase(str)) return LocalDate.now();
else return LocalDate.parse(str);
}
protected LocalDateTime evaluateAsDateTime(String str) {
if ("now".equalsIgnoreCase(str)) return LocalDateTime.now();
else return LocalDateTime.parse(str);
}
protected String unquote(String str) {
if (str.length() < 2) return str;
else if (str.charAt(0) == '\'' && str.charAt(str.length()-1) == '\'') return str.substring(1, str.length()-1);
else if (str.charAt(0) == '\"' && str.charAt(str.length()-1) == '\"') return str.substring(1, str.length()-1);
else return str;
}
}
@@ -0,0 +1,74 @@
package com.inteligr8.alfresco.asie.compute;
import java.util.List;
import java.util.Set;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.search.SearchParameters.Operator;
import org.alfresco.service.namespace.QName;
import org.antlr.runtime.RecognitionException;
public interface QueryInspector {
Set<String> getSupportedLanguages();
List<QueryValue> findRequiredPropertyValues(String query, Operator defaultOperator, QName property, DataTypeDefinition dataTypeDef) throws RecognitionException;
public interface QueryValue {
}
public class QuerySingleValue<T> implements QueryValue {
private T value;
public QuerySingleValue(T value) {
this.value = value;
}
public T getValue() {
return value;
}
@Override
public String toString() {
return this.value.toString();
}
}
public class QueryRangeValue<T> implements QueryValue {
private boolean includeStart;
private T start;
private boolean includeEnd;
private T end;
public QueryRangeValue(boolean includeStart, T start, boolean includeEnd, T end) {
this.includeStart = includeStart;
this.start = start;
this.includeEnd = includeEnd;
this.end = end;
}
public boolean isIncludeStart() {
return includeStart;
}
public boolean isIncludeEnd() {
return includeEnd;
}
public T getStart() {
return start;
}
public T getEnd() {
return end;
}
}
}
@@ -0,0 +1,32 @@
package com.inteligr8.alfresco.asie.compute;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.alfresco.service.cmr.search.SearchParameters;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class QueryInspectorFactory implements InitializingBean {
@Autowired
private List<QueryInspector> inspectors;
private Map<String, QueryInspector> languageInspectorMap = new HashMap<>();
@Override
public void afterPropertiesSet() throws Exception {
for (QueryInspector inspector : this.inspectors) {
for (String language : inspector.getSupportedLanguages())
this.languageInspectorMap.put(language, inspector);
}
}
public QueryInspector selectQueryInspector(SearchParameters searchParams) {
return this.languageInspectorMap.get(searchParams.getLanguage());
}
}
@@ -0,0 +1,240 @@
package com.inteligr8.alfresco.asie.service;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.repo.index.shard.ShardMethodEnum;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.Pair;
import org.alfresco.util.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.CommunityConstants;
import com.inteligr8.alfresco.asie.model.Shard;
import com.inteligr8.alfresco.asie.model.ShardInstance;
import com.inteligr8.alfresco.asie.model.ShardInstanceState;
import com.inteligr8.alfresco.asie.model.ShardSet;
import com.inteligr8.alfresco.asie.model.SolrHost;
import com.inteligr8.alfresco.cachext.CollectionCache;
import com.inteligr8.alfresco.cachext.MultiValueCache;
@Component
public class ShardDiscoveryService implements com.inteligr8.alfresco.asie.spi.ShardDiscoveryService {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
@Qualifier(CommunityConstants.BEAN_SHARDSETS_CACHE)
private SimpleCache<String, ShardSet> shardsetsCache;
@Autowired
@Qualifier(CommunityConstants.BEAN_NODES_CACHE)
private SimpleCache<String, SolrHost> nodesCache;
@Autowired
@Qualifier(CommunityConstants.BEAN_SHARD_NODES_CACHE)
private MultiValueCache<Shard, SolrHost> shardNodesCache;
@Autowired
@Qualifier(CommunityConstants.BEAN_SHARDINST_STATE_CACHE)
private SimpleCache<ShardInstance, ShardInstanceState> shardInstanceStatesCache;
@Autowired
@Qualifier(CommunityConstants.BEAN_NODE_UNAVAIL_CACHE)
private CollectionCache<SolrHost, HashSet<SolrHost>> nodeUnavailableCache;
@Autowired
@Qualifier(CommunityConstants.BEAN_NODE_DISABLE_CACHE)
private CollectionCache<SolrHost, HashSet<SolrHost>> nodeDisableCache;
@Autowired
@Qualifier(CommunityConstants.BEAN_SHARDINST_UNAVAIL_CACHE)
private CollectionCache<ShardInstance, HashSet<ShardInstance>> shardInstanceUnavailableCache;
@Autowired
@Qualifier(CommunityConstants.BEAN_SHARDINST_DISABLE_CACHE)
private CollectionCache<ShardInstance, HashSet<ShardInstance>> shardInstanceDisableCache;
@Autowired
@Qualifier(CommunityConstants.BEAN_CORE_EXPLICIT_CACHE)
private SimpleCache<String, QName> coreExplicitIdCache;
@Override
public ShardSet findSetByCore(String core) {
return this.shardsetsCache.get(core);
}
@Override
public SolrHost findNode(String nodeHostname, int nodePort) {
Map<String, InetAddress> resolvedAddresses = new HashMap<>();
for (String nodeSpec : this.nodesCache.getKeys()) {
SolrHost node = this.nodesCache.get(nodeSpec);
if (!nodeHostname.equalsIgnoreCase(node.getHostname())) {
if (!resolvedAddresses.containsKey(nodeHostname))
resolvedAddresses.put(nodeHostname, this.resolve(nodeHostname));
InetAddress nodeAddress = resolvedAddresses.get(nodeHostname);
this.logger.trace("Resolved: {} => {}", nodeHostname, nodeAddress);
if (nodeAddress == null)
continue;
if (!resolvedAddresses.containsKey(node.getHostname()))
resolvedAddresses.put(node.getHostname(), this.resolve(node.getHostname()));
InetAddress shardInstanceAddress = resolvedAddresses.get(node.getHostname());
this.logger.trace("Resolved: {} => {}", node.getHostname(), shardInstanceAddress);
if (!nodeAddress.equals(shardInstanceAddress))
continue;
}
if (nodePort == node.getPort()) {
this.logger.debug("Found node: {}", node);
return node;
}
}
return null;
}
private InetAddress resolve(String hostname) {
try {
return InetAddress.getByName(hostname);
} catch (UnknownHostException uhe) {
return null;
}
}
@Override
public Map<ShardSet, Map<Integer, ShardInstanceState>> findByNode(SolrHost node) {
Map<ShardSet, Map<Integer, ShardInstanceState>> response = new HashMap<>();
for (Shard shard : this.shardNodesCache.getKeys()) {
ShardSet shardSet = this.shardsetsCache.get(shard.extractShardSetCore());
if (this.shardNodesCache.contains(shard, node)) {
ShardInstance shardNode = ShardInstance.from(shard, node);
ShardInstanceState state = this.shardInstanceStatesCache.get(shardNode);
Map<Integer, ShardInstanceState> shards = response.get(shardSet);
if (shards == null)
response.put(shardSet, shards = new HashMap<>());
shards.put(shard.extractShardId(), state);
}
}
return response;
}
@Override
public Set<ShardSet> findSetsByShardMethod(ShardMethodEnum... shardMethods) {
Set<ShardSet> shardSets = new HashSet<>();
Set<ShardMethodEnum> methods = CollectionUtils.asSet(shardMethods);
for (String core : this.shardsetsCache.getKeys()) {
ShardSet shardSet = this.shardsetsCache.get(core);
if (methods.contains(shardSet.getMethod()))
shardSets.add(shardSet);
}
return shardSets;
}
@Override
public Set<SolrHost> findNodes(ShardSet shardSet) {
Set<SolrHost> nodes = new HashSet<>();
for (Shard shard : this.shardNodesCache.getKeys()) {
if (shardSet.getCore().equals(shard.extractShardSetCore()))
nodes.addAll(this.shardNodesCache.get(shard));
}
return nodes;
}
@Override
public Set<SolrHost> findNodesByShard(ShardSet shardSet, int shardId) {
Set<SolrHost> nodes = new HashSet<>();
for (Shard shard : this.shardNodesCache.getKeys()) {
if (shardSet.getCore().equals(shard.extractShardSetCore()) && shardId == shard.extractShardId())
nodes.addAll(this.shardNodesCache.get(shard));
}
return nodes;
}
@Override
public Map<Integer, Pair<SolrHost, ShardInstanceState>> findLatestNodeStates(ShardSet shardSet) {
Map<Integer, Pair<SolrHost, ShardInstanceState>> response = new HashMap<>();
for (Shard shard : this.shardNodesCache.getKeys()) {
if (!shardSet.getCore().equals(shard.extractShardSetCore()))
continue;
SolrHost latestNode = null;
ShardInstanceState latestState = null;
for (SolrHost node : this.shardNodesCache.get(shard)) {
ShardInstance shardNode = ShardInstance.from(shard, node);
ShardInstanceState state = this.shardInstanceStatesCache.get(shardNode);
if (latestState == null || state.compareTo(latestState) < 0) {
latestState = state;
latestNode = node;
}
}
if (latestNode != null)
response.put(shard.extractShardId(), new Pair<>(latestNode, latestState));
}
return response;
}
@Override
public List<Pair<SolrHost, ShardInstanceState>> findNodeStatesByShard(ShardSet shardSet, int shardId) {
List<Pair<SolrHost, ShardInstanceState>> response = new LinkedList<>();
for (Shard shard : this.shardNodesCache.getKeys()) {
if (!shardSet.getCore().equals(shard.extractShardSetCore()))
continue;
for (SolrHost node : this.shardNodesCache.get(shard)) {
ShardInstance shardNode = ShardInstance.from(shard, node);
ShardInstanceState state = this.shardInstanceStatesCache.get(shardNode);
response.add(new Pair<>(node, state));
}
}
return response;
}
@Override
public Set<Integer> findIdsByNode(ShardSet shardSet, SolrHost node) {
Set<Integer> shardIds = new HashSet<>();
for (Shard shard : this.shardNodesCache.getKeys()) {
if (shardSet.getCore().equals(shard.extractShardSetCore()) && this.shardNodesCache.contains(shard, node))
shardIds.add(shard.extractShardId());
}
return shardIds;
}
@Override
public Map<Integer, ShardInstanceState> findStatesByNode(ShardSet shardSet, SolrHost node) {
// TODO Auto-generated method stub
return null;
}
}
@@ -1,80 +1,25 @@
package com.inteligr8.alfresco.asie.service; package com.inteligr8.alfresco.asie.service;
import java.io.Serializable;
import java.util.Arrays;
import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.repo.index.shard.ShardInstance;
import org.alfresco.repo.index.shard.ShardState;
import org.alfresco.service.cmr.attributes.AttributeService; import org.alfresco.service.cmr.attributes.AttributeService;
import org.alfresco.service.cmr.attributes.AttributeService.AttributeQueryCallback;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.Constants; import com.inteligr8.alfresco.asie.Constants;
import com.inteligr8.alfresco.asie.enterprise.EnterpriseConstants;
@Component @Component
public class ShardStateService implements com.inteligr8.alfresco.asie.spi.ShardStateService { public class ShardStateService implements com.inteligr8.alfresco.asie.spi.ShardStateService {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired @Autowired
@Qualifier(Constants.QUALIFIER_ASIE) @Qualifier(Constants.QUALIFIER_ASIE)
private AttributeService attrService; private AttributeService attrService;
@Autowired @Autowired
@Qualifier(Constants.BEAN_SHARD_STATE_CACHE) private SolrShardRegistry shardRegistry;
private SimpleCache<ShardInstance, ShardState> shardStateCache;
@Override
public void clear() { public void clear() {
this.logger.info("Removing all nodes/shards from the shard registry"); this.shardRegistry.purge();
// this clears the state from the backend database
this.attrService.removeAttributes(EnterpriseConstants.ATTR_SHARD_STATE);
this.attrService.removeAttributes(EnterpriseConstants.ATTR_SHARD_SUBSCRIPTION);
this.attrService.removeAttributes(Constants.ATTR_ASIE);
// this clears the state from Hazelcast
this.shardStateCache.clear();
this.shardToGuidCache.clear();
}
public void remove(Serializable... keys) {
if (keys.length == 0)
throw new IllegalArgumentException();
this.logger.info("Removing from the shard registry: {}", Arrays.toString(keys));
Serializable[] shardStateKeys = keys;
Serializable[] shardSubKeys;
if (EnterpriseConstants.ATTR_SHARD_STATE.equals(keys[0])) {
shardSubKeys = ArrayUtils.clone(keys);
shardSubKeys[0] = EnterpriseConstants.ATTR_SHARD_SUBSCRIPTION;
} else {
shardStateKeys = ArrayUtils.addFirst(keys, EnterpriseConstants.ATTR_SHARD_STATE);
shardSubKeys = ArrayUtils.addFirst(keys, EnterpriseConstants.ATTR_SHARD_SUBSCRIPTION);
}
ShardState shardState = (ShardState) this.attrService.getAttribute(shardStateKeys);
// this clears the state from the backend database
this.attrService.removeAttribute(shardStateKeys);
this.attrService.removeAttribute(shardSubKeys);
// this clears the state from Hazelcast
if (shardState != null) {
this.shardStateCache.remove(shardState.getShardInstance());
this.shardToGuidCache.remove(shardState.getShardInstance());
}
}
public void iterate(AttributeQueryCallback callback) {
this.attrService.getAttributes(callback, EnterpriseConstants.ATTR_SHARD_STATE);
} }
} }
@@ -1,6 +1,7 @@
package com.inteligr8.alfresco.asie.service; package com.inteligr8.alfresco.asie.service;
import java.io.Serializable; import java.io.Serializable;
import java.time.OffsetDateTime;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
@@ -9,67 +10,54 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.OptionalInt; import java.util.OptionalInt;
import java.util.Map.Entry; import java.util.Random;
import java.util.Set; import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.alfresco.repo.cache.SimpleCache; import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.repo.index.shard.Floc; import org.alfresco.repo.index.shard.Floc;
import org.alfresco.repo.index.shard.Shard;
import org.alfresco.repo.index.shard.ShardInstance;
import org.alfresco.repo.index.shard.ShardMethodEnum;
import org.alfresco.repo.index.shard.ShardRegistry;
import org.alfresco.repo.index.shard.ShardState; import org.alfresco.repo.index.shard.ShardState;
import org.alfresco.repo.search.impl.QueryParserUtils; import org.alfresco.repo.lock.JobLockService;
import org.alfresco.repo.search.impl.parsers.AlfrescoFunctionEvaluationContext;
import org.alfresco.repo.search.impl.parsers.CMISLexer;
import org.alfresco.repo.search.impl.parsers.FTSLexer;
import org.alfresco.repo.search.impl.parsers.FTSParser;
import org.alfresco.repo.search.impl.parsers.FTSQueryParser;
import org.alfresco.repo.search.impl.querymodel.Conjunction;
import org.alfresco.repo.search.impl.querymodel.Constraint;
import org.alfresco.repo.search.impl.querymodel.Disjunction;
import org.alfresco.repo.search.impl.querymodel.FunctionalConstraint;
import org.alfresco.repo.search.impl.querymodel.QueryEngine;
import org.alfresco.repo.search.impl.querymodel.QueryModelFactory;
import org.alfresco.repo.search.impl.querymodel.QueryOptions;
import org.alfresco.repo.search.impl.querymodel.QueryOptions.Connective;
import org.alfresco.repo.search.impl.querymodel.impl.BaseConstraint;
import org.alfresco.repo.search.impl.querymodel.impl.lucene.LuceneQueryBuilderComponent;
import org.alfresco.service.cmr.attributes.AttributeService; import org.alfresco.service.cmr.attributes.AttributeService;
import org.alfresco.service.cmr.attributes.AttributeService.AttributeQueryCallback; import org.alfresco.service.cmr.attributes.AttributeService.AttributeQueryCallback;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.search.SearchParameters; import org.alfresco.service.cmr.search.SearchParameters;
import org.alfresco.service.cmr.search.SearchParameters.Operator;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
import org.antlr.runtime.ANTLRStringStream; import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.CharStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.tree.CommonTree;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationEvent;
import org.springframework.context.annotation.Primary;
import org.springframework.extensions.surf.util.AbstractLifecycleBean; import org.springframework.extensions.surf.util.AbstractLifecycleBean;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.CommunityConstants;
import com.inteligr8.alfresco.asie.Constants; import com.inteligr8.alfresco.asie.Constants;
import com.inteligr8.alfresco.asie.model.Node; import com.inteligr8.alfresco.asie.compute.QueryInspector;
import com.inteligr8.alfresco.asie.compute.QueryInspector.QueryRangeValue;
import com.inteligr8.alfresco.asie.compute.QueryInspector.QuerySingleValue;
import com.inteligr8.alfresco.asie.compute.QueryInspector.QueryValue;
import com.inteligr8.alfresco.asie.compute.QueryInspectorFactory;
import com.inteligr8.alfresco.asie.model.Shard;
import com.inteligr8.alfresco.asie.model.ShardInstance;
import com.inteligr8.alfresco.asie.model.ShardInstanceState;
import com.inteligr8.alfresco.asie.model.ShardSet; import com.inteligr8.alfresco.asie.model.ShardSet;
import com.inteligr8.alfresco.asie.model.SolrHost;
import com.inteligr8.alfresco.asie.spi.ShardRegistry;
import com.inteligr8.alfresco.cachext.CollectionCache;
import com.inteligr8.alfresco.cachext.MultiValueCache;
@Component @Component
@Primary
public class SolrShardRegistry extends AbstractLifecycleBean implements ShardRegistry { public class SolrShardRegistry extends AbstractLifecycleBean implements ShardRegistry {
private final Logger logger = LoggerFactory.getLogger(this.getClass()); private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final Pattern coreShardPattern = Pattern.compile("(.+)-[0-9]+"); private final Random random = new Random();
private final QName shardLock = QName.createQName(Constants.NAMESPACE_ASIE, "shardLock");
@Autowired
private ShardStateService sss;
@Autowired @Autowired
@Qualifier(Constants.QUALIFIER_ASIE) @Qualifier(Constants.QUALIFIER_ASIE)
@@ -79,23 +67,52 @@ public class SolrShardRegistry extends AbstractLifecycleBean implements ShardReg
private NamespaceService namespaceService; private NamespaceService namespaceService;
@Autowired @Autowired
@Qualifier(Constants.BEAN_SHARD_STATE_CACHE) private DictionaryService dictionaryService;
private SimpleCache<ShardInstance, ShardState> onlineShardCache;
@Autowired @Autowired
@Qualifier(Constants.BEAN_OFFILINE_SHARD_STATE_CACHE) private QueryInspectorFactory queryInspectorFactory;
private SimpleCache<ShardInstance, ShardState> offlineShardCache;
@Autowired @Autowired
@Qualifier(Constants.BEAN_CORE_EXPLICIT_CACHE) private JobLockService jobLockService;
@Autowired
@Qualifier(CommunityConstants.BEAN_SHARDSETS_CACHE)
private SimpleCache<String, ShardSet> shardsetsCache;
@Autowired
@Qualifier(CommunityConstants.BEAN_NODES_CACHE)
private SimpleCache<String, SolrHost> nodesCache;
@Autowired
@Qualifier(CommunityConstants.BEAN_SHARD_NODES_CACHE)
private MultiValueCache<Shard, SolrHost> shardNodesCache;
@Autowired
@Qualifier(CommunityConstants.BEAN_SHARDINST_STATE_CACHE)
private SimpleCache<ShardInstance, ShardInstanceState> shardInstanceStatesCache;
@Autowired
@Qualifier(CommunityConstants.BEAN_NODE_UNAVAIL_CACHE)
private CollectionCache<SolrHost, HashSet<SolrHost>> nodeUnavailableCache;
@Autowired
@Qualifier(CommunityConstants.BEAN_NODE_DISABLE_CACHE)
private CollectionCache<SolrHost, HashSet<SolrHost>> nodeDisableCache;
@Autowired
@Qualifier(CommunityConstants.BEAN_SHARDINST_UNAVAIL_CACHE)
private CollectionCache<ShardInstance, HashSet<ShardInstance>> shardInstanceUnavailableCache;
@Autowired
@Qualifier(CommunityConstants.BEAN_SHARDINST_DISABLE_CACHE)
private CollectionCache<ShardInstance, HashSet<ShardInstance>> shardInstanceDisableCache;
@Autowired
@Qualifier(CommunityConstants.BEAN_CORE_EXPLICIT_CACHE)
private SimpleCache<String, QName> coreExplicitIdCache; private SimpleCache<String, QName> coreExplicitIdCache;
@Autowired @Value("${inteligr8.asie.registerUnknownShardDisabled}")
@Qualifier(Constants.BEAN_FLOC_CACHE) private boolean registerDisabled;
private SimpleCache<Floc, Boolean> flocCache;
@Value("${inteligr8.asie.registerUnknownShardOffline}")
private boolean registerOffline;
@Value("${inteligr8.asie.offlineIdleShardInSeconds}") @Value("${inteligr8.asie.offlineIdleShardInSeconds}")
private int offlineIdleShardInSeconds; private int offlineIdleShardInSeconds;
@@ -105,144 +122,364 @@ public class SolrShardRegistry extends AbstractLifecycleBean implements ShardReg
@Override @Override
protected void onBootstrap(ApplicationEvent event) { protected void onBootstrap(ApplicationEvent event) {
this.attrService.getAttributes(new AttributeQueryCallback() { this.loadPersistedToCache();
@Override
public boolean handleAttribute(Long id, Serializable value, Serializable[] keys) {
switch ((String) keys[2]) {
case Constants.ATTR_STATE:
ShardState shardNodeState = (ShardState) value;
ShardInstance shardNode = shardNodeState.getShardInstance();
cacheShard(shardNode, shardNodeState, (String) keys[1]);
if (ShardMethodEnum.EXPLICIT_ID.toString().equals(shardNodeState.getPropertyBag().get("shard.method"))) {
String coreName = shardNode.getShard().getFloc().getPropertyBag().get("coreName");
if (coreName != null && !coreExplicitIdCache.contains(coreName)) {
String property = shardNodeState.getPropertyBag().get("shard.key");
QName propertyQname = QName.createQName(property, namespaceService);
logger.debug("Mapping core to explicit ID: {} => {}", coreName, propertyQname);
coreExplicitIdCache.put(coreName, propertyQname);
}
}
return true;
default:
return true;
}
}
}, Constants.ATTR_ASIE_NODES);
} }
@Override @Override
protected void onShutdown(ApplicationEvent event) { protected void onShutdown(ApplicationEvent event) {
} }
protected void loadPersistedToCache() {
String lockId = this.jobLockService.getLock(this.shardLock, 2500L, 500L, 10);
try {
this.attrService.getAttributes(new AttributeQueryCallback() {
@Override
public boolean handleAttribute(Long id, Serializable value, Serializable[] keys) {
String core = (String) keys[1];
if (!shardsetsCache.contains(core)) {
ShardSet shardSet = (ShardSet) value;
shardsetsCache.put(core, shardSet);
switch (shardSet.getMethod()) {
case EXPLICIT_ID:
cacheExplicitShard(shardSet, false);
break;
default:
}
}
return true;
}
}, CommunityConstants.ATTR_ASIE_SHARDSET);
this.attrService.getAttributes(new AttributeQueryCallback() {
@Override
public boolean handleAttribute(Long id, Serializable value, Serializable[] keys) {
String nodeSpec = (String) keys[2];
SolrHost node = (SolrHost) value;
if (!nodesCache.contains(nodeSpec))
nodesCache.put(nodeSpec, node);
if (Boolean.TRUE.equals(attrService.getAttribute(CommunityConstants.ATTR_ASIE_NODE, CommunityConstants.ATTR_DISABLE, nodeSpec))) {
if (!nodeDisableCache.contains(node))
nodeDisableCache.add(node);
} else if (nodeDisableCache.contains(node)) {
nodeDisableCache.remove(node);
}
return true;
}
}, CommunityConstants.ATTR_ASIE_NODE, CommunityConstants.ATTR_OBJECT);
this.attrService.getAttributes(new AttributeQueryCallback() {
@Override
public boolean handleAttribute(Long id, Serializable value, Serializable[] keys) {
Shard shard = (Shard) keys[1];
SolrHost node = (SolrHost) keys[2];
if (!shardNodesCache.contains(shard, node))
shardNodesCache.add(shard, node);
return true;
}
}, CommunityConstants.ATTR_ASIE_SHARD_NODES);
this.attrService.getAttributes(new AttributeQueryCallback() {
@Override
public boolean handleAttribute(Long id, Serializable value, Serializable[] keys) {
ShardInstance shardNode = (ShardInstance) keys[2];
ShardInstanceState state = (ShardInstanceState) value;
if (!shardInstanceStatesCache.contains(shardNode))
shardInstanceStatesCache.put(shardNode, state);
if (Boolean.TRUE.equals(attrService.getAttribute(CommunityConstants.ATTR_ASIE_SHARD_NODE, CommunityConstants.ATTR_DISABLE, shardNode))) {
if (!shardInstanceDisableCache.contains(shardNode))
shardInstanceDisableCache.add(shardNode);
} else if (shardInstanceDisableCache.contains(shardNode)) {
shardInstanceDisableCache.remove(shardNode);
}
return true;
}
}, CommunityConstants.ATTR_ASIE_NODE, CommunityConstants.ATTR_OBJECT);
} finally {
this.jobLockService.releaseLock(lockId, this.shardLock);
}
}
protected void cacheShard(ShardInstance shardNode, ShardState shardNodeState, String nodeId) { private void cacheExplicitShard(ShardSet shardSet, boolean overwrite) {
SimpleCache<ShardInstance, ShardState> shardCache = this.onlineShardCache; if (overwrite || !this.coreExplicitIdCache.contains(shardSet.getCore())) {
ShardState cachedShardNodeState = this.onlineShardCache.get(shardNode); String property = shardSet.getPrefixedProperty();
if (cachedShardNodeState == null) { QName propertyQName = QName.createQName(property, namespaceService);
cachedShardNodeState = this.offlineShardCache.get(shardNode);
shardCache = this.offlineShardCache; this.logger.debug("Mapping core to explicit ID: {} => {}", shardSet.getCore(), propertyQName);
this.coreExplicitIdCache.put(shardSet.getCore(), propertyQName);
}
}
protected void persistCache() {
String lockId = this.jobLockService.getLock(this.shardLock, 2500L, 100L, 50);
try {
this.persistShardSetCache();
this.persistNodeCache();
this.persistShardNodesCache();
this.persistShardInstanceCache();
} finally {
this.jobLockService.releaseLock(lockId, this.shardLock);
}
}
private void persistShardSetCache() {
// add anything missing
// update anything changed
for (String core : this.shardsetsCache.getKeys()) {
ShardSet shardSet = this.shardsetsCache.get(core);
this.checkSetAttribute(shardSet, CommunityConstants.ATTR_ASIE_SHARDSET, core);
} }
if (cachedShardNodeState == null) { // we are not removing anything removed from the cache, as it might have expired
Boolean online = (Boolean) this.attrService.getAttribute(Constants.ATTR_ASIE_NODES, nodeId, Constants.ATTR_ONLINE); // it will just recache on the next load
if (online != null) { }
if (online.booleanValue()) {
this.onlineShardCache.put(shardNode, cachedShardNodeState); private void persistNodeCache() {
} else { // add anything missing
this.offlineShardCache.put(shardNode, cachedShardNodeState); // update anything changed
} for (String nodeSpec : this.nodesCache.getKeys()) {
SolrHost node = this.nodesCache.get(nodeSpec);
this.checkSetAttribute(node, CommunityConstants.ATTR_ASIE_NODE, CommunityConstants.ATTR_OBJECT, nodeSpec);
}
// we are not removing anything removed from the cache, as it might have expired
// it will just recache on the next load
// add anything disabled
for (SolrHost node : this.nodeDisableCache.values())
this.checkSetAttribute(Boolean.TRUE, CommunityConstants.ATTR_ASIE_NODE, CommunityConstants.ATTR_DISABLE, node.getSpec());
// remove anything not disabled
this.attrService.getAttributes(new AttributeQueryCallback() {
@Override
public boolean handleAttribute(Long id, Serializable value, Serializable[] keys) {
SolrHost node = SolrHost.from((String) keys[2]);
if (!nodeDisableCache.contains(node))
attrService.removeAttribute(keys);
return true;
}
}, CommunityConstants.ATTR_ASIE_NODE, CommunityConstants.ATTR_DISABLE);
}
private void persistShardNodesCache() {
// add anything missing
// update anything changed
for (Shard shard : this.shardNodesCache.getKeys()) {
Collection<SolrHost> nodes = this.shardNodesCache.get(shard);
for (SolrHost node : nodes) {
this.checkSetAttribute(node, CommunityConstants.ATTR_ASIE_SHARD_NODES, shard, node.getSpec());
}
}
// we are not removing anything removed from the cache, as it might have expired
// it will just recache on the next load
}
private void persistShardInstanceCache() {
// add anything missing
// update anything changed
for (ShardInstance shardNode : this.shardInstanceStatesCache.getKeys()) {
ShardInstanceState state = this.shardInstanceStatesCache.get(shardNode);
this.checkSetAttribute(state, shardNode);
}
// we are not removing anything removed from the cache, as it might have expired
// it will just recache on the next load
// add anything disabled
for (ShardInstance shardNode : this.shardInstanceDisableCache.values())
this.checkSetAttribute(Boolean.TRUE, CommunityConstants.ATTR_ASIE_SHARD_NODE, CommunityConstants.ATTR_DISABLE, shardNode);
// remove anything not disabled
this.attrService.getAttributes(new AttributeQueryCallback() {
@Override
public boolean handleAttribute(Long id, Serializable value, Serializable[] keys) {
ShardInstance shardNode = (ShardInstance) keys[2];
if (!shardInstanceDisableCache.contains(shardNode))
attrService.removeAttribute(keys);
return true;
}
}, CommunityConstants.ATTR_ASIE_SHARD_NODE, CommunityConstants.ATTR_DISABLE);
}
private void checkSetAttribute(ShardInstanceState state, ShardInstance shardNode) {
ShardInstanceState currentState = (ShardInstanceState) this.attrService.getAttribute(CommunityConstants.ATTR_ASIE_SHARD_NODE, CommunityConstants.ATTR_OBJECT, shardNode);
if (currentState != null) {
if (currentState.compareTo(state) >= 0) {
// current state is older (greater; further down the list)
// do nothing
} else { } else {
if (this.registerOffline) { this.logger.debug("The persisted state was old; updating: {}: {} => {}", shardNode, currentState, state);
this.offlineShardCache.put(shardNode, cachedShardNodeState); this.attrService.setAttribute(state, CommunityConstants.ATTR_ASIE_SHARD_NODE, CommunityConstants.ATTR_OBJECT, shardNode);
} else {
this.onlineShardCache.put(shardNode, cachedShardNodeState);
}
} }
} else if (cachedShardNodeState.getLastIndexedTxId() < shardNodeState.getLastIndexedTxId()) { } else {
shardCache.put(shardNode, shardNodeState); this.attrService.setAttribute(state, CommunityConstants.ATTR_ASIE_SHARD_NODE, CommunityConstants.ATTR_OBJECT, shardNode);
} }
} }
protected void fixFlocPropertyBag(ShardState shardNodeState) { private void checkSetAttribute(Serializable value, Serializable... keys) {
Floc floc = shardNodeState.getShardInstance().getShard().getFloc(); Serializable currentValue = this.attrService.getAttribute(keys);
if (floc.getPropertyBag().isEmpty()) { if (currentValue != null) {
for (Entry<String, String> prop : shardNodeState.getPropertyBag().entrySet()) { if (currentValue.equals(value))
if (prop.getKey().startsWith("shard.")) { return;
floc.getPropertyBag().put(prop.getKey(), prop.getValue()); this.logger.warn("The attribute value unexpectedly changed: {}: {} => {}", keys, currentValue, value);
} else if (prop.getKey().equals("coreName")) {
String coreName = this.extractCoreName(prop.getValue());
if (coreName != null)
floc.getPropertyBag().put(prop.getKey(), coreName);
}
}
} }
}
this.attrService.setAttribute(value, keys);
protected String extractCoreName(String coreShardName) {
Matcher matcher = coreShardPattern.matcher(coreShardName);
if (!matcher.matches())
return null;
return matcher.group(1);
} }
@Override @Override
public void registerShardState(ShardState shardNodeState) { public void registerShardState(ShardState shardNodeState) {
ShardInstance shardNode = shardNodeState.getShardInstance(); ShardSet shardSet = ShardSet.from(shardNodeState.getShardInstance().getShard().getFloc(), shardNodeState);
Node node = new Node(shardNode); Shard shard = Shard.from(shardSet, shardNodeState.getShardInstance().getShard().getInstance());
this.fixFlocPropertyBag(shardNodeState); SolrHost node = SolrHost.from(shardNodeState.getShardInstance());
this.cacheShard(shardNode, shardNodeState, node.getId()); ShardInstance shardNode = ShardInstance.from(shard, node);
ShardInstanceState state = ShardInstanceState.from(shardNodeState);
String lockId = this.jobLockService.getLock(this.shardLock, 2500L, 500L, 10);
try {
if (!this.shardsetsCache.contains(shardSet.getCore()))
this.shardsetsCache.put(shardSet.getCore(), shardSet);
this.checkSetAttribute(shardSet, CommunityConstants.ATTR_ASIE_SHARDSET, shardSet.getCore());
if (!this.nodesCache.contains(node.getSpec()))
this.nodesCache.put(node.getSpec(), node);
this.checkSetAttribute(node, CommunityConstants.ATTR_ASIE_NODE, CommunityConstants.ATTR_OBJECT, node.getSpec());
if (!this.shardNodesCache.contains(shard, node))
this.shardNodesCache.add(shard, node);
this.checkSetAttribute(node, CommunityConstants.ATTR_ASIE_SHARD_NODES, shard, node.getSpec());
ShardInstanceState currentState = this.shardInstanceStatesCache.get(shardNode);
if (currentState == null || currentState.compareTo(state) > 0)
this.shardInstanceStatesCache.put(shardNode, state);
this.checkSetAttribute(state, shardNode);
if (this.registerDisabled && !this.shardInstanceDisableCache.contains(shardNode))
this.shardInstanceDisableCache.add(shardNode);
} finally {
this.jobLockService.releaseLock(lockId, this.shardLock);
}
} }
@Override @Override
public Map<Floc, Map<Shard, Set<ShardState>>> getFlocs() { public void unregisterShardInstance(org.alfresco.repo.index.shard.ShardInstance shardInstance) {
Map<Floc, Map<Shard, Set<ShardState>>> flocs = new HashMap<>(); ShardSet shardSet = ShardSet.from(shardInstance.getShard().getFloc(), null);
Shard shard = Shard.from(shardSet, shardInstance.getShard().getInstance());
SolrHost node = SolrHost.from(shardInstance);
ShardInstance shardNode = ShardInstance.from(shard, node);
String lockId = this.jobLockService.getLock(this.shardLock, 2500L, 500L, 10);
try {
this.shardInstanceStatesCache.remove(shardNode);
this.shardInstanceDisableCache.remove(shardNode);
this.shardInstanceUnavailableCache.remove(shardNode);
this.nodeDisableCache.remove(node);
this.nodeUnavailableCache.remove(node);
this.attrService.removeAttribute(CommunityConstants.ATTR_ASIE_SHARD_NODES, shard, node.getSpec());
} finally {
this.jobLockService.releaseLock(lockId, this.shardLock);
}
}
@Override
public Map<Floc, Map<org.alfresco.repo.index.shard.Shard, Set<ShardState>>> getFlocs() {
Map<String, Floc> flocs = new HashMap<>();
Map<Floc, Map<org.alfresco.repo.index.shard.Shard, Set<ShardState>>> response = new HashMap<>();
for (ShardInstance shardNode : this.onlineShardCache.getKeys()) { for (Shard shard : this.shardNodesCache.getKeys()) {
Floc floc = shardNode.getShard().getFloc(); String core = shard.extractShardSetCore();
ShardSet shardSet = this.shardsetsCache.get(core);
Map<org.alfresco.repo.index.shard.Shard, Set<ShardState>> shards;
Floc floc = flocs.get(core);
if (floc != null) {
floc = shardSet.toAlfrescoModel();
shards = new HashMap<>();
} else {
shards = response.get(floc);
}
Map<Shard, Set<ShardState>> shards = flocs.get(floc); org.alfresco.repo.index.shard.Shard shard_ = shard.toAlfrescoModel(floc);
if (shards == null) Set<ShardState> states = shards.get(shard_);
flocs.put(floc, shards = new HashMap<>()); if (states == null)
states = new HashSet<>();
Set<ShardState> shardNodeStates = shards.get(shardNode.getShard()); for (SolrHost node : this.shardNodesCache.get(shard)) {
if (shardNodeStates == null) if (this.nodeDisableCache.contains(node) || this.nodeUnavailableCache.contains(node)) {
shards.put(shardNode.getShard(), shardNodeStates = new HashSet<>()); this.logger.debug("Excluding node as it is disabled or considered unavailable: {}", node);
continue;
}
ShardInstance shardNode = ShardInstance.from(shard, node);
if (this.shardInstanceDisableCache.contains(shardNode) || this.shardInstanceUnavailableCache.contains(shardNode)) {
this.logger.debug("Excluding shard node as it is disabled or considered unavailable: {}", shardNode);
continue;
}
ShardInstanceState shardNodeState = this.shardInstanceStatesCache.get(shardNode);
states.add(shardNodeState.toAlfrescoModel(shardNode.toAlfrescoModel(shard_)));
}
ShardState shardNodeState = this.onlineShardCache.get(shardNode); if (!states.isEmpty())
if (shardNodeState != null) // in case it was removed during the looping (very rare) shards.put(shard_, states);
shardNodeStates.add(shardNodeState); if (!shards.isEmpty())
response.put(floc, shards);
} }
return flocs; return response;
} }
@Override @Override
public void purge() { public void purge() {
this.sss.clear(); String lockId = this.jobLockService.getLock(this.shardLock, 2500L, 100L, 50);
try {
this.logger.info("Removing all nodes/shards from the shard registry");
this.shardsetsCache.clear();
this.attrService.removeAttributes(CommunityConstants.ATTR_ASIE_SHARDSET);
this.nodesCache.clear();
this.nodeDisableCache.clear();
this.nodeUnavailableCache.clear();
this.attrService.removeAttributes(CommunityConstants.ATTR_ASIE_NODE);
this.shardNodesCache.clear();
this.attrService.removeAttributes(CommunityConstants.ATTR_ASIE_SHARD_NODES);
this.shardInstanceStatesCache.clear();
this.shardInstanceDisableCache.clear();
this.shardInstanceUnavailableCache.clear();
this.attrService.removeAttributes(CommunityConstants.ATTR_ASIE_SHARD_NODE);
this.coreExplicitIdCache.clear();
} finally {
this.jobLockService.releaseLock(lockId, this.shardLock);
}
} }
@Override @Override
public void purgeAgedOutShards() { public void purgeAgedOutShards() {
long onlineExpired = System.currentTimeMillis() - this.offlineIdleShardInSeconds * 1000L; OffsetDateTime onlineExpired = OffsetDateTime.now().minusSeconds(this.offlineIdleShardInSeconds);
long offlineExpired = System.currentTimeMillis() - this.forgetOfflineShardInSeconds * 1000L; OffsetDateTime offlineExpired = OffsetDateTime.now().minusSeconds(this.forgetOfflineShardInSeconds);
for (ShardInstance shardNode : this.onlineShardCache.getKeys()) { for (ShardInstance shardNode : this.shardInstanceStatesCache.getKeys()) {
ShardState shardNodeState = this.onlineShardCache.get(shardNode); ShardInstanceState state = this.shardInstanceStatesCache.get(shardNode);
if (shardNodeState.getLastUpdated() < onlineExpired) { SolrHost node = shardNode.extractNode();
if (this.shardInstanceDisableCache.contains(shardNode)) {
this.logger.debug("Ignoring disabled shard instance during purgeAgedOutShards()");
} else if (this.nodeDisableCache.contains(node)) {
this.logger.debug("Ignoring disabled node during purgeAgedOutShards()");
} else if (state.getLastUpdated().isBefore(offlineExpired)) {
this.shardInstanceStatesCache.remove(shardNode);
if (this.shardInstanceUnavailableCache.remove(shardNode)) {
this.logger.info("Forgetting about already offline shard: {}", shardNode);
} else if (this.nodeUnavailableCache.remove(node)) {
this.logger.info("Forgetting about already offline shard: {}", shardNode);
} else {
this.logger.warn("Forgetting about online shard: {}", shardNode);
}
} else if (state.getLastUpdated().isBefore(onlineExpired)) {
this.logger.warn("Taking shard offline: {}", shardNode); this.logger.warn("Taking shard offline: {}", shardNode);
this.onlineShardCache.remove(shardNode); this.shardInstanceUnavailableCache.add(shardNode);
this.offlineShardCache.put(shardNode, shardNodeState);
}
}
for (ShardInstance shardNode : this.offlineShardCache.getKeys()) {
ShardState shardNodeState = this.offlineShardCache.get(shardNode);
if (shardNodeState.getLastUpdated() < offlineExpired) {
this.logger.info("Forgetting about already offline shard: {}", shardNode);
this.offlineShardCache.remove(shardNode);
} }
} }
} }
@@ -256,8 +493,15 @@ public class SolrShardRegistry extends AbstractLifecycleBean implements ShardReg
public Set<Integer> getShardInstanceList(String coreName) { public Set<Integer> getShardInstanceList(String coreName) {
Set<Integer> shardIds = new HashSet<>(); Set<Integer> shardIds = new HashSet<>();
for (ShardInstance shardNode : this.onlineShardCache.getKeys()) { ShardSet shardSet = this.shardsetsCache.get(coreName);
shardIds.add(shardNode.getShard().getInstance()); if (shardSet == null)
return Collections.emptySet();
for (Shard shard : this.shardNodesCache.getKeys()) {
if (shardSet.getCore().equals(shard.extractShardSetCore())) {
shardIds.add(shard.extractShardId());
}
} }
return shardIds; return shardIds;
@@ -269,95 +513,116 @@ public class SolrShardRegistry extends AbstractLifecycleBean implements ShardReg
} }
@Override @Override
public List<ShardInstance> getIndexSlice(SearchParameters searchParameters) { public List<org.alfresco.repo.index.shard.ShardInstance> getIndexSlice(SearchParameters searchParameters) {
for (Floc floc : this.flocCache.getKeys()) { if (searchParameters.getQuery() == null)
Set<Integer> shardIds = new HashSet<>(); return Collections.emptyList();
List<org.alfresco.repo.index.shard.ShardInstance> bestShards = null;
for (String shardSetSpec : this.shardsetsCache.getKeys()) {
ShardSet shardSet = this.shardsetsCache.get(shardSetSpec);
switch (floc.getShardMethod()) { Set<Integer> shardIds = this.getIndexSlice(searchParameters, shardSet);
if (shardIds == null)
continue;
List<org.alfresco.repo.index.shard.ShardInstance> shards = this.selectRandomNodes(shardSet, shardIds);
if (!shards.isEmpty() && (bestShards == null || shards.size() < bestShards.size()))
bestShards = shards;
if (bestShards != null && bestShards.size() == 1)
break;
}
return bestShards;
}
protected Set<Integer> getIndexSlice(SearchParameters searchParameters, ShardSet shardSet) {
try {
switch (shardSet.getMethod()) {
case EXPLICIT_ID: case EXPLICIT_ID:
String property = floc.getPropertyBag().get("shard.key"); return this.getExplicitIdIndexSlice(searchParameters, shardSet);
// check filters and other parameters default:
if (searchParameters.getQuery() != null) { // no optimization available
SearchTerm term = this.extractPropertySearchTeam(searchParameters, property); return null;
if (term != null && term.operator.equals("=")) { }
try { } catch (RecognitionException re) {
shardIds.add(Integer.parseInt(term.value)); this.logger.debug("Failed to parse the query: " + searchParameters.getQuery(), re);
} catch (NumberFormatException nfe) { // no optimization available
// skip return null;
} }
} }
}
break; protected Set<Integer> getExplicitIdIndexSlice(SearchParameters searchParameters, ShardSet shardSet) throws RecognitionException {
this.logger.trace("Found {} shard set, which is the highest priority", shardSet.getMethod());
QueryInspector inspector = this.queryInspectorFactory.selectQueryInspector(searchParameters);
if (inspector == null) {
this.logger.debug("The search is using an unsupported query language; unable to optimize for {}: {}", shardSet.getMethod(), searchParameters.getLanguage());
return null;
}
String property = shardSet.getPrefixedProperty();
QName propertyQName = QName.createQName(property, this.namespaceService);
this.logger.trace("Will attempt to see if search has a required constraint on explicit shard ID property: {}", propertyQName);
DataTypeDefinition dtdef = this.dictionaryService.getProperty(propertyQName).getDataType();
Set<Integer> shardIds = new HashSet<>();
List<QueryValue> values = inspector.findRequiredPropertyValues(searchParameters.getQuery(), searchParameters.getDefaultOperator(), propertyQName, dtdef);
this.logger.trace("Found {} matching terms query: {}: {}", values.size(), propertyQName, searchParameters.getQuery());
for (QueryValue value : values) {
if (value instanceof QuerySingleValue<?>) {
@SuppressWarnings("unchecked")
Number num = ((QuerySingleValue<? extends Number>) value).getValue();
shardIds.add(num.intValue());
} else if (value instanceof QueryRangeValue<?>) {
@SuppressWarnings("unchecked")
QueryRangeValue<? extends Number> num = (QueryRangeValue<? extends Number>) value;
int start = num.getStart().intValue();
if (!num.isIncludeStart())
start++;
int end = num.getStart().intValue();
if (!num.isIncludeEnd())
end--;
for (int shardId = start; shardId <= end; shardId++)
shardIds.add(shardId);
} }
} }
searchParameters.get
// TODO Auto-generated method stub if (shardIds.isEmpty()) {
return null; this.logger.trace("The {} shard set cannot not be used to optimize the query", shardSet.getMethod());
} return null;
private SearchTerm extractPropertySearchTeam(SearchParameters searchParameters, String property) {
switch (searchParameters.getLanguage()) {
case SearchService.LANGUAGE_CMIS_ALFRESCO:
case SearchService.LANGUAGE_CMIS_STRICT:
case SearchService.LANGUAGE_INDEX_CMIS:
case SearchService.LANGUAGE_SOLR_CMIS:
return this.extractCmisPropertySearchTerm(searchParameters, property, "=");
case SearchService.LANGUAGE_FTS_ALFRESCO:
case SearchService.LANGUAGE_INDEX_ALFRESCO:
case SearchService.LANGUAGE_INDEX_FTS_ALFRESCO:
case SearchService.LANGUAGE_LUCENE:
case SearchService.LANGUAGE_SOLR_ALFRESCO:
case SearchService.LANGUAGE_SOLR_FTS_ALFRESCO:
return this.extractFtsPropertySearchTerm(searchParameters, "=@" + property);
default:
return null;
} }
this.logger.debug("The {} shard set was used to optimize the query to use only shards: {}", shardSet.getMethod(), shardIds);
return shardIds;
} }
@Autowired protected List<org.alfresco.repo.index.shard.ShardInstance> selectRandomNodes(ShardSet shardSet, Collection<Integer> shardIds) {
private QueryEngine queryEngine; List<org.alfresco.repo.index.shard.ShardInstance> shardNodes = new LinkedList<>();
@Autowired
private DictionaryService dictionaryService;
private SearchTerm extractFtsPropertySearchTerm(SearchParameters searchParameters, String field) {
// TODO include filter and other possible constraints
if (searchParameters.getQuery() == null) for (Integer shardId : shardIds) {
return null; Shard shard = Shard.from(shardSet, shardId);
CharStream cs = new ANTLRStringStream(searchParameters.getQuery()); Collection<SolrHost> nodes = this.shardNodesCache.get(shard);
FTSLexer lexer = new FTSLexer(cs); List<SolrHost> availableNodes = new LinkedList<>();
CommonTokenStream tokens = new CommonTokenStream(lexer); for (SolrHost node : nodes) {
FTSParser parser = new FTSParser(tokens); if (this.nodeDisableCache.contains(node) || this.nodeUnavailableCache.contains(node))
parser.setDefaultFieldConjunction(searchParameters.getDefaultFTSOperator().equals(Operator.AND)); continue;
parser.setMode(searchParameters.getDefaultFTSOperator().equals(Operator.AND) ? FTSParser.Mode.DEFAULT_CONJUNCTION : FTSParser.Mode.DEFAULT_DISJUNCTION);
CommonTree ftsNode = (CommonTree) parser.ftsQuery().getTree(); ShardInstance shardNode = ShardInstance.from(shard, node);
} if (this.shardInstanceDisableCache.contains(shardNode) || this.shardInstanceUnavailableCache.contains(shardNode))
continue;
private SearchTerm extractCmisPropertySearchTerm(SearchParameters searchParameters, String field, String operator) {
// TODO include filter and other possible constraints availableNodes.add(node);
}
if (searchParameters.getQuery() == null)
return null; SolrHost randomNode = availableNodes.get(this.random.nextInt(availableNodes.size()));
CharStream cs = new ANTLRStringStream(searchParameters.getQuery()); shardNodes.add(ShardInstance.from(shard, randomNode).toAlfrescoModel(shard.toAlfrescoModel(shardSet.toAlfrescoModel())));
CMISLexer lexer = new CMISLexer(); }
CommonTokenStream tokens = new CommonTokenStream(lexer);
FTSParser parser = new FTSParser(tokens);
parser.setDefaultFieldConjunction(searchParameters.getDefaultFTSOperator().equals(Operator.AND));
parser.setMode(searchParameters.getDefaultFTSOperator().equals(Operator.AND) ? FTSParser.Mode.DEFAULT_CONJUNCTION : FTSParser.Mode.DEFAULT_DISJUNCTION);
CommonTree ftsNode = (CommonTree) parser.ftsQuery().getTree();
}
private class SearchTerm {
private String field;
private String operator;
private String value;
return shardNodes;
} }
} }
@@ -0,0 +1,79 @@
package com.inteligr8.alfresco.asie.util;
import java.util.Comparator;
import org.alfresco.repo.index.shard.ShardMethodEnum;
import com.inteligr8.alfresco.asie.model.ShardSet;
public class ShardSetSearchComparator implements Comparator<ShardSet> {
@Override
public int compare(ShardSet ss1, ShardSet ss2) {
int compare = this.compare(ss1.getMethod(), ss2.getMethod());
if (compare != 0)
return compare;
return this.compare(ss1.getShards(), ss2.getShards());
}
private int compare(ShardMethodEnum method1, ShardMethodEnum method2) {
if (method1.equals(method2))
return 0;
switch (method1) {
case EXPLICIT_ID:
case EXPLICIT_ID_FALLBACK_LRIS:
return -1;
case PROPERTY:
case DATE:
switch (method2) {
case EXPLICIT_ID:
case EXPLICIT_ID_FALLBACK_LRIS:
return 1;
default:
return -1;
}
case ACL_ID:
case MOD_ACL_ID:
switch (method2) {
case EXPLICIT_ID:
case EXPLICIT_ID_FALLBACK_LRIS:
case PROPERTY:
case DATE:
return 1;
default:
return -1;
}
default:
switch (method2) {
case EXPLICIT_ID:
case EXPLICIT_ID_FALLBACK_LRIS:
case PROPERTY:
case DATE:
case ACL_ID:
case MOD_ACL_ID:
return 1;
default:
}
}
return 0;
}
private int compare(Short shards1, Short shards2) {
// the larger the shard count, the more shards that may need to be queried
// so prefer smaller shard counts
// no shard count (DB_ID_RANGE) should be treated as the worst (unlimited)
if (shards1 == null && shards2 == null) {
return 0;
} else if (shards1 == null) {
return 1;
} else if (shards2 == null) {
return -1;
} else {
return shards1.compareTo(shards2);
}
}
}
@@ -1,28 +1,109 @@
inteligr8.asie.registerUnknownShardOffline=false inteligr8.asie.registerUnknownShardDisabled=false
inteligr8.asie.idleShardExpirationInSeconds=${} inteligr8.asie.offlineIdleShardInSeconds=120
inteligr8.asie.forgetOfflineShardInSeconds=86400
# maxItems needs to be greater than total shards, including HA instances # we don't want items expiring out of the following caches
cache.offlineShardStateSharedCache.tx.maxItems=1024 # an evicition policy of NONE disables the maxItems limits
cache.offlineShardStateSharedCache.tx.statsEnabled=${caches.tx.statsEnabled}
cache.offlineShardStateSharedCache.maxItems=1024
cache.offlineShardStateSharedCache.timeToLiveSeconds=1800
cache.offlineShardStateSharedCache.maxIdleSeconds=0
cache.offlineShardStateSharedCache.cluster.type=fully-distributed
cache.offlineShardStateSharedCache.backup-count=1
cache.offlineShardStateSharedCache.eviction-policy=LRU
cache.offlineShardStateSharedCache.merge-policy=com.hazelcast.spi.merge.PutIfAbsentMergePolicy
cache.offlineShardStateSharedCache.readBackupData=false
cache.coreExplicitIdSharedCache.tx.maxItems=1024 # Overrides of alfresco-repository.jar/alfresco/caches.properties
cache.coreExplicitIdSharedCache.tx.statsEnabled=${caches.tx.statsEnabled} cache.asieShardsetsSharedCache.tx.maxItems=65536
cache.coreExplicitIdSharedCache.maxItems=1024 cache.asieShardsetsSharedCache.tx.statsEnabled=${caches.tx.statsEnabled}
cache.coreExplicitIdSharedCache.timeToLiveSeconds=1800 cache.asieShardsetsSharedCache.maxItems=65536
cache.coreExplicitIdSharedCache.maxIdleSeconds=0 cache.asieShardsetsSharedCache.timeToLiveSeconds=0
cache.coreExplicitIdSharedCache.cluster.type=fully-distributed cache.asieShardsetsSharedCache.maxIdleSeconds=0
cache.coreExplicitIdSharedCache.backup-count=1 cache.asieShardsetsSharedCache.cluster.type=fully-distributed
cache.coreExplicitIdSharedCache.eviction-policy=LRU cache.asieShardsetsSharedCache.backup-count=1
cache.coreExplicitIdSharedCache.merge-policy=com.hazelcast.spi.merge.PutIfAbsentMergePolicy cache.asieShardsetsSharedCache.eviction-policy=NONE
cache.coreExplicitIdSharedCache.readBackupData=false cache.asieShardsetsSharedCache.merge-policy=com.hazelcast.map.merge.PutIfAbsentMapMergePolicy
cache.asieShardsetsSharedCache.readBackupData=false
cache.asieNodesSharedCache.tx.maxItems=65536
cache.asieNodesSharedCache.tx.statsEnabled=${caches.tx.statsEnabled}
cache.asieNodesSharedCache.maxItems=65536
cache.asieNodesSharedCache.timeToLiveSeconds=0
cache.asieNodesSharedCache.maxIdleSeconds=0
cache.asieNodesSharedCache.cluster.type=fully-distributed
cache.asieNodesSharedCache.backup-count=1
cache.asieNodesSharedCache.eviction-policy=NONE
cache.asieNodesSharedCache.merge-policy=com.hazelcast.map.merge.PutIfAbsentMapMergePolicy
cache.asieNodesSharedCache.readBackupData=false
cache.asieShardNodesSharedCache.tx.maxItems=65536
cache.asieShardNodesSharedCache.tx.statsEnabled=${caches.tx.statsEnabled}
cache.asieShardNodesSharedCache.maxItems=65536
cache.asieShardNodesSharedCache.timeToLiveSeconds=0
cache.asieShardNodesSharedCache.maxIdleSeconds=0
cache.asieShardNodesSharedCache.cluster.type=fully-distributed
cache.asieShardNodesSharedCache.backup-count=1
cache.asieShardNodesSharedCache.eviction-policy=NONE
cache.asieShardNodesSharedCache.merge-policy=com.hazelcast.map.merge.PutIfAbsentMapMergePolicy
cache.asieShardNodesSharedCache.readBackupData=false
cache.asieShardInstanceStateSharedCache.tx.maxItems=65536
cache.asieShardInstanceStateSharedCache.tx.statsEnabled=${caches.tx.statsEnabled}
cache.asieShardInstanceStateSharedCache.maxItems=65536
cache.asieShardInstanceStateSharedCache.timeToLiveSeconds=0
cache.asieShardInstanceStateSharedCache.maxIdleSeconds=0
cache.asieShardInstanceStateSharedCache.cluster.type=fully-distributed
cache.asieShardInstanceStateSharedCache.backup-count=1
cache.asieShardInstanceStateSharedCache.eviction-policy=NONE
cache.asieShardInstanceStateSharedCache.merge-policy=com.hazelcast.map.merge.PutIfAbsentMapMergePolicy
cache.asieShardInstanceStateSharedCache.readBackupData=false
cache.asieNodeDisabledSharedCache.tx.maxItems=65536
cache.asieNodeDisabledSharedCache.tx.statsEnabled=${caches.tx.statsEnabled}
cache.asieNodeDisabledSharedCache.maxItems=65536
cache.asieNodeDisabledSharedCache.timeToLiveSeconds=0
cache.asieNodeDisabledSharedCache.maxIdleSeconds=0
cache.asieNodeDisabledSharedCache.cluster.type=fully-distributed
cache.asieNodeDisabledSharedCache.backup-count=1
cache.asieNodeDisabledSharedCache.eviction-policy=NONE
cache.asieNodeDisabledSharedCache.merge-policy=com.hazelcast.map.merge.PutIfAbsentMapMergePolicy
cache.asieNodeDisabledSharedCache.readBackupData=false
cache.asieNodeUnavailableSharedCache.tx.maxItems=65536
cache.asieNodeUnavailableSharedCache.tx.statsEnabled=${caches.tx.statsEnabled}
cache.asieNodeUnavailableSharedCache.maxItems=65536
cache.asieNodeUnavailableSharedCache.timeToLiveSeconds=0
cache.asieNodeUnavailableSharedCache.maxIdleSeconds=0
cache.asieNodeUnavailableSharedCache.cluster.type=fully-distributed
cache.asieNodeUnavailableSharedCache.backup-count=1
cache.asieNodeUnavailableSharedCache.eviction-policy=NONE
cache.asieNodeUnavailableSharedCache.merge-policy=com.hazelcast.map.merge.PutIfAbsentMapMergePolicy
cache.asieNodeUnavailableSharedCache.readBackupData=false
cache.asieShardInstanceDisabledSharedCache.tx.maxItems=65536
cache.asieShardInstanceDisabledSharedCache.tx.statsEnabled=${caches.tx.statsEnabled}
cache.asieShardInstanceDisabledSharedCache.maxItems=65536
cache.asieShardInstanceDisabledSharedCache.timeToLiveSeconds=0
cache.asieShardInstanceDisabledSharedCache.maxIdleSeconds=0
cache.asieShardInstanceDisabledSharedCache.cluster.type=fully-distributed
cache.asieShardInstanceDisabledSharedCache.backup-count=1
cache.asieShardInstanceDisabledSharedCache.eviction-policy=NONE
cache.asieShardInstanceDisabledSharedCache.merge-policy=com.hazelcast.map.merge.PutIfAbsentMapMergePolicy
cache.asieShardInstanceDisabledSharedCache.readBackupData=false
cache.asieShardInstanceUnavailableSharedCache.tx.maxItems=65536
cache.asieShardInstanceUnavailableSharedCache.tx.statsEnabled=${caches.tx.statsEnabled}
cache.asieShardInstanceUnavailableSharedCache.maxItems=65536
cache.asieShardInstanceUnavailableSharedCache.timeToLiveSeconds=0
cache.asieShardInstanceUnavailableSharedCache.maxIdleSeconds=0
cache.asieShardInstanceUnavailableSharedCache.cluster.type=fully-distributed
cache.asieShardInstanceUnavailableSharedCache.backup-count=1
cache.asieShardInstanceUnavailableSharedCache.eviction-policy=NONE
cache.asieShardInstanceUnavailableSharedCache.merge-policy=com.hazelcast.map.merge.PutIfAbsentMapMergePolicy
cache.asieShardInstanceUnavailableSharedCache.readBackupData=false
cache.asieCoreExplicitSharedCache.tx.maxItems=65536
cache.asieCoreExplicitSharedCache.tx.statsEnabled=${caches.tx.statsEnabled}
cache.asieCoreExplicitSharedCache.maxItems=65536
cache.asieCoreExplicitSharedCache.timeToLiveSeconds=0
cache.asieCoreExplicitSharedCache.maxIdleSeconds=0
cache.asieCoreExplicitSharedCache.cluster.type=fully-distributed
cache.asieCoreExplicitSharedCache.backup-count=1
cache.asieCoreExplicitSharedCache.eviction-policy=NONE
cache.asieCoreExplicitSharedCache.merge-policy=com.hazelcast.map.merge.PutIfAbsentMapMergePolicy
cache.asieCoreExplicitSharedCache.readBackupData=false
@@ -1,3 +0,0 @@
logger.inteligr8-asie.name=com.inteligr8.alfresco.asie
logger.inteligr8-asie.level=INFO
@@ -7,12 +7,44 @@
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean name="offlineShardStateCache" factory-bean="cacheFactory" factory-method="createCache"> <bean name="asieShardsetsCache" factory-bean="cacheFactory" factory-method="createCache">
<constructor-arg value="cache.offlineShardStateSharedCache" /> <constructor-arg value="cache.asieShardsetsSharedCache" />
</bean> </bean>
<bean name="coreExplicitIdCache" factory-bean="cacheFactory" factory-method="createCache"> <bean name="asieNodesCache" factory-bean="cacheFactory" factory-method="createCache">
<constructor-arg value="cache.coreExplicitIdSharedCache" /> <constructor-arg value="cache.asieNodesSharedCache" />
</bean>
<bean name="asieShardNodesCache" factory-bean="cacheFactory" factory-method="createMultiValueCache">
<constructor-arg value="cache.asieShardNodesSharedCache" />
</bean>
<bean name="asieShardInstanceStateCache" factory-bean="cacheFactory" factory-method="createCache">
<constructor-arg value="cache.asieShardInstanceStateSharedCache" />
</bean>
<bean name="asieNodeDisabledCache" factory-bean="cacheFactory" factory-method="createCollectionCache">
<constructor-arg value="cache.asieNodeDisabledSharedCache" />
<constructor-arg value="java.util.HashSet" />
</bean>
<bean name="asieNodeUnavailableCache" factory-bean="cacheFactory" factory-method="createCollectionCache">
<constructor-arg value="cache.asieNodeUnavailableSharedCache" />
<constructor-arg value="java.util.HashSet" />
</bean>
<bean name="asieShardInstanceDisabledCache" factory-bean="cacheFactory" factory-method="createCollectionCache">
<constructor-arg value="cache.asieShardInstanceDisabledSharedCache" />
<constructor-arg value="java.util.HashSet" />
</bean>
<bean name="asieShardInstanceUnavailableCache" factory-bean="cacheFactory" factory-method="createCollectionCache">
<constructor-arg value="cache.asieShardInstanceUnavailableSharedCache" />
<constructor-arg value="java.util.HashSet" />
</bean>
<bean name="asieCoreExplicitCache" factory-bean="cacheFactory" factory-method="createCache">
<constructor-arg value="cache.asieCoreExplicitSharedCache" />
</bean> </bean>
</beans> </beans>
@@ -7,4 +7,5 @@ module.version=${module.version}
module.repo.version.min=23.0 module.repo.version.min=23.0
# this is creating all sorts of problems; probably because of the non-standard versioning # this is creating all sorts of problems; probably because of the non-standard versioning
module.depends.com.inteligr8.alfresco.cachext-platform-module=*
module.depends.com.inteligr8.alfresco.cxf-jaxrs-platform-module=* module.depends.com.inteligr8.alfresco.cxf-jaxrs-platform-module=*
@@ -0,0 +1,146 @@
package com.inteligr8.alfresco.asie;
import java.util.ArrayList;
import java.util.List;
import org.alfresco.repo.search.impl.parsers.FTSLexer;
import org.alfresco.repo.search.impl.parsers.FTSParser;
import org.alfresco.service.cmr.search.SearchParameters.Operator;
import org.antlr.runtime.ANTLRStringStream;
import org.antlr.runtime.CharStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.tree.CommonTree;
import org.antlr.runtime.tree.Tree;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
public class QueryConstraintUnitTest {
private static final ObjectMapper om = new ObjectMapper();
@BeforeClass
public static void init() {
SimpleModule module = new SimpleModule();
module.addSerializer(Tree.class, new TreeSerializer());
om.registerModule(module);
}
@Test
public void testSingleExactTerm() throws RecognitionException, JsonProcessingException {
Tree tree = this.parseFts("=@cm:title:test", Operator.AND);
tree = this.validateChildren(tree, "DISJUNCTION");
tree = this.validateChildren(tree, "CONJUNCTION");
tree = this.validateChildren(tree, "DEFAULT");
tree = this.validateChildren(tree, "EXACT_TERM", "test");
tree = this.validateChildren(tree, "FIELD_REF", "title");
this.validate(tree, "PREFIX", "cm");
}
@Test
public void testSingleFuzzyTerm() throws RecognitionException, JsonProcessingException {
Tree tree = this.parseFts("@cm:title:test", Operator.AND);
tree = this.validateChildren(tree, "DISJUNCTION");
tree = this.validateChildren(tree, "CONJUNCTION");
tree = this.validateChildren(tree, "DEFAULT");
tree = this.validateChildren(tree, "TERM", "test");
tree = this.validateChildren(tree, "FIELD_REF", "title");
this.validate(tree, "PREFIX", "cm");
}
@Test
public void testSingleFuzzyString() throws RecognitionException, JsonProcessingException {
Tree tree = this.parseFts("@cm:title:'testing'", Operator.AND);
tree = this.validateChildren(tree, "DISJUNCTION");
tree = this.validateChildren(tree, "CONJUNCTION");
tree = this.validateChildren(tree, "DEFAULT");
tree = this.validateChildren(tree, "PHRASE", "'testing'");
tree = this.validateChildren(tree, "FIELD_REF", "title");
this.validate(tree, "PREFIX", "cm");
}
@Test
public void testSingleFuzzyStringDoubleQuotes() throws RecognitionException, JsonProcessingException {
Tree tree = this.parseFts("cm:title:\"testing\"", Operator.AND);
tree = this.validateChildren(tree, "DISJUNCTION");
tree = this.validateChildren(tree, "CONJUNCTION");
tree = this.validateChildren(tree, "DEFAULT");
tree = this.validateChildren(tree, "PHRASE", "\"testing\"");
tree = this.validateChildren(tree, "FIELD_REF", "title");
this.validate(tree, "PREFIX", "cm");
}
@Test
public void testSingleRange() throws RecognitionException, JsonProcessingException {
Tree tree = this.parseFts("@cm:created:[NOW TO '2025-01-01T00:00:00'>", Operator.AND);
tree = this.validateChildren(tree, "DISJUNCTION");
tree = this.validateChildren(tree, "CONJUNCTION");
tree = this.validateChildren(tree, "DEFAULT");
tree = this.validateChildren(tree, "RANGE", "INCLUSIVE", "NOW", "'2025-01-01T00:00:00'", "EXCLUSIVE");
tree = this.validateChildren(tree, "FIELD_REF", "created");
this.validate(tree, "PREFIX", "cm");
}
@Test
public void testTwoTerms() throws RecognitionException, JsonProcessingException {
Tree tree = this.parseFts("=@cm:title:test1 AND @cm:author:test2", Operator.AND);
tree = this.validateChildren(tree, "DISJUNCTION");
List<Tree> trees = this.validateChildren(tree, "CONJUNCTION", 2);
tree = trees.get(0);
tree = this.validateChildren(tree, "DEFAULT");
tree = this.validateChildren(tree, "EXACT_TERM", "test1");
tree = this.validateChildren(tree, "FIELD_REF", "title");
this.validate(tree, "PREFIX", "cm");
tree = trees.get(1);
tree = this.validateChildren(tree, "DEFAULT");
tree = this.validateChildren(tree, "TERM", "test2");
tree = this.validateChildren(tree, "FIELD_REF", "author");
this.validate(tree, "PREFIX", "cm");
}
protected void validate(Tree tree, String text, String... extraValues) {
Assert.assertNotNull(tree);
Assert.assertEquals(text, tree.getText());
Assert.assertEquals(extraValues.length, tree.getChildCount());
for (int c = 0; c < extraValues.length; c++)
Assert.assertEquals(extraValues[c], tree.getChild(c).getText());
}
protected Tree validateChildren(Tree tree, String text, String... extraValues) {
Assert.assertNotNull(tree);
Assert.assertEquals(text, tree.getText());
Assert.assertEquals(extraValues.length + 1, tree.getChildCount());
for (int c = 0; c < extraValues.length; c++)
Assert.assertEquals(extraValues[c], tree.getChild(c).getText());
return tree.getChild(extraValues.length);
}
protected List<Tree> validateChildren(Tree tree, String text, int count) {
Assert.assertNotNull(tree);
Assert.assertEquals(text, tree.getText());
Assert.assertEquals(count, tree.getChildCount());
List<Tree> children = new ArrayList<>();
for (int c = 0; c < tree.getChildCount(); c++)
children.add(tree.getChild(c));
return children;
}
protected Tree parseFts(String ftsQuery, Operator defaultOperator) throws RecognitionException, JsonProcessingException {
CharStream cs = new ANTLRStringStream(ftsQuery);
FTSLexer lexer = new FTSLexer(cs);
CommonTokenStream tokens = new CommonTokenStream(lexer);
FTSParser parser = new FTSParser(tokens);
parser.setDefaultFieldConjunction(defaultOperator.equals(Operator.AND));
parser.setMode(defaultOperator.equals(Operator.AND) ? FTSParser.Mode.DEFAULT_CONJUNCTION : FTSParser.Mode.DEFAULT_DISJUNCTION);
CommonTree tree = (CommonTree) parser.ftsQuery().getTree();
return tree;
}
}
@@ -0,0 +1,44 @@
package com.inteligr8.alfresco.asie;
import java.io.IOException;
import org.antlr.runtime.tree.Tree;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
public class TreeSerializer extends StdSerializer<Tree> {
private static final long serialVersionUID = -2714782538361726878L;
public TreeSerializer() {
super(Tree.class);
}
public TreeSerializer(Class<Tree> type) {
super(type);
}
public TreeSerializer(JavaType type) {
super(type);
}
@Override
public void serialize(Tree value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeStartObject();
if (value.getText() != null)
gen.writeStringField("text", value.getText());
if (value.getChildCount() > 0) {
gen.writeArrayFieldStart("children");
for (int c = 0; c < value.getChildCount(); c++)
gen.writeObject(value.getChild(c));
gen.writeEndArray();
}
gen.writeEndObject();
}
}
Binary file not shown.
+39 -32
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.1-SNAPSHOT</version> <version>1.3.3</version>
<relativePath>../</relativePath> <relativePath>../</relativePath>
</parent> </parent>
@@ -16,8 +16,12 @@
<name>ASIE Platform Module for ACS Enterprise</name> <name>ASIE Platform Module for ACS Enterprise</name>
<properties> <properties>
<alfresco.sdk.version>5.2.0</alfresco.sdk.version> <alfresco.sdk.version>4.9.0</alfresco.sdk.version>
<alfresco.platform.version>23.3.0</alfresco.platform.version> <alfresco.platform.version>23.3.0</alfresco.platform.version>
<alfresco.platform.war.version>23.3.0.98</alfresco.platform.war.version>
<tomcat-rad.version>10-2.1</tomcat-rad.version>
<beedk.rad.acs-search.enabled>true</beedk.rad.acs-search.enabled>
</properties> </properties>
<dependencyManagement> <dependencyManagement>
@@ -29,35 +33,6 @@
<type>pom</type> <type>pom</type>
<scope>import</scope> <scope>import</scope>
</dependency> </dependency>
<!-- Provided by cxf-jaxrs-platform-module, but packaged due to shared -->
<dependency>
<groupId>com.inteligr8</groupId>
<artifactId>common-rest-client</artifactId>
<scope>provided</scope>
</dependency>
<!-- Provided by cxf-jaxrs-platform-module, but packaged due to solr-api -->
<dependency>
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jakarta-xmlbind-annotations</artifactId>
<scope>provided</scope>
</dependency>
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>
@@ -79,7 +54,39 @@
<dependency> <dependency>
<groupId>com.inteligr8.alfresco</groupId> <groupId>com.inteligr8.alfresco</groupId>
<artifactId>cxf-jaxrs-platform-module</artifactId> <artifactId>cxf-jaxrs-platform-module</artifactId>
<version>1.3.1-acs-v23.3</version> <version>1.3.2-acs-v23.3</version>
<type>amp</type>
</dependency>
<!-- Provided by cxf-jaxrs-platform-module, but packaged due to shared -->
<dependency>
<groupId>com.inteligr8</groupId>
<artifactId>common-rest-client</artifactId>
<scope>provided</scope>
</dependency>
<!-- Provided by cxf-jaxrs-platform-module, but packaged due to solr-api -->
<dependency>
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
<version>4.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.17.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jakarta-xmlbind-annotations</artifactId>
<version>2.17.2</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
+4 -4
View File
@@ -1,22 +1,22 @@
#!/bin/sh #!/bin/sh
discoverArtifactId() { discoverArtifactId() {
ARTIFACT_ID=`mvn -q -Dexpression=project.artifactId -DforceStdout help:evaluate` ARTIFACT_ID=`mvn -q -Dexpression=project.artifactId -DforceStdout help:evaluate | sed 's/\x1B\[[0-9;]\{1,\}[A-Za-z]//g'`
} }
rebuild() { rebuild() {
echo "Rebuilding project ..." echo "Rebuilding project ..."
mvn process-classes mvn process-test-classes
} }
start() { start() {
echo "Rebuilding project and starting Docker containers to support rapid application development ..." echo "Rebuilding project and starting Docker containers to support rapid application development ..."
mvn -Drad process-classes mvn -Drad process-test-classes
} }
start_log() { start_log() {
echo "Rebuilding project and starting Docker containers to support rapid application development ..." echo "Rebuilding project and starting Docker containers to support rapid application development ..."
mvn -Drad -Ddocker.showLogs process-classes mvn -Drad -Ddocker.showLogs process-test-classes
} }
stop() { stop() {
@@ -1,4 +1,4 @@
package com.inteligr8.alfresco.asie.rest; package com.inteligr8.alfresco.asie.enterprise.rest;
import java.io.IOException; import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
@@ -9,6 +9,7 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.alfresco.repo.index.shard.Shard;
import org.alfresco.repo.index.shard.ShardState; import org.alfresco.repo.index.shard.ShardState;
import org.alfresco.service.cmr.attributes.AttributeService; import org.alfresco.service.cmr.attributes.AttributeService;
import org.alfresco.service.cmr.attributes.AttributeService.AttributeQueryCallback; import org.alfresco.service.cmr.attributes.AttributeService.AttributeQueryCallback;
@@ -17,16 +18,17 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.extensions.webscripts.WebScriptException;
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 org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import com.inteligr8.alfresco.asie.Constants; import com.inteligr8.alfresco.asie.Constants;
import com.inteligr8.alfresco.asie.api.CoreAdminApi; import com.inteligr8.alfresco.asie.api.CoreAdminApi;
import com.inteligr8.alfresco.asie.model.NodeParameterSet; import com.inteligr8.alfresco.asie.model.ShardSet;
import com.inteligr8.alfresco.asie.service.ShardBackupService; import com.inteligr8.alfresco.asie.rest.AbstractAsieNodeWebScript;
import com.inteligr8.alfresco.asie.spi.ShardStateService; import com.inteligr8.alfresco.asie.rest.model.NodeParameterSet;
import com.inteligr8.alfresco.asie.spi.ShardBackupService;
import com.inteligr8.alfresco.asie.enterprise.service.ShardStateService;
import com.inteligr8.solr.model.CoreMetadata; import com.inteligr8.solr.model.CoreMetadata;
import com.inteligr8.solr.model.core.StatusRequest; import com.inteligr8.solr.model.core.StatusRequest;
import com.inteligr8.solr.model.core.StatusResponse; import com.inteligr8.solr.model.core.StatusResponse;
@@ -84,19 +86,25 @@ public abstract class AbstractUnregisterNodeWebScript<T extends NodeParameterSet
ShardState shardNode = matchingCore.getValue(); ShardState shardNode = matchingCore.getValue();
String core = shardNode.getPropertyBag().get("coreName"); String core = shardNode.getPropertyBag().get("coreName");
StatusResponse status = this.getCoreStatus(nodeHostname, nodePort, core); try {
if (status == null) StatusResponse status = this.getCoreStatus(nodeHostname, nodePort, core);
throw new WebScriptException(HttpStatus.INTERNAL_SERVER_ERROR.value(), "This should never happen"); if (status == null) {
CoreMetadata coreMetadata = status.getStatus().getCores().get(core); this.logger.warn("Registered host/core status could not be retrieved: {}:{}/solr/{}", nodeHostname, nodePort, core);
if (coreMetadata == null || coreMetadata.getName() == null) { } else {
this.logger.warn("Registered core does not actually exist on the node host; could be a DNS issue: {}:{}/solr/{}", nodeHostname, nodePort, core); CoreMetadata coreMetadata = status.getCores().getByCore(core);
} else { if (coreMetadata == null || coreMetadata.getName() == null) {
this.unloadCore(nodeHostname, nodePort, core); this.logger.warn("Registered core does not actually exist on the node host; could be a DNS issue: {}:{}/solr/{}", nodeHostname, nodePort, core);
cores.put(core, coreMetadata.getInstancePath()); } else {
this.unloadCore(nodeHostname, nodePort, core);
cores.put(core, coreMetadata.getInstancePath());
}
}
} finally {
this.sss.remove(matchingCore.getKey());
Shard shard = shardNode.getShardInstance().getShard();
this.sbs.forget(ShardSet.from(shard.getFloc(), shardNode), shard.getInstance());
} }
this.sss.remove(matchingCore.getKey());
this.sbs.forget(shardNode);
} }
} finally { } finally {
// FIXME maybe a separate tx? // FIXME maybe a separate tx?
@@ -133,13 +141,13 @@ public abstract class AbstractUnregisterNodeWebScript<T extends NodeParameterSet
protected StatusResponse getCoreStatus(String nodeHostname, int nodePort, String core) { protected StatusResponse getCoreStatus(String nodeHostname, int nodePort, String core) {
this.logger.debug("Retrieving status for core {} on ASIE node: {}", core, nodeHostname); this.logger.debug("Retrieving status for core {} on ASIE node: {}", core, nodeHostname);
CoreAdminApi api = this.createApi(nodeHostname, nodePort); CoreAdminApi api = this.getApiService().createApi(nodeHostname, nodePort, CoreAdminApi.class);
return api.getStatus(new StatusRequest().withCore(core)); return api.getStatus(new StatusRequest().withCore(core));
} }
protected void unloadCore(String nodeHostname, int nodePort, String core) { protected void unloadCore(String nodeHostname, int nodePort, String core) {
this.logger.info("Unloading core {} on ASIE node: {}", core, nodeHostname); this.logger.info("Unloading core {} on ASIE node: {}", core, nodeHostname);
CoreAdminApi api = this.createApi(nodeHostname, nodePort); CoreAdminApi api = this.getApiService().createApi(nodeHostname, nodePort, CoreAdminApi.class);
api.unload(new UnloadRequest().withCore(core)); api.unload(new UnloadRequest().withCore(core));
} }
@@ -1,4 +1,4 @@
package com.inteligr8.alfresco.asie.rest; package com.inteligr8.alfresco.asie.enterprise.rest;
import java.io.IOException; import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
@@ -21,6 +21,7 @@ import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.Constants; import com.inteligr8.alfresco.asie.Constants;
import com.inteligr8.alfresco.asie.api.CoreAdminApi; import com.inteligr8.alfresco.asie.api.CoreAdminApi;
import com.inteligr8.alfresco.asie.rest.AbstractAsieNodeWebScript;
import com.inteligr8.solr.model.ExceptionResponse; import com.inteligr8.solr.model.ExceptionResponse;
import com.inteligr8.solr.model.core.CreateRequest; import com.inteligr8.solr.model.core.CreateRequest;
import com.inteligr8.solr.model.core.ReloadRequest; import com.inteligr8.solr.model.core.ReloadRequest;
@@ -62,7 +63,7 @@ public class ReloadNodeShardWebScript extends AbstractAsieNodeWebScript {
throw new WebScriptException(HttpStatus.NOT_FOUND.value(), "The specified node/shard could not be found or formulated"); throw new WebScriptException(HttpStatus.NOT_FOUND.value(), "The specified node/shard could not be found or formulated");
this.logger.info("Reloading core {} on ASIE node: {}", coreName, nodeHostname); this.logger.info("Reloading core {} on ASIE node: {}", coreName, nodeHostname);
CoreAdminApi api = this.createApi(nodeHostname, nodePort); CoreAdminApi api = this.getApiService().createApi(nodeHostname, nodePort, CoreAdminApi.class);
try { try {
api.create(new CreateRequest() api.create(new CreateRequest()
.withCore(coreName) .withCore(coreName)
@@ -1,4 +1,4 @@
package com.inteligr8.alfresco.asie.rest; package com.inteligr8.alfresco.asie.enterprise.rest;
import java.io.IOException; import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
@@ -23,6 +23,7 @@ import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.Constants; import com.inteligr8.alfresco.asie.Constants;
import com.inteligr8.alfresco.asie.api.CoreAdminApi; import com.inteligr8.alfresco.asie.api.CoreAdminApi;
import com.inteligr8.alfresco.asie.rest.AbstractAsieNodeWebScript;
import com.inteligr8.solr.model.ExceptionResponse; import com.inteligr8.solr.model.ExceptionResponse;
import com.inteligr8.solr.model.core.CreateRequest; import com.inteligr8.solr.model.core.CreateRequest;
import com.inteligr8.solr.model.core.ReloadRequest; import com.inteligr8.solr.model.core.ReloadRequest;
@@ -63,7 +64,7 @@ public class ReloadNodeWebScript extends AbstractAsieNodeWebScript {
String coreInstancePath = core.getValue(); String coreInstancePath = core.getValue();
this.logger.info("Reloading core {} on ASIE node: {}", coreName, nodeHostname); this.logger.info("Reloading core {} on ASIE node: {}", coreName, nodeHostname);
CoreAdminApi api = this.createApi(nodeHostname, nodePort); CoreAdminApi api = this.getApiService().createApi(nodeHostname, nodePort, CoreAdminApi.class);
try { try {
api.create(new CreateRequest() api.create(new CreateRequest()
.withCore(coreName) .withCore(coreName)
@@ -1,26 +1,38 @@
package com.inteligr8.alfresco.asie.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.NodeShardParameterSet;
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.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
protected boolean matches(NodeShardParameterSet params, ShardState shardState) { protected boolean matches(NodeShardParameterSet params, ShardState shardState) {
if (!params.getShardSet().isFor(shardState)) if (!params.getShardSet().contains(shardState))
return false; return false;
if (params.getShardId() != shardState.getShardInstance().getShard().getInstance()) if (params.getShardId() != shardState.getShardInstance().getShard().getInstance())
return false; return false;
@@ -1,9 +1,9 @@
package com.inteligr8.alfresco.asie.rest; package com.inteligr8.alfresco.asie.enterprise.rest;
import org.springframework.extensions.webscripts.WebScriptRequest; import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.model.NodeParameterSet; import com.inteligr8.alfresco.asie.rest.model.NodeParameterSet;
@Component(value = "webscript.com.inteligr8.alfresco.asie.node.delete") @Component(value = "webscript.com.inteligr8.alfresco.asie.node.delete")
public class UnloadNodeWebScript extends AbstractUnregisterNodeWebScript<NodeParameterSet> { public class UnloadNodeWebScript extends AbstractUnregisterNodeWebScript<NodeParameterSet> {
@@ -0,0 +1,411 @@
package com.inteligr8.alfresco.asie.enterprise.service;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.alfresco.repo.index.shard.Floc;
import org.alfresco.repo.index.shard.Shard;
import org.alfresco.repo.index.shard.ShardInstance;
import org.alfresco.repo.index.shard.ShardMethodEnum;
import org.alfresco.repo.index.shard.ShardRegistry;
import org.alfresco.repo.index.shard.ShardState;
import org.alfresco.util.Pair;
import org.alfresco.util.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.Constants;
import com.inteligr8.alfresco.asie.model.ShardSet;
import com.inteligr8.alfresco.asie.model.SolrHost;
import com.inteligr8.alfresco.asie.model.ShardInstanceState;
@Component
public class ShardDiscoveryService implements com.inteligr8.alfresco.asie.spi.ShardDiscoveryService {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
@Qualifier(Constants.QUALIFIER_ASIE)
private ShardRegistry shardRegistry;
@Override
public ShardSet findSetByCore(String core) {
Map<Floc, Map<Shard, Set<ShardState>>> flocs = this.shardRegistry.getFlocs();
if (flocs.isEmpty())
return null;
this.logger.trace("Found {} shard sets", flocs.size());
for (Entry<Floc, Map<Shard, Set<ShardState>>> floc : flocs.entrySet()) {
for (Entry<Shard, Set<ShardState>> flocShard : floc.getValue().entrySet()) {
for (ShardState shardState : flocShard.getValue()) {
ShardSet shardSet = ShardSet.from(floc.getKey(), shardState);
if (shardSet.getCore().equals(core)) {
this.logger.debug("Found matching shard set: {}: {}", core, shardSet);
return shardSet;
}
}
}
}
this.logger.trace("Did not find matching shard set: {}", core);
return null;
}
@Override
public SolrHost findNode(String nodeHostname, int nodePort) {
Map<Floc, Map<Shard, Set<ShardState>>> flocs = this.shardRegistry.getFlocs();
if (flocs.isEmpty())
return null;
this.logger.trace("Found {} shard sets", flocs.size());
Map<String, InetAddress> resolvedAddresses = new HashMap<>();
for (Entry<Floc, Map<Shard, Set<ShardState>>> floc : flocs.entrySet()) {
for (Entry<Shard, Set<ShardState>> flocShard : floc.getValue().entrySet()) {
for (ShardState shardState : flocShard.getValue()) {
ShardInstance shardInstance = shardState.getShardInstance();
if (!nodeHostname.equalsIgnoreCase(shardInstance.getHostName())) {
if (!resolvedAddresses.containsKey(nodeHostname))
resolvedAddresses.put(nodeHostname, this.resolve(nodeHostname));
InetAddress nodeAddress = resolvedAddresses.get(nodeHostname);
this.logger.trace("Resolved: {} => {}", nodeHostname, nodeAddress);
if (nodeAddress == null)
continue;
if (!resolvedAddresses.containsKey(shardInstance.getHostName()))
resolvedAddresses.put(shardInstance.getHostName(), this.resolve(shardInstance.getHostName()));
InetAddress shardInstanceAddress = resolvedAddresses.get(shardInstance.getHostName());
this.logger.trace("Resolved: {} => {}", shardInstance.getHostName(), shardInstanceAddress);
if (!nodeAddress.equals(shardInstanceAddress))
continue;
}
if (nodePort == shardInstance.getPort()) {
SolrHost node = SolrHost.from(shardInstance);
this.logger.debug("Found node: {}", node);
return node;
}
}
}
}
this.logger.trace("Did not find matching node: {}:{}", nodeHostname, nodePort);
return null;
}
@Override
public Map<ShardSet, Map<Integer, ShardInstanceState>> findByNode(SolrHost node) {
Map<Floc, Map<Shard, Set<ShardState>>> flocs = this.shardRegistry.getFlocs();
if (flocs.isEmpty())
return Collections.emptyMap();
this.logger.trace("Found {} shard sets", flocs.size());
Map<ShardSet, Map<Integer, ShardInstanceState>> setShardStates = new HashMap<>();
for (Entry<Floc, Map<Shard, Set<ShardState>>> floc : flocs.entrySet())
setShardStates.putAll(this.findByNode(node, floc.getKey(), floc.getValue()));
return setShardStates;
}
private Map<ShardSet, Map<Integer, ShardInstanceState>> findByNode(SolrHost node, Floc floc, Map<Shard, Set<ShardState>> shards) {
ShardSet shardSet = null;
Map<ShardSet, Map<Integer, ShardInstanceState>> setShardStates = new HashMap<>();
int shardStateCount = 0;
for (Entry<Shard, Set<ShardState>> flocShard : shards.entrySet()) {
for (ShardState shardState : flocShard.getValue()) {
if (shardSet == null)
shardSet = ShardSet.from(floc, shardState);
ShardInstance shardInstance = shardState.getShardInstance();
if (node.equals(SolrHost.from(shardInstance))) {
this.logger.trace("Found shard on node: {}: {}", node, shardInstance.getShard().getInstance());
Map<Integer, ShardInstanceState> shardStates = setShardStates.get(shardSet);
if (shardStates == null)
setShardStates.put(shardSet, shardStates = new HashMap<>());
shardStates.put(flocShard.getKey().getInstance(), ShardInstanceState.from(shardState));
shardStateCount++;
}
}
}
this.logger.debug("Found {} shard states for node: {}", shardStateCount, node);
return setShardStates;
}
@Override
public Set<ShardSet> findSetsByShardMethod(ShardMethodEnum... shardMethods) {
Map<Floc, Map<Shard, Set<ShardState>>> flocs = this.shardRegistry.getFlocs();
if (flocs.isEmpty())
return Collections.emptySet();
this.logger.trace("Found {} shard sets", flocs.size());
Set<ShardMethodEnum> shardMethodSet = CollectionUtils.asSet(shardMethods);
Set<ShardSet> shardSets = new HashSet<>();
for (Entry<Floc, Map<Shard, Set<ShardState>>> floc : flocs.entrySet()) {
if (shardMethodSet.contains(floc.getKey().getShardMethod())) {
this.logger.trace("Found qualifying shard method: {}", floc.getKey().getShardMethod());
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));
}
}
this.logger.debug("Found {} shard sets of methods: {}", flocs.size(), shardMethods);
return shardSets;
}
@Override
public Set<SolrHost> findNodes(ShardSet shardSet) {
Map<Floc, Map<Shard, Set<ShardState>>> flocs = this.shardRegistry.getFlocs();
if (flocs.isEmpty())
return Collections.emptySet();
this.logger.trace("Found {} shard sets", flocs.size());
for (Entry<Floc, Map<Shard, Set<ShardState>>> floc : flocs.entrySet()) {
Set<SolrHost> nodes = this.findNodes(shardSet, null, floc.getKey(), floc.getValue());
if (nodes != null) {
this.logger.debug("Found {} nodes for set: {}", nodes.size(), shardSet);
return nodes;
}
}
this.logger.debug("Found {} nodes for set: {}", 0, shardSet);
return Collections.emptySet();
}
@Override
public Set<SolrHost> findNodesByShard(ShardSet shardSet, int shardId) {
Map<Floc, Map<Shard, Set<ShardState>>> flocs = this.shardRegistry.getFlocs();
if (flocs.isEmpty())
return Collections.emptySet();
this.logger.trace("Found {} shard sets", flocs.size());
for (Entry<Floc, Map<Shard, Set<ShardState>>> floc : flocs.entrySet()) {
Set<SolrHost> nodes = this.findNodes(shardSet, shardId, floc.getKey(), floc.getValue());
if (nodes != null) {
this.logger.debug("Found {} nodes for shard #{} in set: {}", nodes.size(), shardId, shardSet);
return nodes;
}
}
this.logger.debug("Found {} nodes for shard #{} in set: {}", 0, shardId, shardSet);
return Collections.emptySet();
}
private Set<SolrHost> findNodes(ShardSet shardSet, Integer shardId, Floc floc, Map<Shard, Set<ShardState>> shards) {
Set<SolrHost> nodes = new HashSet<>();
boolean checked = false;
for (Entry<Shard, Set<ShardState>> shard : shards.entrySet()) {
if (shardId == null || shardId.intValue() == shard.getKey().getInstance()) {
for (ShardState shardState : shard.getValue()) {
if (!checked && !shardSet.equals(ShardSet.from(floc, shardState)))
return null;
checked = true;
ShardInstance shardInstance = shardState.getShardInstance();
SolrHost node = SolrHost.from(shardInstance);
nodes.add(node);
}
}
}
return nodes;
}
@Override
public Map<Integer, Pair<SolrHost, ShardInstanceState>> findLatestNodeStates(ShardSet shardSet) {
Map<Floc, Map<Shard, Set<ShardState>>> flocs = this.shardRegistry.getFlocs();
if (flocs.isEmpty())
return Collections.emptyMap();
this.logger.trace("Found {} shard sets", flocs.size());
for (Entry<Floc, Map<Shard, Set<ShardState>>> floc : flocs.entrySet()) {
Map<Integer, Pair<SolrHost, ShardInstanceState>> shardNodeStates = this.findLatestNodeStates(shardSet, floc.getKey(), floc.getValue());
if (shardNodeStates != null) {
this.logger.debug("Found {} shard node states for set: {}", shardNodeStates.size(), shardSet);
return shardNodeStates;
}
}
this.logger.debug("Found {} shard node states for set: {}", 0, shardSet);
return Collections.emptyMap();
}
private Map<Integer, Pair<SolrHost, ShardInstanceState>> findLatestNodeStates(ShardSet shardSet, Floc floc, Map<Shard, Set<ShardState>> shards) {
Map<Integer, Pair<SolrHost, ShardInstanceState>> shardNodeStates = new HashMap<>();
boolean checked = false;
com.inteligr8.alfresco.asie.spi.ShardDiscoveryService.ShardedNodeShardStateComparator comparator = new com.inteligr8.alfresco.asie.spi.ShardDiscoveryService.ShardedNodeShardStateComparator();
for (Entry<Shard, Set<ShardState>> shard : shards.entrySet()) {
int shardId = shard.getKey().getInstance();
for (ShardState shardState : shard.getValue()) {
if (!checked && !shardSet.equals(ShardSet.from(floc, shardState)))
return null;
checked = true;
ShardInstance shardInstance = shardState.getShardInstance();
SolrHost node = SolrHost.from(shardInstance);
ShardInstanceState nodeShardState = ShardInstanceState.from(shardState);
Pair<SolrHost, ShardInstanceState> pair = new Pair<>(node, nodeShardState);
if (comparator.compare(pair, shardNodeStates.get(shardId)) < 0)
shardNodeStates.put(shardId, pair);
}
}
return shardNodeStates;
}
@Override
public List<Pair<SolrHost, ShardInstanceState>> findNodeStatesByShard(ShardSet shardSet, int shardId) {
Map<Floc, Map<Shard, Set<ShardState>>> flocs = this.shardRegistry.getFlocs();
if (flocs.isEmpty())
return Collections.emptyList();
this.logger.trace("Found {} shard sets", flocs.size());
for (Entry<Floc, Map<Shard, Set<ShardState>>> floc : flocs.entrySet()) {
List<Pair<SolrHost, ShardInstanceState>> nodeStates = this.findNodeStates(shardSet, shardId, floc.getKey(), floc.getValue());
if (nodeStates != null) {
this.logger.debug("Found {} node states for shard #{} in set: {}", nodeStates.size(), shardId, shardSet);
return nodeStates;
}
}
this.logger.debug("Found {} node states for shard #{} in set: {}", 0, shardId, shardSet);
return Collections.emptyList();
}
private List<Pair<SolrHost, ShardInstanceState>> findNodeStates(ShardSet shardSet, Integer shardId, Floc floc, Map<Shard, Set<ShardState>> shards) {
List<Pair<SolrHost, ShardInstanceState>> nodeStates = new LinkedList<>();
boolean checked = false;
for (Entry<Shard, Set<ShardState>> shard : shards.entrySet()) {
if (shardId == null || shardId.intValue() == shard.getKey().getInstance()) {
for (ShardState shardState : shard.getValue()) {
if (!checked && !shardSet.equals(ShardSet.from(floc, shardState)))
return null;
checked = true;
ShardInstance shardInstance = shardState.getShardInstance();
SolrHost node = SolrHost.from(shardInstance);
ShardInstanceState nodeShardState = ShardInstanceState.from(shardState);
nodeStates.add(new Pair<>(node, nodeShardState));
}
}
}
return nodeStates;
}
@Override
public Set<Integer> findIdsByNode(ShardSet shardSet, SolrHost node) {
Map<Floc, Map<Shard, Set<ShardState>>> flocs = this.shardRegistry.getFlocs();
if (flocs.isEmpty())
return Collections.emptySet();
this.logger.trace("Found {} shard sets", flocs.size());
for (Entry<Floc, Map<Shard, Set<ShardState>>> floc : flocs.entrySet()) {
Set<Integer> shardIds = this.findIdsByNode(shardSet, node, floc.getKey(), floc.getValue());
if (shardIds != null) {
this.logger.debug("Found {} shards for node '{}' in set: {}", shardIds.size(), node, shardSet);
return shardIds;
}
}
this.logger.debug("Found {} node states for node '{}' in set: {}", 0, node, shardSet);
return Collections.emptySet();
}
private Set<Integer> findIdsByNode(ShardSet shardSet, SolrHost node, Floc floc, Map<Shard, Set<ShardState>> shards) {
Set<Integer> shardIds = new HashSet<>();
boolean checked = false;
for (Entry<Shard, Set<ShardState>> shard : shards.entrySet()) {
for (ShardState shardState : shard.getValue()) {
if (!checked && !shardSet.equals(ShardSet.from(floc, shardState)))
return null;
checked = true;
ShardInstance shardInstance = shardState.getShardInstance();
if (node.equals(SolrHost.from(shardInstance)))
shardIds.add(shard.getKey().getInstance());
}
}
return shardIds;
}
@Override
public Map<Integer, ShardInstanceState> findStatesByNode(ShardSet shardSet, SolrHost node) {
Map<Floc, Map<Shard, Set<ShardState>>> flocs = this.shardRegistry.getFlocs();
if (flocs.isEmpty())
return Collections.emptyMap();
this.logger.trace("Found {} shard sets", flocs.size());
for (Entry<Floc, Map<Shard, Set<ShardState>>> floc : flocs.entrySet()) {
Map<Integer, ShardInstanceState> shardStates = this.findStatesByNode(shardSet, node, floc.getKey(), floc.getValue());
if (shardStates != null) {
this.logger.debug("Found {} shard states for node '{}' in set: {}", shardStates.size(), node, shardSet);
return shardStates;
}
}
this.logger.debug("Found {} shard states for node '{}' in set: {}", 0, node, shardSet);
return Collections.emptyMap();
}
private Map<Integer, ShardInstanceState> findStatesByNode(ShardSet shardSet, SolrHost node, Floc floc, Map<Shard, Set<ShardState>> shards) {
Map<Integer, ShardInstanceState> shardStates = new HashMap<>();
boolean checked = false;
for (Entry<Shard, Set<ShardState>> shard : shards.entrySet()) {
for (ShardState shardState : shard.getValue()) {
if (!checked && !shardSet.equals(ShardSet.from(floc, shardState)))
return null;
checked = true;
ShardInstance shardInstance = shardState.getShardInstance();
if (node.equals(SolrHost.from(shardInstance)))
shardStates.put(shard.getKey().getInstance(), ShardInstanceState.from(shardState));
}
}
return shardStates;
}
private ShardState extractAnyShardState(Map<Shard, Set<ShardState>> shards) {
if (shards.isEmpty())
return null;
for (Set<ShardState> shardStates : shards.values())
for (ShardState shardState : shardStates)
return shardState;
return null;
}
private InetAddress resolve(String hostname) {
try {
return InetAddress.getByName(hostname);
} catch (UnknownHostException uhe) {
return null;
}
}
}
@@ -34,7 +34,8 @@ public class ShardStateService implements com.inteligr8.alfresco.asie.spi.ShardS
@Autowired @Autowired
@Qualifier(Constants.BEAN_SHARD_GUID_CACHE) @Qualifier(Constants.BEAN_SHARD_GUID_CACHE)
private SimpleCache<ShardInstance, String> shardToGuidCache; private SimpleCache<ShardInstance, String> shardToGuidCache;
@Override
public void clear() { public void clear() {
this.logger.info("Removing all nodes/shards from the shard registry"); this.logger.info("Removing all nodes/shards from the shard registry");
@@ -76,7 +77,7 @@ public class ShardStateService implements com.inteligr8.alfresco.asie.spi.ShardS
this.shardToGuidCache.remove(shardState.getShardInstance()); this.shardToGuidCache.remove(shardState.getShardInstance());
} }
} }
public void iterate(AttributeQueryCallback callback) { public void iterate(AttributeQueryCallback callback) {
this.attrService.getAttributes(callback, EnterpriseConstants.ATTR_SHARD_STATE); this.attrService.getAttributes(callback, EnterpriseConstants.ATTR_SHARD_STATE);
} }
@@ -1,3 +1,3 @@
logger.inteligr8-asie.name=com.inteligr8.alfresco.asie.enterprise logger.inteligr8-asie-ent.name=com.inteligr8.alfresco.asie.enterprise
logger.inteligr8-asie.level=INFO logger.inteligr8-asie-ent.level=INFO
+49 -17
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.1-SNAPSHOT</version> <version>1.3.3</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<name>ASIE Platform Module Parent</name> <name>ASIE Platform Module Parent</name>
@@ -39,29 +39,62 @@
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source> <maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target> <maven.compiler.target>17</maven.compiler.target>
<maven.compiler.release>11</maven.compiler.release> <maven.compiler.release>17</maven.compiler.release>
<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>
<artifactId>maven-site-plugin</artifactId> <artifactId>maven-site-plugin</artifactId>
<version>3.12.1</version> <version>3.21.0</version>
</plugin> </plugin>
<!-- 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.0</version> <version>3.9.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.4</version>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.14.0</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.5.4</version>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.14.0</version>
</dependency>
</dependencies>
</plugin> </plugin>
</plugins> </plugins>
</pluginManagement> </pluginManagement>
@@ -72,12 +105,11 @@
<module>asie-api</module> <module>asie-api</module>
<module>shared</module> <module>shared</module>
<module>enterprise-module</module> <module>enterprise-module</module>
<module>community-module</module>
</modules> </modules>
<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>
@@ -117,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>
+8 -4
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.1-SNAPSHOT</version> <version>1.3.3</version>
<relativePath>../</relativePath> <relativePath>../</relativePath>
</parent> </parent>
@@ -16,7 +16,7 @@
<name>ASIE Shared Library for Platform Modules</name> <name>ASIE Shared Library for Platform Modules</name>
<properties> <properties>
<alfresco.sdk.version>5.2.0</alfresco.sdk.version> <alfresco.sdk.version>4.9.0</alfresco.sdk.version>
<alfresco.platform.version>23.3.0</alfresco.platform.version> <alfresco.platform.version>23.3.0</alfresco.platform.version>
</properties> </properties>
@@ -36,15 +36,19 @@
<dependency> <dependency>
<groupId>com.inteligr8.alfresco</groupId> <groupId>com.inteligr8.alfresco</groupId>
<artifactId>asie-api</artifactId> <artifactId>asie-api</artifactId>
<version>1.0-SNAPSHOT-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.1-cxf</version>
</dependency> </dependency>
<!-- Needed by this module, but provided by ACS --> <!-- Needed by this module, but provided by ACS -->
<dependency>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-data-model</artifactId>
<scope>provided</scope>
</dependency>
<dependency> <dependency>
<groupId>org.alfresco</groupId> <groupId>org.alfresco</groupId>
<artifactId>alfresco-repository</artifactId> <artifactId>alfresco-repository</artifactId>
+4 -7
View File
@@ -2,22 +2,19 @@ package com.inteligr8.alfresco.asie;
public interface Constants { public interface Constants {
static final String NAMESPACE_ASIE = "http://inteligr8.com/alfresco/asie";
static final String QUALIFIER_ASIE = "asie"; static final String QUALIFIER_ASIE = "asie";
// OOTB // defined OOTB
static final String BEAN_SHARD_STATE_CACHE = "shardStateCache"; static final String BEAN_SHARD_STATE_CACHE = "shardStateCache";
static final String BEAN_SHARD_GUID_CACHE = "shardToGuidCache"; static final String BEAN_SHARD_GUID_CACHE = "shardToGuidCache";
static final String BEAN_OFFILINE_SHARD_STATE_CACHE = "offlineShardStateCache"; static final String BEAN_SHARD_REGISTRY = "asie.ShardRegistry";
static final String BEAN_CORE_EXPLICIT_CACHE = "coreExplicitIdCache";
static final String BEAN_OBJECT_MAPPER = "asie.ObjectMapper"; static final String BEAN_OBJECT_MAPPER = "asie.ObjectMapper";
static final String BEAN_ATTRIBUTE_SERVICE = "asie.AttributeService"; static final String BEAN_ATTRIBUTE_SERVICE = "asie.AttributeService";
static final String BEAN_SHARD_REGISTRY = "asie.ShardRegistry";
static final String ATTR_ASIE = "inteligr8.asie"; static final String ATTR_ASIE = "inteligr8.asie";
static final String ATTR_ASIE_NODES = "inteligr8.asie.nodes";
static final String ATTR_STATE = "state";
static final String ATTR_ONLINE = "online";
static final String ATTR_UNLOADED = "unloadedNode.cores"; static final String ATTR_UNLOADED = "unloadedNode.cores";
} }
@@ -0,0 +1,48 @@
package com.inteligr8.alfresco.asie;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
public class SimpleCaster {
public static <T> T transform(String str, Class<T> returnType) {
if (str == null)
return null;
if (returnType.isAssignableFrom(String.class)) {
@SuppressWarnings("unchecked")
T t = (T) str;
return t;
}
try {
Constructor<T> constructor = returnType.getConstructor(String.class);
return constructor.newInstance(str);
} catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
// suppress
}
for (String staticMethod : Arrays.asList("from", "valueOf")) {
try {
return invoke(returnType, staticMethod, str);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
// suppress
}
}
throw new IllegalArgumentException();
}
private static <T> T invoke(Class<T> returnType, String staticMethodName, Object... arguments) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Method method = returnType.getDeclaredMethod(staticMethodName, String.class);
if (!returnType.isAssignableFrom(method.getReturnType()))
throw new NoSuchMethodException();
@SuppressWarnings("unchecked")
T t = (T) method.invoke(null, arguments);
return t;
}
}
@@ -1,56 +0,0 @@
package com.inteligr8.alfresco.asie.model;
import java.io.Serializable;
import org.alfresco.repo.index.shard.ShardInstance;
public class Node implements Serializable {
private static final long serialVersionUID = -8834744746109388928L;
private final String id;
private final ShardInstance shardNode;
public Node(ShardInstance shardNode) {
this.shardNode = shardNode;
this.id = this.getHostname() + ":" + this.getPort() + this.getPath();
}
public String getId() {
return this.id;
}
public String getHostname() {
return this.shardNode.getHostName();
}
public int getPort() {
return this.shardNode.getPort();
}
public String getPath() {
// baseUrl is to the shard; we want to the node, so exclude the core
int lastSlash = this.shardNode.getBaseUrl().lastIndexOf('/');
return this.shardNode.getBaseUrl().substring(0, lastSlash);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Node))
return false;
Node node = (Node) obj;
return this.id.equals(node.id);
}
@Override
public int hashCode() {
return this.id.hashCode();
}
@Override
public String toString() {
return this.id;
}
}
@@ -0,0 +1,36 @@
package com.inteligr8.alfresco.asie.model;
import java.io.Serializable;
public class PersistedNode implements Serializable {
private static final long serialVersionUID = 4105196543023419818L;
private final SolrHost node;
private final long persistMillis;
private long expireTimeMillis;
public PersistedNode(SolrHost node, int persistMinutes) {
this.node = node;
this.persistMillis = persistMinutes * 60L * 1000L;
this.reset();
}
public void reset() {
this.expireTimeMillis = System.currentTimeMillis() + this.persistMillis;
}
public boolean isExpired() {
return this.expireTimeMillis < System.currentTimeMillis();
}
public SolrHost getNode() {
return this.node;
}
@Override
public String toString() {
return "node: " + this.node + "; expires in: " + (System.currentTimeMillis() - this.expireTimeMillis) + " ms";
}
}
@@ -1,5 +0,0 @@
package com.inteligr8.alfresco.asie.model;
public interface RequestParameterSet {
}
@@ -0,0 +1,72 @@
package com.inteligr8.alfresco.asie.model;
import java.io.Serializable;
import org.alfresco.repo.index.shard.Floc;
public class Shard implements Serializable {
private static final long serialVersionUID = 5683743181748541736L;
public static Shard from(ShardSet shardSet, int shardId) {
return new Shard(shardSet, shardId);
}
public static Shard from(String spec) {
return new Shard(spec);
}
private final String spec;
protected Shard(ShardSet shardSet, int shardId) {
this.spec = shardSet.getCore() + "-" + shardId;
}
protected Shard(String spec) {
this.spec = spec;
}
public org.alfresco.repo.index.shard.Shard toAlfrescoModel(Floc floc) {
org.alfresco.repo.index.shard.Shard shard = new org.alfresco.repo.index.shard.Shard();
shard.setFloc(floc);
shard.setInstance(this.extractShardId());
return shard;
}
public String getSpec() {
return this.spec;
}
public String getCoreName() {
return this.spec;
}
public String extractShardSetCore() {
int pos = this.spec.lastIndexOf('-');
return this.spec.substring(0, pos);
}
public int extractShardId() {
int pos = this.spec.lastIndexOf('-');
return Integer.parseInt(this.spec.substring(pos+1));
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Shard))
return false;
Shard shard = (Shard) obj;
return this.spec.equals(shard.spec);
}
@Override
public int hashCode() {
return this.spec.hashCode();
}
@Override
public String toString() {
return this.spec;
}
}
@@ -0,0 +1,64 @@
package com.inteligr8.alfresco.asie.model;
import java.io.Serializable;
public class ShardInstance implements Serializable {
private static final long serialVersionUID = 7455521296197234581L;
public static ShardInstance from(Shard shard, SolrHost node) {
return new ShardInstance(shard, node);
}
private final String spec;
protected ShardInstance(Shard shard, SolrHost node) {
this.spec = node.getSpec() + "~" + shard.getSpec();
}
public org.alfresco.repo.index.shard.ShardInstance toAlfrescoModel(org.alfresco.repo.index.shard.Shard shard) {
SolrHost node = this.extractNode();
String core = shard.getFloc().getPropertyBag().get("coreName");
org.alfresco.repo.index.shard.ShardInstance shardInstance = new org.alfresco.repo.index.shard.ShardInstance();
shardInstance.setHostName(node.getHostname());
shardInstance.setPort(node.getPort());
shardInstance.setBaseUrl(node.getPath() + "/" + core + "-" + shard.getInstance());
shardInstance.setShard(shard);
return shardInstance;
}
public String getSpec() {
return spec;
}
public SolrHost extractNode() {
int pos = this.spec.indexOf('~');
return SolrHost.from(this.spec.substring(0, pos));
}
public Shard extractShard() {
int pos = this.spec.indexOf('~');
return Shard.from(this.spec.substring(pos+1));
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ShardInstance))
return false;
ShardInstance shard = (ShardInstance) obj;
return this.spec.equals(shard.spec);
}
@Override
public int hashCode() {
return this.spec.hashCode();
}
@Override
public String toString() {
return this.spec;
}
}
@@ -0,0 +1,102 @@
package com.inteligr8.alfresco.asie.model;
import java.io.Serializable;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.Map.Entry;
import org.alfresco.repo.index.shard.ShardInstance;
import org.alfresco.repo.index.shard.ShardState;
import org.apache.commons.lang3.builder.HashCodeBuilder;
public class ShardInstanceState implements Serializable, Comparable<ShardInstanceState> {
private static final long serialVersionUID = 2893797002689889478L;
public static ShardInstanceState from(ShardState shardState) {
return new ShardInstanceState(shardState);
}
private final OffsetDateTime lastUpdated;
private final long lastIndexedChangeSetId;
private final OffsetDateTime lastIndexedChangeSetTime;
private final long lastIndexedTxId;
private final OffsetDateTime lastIndexedTxTime;
private transient Integer hash = null;
private ShardInstanceState(ShardState shardState) {
this.lastUpdated = Instant.ofEpochMilli(shardState.getLastUpdated()).atOffset(ZoneOffset.UTC);
this.lastIndexedChangeSetId = shardState.getLastIndexedChangeSetId();
this.lastIndexedChangeSetTime = Instant.ofEpochMilli(shardState.getLastIndexedChangeSetCommitTime()).atOffset(ZoneOffset.UTC);
this.lastIndexedTxId = shardState.getLastIndexedTxId();
this.lastIndexedTxTime = Instant.ofEpochMilli(shardState.getLastIndexedTxCommitTime()).atOffset(ZoneOffset.UTC);
}
public ShardState toAlfrescoModel(ShardInstance shardInstance) {
ShardState state = new ShardState();
state.setLastIndexedChangeSetCommitTime(this.lastIndexedChangeSetTime.toInstant().toEpochMilli());
state.setLastIndexedChangeSetId(this.lastIndexedChangeSetId);
state.setLastIndexedTxCommitTime(this.lastIndexedTxTime.toInstant().toEpochMilli());
state.setLastIndexedTxId(this.lastIndexedTxId);
state.setLastUpdated(this.lastUpdated.toInstant().toEpochMilli());
state.setShardInstance(shardInstance);
for (Entry<String, String> prop : shardInstance.getShard().getFloc().getPropertyBag().entrySet())
if (prop.getKey().startsWith("shard."))
state.getPropertyBag().put(prop.getKey(), prop.getValue());
String core = shardInstance.getShard().getFloc().getPropertyBag().get("coreName");
if (core != null)
state.getPropertyBag().put("coreName", core + "-" + shardInstance.getShard().getInstance());
return state;
}
public OffsetDateTime getLastUpdated() {
return lastUpdated;
}
public long getLastIndexedChangeSetId() {
return lastIndexedChangeSetId;
}
public OffsetDateTime getLastIndexedChangeSetTime() {
return lastIndexedChangeSetTime;
}
public long getLastIndexedTxId() {
return lastIndexedTxId;
}
public OffsetDateTime getLastIndexedTxTime() {
return lastIndexedTxTime;
}
@Override
public int compareTo(ShardInstanceState o) {
return -this.lastUpdated.compareTo(o.lastUpdated);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ShardInstanceState))
return false;
ShardInstanceState snss = (ShardInstanceState) obj;
return this.lastIndexedChangeSetId == snss.lastIndexedChangeSetId &&
this.lastIndexedTxId == snss.lastIndexedTxId;
}
@Override
public int hashCode() {
if (this.hash == null) {
this.hash = new HashCodeBuilder()
.append(this.lastIndexedTxId)
.append(this.lastIndexedChangeSetId)
.build();
}
return this.hash.intValue();
}
}
@@ -1,53 +1,194 @@
package com.inteligr8.alfresco.asie.model; package com.inteligr8.alfresco.asie.model;
import java.util.HashMap; import java.io.Serializable;
import java.util.Map; import java.util.HashSet;
import java.util.Map.Entry; import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.alfresco.repo.index.shard.Floc; import org.alfresco.repo.index.shard.Floc;
import org.alfresco.repo.index.shard.ShardMethodEnum; import org.alfresco.repo.index.shard.ShardMethodEnum;
import org.alfresco.repo.index.shard.ShardState; import org.alfresco.repo.index.shard.ShardState;
import org.apache.commons.lang3.builder.HashCodeBuilder; import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.util.collections.CollectionUtils;
import org.apache.commons.collections4.map.CompositeMap;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
public class ShardSet { import com.inteligr8.alfresco.asie.SimpleCaster;
public class ShardSet implements Serializable {
/** private static final long serialVersionUID = -8891094367429601316L;
* Examples: private static final String DEFAULT_SOLR_TEMPLATE = "rerank";
*
* MOD_ACL_ID
* ACL_ID
* DB_ID
* DB_ID_RANGE;range:0-20000
* DATE;key:cm:created
* DATE;key:cm:created;date.grouping:3
* PROPERTY;key:cm:created;regex:^d{4}
*/
private final Pattern shardSetPattern = Pattern.compile("([A-Z]+)(;fulltext)?(;([a-z]+):([^;]+))?(;([a-z]+):([^;]+))?");
private final ShardMethodEnum method; public static ShardSet from(Floc floc, ShardState anyShardState) {
private final boolean hasContent; return new ShardSet(floc, anyShardState);
private final Map<String, String> config;
private Integer hash;
public ShardSet(Floc floc, ShardState anyShardNode) {
this.method = floc.getShardMethod();
this.hasContent = floc.hasContent();
this.config = (floc.getPropertyBag().isEmpty() && anyShardNode != null) ? anyShardNode.getPropertyBag() : floc.getPropertyBag();
} }
public ShardSet(String shardSetSpec) { public static ShardSet from(String coreName, String spec) {
Matcher matcher = this.shardSetPattern.matcher(shardSetSpec); return new ShardSet(coreName, spec);
if (!matcher.find()) }
throw new IllegalArgumentException("The shard set '" + shardSetSpec + "' is not properly formatted");
private final String core;
this.method = ShardMethodEnum.valueOf(matcher.group(1)); private final ShardMethodEnum method;
this.hasContent = ";fulltext".equals(matcher.group(2)); private final boolean hasContent;
this.config = new HashMap<>(); private final String template;
for (int g = 3; g < matcher.groupCount(); g += 3) private final Set<StoreRef> storeRefs;
if (matcher.group(g) != null) private final Short shards;
this.config.put("shard." + matcher.group(g+1), matcher.group(g+2)); private final Pair<Long, Long> range;
private final Byte dateGrouping;
private final String prefixedProperty;
private final Pattern regex;
private transient String spec;
private ShardSet(Floc floc, ShardState anyShardNode) {
String shardCoreName = anyShardNode.getPropertyBag().get("coreName");
int lastDash = shardCoreName.lastIndexOf('-');
this.core = shardCoreName.substring(0, lastDash);
this.method = floc.getShardMethod();
this.hasContent = floc.hasContent();
this.template = floc.getTemplate();
this.storeRefs = floc.getStoreRefs();
CompositeMap<String, String> propbag = new CompositeMap<>(floc.getPropertyBag(), anyShardNode.getPropertyBag());
Short shards = null;
Pair<Long, Long> range = null;
Byte dateGrouping = null;
String prefixedProperty = null;
String regex = null;
switch (this.method) {
case DB_ID_RANGE:
range = this.strToRange(propbag.get("shard.range"));
break;
case DATE:
dateGrouping = SimpleCaster.transform(propbag.get("shard.date.grouping"), Byte.class);
case PROPERTY:
case EXPLICIT_ID:
prefixedProperty = StringUtils.trimToNull(propbag.get("shard.key"));
regex = StringUtils.trimToNull(propbag.get("shard.regex"));
default:
shards = (short) floc.getNumberOfShards();
}
this.range = range;
this.shards = shards;
this.dateGrouping = dateGrouping;
this.prefixedProperty = prefixedProperty;
this.regex = regex == null ? null : Pattern.compile(regex);
}
public Floc toAlfrescoModel() {
Floc floc = new Floc();
floc.setShardMethod(this.method);
floc.setHasContent(this.hasContent);
floc.setTemplate(this.template);
floc.setStoreRefs(new HashSet<>(this.storeRefs));
floc.getPropertyBag().put("coreName", this.core);
switch (this.method) {
case DB_ID_RANGE:
floc.getPropertyBag().put("shard.range", this.range.getLeft() + "-" + this.range.getRight());
break;
case DATE:
if (this.dateGrouping != null)
floc.getPropertyBag().put("shard.date.grouping", this.dateGrouping.toString());
case PROPERTY:
case EXPLICIT_ID:
floc.getPropertyBag().put("shard.key", this.prefixedProperty);
if (this.regex != null)
floc.getPropertyBag().put("shard.regex", this.regex.pattern());
default:
if (this.shards != null)
floc.setNumberOfShards(this.shards.intValue());
}
return floc;
}
private ShardSet(String coreName, String shardSetSpec) {
String[] parts = shardSetSpec.split("[;|_]");
if (parts.length == 0)
throw new IllegalArgumentException();
this.core = coreName;
this.method = ShardMethodEnum.valueOf(parts[0].toUpperCase());
boolean hasContent = false;
String template = DEFAULT_SOLR_TEMPLATE;
StoreRef storeRef = StoreRef.STORE_REF_WORKSPACE_SPACESSTORE;
Pair<Long, Long> range = null;
Short shards = null;
Byte dateGrouping = null;
String prefixedProperty = null;
String regex = null;
for (int i = 1; i < parts.length; i++) {
int colon = parts[i].indexOf(":");
String fieldName = colon < 0 ? parts[i] : parts[i].substring(0, colon);
String fieldValue = colon < 0 ? null : parts[i].substring(colon+1);
switch (fieldName) {
case "txt":
case "text":
case "fulltext":
case "content":
case "hasContent":
case "hasText":
hasContent = true;
break;
case "t":
case "template":
template = fieldValue;
break;
case "sref":
case "storeRef":
storeRef = new StoreRef(fieldValue);
break;
case "s":
case "shards":
shards = SimpleCaster.transform(fieldValue, Short.class);
break;
case "range":
range = this.strToRange(fieldValue);
break;
case "date.grouping":
dateGrouping = SimpleCaster.transform(fieldValue, Byte.class);
break;
case "k":
case "key":
prefixedProperty = fieldValue;
break;
case "regex":
regex = fieldValue;
break;
default:
throw new IllegalArgumentException();
}
}
this.hasContent = hasContent;
this.template = template;
this.storeRefs = CollectionUtils.asSet(storeRef);
this.shards = shards;
this.range = range;
this.dateGrouping = dateGrouping;
this.prefixedProperty = prefixedProperty;
this.regex = regex == null ? null : Pattern.compile(regex);
}
private Pair<Long, Long> strToRange(String str) {
str = StringUtils.trimToNull(str);
if (str == null)
return null;
String[] rangeValues = str.split("-");
return Pair.of(Long.valueOf(rangeValues[0]), Long.valueOf(rangeValues[1]));
}
public String getCore() {
return core;
} }
public ShardMethodEnum getMethod() { public ShardMethodEnum getMethod() {
@@ -58,35 +199,70 @@ public class ShardSet {
return hasContent; return hasContent;
} }
public String getTemplate() {
return template;
}
public Set<StoreRef> getStoreRefs() {
return storeRefs;
}
public Short getShards() {
return shards;
}
public Pair<Long, Long> getRange() {
return range;
}
public Byte getDateGrouping() {
return dateGrouping;
}
public String getPrefixedProperty() {
return prefixedProperty;
}
public Pattern getRegex() {
return regex;
}
public String toSpec() { public String toSpec() {
StringBuilder spec = new StringBuilder(this.method.toString()); if (this.spec == null) {
if (this.hasContent) StringBuilder spec = new StringBuilder(this.method.toString());
spec.append(";fulltext"); if (this.hasContent)
for (Entry<String, String> c : this.config.entrySet()) { spec.append(";txt");
if (!c.getKey().startsWith("shard.")) spec.append(";t:").append(this.template);
continue; spec.append(";sref:").append(StringUtils.join(this.storeRefs, ','));
spec.append(';').append(c.getKey().substring(6)).append(':').append(c.getValue()); if (this.shards != null)
} spec.append(";s:").append(this.shards);
return spec.toString(); if (this.range != null)
} spec.append(";range:").append(this.range.getLeft()).append('-').append(this.range.getRight());
if (this.dateGrouping != null)
public Map<String, String> getConfig() { spec.append(";date.grouping:").append(this.dateGrouping);
return config; if (this.prefixedProperty != null)
} spec.append(";k:").append(this.prefixedProperty);
if (this.regex != null)
public boolean isFor(ShardState shardState) { spec.append(";regex:").append(this.regex);
return this.method.equals(shardState.getShardInstance().getShard().getFloc().getShardMethod()) && this.spec = spec.toString();
this.hasContent == shardState.getShardInstance().getShard().getFloc().hasContent() &&
this.isConfigurationFor(shardState.getPropertyBag());
}
public boolean isConfigurationFor(Map<String, String> propertyBag) {
for (Entry<String, String> config : this.config.entrySet()) {
if (config.getValue() == null || !config.getValue().equals(propertyBag.get(config.getKey())))
return false;
} }
return true; return spec;
}
public boolean contains(ShardState shardState) {
Floc floc = shardState.getShardInstance().getShard().getFloc();
CompositeMap<String, String> propbag = new CompositeMap<>(shardState.getPropertyBag(), floc.getPropertyBag());
return this.method.equals(floc.getShardMethod()) &&
this.hasContent == floc.hasContent() &&
StringUtils.equals(this.template, floc.getTemplate()) &&
this.storeRefs.equals(floc.getStoreRefs()) &&
this.equals(this.shards, floc.getNumberOfShards()) &&
this.equals(this.range, this.strToRange(propbag.get("shard.range"))) &&
this.equals(this.dateGrouping, propbag.get("shard.date.grouping")) &&
StringUtils.equals(this.prefixedProperty, propbag.get("shard.key")) &&
this.equals(this.regex, propbag.get("shard.regex"));
} }
@Override @Override
@@ -95,21 +271,34 @@ public class ShardSet {
return false; return false;
ShardSet shardSet = (ShardSet) obj; ShardSet shardSet = (ShardSet) obj;
return this.method.equals(shardSet.method) && this.config.equals(shardSet.config); return this.core.equals(shardSet.core);
}
private <T> boolean equals(Pattern p1, String s2) {
s2 = StringUtils.trimToNull(s2);
if (p1 == null) {
return s2 == null;
} else {
return p1.pattern().equals(s2);
}
}
private <T> boolean equals(T t1, T t2) {
if (t1 == null) {
return t2 == null;
} else {
return t1.equals(t2);
}
} }
@Override @Override
public int hashCode() { public int hashCode() {
if (this.hash == null) { return this.core.hashCode();
this.hash = new HashCodeBuilder().append(this.method).append(this.hasContent).append(this.config).build();
}
return this.hash;
} }
@Override @Override
public String toString() { public String toString() {
return this.toSpec(); return this.core;
} }
} }
@@ -0,0 +1,94 @@
package com.inteligr8.alfresco.asie.model;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.alfresco.repo.index.shard.ShardInstance;
public class SolrHost implements Serializable {
private static final long serialVersionUID = -8834744746109388928L;
private static final Pattern PATTERN = Pattern.compile("([^:]+):([0-9]+)([^0-9]?.*)");
public static SolrHost from(ShardInstance shardNode) {
return new SolrHost(shardNode);
}
public static SolrHost from(String spec) {
return new SolrHost(spec);
}
private final String spec;
private final String hostname;
private final int port;
private final String path;
protected SolrHost(ShardInstance shardNode) {
this.hostname = shardNode.getHostName();
this.port = shardNode.getPort();
// baseUrl is to the shard; we want to the node, so exclude the core
int lastSlash = shardNode.getBaseUrl().lastIndexOf('/');
this.path = shardNode.getBaseUrl().substring(0, lastSlash);
this.spec = this.hostname + ":" + this.port + this.path;
}
protected SolrHost(String spec) {
this.spec = spec;
Matcher matcher = PATTERN.matcher(spec);
if (!matcher.find())
throw new IllegalArgumentException();
this.hostname = matcher.group(1);
this.port = Integer.parseInt(matcher.group(2));
this.path = matcher.group(3);
}
public String getSpec() {
return this.spec;
}
public String getHostname() {
return this.hostname;
}
public int getPort() {
return this.port;
}
public String getPath() {
return this.path;
}
public URL toUrl(String protocol) {
try {
return new URL(protocol + "://" + this.hostname + ':' + this.port + this.path);
} catch (MalformedURLException mue) {
throw new IllegalArgumentException(mue.getMessage());
}
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof SolrHost))
return false;
SolrHost shardNode = (SolrHost) obj;
return this.spec.equals(shardNode.spec);
}
@Override
public int hashCode() {
return this.spec.hashCode();
}
@Override
public String toString() {
return this.spec;
}
}
@@ -0,0 +1,119 @@
package com.inteligr8.alfresco.asie.rest;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.extensions.webscripts.WebScriptException;
import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.extensions.webscripts.WebScriptResponse;
import org.springframework.http.HttpStatus;
import com.inteligr8.alfresco.asie.model.ShardInstance;
import com.inteligr8.alfresco.asie.spi.ActionCallback;
public abstract class AbstractAcsNodeActionWebScript extends AbstractAsieWebScript {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private NodeService nodeService;
@Override
public void executeAuthorized(WebScriptRequest request, WebScriptResponse response) throws IOException {
String nodeId = request.getServiceMatch().getTemplateVars().get("nodeId");
NodeRef nodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, nodeId);
long nodeDbId = this.findNodeDbId(nodeRef);
this.logger.trace("Found node database ID: {}: {}", nodeId, nodeDbId);
try {
Map<String, Object> responseMap = new HashMap<>();
responseMap.put("nodeDbId", nodeDbId);
ActionCallback callback = new ActionCallback() {
@Override
public void success(ShardInstance instance) {
@SuppressWarnings("unchecked")
List<String> instances = (List<String>) responseMap.get("success");
if (instances == null)
responseMap.put("success", instances = new LinkedList<>());
instances.add(instance.getSpec());
}
@Override
public void scheduled(ShardInstance instance) {
@SuppressWarnings("unchecked")
List<String> instances = (List<String>) responseMap.get("scheduled");
if (instances == null)
responseMap.put("scheduled", instances = new LinkedList<>());
instances.add(instance.getSpec());
}
@Override
public void error(ShardInstance instance, String message) {
@SuppressWarnings("unchecked")
Map<String, Object> instances = (Map<String, Object>) responseMap.get("error");
if (instances == null)
responseMap.put("error", instances = new HashMap<>());
instances.put(instance.getSpec(), Collections.singletonMap("message", message));
}
@Override
public void unknownResult(ShardInstance instance) {
@SuppressWarnings("unchecked")
List<String> instances = (List<String>) responseMap.get("unknown");
if (instances == null)
responseMap.put("unknown", instances = new LinkedList<>());
instances.add(instance.getSpec());
}
};
this.executeAction(nodeDbId, callback, 10L, TimeUnit.SECONDS, 30L, TimeUnit.SECONDS);
if (responseMap.containsKey("error")) {
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
} else if (responseMap.containsKey("scheduled")) {
response.setStatus(HttpStatus.ACCEPTED.value());
} else {
response.setStatus(HttpStatus.OK.value());
}
response.setContentType("application/json");
this.getObjectMapper().writeValue(response.getWriter(), responseMap);
} catch (UnsupportedOperationException uoe) {
throw new WebScriptException(HttpStatus.NOT_IMPLEMENTED.value(), uoe.getMessage(), uoe);
} catch (InterruptedException ie) {
throw new WebScriptException(HttpStatus.SERVICE_UNAVAILABLE.value(), "The execution was interrupted", ie);
} catch (TimeoutException te) {
throw new WebScriptException(HttpStatus.INTERNAL_SERVER_ERROR.value(), "The execution may continue, but timed-out waiting", te);
}
}
protected abstract void executeAction(
long nodeDbId, ActionCallback callback,
long fullQueueTimeout, TimeUnit fullQueueUnit,
long execTimeout, TimeUnit execUnit) throws TimeoutException, InterruptedException;
private long findNodeDbId(NodeRef nodeRef) {
try {
return (Long) this.nodeService.getProperty(nodeRef, ContentModel.PROP_NODE_DBID);
} catch (InvalidNodeRefException inre) {
throw new WebScriptException(HttpStatus.NOT_FOUND.value(), "The node does not exist");
}
}
}
@@ -0,0 +1,102 @@
package com.inteligr8.alfresco.asie.rest;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.extensions.webscripts.WebScriptException;
import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.extensions.webscripts.WebScriptResponse;
import org.springframework.http.HttpStatus;
import com.inteligr8.alfresco.asie.model.ShardInstance;
import com.inteligr8.alfresco.asie.spi.ActionCallback;
public abstract class AbstractActionWebScript extends AbstractAsieWebScript {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public void executeAuthorized(WebScriptRequest request, WebScriptResponse response) throws IOException {
try {
Map<String, Object> responseMap = new HashMap<>();
ActionCallback callback = new ActionCallback() {
@Override
public void success(ShardInstance instance) {
@SuppressWarnings("unchecked")
List<String> instances = (List<String>) responseMap.get("success");
if (instances == null)
responseMap.put("success", instances = new LinkedList<>());
instances.add(instance.getSpec());
}
@Override
public void scheduled(ShardInstance instance) {
@SuppressWarnings("unchecked")
List<String> instances = (List<String>) responseMap.get("scheduled");
if (instances == null)
responseMap.put("scheduled", instances = new LinkedList<>());
instances.add(instance.getSpec());
}
@Override
public void error(ShardInstance instance, String message) {
@SuppressWarnings("unchecked")
Map<String, Object> instances = (Map<String, Object>) responseMap.get("error");
if (instances == null)
responseMap.put("error", instances = new HashMap<>());
instances.put(instance.getSpec(), Collections.singletonMap("message", message));
}
@Override
public void unknownResult(ShardInstance instance) {
@SuppressWarnings("unchecked")
List<String> instances = (List<String>) responseMap.get("unknown");
if (instances == null)
responseMap.put("unknown", instances = new LinkedList<>());
instances.add(instance.getSpec());
}
};
this.executeAction(callback, 10L, TimeUnit.SECONDS, 30L, TimeUnit.SECONDS);
if (responseMap.containsKey("error")) {
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
} else if (responseMap.containsKey("scheduled")) {
response.setStatus(HttpStatus.ACCEPTED.value());
} else {
response.setStatus(HttpStatus.OK.value());
}
response.setContentType("application/json");
this.getObjectMapper().writeValue(response.getWriter(), responseMap);
} catch (UnsupportedOperationException uoe) {
throw new WebScriptException(HttpStatus.NOT_IMPLEMENTED.value(), uoe.getMessage(), uoe);
} catch (InterruptedException ie) {
throw new WebScriptException(HttpStatus.SERVICE_UNAVAILABLE.value(), "The execution was interrupted", ie);
} catch (TimeoutException te) {
throw new WebScriptException(HttpStatus.INTERNAL_SERVER_ERROR.value(), "The execution may continue, but timed-out waiting", te);
}
}
protected abstract void executeAction(
ActionCallback callback,
long fullQueueTimeout, TimeUnit fullQueueUnit,
long execTimeout, TimeUnit execUnit) throws TimeoutException, InterruptedException;
}
@@ -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());
@@ -20,16 +18,16 @@ public abstract class AbstractAsieNodeShardWebScript extends AbstractAsieShardab
String nodeEndpoint = this.getRequiredPathParameter(req, "nodeEndpoint"); String nodeEndpoint = this.getRequiredPathParameter(req, "nodeEndpoint");
int colon = nodeEndpoint.lastIndexOf(':'); int colon = nodeEndpoint.lastIndexOf(':');
String nodeHostname = colon < 0 ? nodeEndpoint : nodeEndpoint.substring(0, colon); String nodeHostname = colon < 0 ? nodeEndpoint : nodeEndpoint.substring(0, colon);
int nodePort = colon < 0 ? this.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;
} }
@@ -1,9 +1,8 @@
package com.inteligr8.alfresco.asie.rest; package com.inteligr8.alfresco.asie.rest;
import java.io.IOException; import java.io.IOException;
import java.util.Set; import java.util.Map;
import org.alfresco.repo.index.shard.ShardState;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -12,7 +11,10 @@ import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.extensions.webscripts.WebScriptResponse; import org.springframework.extensions.webscripts.WebScriptResponse;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import com.inteligr8.alfresco.asie.service.ShardDiscoveryService; import com.inteligr8.alfresco.asie.model.ShardSet;
import com.inteligr8.alfresco.asie.model.SolrHost;
import com.inteligr8.alfresco.asie.model.ShardInstanceState;
import com.inteligr8.alfresco.asie.spi.ShardDiscoveryService;
public abstract class AbstractAsieNodeWebScript extends AbstractAsieShardableWebScript { public abstract class AbstractAsieNodeWebScript extends AbstractAsieShardableWebScript {
@@ -29,23 +31,24 @@ public abstract class AbstractAsieNodeWebScript extends AbstractAsieShardableWeb
int colon = nodeEndpoint.lastIndexOf(':'); int colon = nodeEndpoint.lastIndexOf(':');
String nodeHostname = colon < 0 ? nodeEndpoint : nodeEndpoint.substring(0, colon); String nodeHostname = colon < 0 ? nodeEndpoint : nodeEndpoint.substring(0, colon);
nodeHostname = nodeHostname.replace('_', '.'); nodeHostname = nodeHostname.replace('_', '.');
int nodePort = colon < 0 ? this.getDefaultSolrPort() : Integer.parseInt(nodeEndpoint.substring(colon+1)); int nodePort = colon < 0 ? this.getApiService().getDefaultSolrPort() : Integer.parseInt(nodeEndpoint.substring(colon+1));
this.execute(req, res, nodeHostname, nodePort); this.execute(req, res, nodeHostname, nodePort);
} }
protected void execute(WebScriptRequest req, WebScriptResponse res, String nodeHostname, int nodePort) throws IOException { protected void execute(WebScriptRequest req, WebScriptResponse res, String nodeHostname, int nodePort) throws IOException {
this.logger.trace("execute({}, {})", nodeHostname, nodePort); this.logger.trace("execute({}, {})", nodeHostname, nodePort);
Set<ShardState> shardsOnNode = this.sds.findByNode(nodeHostname, nodePort); SolrHost node = this.sds.findNode(nodeHostname, nodePort);
Map<ShardSet, Map<Integer, ShardInstanceState>> shardsOnNode = this.sds.findByNode(node);
if (shardsOnNode == null || shardsOnNode.isEmpty()) if (shardsOnNode == null || shardsOnNode.isEmpty())
throw new WebScriptException(HttpStatus.NOT_FOUND.value(), "The ASIE node could not be found"); throw new WebScriptException(HttpStatus.NOT_FOUND.value(), "The ASIE node could not be found");
this.execute(req, res, shardsOnNode); this.execute(req, res, node, shardsOnNode);
} }
protected void execute(WebScriptRequest req, WebScriptResponse res, Set<ShardState> registeredNodeShards) throws IOException { protected void execute(WebScriptRequest req, WebScriptResponse res, SolrHost node, Map<ShardSet, Map<Integer, ShardInstanceState>> shardSetShardStates) throws IOException {
this.logger.trace("execute({})", registeredNodeShards.size()); this.logger.trace("execute({})", shardSetShardStates.size());
// made to be optionally overridden // made to be optionally overridden
} }
@@ -1,9 +1,9 @@
package com.inteligr8.alfresco.asie.rest; package com.inteligr8.alfresco.asie.rest;
import java.io.IOException; import java.io.IOException;
import java.util.Set; import java.util.List;
import org.alfresco.repo.index.shard.ShardState; import org.alfresco.util.Pair;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -13,7 +13,9 @@ import org.springframework.extensions.webscripts.WebScriptResponse;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import com.inteligr8.alfresco.asie.model.ShardSet; import com.inteligr8.alfresco.asie.model.ShardSet;
import com.inteligr8.alfresco.asie.service.ShardDiscoveryService; import com.inteligr8.alfresco.asie.model.SolrHost;
import com.inteligr8.alfresco.asie.model.ShardInstanceState;
import com.inteligr8.alfresco.asie.spi.ShardDiscoveryService;
public abstract class AbstractAsieShardWebScript extends AbstractAsieShardableWebScript { public abstract class AbstractAsieShardWebScript extends AbstractAsieShardableWebScript {
@@ -26,21 +28,24 @@ public abstract class AbstractAsieShardWebScript extends AbstractAsieShardableWe
public void executeAuthorized(WebScriptRequest req, WebScriptResponse res) throws IOException { public void executeAuthorized(WebScriptRequest req, WebScriptResponse res) throws IOException {
this.logger.trace("execute()"); this.logger.trace("execute()");
ShardSet shardSet = this.getRequiredPathParameter(req, "shardSet", ShardSet.class); String coreName = this.getRequiredPathParameter(req, "shardCore");
this.logger.debug("Parsed shard set: {}", shardSet);
int shardId = this.getRequiredPathParameter(req, "shardId", Integer.class); int shardId = this.getRequiredPathParameter(req, "shardId", Integer.class);
try { try {
Set<ShardState> registeredShardNodes = this.sds.findByShard(shardSet, shardId); ShardSet shardSet = this.sds.findSetByCore(coreName);
if (registeredShardNodes == null || registeredShardNodes.isEmpty()) if (shardSet == null)
throw new WebScriptException(HttpStatus.NOT_FOUND.value(), "The ASIE shard set or shard could not be found"); throw new WebScriptException(HttpStatus.NOT_FOUND.value(), "The ASIE shard set could not be found");
this.execute(req, res, registeredShardNodes); List<Pair<SolrHost, ShardInstanceState>> nodeShardStates = this.sds.findNodeStatesByShard(shardSet, shardId);
if (nodeShardStates == null || nodeShardStates.isEmpty())
throw new WebScriptException(HttpStatus.NOT_FOUND.value(), "The ASIE shard could not be found");
this.execute(req, res, shardId, shardSet, nodeShardStates);
} catch (IllegalArgumentException iae) { } catch (IllegalArgumentException iae) {
throw new WebScriptException(HttpStatus.BAD_REQUEST.value(), iae.getMessage()); throw new WebScriptException(HttpStatus.BAD_REQUEST.value(), iae.getMessage());
} }
} }
protected abstract void execute(WebScriptRequest req, WebScriptResponse res, Set<ShardState> registeredShardNodes) throws IOException; protected abstract void execute(WebScriptRequest req, WebScriptResponse res, int shardId, ShardSet shardSet, List<Pair<SolrHost, ShardInstanceState>> nodeShardStates) throws IOException;
} }
@@ -57,7 +57,7 @@ public abstract class AbstractAsieShardableWebScript extends AbstractAsieWebScri
try { try {
SolrShardHashSampleType type = SolrShardHashSampleType.valueOf(matcher.group(1)); SolrShardHashSampleType type = SolrShardHashSampleType.valueOf(matcher.group(1));
int shards = Integer.parseInt(matcher.group(2)); short shards = Short.parseShort(matcher.group(2));
return this.createSampleHashTable(type, shards); return this.createSampleHashTable(type, shards);
} catch (NumberFormatException nfe) { } catch (NumberFormatException nfe) {
// this should never happen, because of the regex // this should never happen, because of the regex
@@ -67,7 +67,7 @@ public abstract class AbstractAsieShardableWebScript extends AbstractAsieWebScri
} }
} }
protected SolrShardHashTable<?> createSampleHashTable(SolrShardHashSampleType sampleType, int shards) { protected SolrShardHashTable<?> createSampleHashTable(SolrShardHashSampleType sampleType, short shards) {
int thisYear = Year.now().getValue(); int thisYear = Year.now().getValue();
switch (sampleType) { switch (sampleType) {
@@ -122,7 +122,7 @@ public abstract class AbstractAsieShardableWebScript extends AbstractAsieWebScri
} }
protected CoreAdminApi getApi(ShardInstance shard) { protected CoreAdminApi getApi(ShardInstance shard) {
return this.createApi(shard.getHostName(), shard.getPort()); return this.getApiService().createApi(shard.getHostName(), shard.getPort(), CoreAdminApi.class);
} }
} }
@@ -1,140 +1,34 @@
package com.inteligr8.alfresco.asie.rest; package com.inteligr8.alfresco.asie.rest;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.extensions.webscripts.WebScriptRequest; import org.springframework.extensions.webscripts.WebScriptRequest;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.inteligr8.alfresco.asie.Constants; import com.inteligr8.alfresco.asie.Constants;
import com.inteligr8.alfresco.asie.api.CoreAdminApi; import com.inteligr8.alfresco.asie.service.ApiService;
import com.inteligr8.rs.AuthorizationFilter;
import com.inteligr8.rs.Client;
import com.inteligr8.rs.ClientCxfConfiguration;
import com.inteligr8.rs.ClientCxfImpl;
import jakarta.ws.rs.client.ClientRequestContext; public abstract class AbstractAsieWebScript extends AbstractWebScript {
public abstract class AbstractAsieWebScript extends AbstractWebScript implements InitializingBean {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Value("${solr.secureComms}")
private String solrSecureComms;
@Value("${solr.port}")
private int solrPort;
@Value("${solr.port.ssl}")
private int solrSslPort;
@Value("${solr.sharedSecret.header}")
private String solrSharedSecretHeader;
@Value("${solr.sharedSecret}")
private String solrSharedSecret;
@Value("${inteligr8.asie.allowedAuthorities}")
private String authorizedAuthoritiesStr;
@Value("${inteligr8.asie.basePath}")
private String solrBaseUrl;
@Autowired @Autowired
@Qualifier(Constants.QUALIFIER_ASIE) @Qualifier(Constants.QUALIFIER_ASIE)
private ObjectMapper objectMapper; private ObjectMapper objectMapper;
private Set<String> authorizedAuthorities; @Autowired
private ApiService api;
@Override
public void afterPropertiesSet() throws Exception {
this.authorizedAuthorities = new HashSet<>();
String[] authorities = this.authorizedAuthoritiesStr.split(",");
for (String authority : authorities) {
authority = StringUtils.trimToNull(authority);
if (authority != null)
this.authorizedAuthorities.add(authority);
}
if (this.authorizedAuthorities.isEmpty())
this.logger.warn("All authenticated users will be authorized to access ASIE web scripts");
this.solrSharedSecret = StringUtils.trimToNull(this.solrSharedSecret);
}
@Override
protected Set<String> getAuthorities() {
return this.authorizedAuthorities;
}
protected ObjectMapper getObjectMapper() { protected ObjectMapper getObjectMapper() {
return this.objectMapper; return this.objectMapper;
} }
protected CoreAdminApi createApi(String hostname, int port) { protected ApiService getApiService() {
String solrBaseUrl = this.formulateSolrBaseUrl(hostname, port); return this.api;
this.logger.trace("Using Solr base URL: {}", solrBaseUrl);
Client solrClient = this.createClient(solrBaseUrl);
return this.getApi(solrClient);
}
protected CoreAdminApi getApi(Client solrClient) {
return solrClient.getApi(CoreAdminApi.class);
}
protected int getDefaultSolrPort() {
boolean isSsl = "https".equals(this.solrSecureComms);
return isSsl ? this.solrSslPort : this.solrPort;
} }
protected String formulateSolrBaseUrl(WebScriptRequest req) { protected String formulateSolrBaseUrl(WebScriptRequest req) {
String hostname = this.getRequiredPathParameter(req, "hostname"); String hostname = this.getRequiredPathParameter(req, "hostname");
Integer port = this.getOptionalPathParameter(req, "port", Integer.class); Integer port = this.getOptionalPathParameter(req, "port", Integer.class);
return this.formulateSolrBaseUrl(hostname, port); return this.api.formulateSolrBaseUrl(hostname, port);
}
protected String formulateSolrBaseUrl(String hostname, Integer port) {
boolean isSsl = "https".equals(this.solrSecureComms);
StringBuilder baseUrl = new StringBuilder(isSsl ? "https" : "http").append("://").append(hostname);
baseUrl.append(':').append(port == null ? (isSsl ? this.solrSslPort : this.solrPort) : port);
baseUrl.append(this.solrBaseUrl);
return baseUrl.toString();
}
protected Client createClient(final String baseUrl) {
ClientCxfImpl client = new ClientCxfImpl(new ClientCxfConfiguration() {
@Override
public String getBaseUrl() {
return baseUrl.toString();
}
@Override
public AuthorizationFilter createAuthorizationFilter() {
return solrSharedSecret == null ? null : new AuthorizationFilter() {
@Override
public void filter(ClientRequestContext requestContext) throws IOException {
logger.debug("Adding authorization headers for ASIE shared auth: {}", solrSharedSecretHeader);
requestContext.getHeaders().putSingle(solrSharedSecretHeader, solrSharedSecret);
}
};
}
@Override
public boolean isDefaultBusEnabled() {
return false;
}
});
client.register();
return client;
} }
} }
@@ -4,11 +4,19 @@ import java.io.IOException;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set; import java.util.Set;
import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.service.cmr.security.AuthorityService;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.extensions.webscripts.Description.RequiredAuthentication; import org.springframework.extensions.webscripts.Description.RequiredAuthentication;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.extensions.webscripts.WebScriptException; import org.springframework.extensions.webscripts.WebScriptException;
import org.springframework.extensions.webscripts.WebScriptRequest; import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.extensions.webscripts.WebScriptResponse; import org.springframework.extensions.webscripts.WebScriptResponse;
@@ -16,9 +24,38 @@ import org.springframework.http.HttpStatus;
import net.sf.acegisecurity.GrantedAuthority; import net.sf.acegisecurity.GrantedAuthority;
public abstract class AbstractWebScript extends org.springframework.extensions.webscripts.AbstractWebScript { public abstract class AbstractWebScript extends org.springframework.extensions.webscripts.AbstractWebScript implements InitializingBean {
protected abstract Set<String> getAuthorities(); private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Value("${inteligr8.asie.allowedAuthorities}")
private String authorizedAuthoritiesStr;
@Autowired
private AuthorityService authorityService;
private Set<String> authorizedAuthorities;
@Override
public void afterPropertiesSet() throws Exception {
this.authorizedAuthorities = new HashSet<>();
String[] authorities = this.authorizedAuthoritiesStr.split(",");
for (String authority : authorities) {
authority = StringUtils.trimToNull(authority);
if (authority != null)
this.authorizedAuthorities.add(authority);
}
if (this.authorizedAuthorities.isEmpty()) {
this.logger.warn("All authenticated users will be authorized to access web scripts");
} else {
this.logger.debug("Allowing only authorities: {}", this.authorizedAuthorities);
}
}
protected Set<String> getAuthorities() {
return this.authorizedAuthorities;
}
@Override @Override
public final void execute(WebScriptRequest request, WebScriptResponse response) throws IOException { public final void execute(WebScriptRequest request, WebScriptResponse response) throws IOException {
@@ -38,6 +75,13 @@ public abstract class AbstractWebScript extends org.springframework.extensions.w
return true; return true;
} }
Set<String> authorities = this.authorityService.getAuthoritiesForUser(AuthenticationUtil.getFullyAuthenticatedUser());
if (authorities != null) {
if (!Collections.disjoint(this.getAuthorities(), authorities))
return true;
}
this.logger.trace("Not authorized: user '{}'; authorities: {} + {}", AuthenticationUtil.getFullyAuthenticatedUser(), AuthenticationUtil.getFullAuthentication().getAuthorities(), authorities);
return false; return false;
} }
@@ -3,7 +3,6 @@ package com.inteligr8.alfresco.asie.rest;
import java.io.IOException; import java.io.IOException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.extensions.webscripts.AbstractWebScript;
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 org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
@@ -20,13 +19,13 @@ public class ClearRegistryWebScript extends AbstractWebScript {
@Autowired @Autowired
private ShardStateService sss; private ShardStateService sss;
@Override @Override
public void execute(WebScriptRequest req, WebScriptResponse res) throws IOException { public void executeAuthorized(WebScriptRequest request, WebScriptResponse response) throws IOException {
this.sss.clear(); this.sss.clear();
this.sbs.forget(); this.sbs.forget();
res.setStatus(HttpStatus.OK.value()); response.setStatus(HttpStatus.OK.value());
} }
} }
@@ -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.FixService;
import com.inteligr8.alfresco.asie.spi.ActionCallback;
@Component(value = "webscript.com.inteligr8.alfresco.asie.fix.post")
public class FixWebScript extends AbstractActionWebScript {
@Autowired
private FixService fixSerivce;
@Override
protected void executeAction(ActionCallback callback, long fullQueueTimeout, TimeUnit fullQueueUnit,
long execTimeout, TimeUnit execUnit) throws TimeoutException, InterruptedException {
this.fixSerivce.fix(callback, 10L, TimeUnit.SECONDS, 30L, TimeUnit.SECONDS);
}
}
@@ -1,19 +1,21 @@
package com.inteligr8.alfresco.asie.rest; package com.inteligr8.alfresco.asie.rest;
import java.io.IOException; import java.io.IOException;
import java.util.Set; import java.util.Collections;
import java.util.List;
import org.alfresco.repo.index.shard.ShardState; import org.alfresco.util.Pair;
import org.springframework.beans.factory.annotation.Autowired; 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.extensions.webscripts.WebScriptResponse; import org.springframework.extensions.webscripts.WebScriptResponse;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.model.Node; import com.inteligr8.alfresco.asie.model.ShardSet;
import com.inteligr8.alfresco.asie.model.SolrHost;
import com.inteligr8.alfresco.asie.model.ShardInstanceState;
import com.inteligr8.alfresco.asie.service.ShardBackupService; import com.inteligr8.alfresco.asie.service.ShardBackupService;
import com.inteligr8.alfresco.asie.spi.ShardDiscoveryService;
@Component(value = "webscript.com.inteligr8.alfresco.asie.backupNode.get") @Component(value = "webscript.com.inteligr8.alfresco.asie.backupNode.get")
public class GetBackupNodeWebScript extends AbstractAsieShardWebScript { public class GetBackupNodeWebScript extends AbstractAsieShardWebScript {
@@ -22,15 +24,15 @@ public class GetBackupNodeWebScript extends AbstractAsieShardWebScript {
private ShardBackupService sbs; private ShardBackupService sbs;
@Override @Override
public void execute(WebScriptRequest req, WebScriptResponse res, Set<ShardState> shardNodes) throws IOException { public void execute(WebScriptRequest req, WebScriptResponse res, int shardId, ShardSet shardSet, List<Pair<SolrHost, ShardInstanceState>> nodeShardStates) throws IOException {
if (shardNodes.isEmpty()) Collections.sort(nodeShardStates, new ShardDiscoveryService.ShardedNodeShardStateComparator());
throw new WebScriptException(HttpStatus.NOT_FOUND.value(), "The ASIE shard state could not be found"); SolrHost mostRecentNode = nodeShardStates.iterator().next().getFirst();
Node node = this.sbs.fetchNode(shardNodes); SolrHost backupNode = this.sbs.selectNode(shardSet, shardId, mostRecentNode);
res.setContentType(MediaType.APPLICATION_JSON_VALUE); res.setContentType(MediaType.APPLICATION_JSON_VALUE);
res.setContentEncoding("utf-8"); res.setContentEncoding("utf-8");
this.getObjectMapper().writeValue(res.getWriter(), node.getId()); this.getObjectMapper().writeValue(res.getWriter(), backupNode.getSpec());
} }
} }
@@ -1,10 +1,9 @@
package com.inteligr8.alfresco.asie.rest; package com.inteligr8.alfresco.asie.rest;
import java.io.IOException; import java.io.IOException;
import java.util.Set; import java.util.List;
import org.alfresco.repo.index.shard.ShardInstance; import org.alfresco.util.Pair;
import org.alfresco.repo.index.shard.ShardState;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.extensions.webscripts.WebScriptException; import org.springframework.extensions.webscripts.WebScriptException;
import org.springframework.extensions.webscripts.WebScriptRequest; import org.springframework.extensions.webscripts.WebScriptRequest;
@@ -13,7 +12,9 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.model.Node; import com.inteligr8.alfresco.asie.model.ShardSet;
import com.inteligr8.alfresco.asie.model.SolrHost;
import com.inteligr8.alfresco.asie.model.ShardInstanceState;
import com.inteligr8.alfresco.asie.spi.ShardDiscoveryService; import com.inteligr8.alfresco.asie.spi.ShardDiscoveryService;
@Component(value = "webscript.com.inteligr8.alfresco.asie.leadNode.get") @Component(value = "webscript.com.inteligr8.alfresco.asie.leadNode.get")
@@ -23,17 +24,14 @@ public class GetLeadNodeWebScript extends AbstractAsieShardWebScript {
private ShardDiscoveryService sds; private ShardDiscoveryService sds;
@Override @Override
public void execute(WebScriptRequest req, WebScriptResponse res, Set<ShardState> shardNodesCache) throws IOException { public void execute(WebScriptRequest req, WebScriptResponse res, int shardId, ShardSet shardSet, List<Pair<SolrHost, ShardInstanceState>> nodeShardStates) throws IOException {
if (shardNodesCache.isEmpty()) SolrHost latestNode = this.sds.computeLeadNode(nodeShardStates);
throw new WebScriptException(HttpStatus.NOT_FOUND.value(), "The ASIE shard state could not be found");
ShardInstance latestNode = this.sds.computeLeadShard(shardNodesCache);
if (latestNode == null) if (latestNode == null)
throw new WebScriptException(HttpStatus.NOT_FOUND.value(), "The ASIE shard state could not be found"); throw new WebScriptException(HttpStatus.NOT_FOUND.value(), "The ASIE node could not be found");
res.setContentType(MediaType.APPLICATION_JSON_VALUE); res.setContentType(MediaType.APPLICATION_JSON_VALUE);
res.setContentEncoding("utf-8"); res.setContentEncoding("utf-8");
this.getObjectMapper().writeValue(res.getWriter(), new Node(latestNode).getId()); this.getObjectMapper().writeValue(res.getWriter(), latestNode.getSpec());
} }
} }
@@ -1,18 +1,17 @@
package com.inteligr8.alfresco.asie.rest; package com.inteligr8.alfresco.asie.rest;
import java.io.IOException; import java.io.IOException;
import java.time.Instant; import java.util.Map;
import java.time.OffsetDateTime; import java.util.Map.Entry;
import java.time.ZoneOffset;
import java.util.Set;
import org.alfresco.repo.index.shard.ShardInstance;
import org.alfresco.repo.index.shard.ShardState;
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 org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.compute.SolrShardHashTable; import com.inteligr8.alfresco.asie.compute.SolrShardHashTable;
import com.inteligr8.alfresco.asie.model.ShardSet;
import com.inteligr8.alfresco.asie.model.SolrHost;
import com.inteligr8.alfresco.asie.model.ShardInstanceState;
import com.inteligr8.alfresco.asie.rest.model.NodeInfo; import com.inteligr8.alfresco.asie.rest.model.NodeInfo;
import com.inteligr8.alfresco.asie.rest.model.NodeShardInfo; import com.inteligr8.alfresco.asie.rest.model.NodeShardInfo;
import com.inteligr8.alfresco.asie.rest.model.ShardInfo; import com.inteligr8.alfresco.asie.rest.model.ShardInfo;
@@ -22,32 +21,32 @@ import com.inteligr8.alfresco.asie.rest.model.ShardSetInfo;
public class GetNodeWebScript extends AbstractAsieNodeWebScript { public class GetNodeWebScript extends AbstractAsieNodeWebScript {
@Override @Override
protected void execute(WebScriptRequest req, WebScriptResponse res, Set<ShardState> registeredNodeShards) throws IOException { protected void execute(WebScriptRequest req, WebScriptResponse res, SolrHost node, Map<ShardSet, Map<Integer, ShardInstanceState>> shardSetShardStates) throws IOException {
ShardState anyRegisteredNodeShard = registeredNodeShards.iterator().next();
ShardInstance registeredNode = anyRegisteredNodeShard.getShardInstance();
int maxShards = registeredNode.getShard().getFloc().getNumberOfShards();
SolrShardHashSampleType sampleHashType = this.getOptionalQueryParameter(req, "sampleHashType", SolrShardHashSampleType.class); SolrShardHashSampleType sampleHashType = this.getOptionalQueryParameter(req, "sampleHashType", SolrShardHashSampleType.class);
SolrShardHashTable<?> sampleHashTable = sampleHashType == null ? null : this.createSampleHashTable(sampleHashType, maxShards);
NodeInfo node = new NodeShardInfo(registeredNode); NodeInfo nodeResponse = NodeShardInfo.from(node);
for (ShardState registeredNodeShard : registeredNodeShards) { for (Entry<ShardSet, Map<Integer, ShardInstanceState>> shardSet : shardSetShardStates.entrySet()) {
ShardInfo shard = new ShardInfo(); Short maxShards = shardSet.getKey().getShards();
shard.setId(registeredNodeShard.getShardInstance().getShard().getInstance()); SolrShardHashTable<?> sampleHashTable = null;
shard.setLatestTx(OffsetDateTime.ofInstant(Instant.ofEpochMilli(registeredNodeShard.getLastIndexedTxCommitTime()), ZoneOffset.UTC)); if (sampleHashType != null && maxShards != null)
shard.setTxsCompleted(registeredNodeShard.getLastIndexedTxId()); sampleHashTable = this.createSampleHashTable(sampleHashType, maxShards);
shard.setShardSet(new ShardSetInfo(registeredNodeShard.getShardInstance().getShard().getFloc(), registeredNodeShard)); ShardSetInfo shardSetResponse = ShardSetInfo.from(shardSet.getKey());
if (sampleHashTable != null)
this.addShardHashSamples(shard, sampleHashTable); for (Entry<Integer, ShardInstanceState> shard : shardSet.getValue().entrySet()) {
ShardInfo shardResponse = ShardInfo.from(shard.getKey(), shard.getValue());
node.getShards().put(shard.getId(), shard); shardResponse.setShardSet(shardSetResponse);
} if (sampleHashTable != null)
this.addShardHashSamples(shardResponse, sampleHashTable);
nodeResponse.getShards().put(shardResponse.getId(), shardResponse);
}
}
res.setContentType("application/json"); res.setContentType("application/json");
res.setContentEncoding("utf-8"); res.setContentEncoding("utf-8");
this.getObjectMapper().writeValue(res.getWriter(), node); this.getObjectMapper().writeValue(res.getWriter(), nodeResponse);
} }
} }
@@ -8,6 +8,7 @@ import java.util.TreeMap;
import org.alfresco.repo.index.shard.Floc; import org.alfresco.repo.index.shard.Floc;
import org.alfresco.repo.index.shard.Shard; import org.alfresco.repo.index.shard.Shard;
import org.alfresco.repo.index.shard.ShardMethodEnum;
import org.alfresco.repo.index.shard.ShardState; import org.alfresco.repo.index.shard.ShardState;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -18,7 +19,9 @@ import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.compute.SolrShardHashTable; import com.inteligr8.alfresco.asie.compute.SolrShardHashTable;
import com.inteligr8.alfresco.asie.model.Node; import com.inteligr8.alfresco.asie.model.ShardSet;
import com.inteligr8.alfresco.asie.model.SolrHost;
import com.inteligr8.alfresco.asie.model.ShardInstanceState;
import com.inteligr8.alfresco.asie.rest.model.NodeInfo; import com.inteligr8.alfresco.asie.rest.model.NodeInfo;
import com.inteligr8.alfresco.asie.rest.model.NodeShardInfo; import com.inteligr8.alfresco.asie.rest.model.NodeShardInfo;
import com.inteligr8.alfresco.asie.rest.model.ShardInfo; import com.inteligr8.alfresco.asie.rest.model.ShardInfo;
@@ -39,34 +42,44 @@ public class GetNodesWebScript extends AbstractAsieShardableWebScript {
SolrShardHashSampleType sampleHashType = this.getOptionalQueryParameter(req, "sampleHashType", SolrShardHashSampleType.class); SolrShardHashSampleType sampleHashType = this.getOptionalQueryParameter(req, "sampleHashType", SolrShardHashSampleType.class);
Map<String, NodeInfo> nodes = new TreeMap<>(); Map<String, NodeInfo> nodesResponse = new TreeMap<>();
for (Entry<Floc, Map<Shard, Set<ShardState>>> floc : flocs.entrySet()) { for (Entry<Floc, Map<Shard, Set<ShardState>>> floc : flocs.entrySet()) {
int maxShards = floc.getKey().getNumberOfShards(); Short maxShards = floc.getKey().getShardMethod().equals(ShardMethodEnum.DB_ID_RANGE) ? null : (short) floc.getKey().getNumberOfShards();
SolrShardHashTable<?> sampleHashTable = null;
if (sampleHashType != null && maxShards != null)
sampleHashTable = this.createSampleHashTable(sampleHashType, maxShards);
SolrShardHashTable<?> sampleHashTable = sampleHashType == null ? null : this.createSampleHashTable(sampleHashType, maxShards); ShardSetInfo shardSetResponse = null;
for (Entry<Shard, Set<ShardState>> registeredShards : floc.getValue().entrySet()) { for (Entry<Shard, Set<ShardState>> registeredShards : floc.getValue().entrySet()) {
for (ShardState registeredShardNode : registeredShards.getValue()) { for (ShardState registeredShardNode : registeredShards.getValue()) {
String nodeId = new Node(registeredShardNode.getShardInstance()).getId(); if (shardSetResponse == null) {
NodeInfo node = nodes.get(nodeId); ShardSet shardSet = ShardSet.from(floc.getKey(), registeredShardNode);
if (node == null) { shardSetResponse = ShardSetInfo.from(shardSet);
node = new NodeShardInfo(registeredShardNode.getShardInstance());
nodes.put(node.getId(), node);
} }
ShardInfo shard = new ShardInfo(registeredShardNode); SolrHost node = SolrHost.from(registeredShardNode.getShardInstance());
shard.setShardSet(new ShardSetInfo(floc.getKey(), registeredShardNode)); String nodeSpec = node.getSpec();
NodeInfo nodeResponse = nodesResponse.get(nodeSpec);
if (nodeResponse == null) {
nodeResponse = NodeShardInfo.from(node);
nodesResponse.put(nodeResponse.getId(), nodeResponse);
}
ShardInstanceState nodeShardState = ShardInstanceState.from(registeredShardNode);
ShardInfo shardResponse = ShardInfo.from(registeredShards.getKey().getInstance(), nodeShardState);
shardResponse.setShardSet(shardSetResponse);
if (sampleHashTable != null) if (sampleHashTable != null)
this.addShardHashSamples(shard, sampleHashTable); this.addShardHashSamples(shardResponse, sampleHashTable);
node.getShards().put(shard.getId(), shard); nodeResponse.getShards().put(shardResponse.getId(), shardResponse);
} }
} }
} }
res.setContentType("application/json"); res.setContentType("application/json");
res.setContentEncoding("utf-8"); res.setContentEncoding("utf-8");
this.getObjectMapper().writeValue(res.getWriter(), nodes); this.getObjectMapper().writeValue(res.getWriter(), nodesResponse);
} }
} }
@@ -1,11 +1,7 @@
package com.inteligr8.alfresco.asie.rest; package com.inteligr8.alfresco.asie.rest;
import java.io.IOException; import java.io.IOException;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@@ -14,10 +10,7 @@ import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.TreeMap; import java.util.TreeMap;
import org.alfresco.repo.index.shard.Floc;
import org.alfresco.repo.index.shard.Shard;
import org.alfresco.repo.index.shard.ShardMethodEnum; import org.alfresco.repo.index.shard.ShardMethodEnum;
import org.alfresco.repo.index.shard.ShardState;
import org.alfresco.util.Pair; import org.alfresco.util.Pair;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -30,11 +23,14 @@ import org.springframework.http.MediaType;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.compute.SolrShardHashTable; import com.inteligr8.alfresco.asie.compute.SolrShardHashTable;
import com.inteligr8.alfresco.asie.model.ShardSet;
import com.inteligr8.alfresco.asie.model.SolrHost;
import com.inteligr8.alfresco.asie.model.ShardInstanceState;
import com.inteligr8.alfresco.asie.rest.model.NodeInfo; import com.inteligr8.alfresco.asie.rest.model.NodeInfo;
import com.inteligr8.alfresco.asie.rest.model.PropertyHashShardSetInfo; import com.inteligr8.alfresco.asie.rest.model.PropertyHashShardSetInfo;
import com.inteligr8.alfresco.asie.rest.model.ShardInfo; import com.inteligr8.alfresco.asie.rest.model.ShardInfo;
import com.inteligr8.alfresco.asie.rest.model.ShardNodeInfo; import com.inteligr8.alfresco.asie.rest.model.ShardNodeInfo;
import com.inteligr8.alfresco.asie.service.ShardDiscoveryService; import com.inteligr8.alfresco.asie.spi.ShardDiscoveryService;
@Component(value = "webscript.com.inteligr8.alfresco.asie.propertyHashShards.get") @Component(value = "webscript.com.inteligr8.alfresco.asie.propertyHashShards.get")
public class GetPropertyHashShardsWebScript extends AbstractAsieShardableWebScript { public class GetPropertyHashShardsWebScript extends AbstractAsieShardableWebScript {
@@ -55,19 +51,18 @@ public class GetPropertyHashShardsWebScript extends AbstractAsieShardableWebScri
List<String> values = this.getOptionalQueryParameterAsList(req); List<String> values = this.getOptionalQueryParameterAsList(req);
this.validateParameters(min, max, values); this.validateParameters(min, max, values);
List<PropertyHashShardSetInfo> shardSets = new LinkedList<>(); List<PropertyHashShardSetInfo> shardSetsResponse = new LinkedList<>();
Collection<Pair<Floc, Map<Shard, Set<ShardState>>>> flocs = this.sds.findByShardMethod(ShardMethodEnum.PROPERTY); Set<ShardSet> shardSets = this.sds.findSetsByShardMethod(ShardMethodEnum.PROPERTY);
if (flocs.isEmpty()) if (shardSets.isEmpty())
throw new WebScriptException(HttpStatus.NO_CONTENT.value(), "There are no property-based shards"); throw new WebScriptException(HttpStatus.NO_CONTENT.value(), "There are no property-based shards");
for (Pair<Floc, Map<Shard, Set<ShardState>>> floc : flocs) { for (ShardSet shardSet : shardSets) {
ShardState anyShardNode = this.getAnyShardNode(floc.getSecond()); PropertyHashShardSetInfo shardSetResponse = PropertyHashShardSetInfo.from(shardSet);
PropertyHashShardSetInfo shardSet = new PropertyHashShardSetInfo(floc.getFirst(), anyShardNode); shardSetResponse.setShards(new TreeMap<>());
shardSet.setShards(new TreeMap<>());
int maxShards = floc.getFirst().getNumberOfShards(); Short shardCount = shardSet.getShards();
SolrShardHashTable<?> sampleHashTable = this.createSampleHashTable(sampleHashType, maxShards); SolrShardHashTable<?> sampleHashTable = this.createSampleHashTable(sampleHashType, shardCount);
Map<Integer, List<Object>> shardToHashMap = new HashMap<>(); Map<Integer, List<Object>> shardToHashMap = new HashMap<>();
@@ -83,39 +78,24 @@ public class GetPropertyHashShardsWebScript extends AbstractAsieShardableWebScri
} }
} }
for (Entry<Shard, Set<ShardState>> shardCache : floc.getSecond().entrySet()) { for (Entry<Integer, Pair<SolrHost, ShardInstanceState>> shard : this.sds.findLatestNodeStates(shardSet).entrySet()) {
ShardInfo shard = new ShardInfo(); ShardInfo shardResponse = ShardInfo.from(shard.getKey(), shard.getValue().getSecond());
shard.setId(shardCache.getKey().getInstance()); shardResponse.setNodes(new HashMap<>());
shard.setNodes(new HashMap<>());
for (ShardState shardNodeCache : shardCache.getValue()) {
if (shard.getTxsCompleted() == null || shard.getTxsCompleted().longValue() < shardNodeCache.getLastIndexedTxId()) {
shard.setLatestTx(OffsetDateTime.ofInstant(Instant.ofEpochMilli(shardNodeCache.getLastIndexedTxCommitTime()), ZoneOffset.UTC));
shard.setTxsCompleted(shardNodeCache.getLastIndexedTxId());
}
NodeInfo node = new ShardNodeInfo(shardNodeCache); NodeInfo nodeResponse = ShardNodeInfo.from(shard.getValue().getFirst(), shard.getValue().getSecond());
shard.getNodes().put(node.getId(), node); shardResponse.getNodes().put(nodeResponse.getId(), nodeResponse);
}
List<Object> hashedValues = shardToHashMap.get(shard.getId()); List<Object> hashedValues = shardToHashMap.get(shardResponse.getId());
if (hashedValues != null) for (Object hashedValue : hashedValues) if (hashedValues != null) for (Object hashedValue : hashedValues)
shardSet.getShards().put(hashedValue, shard); shardSetResponse.getShards().put(hashedValue, shardResponse);
} }
shardSets.add(shardSet); shardSetsResponse.add(shardSetResponse);
} }
res.setContentType(MediaType.APPLICATION_JSON_VALUE); res.setContentType(MediaType.APPLICATION_JSON_VALUE);
res.setContentEncoding("utf-8"); res.setContentEncoding("utf-8");
this.getObjectMapper().writeValue(res.getWriter(), shardSets); this.getObjectMapper().writeValue(res.getWriter(), shardSetsResponse);
}
private ShardState getAnyShardNode(Map<Shard, Set<ShardState>> shards) {
for (Set<ShardState> shardNodes : shards.values())
for (ShardState shardNode : shardNodes)
return shardNode;
return null;
} }
private List<String> getOptionalQueryParameterAsList(WebScriptRequest req) { private List<String> getOptionalQueryParameterAsList(WebScriptRequest req) {
@@ -1,20 +1,19 @@
package com.inteligr8.alfresco.asie.rest; package com.inteligr8.alfresco.asie.rest;
import java.io.IOException; import java.io.IOException;
import java.time.Instant; import java.util.List;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.Set;
import java.util.TreeMap; import java.util.TreeMap;
import org.alfresco.repo.index.shard.Shard; import org.alfresco.util.Pair;
import org.alfresco.repo.index.shard.ShardState;
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 org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.compute.SolrShardHashTable; import com.inteligr8.alfresco.asie.compute.SolrShardHashTable;
import com.inteligr8.alfresco.asie.model.ShardSet;
import com.inteligr8.alfresco.asie.model.SolrHost;
import com.inteligr8.alfresco.asie.model.ShardInstanceState;
import com.inteligr8.alfresco.asie.rest.model.NodeInfo; import com.inteligr8.alfresco.asie.rest.model.NodeInfo;
import com.inteligr8.alfresco.asie.rest.model.ShardInfo; import com.inteligr8.alfresco.asie.rest.model.ShardInfo;
import com.inteligr8.alfresco.asie.rest.model.ShardNodeInfo; import com.inteligr8.alfresco.asie.rest.model.ShardNodeInfo;
@@ -24,34 +23,35 @@ import com.inteligr8.alfresco.asie.rest.model.ShardSetInfo;
public class GetShardWebScript extends AbstractAsieShardWebScript { public class GetShardWebScript extends AbstractAsieShardWebScript {
@Override @Override
public void execute(WebScriptRequest req, WebScriptResponse res, Set<ShardState> registeredShardNodes) throws IOException { public void execute(WebScriptRequest req, WebScriptResponse res, int shardId, ShardSet shardSet, List<Pair<SolrHost, ShardInstanceState>> nodeShardStates) throws IOException {
ShardState aRegisteredShardNode = registeredShardNodes.iterator().next();
Shard registeredShard = aRegisteredShardNode.getShardInstance().getShard();
int maxShards = registeredShard.getFloc().getNumberOfShards();
SolrShardHashSampleType sampleHashType = this.getOptionalQueryParameter(req, "sampleHashType", SolrShardHashSampleType.class); SolrShardHashSampleType sampleHashType = this.getOptionalQueryParameter(req, "sampleHashType", SolrShardHashSampleType.class);
SolrShardHashTable<?> sampleHashTable = sampleHashType == null ? null : this.createSampleHashTable(sampleHashType, maxShards);
ShardInfo shard = new ShardInfo(); Short maxShards = shardSet.getShards();
shard.setId(registeredShard.getInstance()); SolrShardHashTable<?> sampleHashTable = null;
shard.setShardSet(new ShardSetInfo(registeredShard.getFloc(), aRegisteredShardNode)); if (sampleHashType != null && maxShards != null)
shard.setNodes(new TreeMap<>()); sampleHashTable = this.createSampleHashTable(sampleHashType, maxShards);
ShardSetInfo shardSetResponse = ShardSetInfo.from(shardSet);
ShardInfo shardResponse = ShardInfo.from(shardId);
shardResponse.setShardSet(shardSetResponse);
shardResponse.setNodes(new TreeMap<>());
if (sampleHashTable != null) if (sampleHashTable != null)
this.addShardHashSamples(shard, sampleHashTable); this.addShardHashSamples(shardResponse, sampleHashTable);
for (ShardState registeredShardNode : registeredShardNodes) { for (Pair<SolrHost, ShardInstanceState> nodeShardState : nodeShardStates) {
if (shard.getTxsCompleted() == null || shard.getTxsCompleted().longValue() < registeredShardNode.getLastIndexedTxId()) { if (shardResponse.getTxsCompleted() == null || shardResponse.getTxsCompleted().longValue() < nodeShardState.getSecond().getLastIndexedTxId()) {
shard.setLatestTx(OffsetDateTime.ofInstant(Instant.ofEpochMilli(registeredShardNode.getLastIndexedTxCommitTime()), ZoneOffset.UTC)); shardResponse.setLatestTx(nodeShardState.getSecond().getLastIndexedTxTime());
shard.setTxsCompleted(registeredShardNode.getLastIndexedTxId()); shardResponse.setTxsCompleted(nodeShardState.getSecond().getLastIndexedTxId());
} }
NodeInfo node = new ShardNodeInfo(registeredShardNode); NodeInfo node = ShardNodeInfo.from(nodeShardState.getFirst(), nodeShardState.getSecond());
shard.getNodes().put(node.getId(), node); shardResponse.getNodes().put(node.getId(), node);
} }
res.setContentType(MediaType.APPLICATION_JSON_VALUE); res.setContentType(MediaType.APPLICATION_JSON_VALUE);
res.setContentEncoding("utf-8"); res.setContentEncoding("utf-8");
this.getObjectMapper().writeValue(res.getWriter(), shard); this.getObjectMapper().writeValue(res.getWriter(), shardResponse);
} }
} }
@@ -22,6 +22,9 @@ import org.springframework.http.MediaType;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.compute.SolrShardHashTable; import com.inteligr8.alfresco.asie.compute.SolrShardHashTable;
import com.inteligr8.alfresco.asie.model.ShardSet;
import com.inteligr8.alfresco.asie.model.SolrHost;
import com.inteligr8.alfresco.asie.model.ShardInstanceState;
import com.inteligr8.alfresco.asie.rest.model.NodeInfo; import com.inteligr8.alfresco.asie.rest.model.NodeInfo;
import com.inteligr8.alfresco.asie.rest.model.ShardInfo; import com.inteligr8.alfresco.asie.rest.model.ShardInfo;
import com.inteligr8.alfresco.asie.rest.model.ShardNodeInfo; import com.inteligr8.alfresco.asie.rest.model.ShardNodeInfo;
@@ -42,42 +45,46 @@ public class GetShardsWebScript extends AbstractAsieShardableWebScript {
SolrShardHashSampleType sampleHashType = this.getOptionalQueryParameter(req, "sampleHashType", SolrShardHashSampleType.class); SolrShardHashSampleType sampleHashType = this.getOptionalQueryParameter(req, "sampleHashType", SolrShardHashSampleType.class);
Map<String, ShardSetInfo> shardSets = new TreeMap<>(); Map<String, ShardSetInfo> shardSetsResponse = new TreeMap<>();
for (Entry<Floc, Map<Shard, Set<ShardState>>> floc : flocs.entrySet()) { for (Entry<Floc, Map<Shard, Set<ShardState>>> floc : flocs.entrySet()) {
int maxShards = floc.getKey().getNumberOfShards();
ShardState anyShardNode = this.getAnyShardNode(floc.getValue()); ShardState anyShardNode = this.getAnyShardNode(floc.getValue());
ShardSetInfo shardSet = new ShardSetInfo(floc.getKey(), anyShardNode); ShardSet shardSet = ShardSet.from(floc.getKey(), anyShardNode);
shardSet.setShards(new TreeMap<>()); ShardSetInfo shardSetResponse = ShardSetInfo.from(shardSet);
shardSetResponse.setShards(new TreeMap<>());
SolrShardHashTable<?> sampleHashTable = sampleHashType == null ? null : this.createSampleHashTable(sampleHashType, maxShards);
Short maxShards = shardSet.getShards();
SolrShardHashTable<?> sampleHashTable = null;
if (sampleHashType != null && maxShards != null)
sampleHashTable = this.createSampleHashTable(sampleHashType, maxShards);
for (Entry<Shard, Set<ShardState>> registeredShard : floc.getValue().entrySet()) { for (Entry<Shard, Set<ShardState>> registeredShard : floc.getValue().entrySet()) {
ShardInfo shard = new ShardInfo(); ShardInfo shardResponse = ShardInfo.from(registeredShard.getKey().getInstance());
shard.setId(registeredShard.getKey().getInstance()); shardResponse.setNodes(new TreeMap<>());
shard.setNodes(new TreeMap<>());
for (ShardState registeredShardNode : registeredShard.getValue()) { for (ShardState registeredShardNode : registeredShard.getValue()) {
if (shard.getTxsCompleted() == null || shard.getTxsCompleted().longValue() < registeredShardNode.getLastIndexedTxId()) { if (shardResponse.getTxsCompleted() == null || shardResponse.getTxsCompleted().longValue() < registeredShardNode.getLastIndexedTxId()) {
shard.setLatestTx(OffsetDateTime.ofInstant(Instant.ofEpochMilli(registeredShardNode.getLastIndexedTxCommitTime()), ZoneOffset.UTC)); shardResponse.setLatestTx(OffsetDateTime.ofInstant(Instant.ofEpochMilli(registeredShardNode.getLastIndexedTxCommitTime()), ZoneOffset.UTC));
shard.setTxsCompleted(registeredShardNode.getLastIndexedTxId()); shardResponse.setTxsCompleted(registeredShardNode.getLastIndexedTxId());
} }
NodeInfo node = new ShardNodeInfo(registeredShardNode); SolrHost node = SolrHost.from(registeredShardNode.getShardInstance());
shard.getNodes().put(node.getId(), node); ShardInstanceState nodeShardState = ShardInstanceState.from(registeredShardNode);
NodeInfo nodeResponse = ShardNodeInfo.from(node, nodeShardState);
shardResponse.getNodes().put(nodeResponse.getId(), nodeResponse);
} }
if (sampleHashTable != null) if (sampleHashTable != null)
this.addShardHashSamples(shardSet, shard, sampleHashTable); this.addShardHashSamples(shardSetResponse, shardResponse, sampleHashTable);
shardSet.getShards().put(shard.getId(), shard); shardSetResponse.getShards().put(shardResponse.getId(), shardResponse);
} }
shardSets.put(shardSet.getMethodSpec(), shardSet); shardSetsResponse.put(shardSetResponse.getSpec(), shardSetResponse);
} }
res.setContentType(MediaType.APPLICATION_JSON_VALUE); res.setContentType(MediaType.APPLICATION_JSON_VALUE);
res.setContentEncoding("utf-8"); res.setContentEncoding("utf-8");
this.getObjectMapper().writeValue(res.getWriter(), shardSets); this.getObjectMapper().writeValue(res.getWriter(), shardSetsResponse);
} }
private ShardState getAnyShardNode(Map<Shard, Set<ShardState>> shards) { private ShardState getAnyShardNode(Map<Shard, Set<ShardState>> shards) {
@@ -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);
}
}
@@ -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.PurgeService;
import com.inteligr8.alfresco.asie.spi.ActionCallback;
@Component(value = "webscript.com.inteligr8.alfresco.asie.purgeAcsNode.put")
public class PurgeAcsNodeWebScript extends AbstractAcsNodeActionWebScript {
@Autowired
private PurgeService purgeSerivce;
@Override
protected void executeAction(long nodeDbId, ActionCallback callback, long fullQueueTimeout, TimeUnit fullQueueUnit,
long execTimeout, TimeUnit execUnit) throws TimeoutException, InterruptedException {
this.purgeSerivce.purge(nodeDbId, callback, 10L, TimeUnit.SECONDS, 30L, TimeUnit.SECONDS);
}
}
@@ -0,0 +1,129 @@
package com.inteligr8.alfresco.asie.rest;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.extensions.webscripts.WebScriptException;
import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.extensions.webscripts.WebScriptResponse;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.model.ShardInstance;
import com.inteligr8.alfresco.asie.service.AcsReconcileService;
import com.inteligr8.alfresco.asie.spi.ReconcileCallback;
@Component(value = "webscript.com.inteligr8.alfresco.asie.reconcileAcsNodes.post")
public class ReconcileAcsNodesWebScript extends AbstractAsieWebScript {
@Autowired
private AcsReconcileService reconcileService;
@Override
public void executeAuthorized(WebScriptRequest request, WebScriptResponse response) throws IOException {
final int fromDbId = this.getRequestTemplateIntegerVariable(request, "fromDbId");
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 includeReconciled = Boolean.TRUE.equals(this.getOptionalQueryParameter(request, "includeReconciled", Boolean.class));
final Map<String, Object> responseMap = new HashMap<>();
ReconcileCallback callback = new ReconcileCallback() {
@Override
public void reconciled(long nodeDbId) {
if (includeReconciled) {
@SuppressWarnings("unchecked")
List<Long> unreconciledNodeDbIds = (List<Long>) responseMap.get("reconciled");
if (unreconciledNodeDbIds == null)
responseMap.put("reconciled", unreconciledNodeDbIds = new LinkedList<>());
unreconciledNodeDbIds.add(nodeDbId);
}
}
@Override
public void unreconciled(long nodeDbId) {
@SuppressWarnings("unchecked")
List<Long> unreconciledNodeDbIds = (List<Long>) responseMap.get("unreconciled");
if (unreconciledNodeDbIds == null)
responseMap.put("unreconciled", unreconciledNodeDbIds = new LinkedList<>());
unreconciledNodeDbIds.add(nodeDbId);
}
@Override
public void processed(long nodeDbId, Set<ShardInstance> instsReconciled, Set<ShardInstance> instsReconciling,
Map<ShardInstance, String> instsErrorMessages) {
if (!instsReconciled.isEmpty()) {
@SuppressWarnings("unchecked")
Map<Long, List<String>> nodeHosts = (Map<Long, List<String>>) responseMap.get("success");
if (nodeHosts == null)
responseMap.put("success", nodeHosts = new HashMap<>());
List<String> instances = new LinkedList<>();
for (ShardInstance instance : instsReconciled)
instances.add(instance.getSpec());
nodeHosts.put(nodeDbId, instances);
}
if (!instsReconciling.isEmpty()) {
@SuppressWarnings("unchecked")
Map<Long, List<String>> nodeHosts = (Map<Long, List<String>>) responseMap.get("scheduled");
if (nodeHosts == null)
responseMap.put("scheduled", nodeHosts = new HashMap<>());
List<String> instances = new LinkedList<>();
for (ShardInstance instance : instsReconciled)
instances.add(instance.getSpec());
nodeHosts.put(nodeDbId, instances);
}
if (!instsErrorMessages.isEmpty()) {
@SuppressWarnings("unchecked")
Map<Long, Map<String, Map<String, String>>> nodeHosts = (Map<Long, Map<String, Map<String, String>>>) responseMap.get("error");
if (nodeHosts == null)
responseMap.put("error", nodeHosts = new HashMap<>());
Map<String, Map<String, String>> nodeHost = new HashMap<>();
for (Entry<ShardInstance, String> message : instsErrorMessages.entrySet())
nodeHost.put(message.getKey().getSpec(), Collections.singletonMap("message", message.getValue()));
nodeHosts.put(nodeDbId, nodeHost);
}
}
};
try {
this.reconcileService.reconcile(fromDbId, toDbId, null, index, reindex, callback, 1L, TimeUnit.HOURS, 2L, TimeUnit.MINUTES);
if (responseMap.containsKey("error")) {
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
} else if (responseMap.containsKey("scheduled")) {
response.setStatus(HttpStatus.ACCEPTED.value());
} else {
response.setStatus(HttpStatus.OK.value());
}
response.setContentType("application/json");
this.getObjectMapper().writeValue(response.getWriter(), responseMap);
} catch (InterruptedException ie) {
throw new WebScriptException(HttpStatus.SERVICE_UNAVAILABLE.value(), "The reindex was interrupted", ie);
} catch (TimeoutException te) {
throw new WebScriptException(HttpStatus.INTERNAL_SERVER_ERROR.value(), "The reindex may continue, but timed-out waiting", te);
}
}
private int getRequestTemplateIntegerVariable(WebScriptRequest request, String templateVariableName) {
String str = request.getServiceMatch().getTemplateVars().get(templateVariableName);
return Integer.valueOf(str);
}
}
@@ -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.ReindexService;
import com.inteligr8.alfresco.asie.spi.ActionCallback;
@Component(value = "webscript.com.inteligr8.alfresco.asie.reindexAcsNode.put")
public class ReindexAcsNodeWebScript extends AbstractAcsNodeActionWebScript {
@Autowired
private ReindexService reindexSerivce;
@Override
protected void executeAction(long nodeDbId, ActionCallback callback, long fullQueueTimeout, TimeUnit fullQueueUnit,
long execTimeout, TimeUnit execUnit) throws TimeoutException, InterruptedException {
this.reindexSerivce.reindex(nodeDbId, callback, 10L, TimeUnit.SECONDS, 30L, TimeUnit.SECONDS);
}
}
@@ -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.RetryService;
import com.inteligr8.alfresco.asie.spi.ActionCallback;
@Component(value = "webscript.com.inteligr8.alfresco.asie.retry.post")
public class RetryWebScript extends AbstractActionWebScript {
@Autowired
private RetryService retrySerivce;
@Override
protected void executeAction(ActionCallback callback, long fullQueueTimeout, TimeUnit fullQueueUnit,
long execTimeout, TimeUnit execUnit) throws TimeoutException, InterruptedException {
this.retrySerivce.retry(callback, 10L, TimeUnit.SECONDS, 30L, TimeUnit.SECONDS);
}
}
@@ -3,12 +3,10 @@ package com.inteligr8.alfresco.asie.rest.model;
import java.util.Map; import java.util.Map;
import java.util.TreeMap; import java.util.TreeMap;
import org.alfresco.repo.index.shard.ShardInstance;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.inteligr8.alfresco.asie.model.Node; import com.inteligr8.alfresco.asie.model.SolrHost;
public abstract class NodeInfo { public abstract class NodeInfo implements ResponseInfo {
@JsonProperty @JsonProperty
private String id; private String id;
@@ -19,8 +17,8 @@ public abstract class NodeInfo {
public NodeInfo() { public NodeInfo() {
} }
public NodeInfo(ShardInstance nodeCache) { protected NodeInfo(SolrHost node) {
this.setId(new Node(nodeCache).getId()); this.id = node.getSpec();
} }
public String getId() { public String getId() {
@@ -1,4 +1,4 @@
package com.inteligr8.alfresco.asie.model; package com.inteligr8.alfresco.asie.rest.model;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
@@ -2,25 +2,28 @@ package com.inteligr8.alfresco.asie.rest.model;
import java.util.Map; import java.util.Map;
import org.alfresco.repo.index.shard.ShardInstance;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.inteligr8.alfresco.asie.model.SolrHost;
@JsonInclude(Include.NON_EMPTY) @JsonInclude(Include.NON_EMPTY)
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class NodeShardInfo extends NodeInfo { public class NodeShardInfo extends NodeInfo {
public static NodeShardInfo from(SolrHost node) {
return new NodeShardInfo(node);
}
@JsonProperty @JsonProperty
private Map<Integer, ShardInfo> shards; private Map<Integer, ShardInfo> shards;
public NodeShardInfo() { public NodeShardInfo() {
} }
public NodeShardInfo(ShardInstance nodeCache) { protected NodeShardInfo(SolrHost node) {
super(nodeCache); super(node);
} }
} }
@@ -1,4 +1,6 @@
package com.inteligr8.alfresco.asie.model; package com.inteligr8.alfresco.asie.rest.model;
import com.inteligr8.alfresco.asie.model.ShardSet;
public class NodeShardParameterSet extends NodeParameterSet { public class NodeShardParameterSet extends NodeParameterSet {
@@ -2,24 +2,25 @@ package com.inteligr8.alfresco.asie.rest.model;
import java.util.Map; import java.util.Map;
import org.alfresco.repo.index.shard.Floc;
import org.alfresco.repo.index.shard.ShardState;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.inteligr8.alfresco.asie.model.ShardSet;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.inteligr8.alfresco.asie.model.ShardSet;
@JsonInclude(Include.NON_EMPTY) @JsonInclude(Include.NON_EMPTY)
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class PropertyHashShardSetInfo { public class PropertyHashShardSetInfo implements ResponseInfo {
public static PropertyHashShardSetInfo from(ShardSet shardSet) {
return new PropertyHashShardSetInfo(shardSet);
}
@JsonProperty @JsonProperty
private String methodSpec; private String core;
@JsonProperty @JsonProperty
private int shardCount; private String spec;
@JsonProperty @JsonProperty
private Map<Object, ShardInfo> shards; private Map<Object, ShardInfo> shards;
@@ -27,26 +28,25 @@ public class PropertyHashShardSetInfo {
public PropertyHashShardSetInfo() { public PropertyHashShardSetInfo() {
} }
public PropertyHashShardSetInfo(Floc floc, ShardState anyShardNode) { protected PropertyHashShardSetInfo(ShardSet shardSet) {
ShardSet shardSet = new ShardSet(floc, anyShardNode); this.core = shardSet.getCore();
this.setMethodSpec(shardSet.toSpec()); this.spec = shardSet.toSpec();
this.setShardCount(floc.getNumberOfShards()); }
public String getCore() {
return core;
}
public void setCore(String core) {
this.core = core;
} }
public String getMethodSpec() { public String getSpec() {
return this.methodSpec; return this.spec;
} }
public void setMethodSpec(String methodSpec) { public void setSpec(String spec) {
this.methodSpec = methodSpec; this.spec = spec;
}
public int getShardCount() {
return shardCount;
}
public void setShardCount(int shardCount) {
this.shardCount = shardCount;
} }
public Map<Object, ShardInfo> getShards() { public Map<Object, ShardInfo> getShards() {
@@ -0,0 +1,5 @@
package com.inteligr8.alfresco.asie.rest.model;
public interface RequestParameterSet {
}
@@ -0,0 +1,5 @@
package com.inteligr8.alfresco.asie.rest.model;
public interface ResponseInfo {
}
@@ -1,22 +1,27 @@
package com.inteligr8.alfresco.asie.rest.model; package com.inteligr8.alfresco.asie.rest.model;
import java.time.Instant;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.Map; import java.util.Map;
import org.alfresco.repo.index.shard.ShardState;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonFormat.Shape;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonFormat.Shape;
import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.inteligr8.alfresco.asie.model.ShardInstanceState;
@JsonInclude(Include.NON_EMPTY) @JsonInclude(Include.NON_EMPTY)
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class ShardInfo { public class ShardInfo implements ResponseInfo {
public static ShardInfo from(int shardId) {
return new ShardInfo(shardId, null);
}
public static ShardInfo from(int shardId, ShardInstanceState nodeShardState) {
return new ShardInfo(shardId, nodeShardState);
}
@JsonProperty @JsonProperty
private int id; private int id;
@@ -40,10 +45,10 @@ public class ShardInfo {
public ShardInfo() { public ShardInfo() {
} }
public ShardInfo(ShardState shard) { protected ShardInfo(int shardId, ShardInstanceState nodeShardState) {
this.setId(shard.getShardInstance().getShard().getInstance()); this.id = shardId;
this.setLatestTx(OffsetDateTime.ofInstant(Instant.ofEpochMilli(shard.getLastIndexedTxCommitTime()), ZoneOffset.UTC)); this.latestTx = nodeShardState == null ? null : nodeShardState.getLastIndexedTxTime();
this.setTxsCompleted(shard.getLastIndexedTxId()); this.txsCompleted = nodeShardState == null ? null : nodeShardState.getLastIndexedTxId();
} }
public int getId() { public int getId() {
@@ -1,10 +1,6 @@
package com.inteligr8.alfresco.asie.rest.model; package com.inteligr8.alfresco.asie.rest.model;
import java.time.Instant;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import org.alfresco.repo.index.shard.ShardState;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonFormat.Shape; import com.fasterxml.jackson.annotation.JsonFormat.Shape;
@@ -12,11 +8,17 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.inteligr8.alfresco.asie.model.SolrHost;
import com.inteligr8.alfresco.asie.model.ShardInstanceState;
@JsonInclude(Include.NON_EMPTY) @JsonInclude(Include.NON_EMPTY)
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class ShardNodeInfo extends NodeInfo { public class ShardNodeInfo extends NodeInfo {
public static ShardNodeInfo from(SolrHost node, ShardInstanceState nodeShardState) {
return new ShardNodeInfo(node, nodeShardState);
}
@JsonProperty @JsonProperty
private Long txsCompleted; private Long txsCompleted;
@@ -24,13 +26,16 @@ public class ShardNodeInfo extends NodeInfo {
@JsonFormat(shape = Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssXXX") @JsonFormat(shape = Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssXXX")
private OffsetDateTime latestTx; private OffsetDateTime latestTx;
/**
* For Spring deserialization
*/
public ShardNodeInfo() { public ShardNodeInfo() {
} }
public ShardNodeInfo(ShardState shard) { protected ShardNodeInfo(SolrHost node, ShardInstanceState nodeShardState) {
super(shard.getShardInstance()); super(node);
this.setLatestTx(OffsetDateTime.ofInstant(Instant.ofEpochMilli(shard.getLastIndexedTxCommitTime()), ZoneOffset.UTC)); this.latestTx = nodeShardState.getLastIndexedTxTime();
this.setTxsCompleted(shard.getLastIndexedTxId()); this.txsCompleted = nodeShardState.getLastIndexedTxId();
} }
public Long getTxsCompleted() { public Long getTxsCompleted() {
@@ -1,4 +1,6 @@
package com.inteligr8.alfresco.asie.model; package com.inteligr8.alfresco.asie.rest.model;
import com.inteligr8.alfresco.asie.model.ShardSet;
public class ShardParameterSet implements RequestParameterSet { public class ShardParameterSet implements RequestParameterSet {
@@ -4,24 +4,40 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.TreeMap; import java.util.TreeMap;
import org.alfresco.repo.index.shard.Floc; import org.alfresco.repo.index.shard.ShardMethodEnum;
import org.alfresco.repo.index.shard.ShardState; import org.alfresco.service.cmr.repository.StoreRef;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.inteligr8.alfresco.asie.model.ShardSet;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.inteligr8.alfresco.asie.model.ShardSet;
@JsonInclude(Include.NON_EMPTY) @JsonInclude(Include.NON_EMPTY)
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class ShardSetInfo { public class ShardSetInfo implements ResponseInfo {
public static ShardSetInfo from(ShardSet shardSet) {
return new ShardSetInfo(shardSet);
}
@JsonProperty @JsonProperty
private String methodSpec; private String spec;
@JsonProperty
private ShardMethodEnum method;
@JsonProperty
private boolean fulltextEnabled;
@JsonProperty
private String template;
@JsonProperty @JsonProperty
private int shardCount; private Set<StoreRef> storeRefs;
@JsonProperty
private Short shardCount;
@JsonProperty @JsonProperty
private Map<Integer, ShardInfo> shards; private Map<Integer, ShardInfo> shards;
@@ -32,25 +48,60 @@ public class ShardSetInfo {
public ShardSetInfo() { public ShardSetInfo() {
} }
public ShardSetInfo(Floc floc, ShardState anyShardNode) { protected ShardSetInfo(ShardSet shardSet) {
ShardSet shardSet = new ShardSet(floc, anyShardNode); this.spec = shardSet.toSpec();
this.methodSpec = shardSet.toSpec(); this.method = shardSet.getMethod();
this.setShardCount(floc.getNumberOfShards()); this.fulltextEnabled = shardSet.hasContent();
this.template = shardSet.getTemplate();
this.storeRefs = shardSet.getStoreRefs();
this.shardCount = shardSet.getShards();
} }
public String getMethodSpec() { public String getSpec() {
return this.methodSpec; return this.spec;
} }
public void setMethodSpec(String methodSpec) { public void setSpec(String spec) {
this.methodSpec = methodSpec; this.spec = spec;
} }
public int getShardCount() { public ShardMethodEnum getMethod() {
return method;
}
public void setMethod(ShardMethodEnum method) {
this.method = method;
}
public boolean isFulltextEnabled() {
return fulltextEnabled;
}
public void setFulltextEnabled(boolean fulltextEnabled) {
this.fulltextEnabled = fulltextEnabled;
}
public String getTemplate() {
return template;
}
public void setTemplate(String template) {
this.template = template;
}
public Set<StoreRef> getStoreRefs() {
return storeRefs;
}
public void setStoreRefs(Set<StoreRef> storeRefs) {
this.storeRefs = storeRefs;
}
public Short getShardCount() {
return shardCount; return shardCount;
} }
public void setShardCount(int shardCount) { public void setShardCount(Short shardCount) {
this.shardCount = shardCount; this.shardCount = shardCount;
} }

Some files were not shown because too many files have changed in this diff Show More