initial checkin

This commit is contained in:
2024-10-28 15:36:37 -04:00
commit 38895141a0
123 changed files with 6878 additions and 0 deletions

12
.gitignore vendored Normal file
View File

@@ -0,0 +1,12 @@
# Maven
target
pom.xml.versionsBackup
# Eclipse
.project
.classpath
.settings
.vscode
# IDEA
/.idea/

1
asie-api/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/target/

70
asie-api/pom.xml Normal file
View File

@@ -0,0 +1,70 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.inteligr8.alfresco</groupId>
<artifactId>asie-api</artifactId>
<version>1.0-SNAPSHOT-asie2</version>
<packaging>jar</packaging>
<name>ASIE JAX-RS API</name>
<description>Alfresco Search &amp; Insight Engine JAX-RS API</description>
<dependencies>
<dependency>
<groupId>com.inteligr8</groupId>
<artifactId>solr-api</artifactId>
<version>1.0-SNAPSHOT-solr6</version>
</dependency>
<dependency>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-repository</artifactId>
<version>14.153</version>
</dependency>
<!-- Including for testing purposes only -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.11.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<maven.compiler.release>11</maven.compiler.release>
</properties>
<build>
<pluginManagement>
<plugins>
<!-- avoids log4j dependency -->
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
</plugin>
<!-- avoids struts dependency -->
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.12.1</version>
</plugin>
<!-- Force use of a new maven-dependency-plugin that doesn't download struts dependency -->
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.7.1</version>
</plugin>
</plugins>
</pluginManagement>
</build>
<repositories>
<repository>
<id>alfresco-public</id>
<url>https://artifacts.alfresco.com/nexus/repository/public/</url>
</repository>
</repositories>
</project>

View File

@@ -0,0 +1,91 @@
package com.inteligr8.alfresco.asie.api;
import com.inteligr8.alfresco.asie.model.ActionResponse;
import com.inteligr8.alfresco.asie.model.EmptyResponse;
import com.inteligr8.alfresco.asie.model.core.CheckRequest;
import com.inteligr8.alfresco.asie.model.core.DisableIndexingRequest;
import com.inteligr8.alfresco.asie.model.core.EnableIndexingRequest;
import com.inteligr8.alfresco.asie.model.core.FixRequest;
import com.inteligr8.alfresco.asie.model.core.FixResponseAction;
import com.inteligr8.alfresco.asie.model.core.IndexingStatusAction;
import com.inteligr8.alfresco.asie.model.core.NewCoreRequest;
import com.inteligr8.alfresco.asie.model.core.NewDefaultIndexRequest;
import com.inteligr8.alfresco.asie.model.core.PurgeRequest;
import com.inteligr8.alfresco.asie.model.core.ReindexRequest;
import com.inteligr8.alfresco.asie.model.core.ReportRequest;
import com.inteligr8.alfresco.asie.model.core.ReportResponse;
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.SummaryResponse;
import com.inteligr8.alfresco.asie.model.core.UpdateCoreRequest;
import com.inteligr8.alfresco.asie.model.core.UpdateLog4jRequest;
import com.inteligr8.alfresco.asie.model.core.UpdateSharedRequest;
import com.inteligr8.solr.model.ResponseAction;
import jakarta.ws.rs.BeanParam;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
@Path("admin/cores")
public interface CoreAdminApi extends com.inteligr8.solr.api.CoreAdminApi {
@GET
@Produces(MediaType.APPLICATION_JSON)
EmptyResponse newCore(@BeanParam NewCoreRequest request);
@GET
@Produces(MediaType.APPLICATION_JSON)
EmptyResponse newDefaultIndex(@BeanParam NewDefaultIndexRequest request);
@GET
@Produces(MediaType.APPLICATION_JSON)
ActionResponse<ResponseAction> updateCore(@BeanParam UpdateCoreRequest request);
@GET
@Produces(MediaType.APPLICATION_JSON)
ActionResponse<ResponseAction> check(@BeanParam CheckRequest request);
@GET
@Produces(MediaType.APPLICATION_JSON)
ActionResponse<ResponseAction> updateShared(@BeanParam UpdateSharedRequest request);
@GET
@Produces(MediaType.APPLICATION_JSON)
ActionResponse<ResponseAction> updateLog4j(@BeanParam UpdateLog4jRequest request);
@GET
@Produces(MediaType.APPLICATION_JSON)
ActionResponse<ResponseAction> purge(@BeanParam PurgeRequest request);
@GET
@Produces(MediaType.APPLICATION_JSON)
ActionResponse<ResponseAction> reindex(@BeanParam ReindexRequest request);
@GET
@Produces(MediaType.APPLICATION_JSON)
ActionResponse<RetryResponseAction> retry(@BeanParam RetryRequest request);
@GET
@Produces(MediaType.APPLICATION_JSON)
ActionResponse<FixResponseAction> fix(@BeanParam FixRequest request);
@GET
@Produces(MediaType.APPLICATION_JSON)
ActionResponse<IndexingStatusAction> enableIndexing(@BeanParam EnableIndexingRequest request);
@GET
@Produces(MediaType.APPLICATION_JSON)
ActionResponse<IndexingStatusAction> disableIndexing(@BeanParam DisableIndexingRequest request);
@GET
@Produces(MediaType.APPLICATION_JSON)
ReportResponse getReport(@BeanParam ReportRequest request);
@GET
@Produces(MediaType.APPLICATION_JSON)
SummaryResponse getSummary(@BeanParam SummaryRequest request);
}

View File

@@ -0,0 +1,22 @@
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;
}
}

View File

@@ -0,0 +1,41 @@
package com.inteligr8.alfresco.asie.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;
public class BaseResponse extends com.inteligr8.solr.model.BaseResponse {
@JsonProperty(value = "STATUS", access = Access.READ_ONLY)
private String reason;
@JsonProperty(value = "exception", access = Access.READ_ONLY)
private String exception;
@JsonProperty(value = "msg", access = Access.READ_ONLY)
private String message;
public String getReason() {
return reason;
}
protected void setReason(String reason) {
this.reason = reason;
}
public String getException() {
return exception;
}
protected void setException(String exception) {
this.exception = exception;
}
public String getMessage() {
return message;
}
protected void setMessage(String message) {
this.message = message;
}
}

View File

@@ -0,0 +1,8 @@
package com.inteligr8.alfresco.asie.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@JsonIgnoreProperties(ignoreUnknown = true)
public class EmptyResponse extends BaseResponse {
}

View File

@@ -0,0 +1,43 @@
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 CheckRequest extends JsonFormattedResponseRequest<CheckRequest> {
private static final String ACTION = "check";
@QueryParam("action")
@DefaultValue(ACTION)
@Nonnull
private String action = ACTION;
@QueryParam("coreName")
@Nonnull
private String core;
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 CheckRequest withCore(String core) {
this.core = core;
return this;
}
}

View File

@@ -0,0 +1,42 @@
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 DisableIndexingRequest extends JsonFormattedResponseRequest<DisableIndexingRequest> {
private static final String ACTION = "disable-indexing";
@QueryParam("action")
@DefaultValue(ACTION)
@Nonnull
private String action = ACTION;
@QueryParam("core")
private String core;
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 DisableIndexingRequest withCore(String core) {
this.core = core;
return this;
}
}

View File

@@ -0,0 +1,42 @@
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 EnableIndexingRequest extends JsonFormattedResponseRequest<EnableIndexingRequest> {
private static final String ACTION = "enable-indexing";
@QueryParam("action")
@DefaultValue(ACTION)
@Nonnull
private String action = ACTION;
@QueryParam("core")
private String core;
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 EnableIndexingRequest withCore(String core) {
this.core = core;
return this;
}
}

View File

@@ -0,0 +1,90 @@
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 FixRequest extends JsonFormattedResponseRequest<FixRequest> {
private static final String ACTION = "fix";
@QueryParam("action")
@DefaultValue(ACTION)
@Nonnull
private String action = ACTION;
@QueryParam("core")
private String core;
@QueryParam("dryRun")
private Boolean dryRun;
@QueryParam("fromTxCommitTime")
private Long fromTransactionCommitTime;
@QueryParam("toTxCommitTime")
private Long toTransactionCommitTime;
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 FixRequest withCore(String core) {
this.core = core;
return this;
}
public Boolean getDryRun() {
return dryRun;
}
public void setDryRun(Boolean dryRun) {
this.dryRun = dryRun;
}
public FixRequest dryRun(boolean dryRun) {
this.dryRun = dryRun;
return this;
}
public Long getFromTransactionCommitTime() {
return fromTransactionCommitTime;
}
public void setFromTransactionCommitTime(Long fromTransactionCommitTime) {
this.fromTransactionCommitTime = fromTransactionCommitTime;
}
public FixRequest fromTransactionCommitTime(Long fromTransactionCommitTime) {
this.fromTransactionCommitTime = fromTransactionCommitTime;
return this;
}
public Long getToTransactionCommitTime() {
return toTransactionCommitTime;
}
public void setToTransactionCommitTime(Long toTransactionCommitTime) {
this.toTransactionCommitTime = toTransactionCommitTime;
}
public FixRequest toTransactionCommitTime(Long toTransactionCommitTime) {
this.toTransactionCommitTime = toTransactionCommitTime;
return this;
}
}

View File

@@ -0,0 +1,34 @@
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;
}
}

View File

@@ -0,0 +1,25 @@
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.inteligr8.solr.model.ResponseAction;
@JsonIgnoreProperties(ignoreUnknown = true)
public class IndexingStatusAction extends ResponseAction {
private Map<String, IndexingStatusMetadata> cores;
@JsonAnyGetter
public Map<String, IndexingStatusMetadata> getCores() {
return cores;
}
@JsonAnySetter
public void setCores(Map<String, IndexingStatusMetadata> cores) {
this.cores = cores;
}
}

View File

@@ -0,0 +1,56 @@
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;
}
}

View File

@@ -0,0 +1,161 @@
package com.inteligr8.alfresco.asie.model.core;
import java.util.Collection;
import org.alfresco.service.cmr.repository.StoreRef;
import org.apache.commons.lang3.StringUtils;
import com.inteligr8.solr.model.JsonFormattedResponseRequest;
import jakarta.annotation.Nonnull;
import jakarta.ws.rs.DefaultValue;
import jakarta.ws.rs.QueryParam;
public class NewCoreRequest extends JsonFormattedResponseRequest<NewCoreRequest> {
private static final String ACTION = "newCore";
@QueryParam("action")
@DefaultValue(ACTION)
@Nonnull
private String action = ACTION;
@QueryParam("coreName")
@Nonnull
private String core;
@QueryParam("storeRef")
@Nonnull
private StoreRef storeRef;
@QueryParam("shardIds")
private String shardIds;
@QueryParam("numShards")
private Integer shardCount;
@QueryParam("template")
private String template;
@QueryParam("replicationFactor")
private Integer replicationFactor;
@QueryParam("nodeInstance")
private Integer nodeId;
@QueryParam("numNodes")
private Integer nodeCount;
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 NewCoreRequest withCore(String core) {
this.core = core;
return this;
}
public StoreRef getStoreRef() {
return storeRef;
}
public void setStoreRef(StoreRef storeRef) {
this.storeRef = storeRef;
}
public NewCoreRequest withStoreRef(StoreRef storeRef) {
this.storeRef = storeRef;
return this;
}
public String getShardIds() {
return shardIds;
}
public void setShardIds(String shardIds) {
this.shardIds = shardIds;
}
public NewCoreRequest withShardIds(Collection<String> shardIds) {
this.shardIds = StringUtils.join(shardIds, ",");
return this;
}
public Integer getShardCount() {
return shardCount;
}
public void setShardCount(Integer shardCount) {
this.shardCount = shardCount;
}
public NewCoreRequest withShardCount(Integer shardCount) {
this.shardCount = shardCount;
return this;
}
public String getTemplate() {
return template;
}
public void setTemplate(String template) {
this.template = template;
}
public NewCoreRequest withTemplate(String template) {
this.template = template;
return this;
}
public Integer getReplicationFactor() {
return replicationFactor;
}
public void setReplicationFactor(Integer replicationFactor) {
this.replicationFactor = replicationFactor;
}
public NewCoreRequest withReplicationFactor(Integer replicationFactor) {
this.replicationFactor = replicationFactor;
return this;
}
public Integer getNodeId() {
return nodeId;
}
public void setNodeId(Integer nodeId) {
this.nodeId = nodeId;
}
public NewCoreRequest withNodeId(Integer nodeId) {
this.nodeId = nodeId;
return this;
}
public Integer getNodeCount() {
return nodeCount;
}
public void setNodeCount(Integer nodeCount) {
this.nodeCount = nodeCount;
}
public NewCoreRequest withNodeCount(Integer nodeCount) {
this.nodeCount = nodeCount;
return this;
}
}

View File

@@ -0,0 +1,78 @@
package com.inteligr8.alfresco.asie.model.core;
import org.alfresco.service.cmr.repository.StoreRef;
import com.inteligr8.solr.model.JsonFormattedResponseRequest;
import jakarta.annotation.Nonnull;
import jakarta.ws.rs.DefaultValue;
import jakarta.ws.rs.QueryParam;
public class NewDefaultIndexRequest extends JsonFormattedResponseRequest<NewDefaultIndexRequest> {
private static final String ACTION = "newDefaultIndex";
@QueryParam("action")
@DefaultValue(ACTION)
@Nonnull
public String action = ACTION;
@QueryParam("coreName")
@Nonnull
public String core;
@QueryParam("storeRef")
@Nonnull
public StoreRef storeRef;
@QueryParam("template")
public String template;
public String getAction() {
return action;
}
protected void setAction(String action) {
this.action = action;
}
public String getCore() {
return core;
}
public void setCore(String core) {
this.core = core;
}
public NewDefaultIndexRequest withCore(String core) {
this.core = core;
return this;
}
public StoreRef getStoreRef() {
return storeRef;
}
public void setStoreRef(StoreRef storeRef) {
this.storeRef = storeRef;
}
public NewDefaultIndexRequest withStoreRef(StoreRef storeRef) {
this.storeRef = storeRef;
return this;
}
public String getTemplate() {
return template;
}
public void setTemplate(String template) {
this.template = template;
}
public NewDefaultIndexRequest withTemplate(String template) {
this.template = template;
return this;
}
}

View File

@@ -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 PurgeRequest extends JsonFormattedResponseRequest<PurgeRequest> {
private static final String ACTION = "purge";
@QueryParam("action")
@DefaultValue(ACTION)
@Nonnull
private String action = ACTION;
@QueryParam("core")
private String core;
@QueryParam("txid")
private Integer transactionId;
@QueryParam("acltxid")
private Integer aclTransactionId;
@QueryParam("nodeId")
private Integer nodeId;
@QueryParam("aclid")
private Integer 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 PurgeRequest withCore(String core) {
this.core = core;
return this;
}
public Integer getTransactionId() {
return transactionId;
}
public void setTransactionId(Integer transactionId) {
this.transactionId = transactionId;
}
public PurgeRequest withTransactionId(Integer transactionId) {
this.transactionId = transactionId;
return this;
}
public Integer getAclTransactionId() {
return aclTransactionId;
}
public void setAclTransactionId(Integer aclTransactionId) {
this.aclTransactionId = aclTransactionId;
}
public PurgeRequest withAclTransactionId(Integer aclTransactionId) {
this.aclTransactionId = aclTransactionId;
return this;
}
public Integer getNodeId() {
return nodeId;
}
public void setNodeId(Integer nodeId) {
this.nodeId = nodeId;
}
public PurgeRequest withNodeId(Integer nodeId) {
this.nodeId = nodeId;
return this;
}
public Integer getAclId() {
return aclId;
}
public void setAclId(Integer aclId) {
this.aclId = aclId;
}
public PurgeRequest withAclId(Integer aclId) {
this.aclId = aclId;
return this;
}
}

View File

@@ -0,0 +1,122 @@
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 ReindexRequest extends JsonFormattedResponseRequest<ReindexRequest> {
private static final String ACTION = "reindex";
@QueryParam("action")
@DefaultValue(ACTION)
@Nonnull
private String action = ACTION;
@QueryParam("core")
private String core;
@QueryParam("txid")
private Integer transactionId;
@QueryParam("acltxid")
private Integer aclTransactionId;
@QueryParam("nodeId")
private Integer nodeId;
@QueryParam("aclid")
private Integer aclId;
@QueryParam("query")
private String query;
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 ReindexRequest withCore(String core) {
this.core = core;
return this;
}
public Integer getTransactionId() {
return transactionId;
}
public void setTransactionId(Integer transactionId) {
this.transactionId = transactionId;
}
public ReindexRequest withTransactionId(Integer transactionId) {
this.transactionId = transactionId;
return this;
}
public Integer getAclTransactionId() {
return aclTransactionId;
}
public void setAclTransactionId(Integer aclTransactionId) {
this.aclTransactionId = aclTransactionId;
}
public ReindexRequest withAclTransactionId(Integer aclTransactionId) {
this.aclTransactionId = aclTransactionId;
return this;
}
public Integer getNodeId() {
return nodeId;
}
public void setNodeId(Integer nodeId) {
this.nodeId = nodeId;
}
public ReindexRequest withNodeId(Integer nodeId) {
this.nodeId = nodeId;
return this;
}
public Integer getAclId() {
return aclId;
}
public void setAclId(Integer aclId) {
this.aclId = aclId;
}
public ReindexRequest withAclId(Integer aclId) {
this.aclId = aclId;
return this;
}
public String getQuery() {
return query;
}
public void setQuery(String query) {
this.query = query;
}
public ReindexRequest withQuery(String query) {
this.query = query;
return this;
}
}

View File

@@ -0,0 +1,24 @@
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;
@JsonIgnoreProperties(ignoreUnknown = true)
public class Report {
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;
}
}

View File

@@ -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 ReportRequest extends JsonFormattedResponseRequest<ReportRequest> {
private static final String ACTION = "report";
@QueryParam("action")
@DefaultValue(ACTION)
@Nonnull
private String action = ACTION;
@QueryParam("core")
private String core;
@QueryParam("fromTime")
private Long fromTime;
@QueryParam("toTime")
private Long toTime;
@QueryParam("fromTx")
private Integer fromTransactionId;
@QueryParam("toTx")
private Integer toTransactionId;
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 ReportRequest withCore(String core) {
this.core = core;
return this;
}
public Long getFromTime() {
return fromTime;
}
public void setFromTime(Long fromTime) {
this.fromTime = fromTime;
}
public ReportRequest fromTime(Long fromTime) {
this.fromTime = fromTime;
return this;
}
public Long getToTime() {
return toTime;
}
public void setToTime(Long toTime) {
this.toTime = toTime;
}
public ReportRequest toTime(Long toTime) {
this.toTime = toTime;
return this;
}
public Integer getFromTransactionId() {
return fromTransactionId;
}
public void setFromTransactionId(Integer fromTransactionId) {
this.fromTransactionId = fromTransactionId;
}
public ReportRequest fromTransactionId(Integer fromTransactionId) {
this.fromTransactionId = fromTransactionId;
return this;
}
public Integer getToTransactionId() {
return toTransactionId;
}
public void setToTransactionId(Integer toTransactionId) {
this.toTransactionId = toTransactionId;
}
public ReportRequest toTransactionId(Integer toTransactionId) {
this.toTransactionId = toTransactionId;
return this;
}
}

View File

@@ -0,0 +1,22 @@
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.alfresco.asie.model.BaseResponse;
@JsonIgnoreProperties(ignoreUnknown = true)
public class ReportResponse extends BaseResponse {
@JsonProperty(access = Access.READ_ONLY)
private Report report;
public Report getReport() {
return report;
}
protected void setReport(Report report) {
this.report = report;
}
}

View File

@@ -0,0 +1,42 @@
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 RetryRequest extends JsonFormattedResponseRequest<RetryRequest> {
private static final String ACTION = "retry";
@QueryParam("action")
@DefaultValue(ACTION)
@Nonnull
private String action = ACTION;
@QueryParam("core")
private String core;
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 RetryRequest withCore(String core) {
this.core = core;
return this;
}
}

View File

@@ -0,0 +1,22 @@
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;
}
}

View File

@@ -0,0 +1,24 @@
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;
@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;
}
}

View File

@@ -0,0 +1,107 @@
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 SummaryRequest extends JsonFormattedResponseRequest<SummaryRequest> {
private static final String ACTION = "summary";
@QueryParam("action")
@DefaultValue(ACTION)
@Nonnull
private String action = ACTION;
@QueryParam("core")
@Nonnull
private String core;
@QueryParam("detail")
private Boolean includeDetails;
@QueryParam("hist")
private Boolean includeHistogram;
@QueryParam("values")
private Boolean includeSomeValues;
@QueryParam("reset")
private Boolean resetStats;
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 SummaryRequest withCore(String core) {
this.core = core;
return this;
}
public Boolean getIncludeDetails() {
return includeDetails;
}
public void setIncludeDetails(Boolean includeDetails) {
this.includeDetails = includeDetails;
}
public SummaryRequest includeDetails(boolean includeDetails) {
this.includeDetails = includeDetails;
return this;
}
public Boolean getIncludeHistogram() {
return includeHistogram;
}
public void setIncludeHistogram(Boolean includeHistogram) {
this.includeHistogram = includeHistogram;
}
public SummaryRequest includeHistogram(boolean includeHistogram) {
this.includeHistogram = includeHistogram;
return this;
}
public Boolean getIncludeSomeValues() {
return includeSomeValues;
}
public void setIncludeSomeValues(Boolean includeSomeValues) {
this.includeSomeValues = includeSomeValues;
}
public SummaryRequest includeSomeValues(boolean includeSomeValues) {
this.includeSomeValues = includeSomeValues;
return this;
}
public Boolean getResetStats() {
return resetStats;
}
public void setResetStats(Boolean resetStats) {
this.resetStats = resetStats;
}
public SummaryRequest resetStats(boolean resetStats) {
this.resetStats = resetStats;
return this;
}
}

View File

@@ -0,0 +1,22 @@
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.alfresco.asie.model.BaseResponse;
@JsonIgnoreProperties(ignoreUnknown = true)
public class SummaryResponse extends BaseResponse {
@JsonProperty(value = "Summary", access = Access.READ_ONLY)
private Summary summary;
public Summary getSummary() {
return summary;
}
public void setSummary(Summary summary) {
this.summary = summary;
}
}

View File

@@ -0,0 +1,43 @@
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 UpdateCoreRequest extends JsonFormattedResponseRequest<UpdateCoreRequest> {
private static final String ACTION = "updateCore";
@QueryParam("action")
@DefaultValue(ACTION)
@Nonnull
private String action = ACTION;
@QueryParam("coreName")
@Nonnull
private String core;
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 UpdateCoreRequest withCore(String core) {
this.core = core;
return this;
}
}

View File

@@ -0,0 +1,26 @@
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 UpdateLog4jRequest extends JsonFormattedResponseRequest<UpdateLog4jRequest> {
private static final String ACTION = "log4j";
@QueryParam("action")
@DefaultValue(ACTION)
@Nonnull
private String action = ACTION;
public String getAction() {
return action;
}
public void setAction(String action) {
this.action = action;
}
}

View File

@@ -0,0 +1,26 @@
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 UpdateSharedRequest extends JsonFormattedResponseRequest<UpdateSharedRequest> {
private static final String ACTION = "updateShared";
@QueryParam("action")
@DefaultValue(ACTION)
@Nonnull
private String action = ACTION;
public String getAction() {
return action;
}
public void setAction(String action) {
this.action = action;
}
}

12
module/.gitignore vendored Normal file
View File

@@ -0,0 +1,12 @@
# Maven
target
pom.xml.versionsBackup
# Eclipse
.project
.classpath
.settings
.vscode
# IDEA
/.idea/

1
module/README.md Normal file
View File

@@ -0,0 +1 @@
# ASIE Platform Module Library

182
module/pom.xml Normal file
View File

@@ -0,0 +1,182 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.inteligr8.alfresco</groupId>
<artifactId>asie-platform-module-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>
<artifactId>asie-platform-module</artifactId>
<packaging>jar</packaging>
<name>ASIE Platform Module</name>
<properties>
<alfresco.sdk.version>5.2.0</alfresco.sdk.version>
<alfresco.platform.version>23.3.2</alfresco.platform.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.alfresco</groupId>
<artifactId>acs-packaging</artifactId>
<version>${alfresco.platform.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Provided by ACS, but packaged due to common-rest-client -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</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.jakarta.rs</groupId>
<artifactId>jackson-jakarta-rs-json-provider</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-client</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.inteligr8.alfresco</groupId>
<artifactId>asie-api</artifactId>
<version>1.0-SNAPSHOT-asie2</version>
</dependency>
<dependency>
<groupId>com.inteligr8</groupId>
<artifactId>common-rest-client</artifactId>
<version>3.0.1-cxf</version>
</dependency>
<!-- Needed by this module, but provided by ACS -->
<dependency>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-enterprise-repository</artifactId>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-elasticsearch-shared</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-repository</artifactId>
<scope>provided</scope>
<exclusions>
<!-- JDK 9+ Eclipse build issue -->
<exclusion>
<groupId>xpp3</groupId>
<artifactId>xpp3</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Alfresco Modules required to use this module -->
<dependency>
<groupId>com.inteligr8.alfresco</groupId>
<artifactId>cxf-jaxrs-platform-module</artifactId>
<version>1.2.0-acs-v23.2</version>
<scope>provided</scope>
</dependency>
<!-- Including for testing purposes only -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<!-- avoids log4j dependency -->
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
</plugin>
<!-- avoids struts dependency -->
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.12.1</version>
</plugin>
<!-- Force use of a new maven-dependency-plugin that doesn't download struts dependency -->
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.7.1</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>io.repaint.maven</groupId>
<artifactId>tiles-maven-plugin</artifactId>
<version>2.40</version>
<extensions>true</extensions>
<configuration>
<tiles>
<!-- 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>
</tiles>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>alfresco-private</id>
<url>https://artifacts.alfresco.com/nexus/content/groups/private</url>
</repository>
</repositories>
</project>

74
module/rad.ps1 Normal file
View File

@@ -0,0 +1,74 @@
function discoverArtifactId {
$script:ARTIFACT_ID=(mvn -q -Dexpression=project"."artifactId -DforceStdout help:evaluate)
}
function rebuild {
echo "Rebuilding project ..."
mvn process-classes
}
function start_ {
echo "Rebuilding project and starting Docker containers to support rapid application development ..."
mvn -Drad process-classes
}
function start_log {
echo "Rebuilding project and starting Docker containers to support rapid application development ..."
mvn -Drad "-Ddocker.showLogs" process-classes
}
function stop_ {
discoverArtifactId
echo "Stopping Docker containers that supported rapid application development ..."
docker container ls --filter name=${ARTIFACT_ID}-*
echo "Stopping containers ..."
docker container stop (docker container ls -q --filter name=${ARTIFACT_ID}-*)
echo "Removing containers ..."
docker container rm (docker container ls -aq --filter name=${ARTIFACT_ID}-*)
}
function tail_logs {
param (
$container
)
discoverArtifactId
docker container logs -f (docker container ls -q --filter name=${ARTIFACT_ID}-${container})
}
function list {
discoverArtifactId
docker container ls --filter name=${ARTIFACT_ID}-*
}
switch ($args[0]) {
"start" {
start_
}
"start_log" {
start_log
}
"stop" {
stop_
}
"restart" {
stop_
start_
}
"rebuild" {
rebuild
}
"tail" {
tail_logs $args[1]
}
"containers" {
list
}
default {
echo "Usage: .\rad.ps1 [ start | start_log | stop | restart | rebuild | tail {container} | containers ]"
}
}
echo "Completed!"

71
module/rad.sh Normal file
View File

@@ -0,0 +1,71 @@
#!/bin/sh
discoverArtifactId() {
ARTIFACT_ID=`mvn -q -Dexpression=project.artifactId -DforceStdout help:evaluate`
}
rebuild() {
echo "Rebuilding project ..."
mvn process-classes
}
start() {
echo "Rebuilding project and starting Docker containers to support rapid application development ..."
mvn -Drad process-classes
}
start_log() {
echo "Rebuilding project and starting Docker containers to support rapid application development ..."
mvn -Drad -Ddocker.showLogs process-classes
}
stop() {
discoverArtifactId
echo "Stopping Docker containers that supported rapid application development ..."
docker container ls --filter name=${ARTIFACT_ID}-*
echo "Stopping containers ..."
docker container stop `docker container ls -q --filter name=${ARTIFACT_ID}-*`
echo "Removing containers ..."
docker container rm `docker container ls -aq --filter name=${ARTIFACT_ID}-*`
}
tail_logs() {
discoverArtifactId
docker container logs -f `docker container ls -q --filter name=${ARTIFACT_ID}-$1`
}
list() {
discoverArtifactId
docker container ls --filter name=${ARTIFACT_ID}-*
}
case "$1" in
start)
start
;;
start_log)
start_log
;;
stop)
stop
;;
restart)
stop
start
;;
rebuild)
rebuild
;;
tail)
tail_logs $2
;;
containers)
list
;;
*)
echo "Usage: ./rad.sh [ start | start_log | stop | restart | rebuild | tail {container} | containers ]"
exit 1
esac
echo "Completed!"

View File

@@ -0,0 +1,114 @@
{
".SHARD_STATE": {
"d2fda078-887c-46a3-bda0-78887c06a305": {
"type": "ShardState",
"shardInstance": {
"type": "ShardInstance",
"shard": {
"type": "Shard",
"floc": {
"type": "Floc",
"storeRefs": [
"workspace://SpacesStore"
],
"numberOfShards": 32,
"shardMethod": "PROPERTY",
"template": "rerank",
"hasContent": true,
"propertyBag": {}
},
"instance": 29
},
"baseUrl": "/solr/alfresco-year-29",
"port": 8983,
"hostName": "localhost"
},
"isMaster": true,
"lastUpdated": 1729099258001,
"lastIndexedChangeSetId": 1189,
"lastIndexedTxCommitTime": 1729098871726,
"lastIndexedTxId": 524708,
"lastIndexedChangeSetCommitTime": 1729010974050,
"propertyBag": {
"shard.key": "cm:created",
"shard.regex": "^(\\d{4})",
"coreName": "alfresco-year-29"
}
},
"8cd7436d-787a-478a-9743-6d787a778a1f": {
"type": "ShardState",
"shardInstance": {
"type": "ShardInstance",
"shard": {
"type": "Shard",
"floc": {
"type": "Floc",
"storeRefs": [
"workspace://SpacesStore"
],
"numberOfShards": 32,
"shardMethod": "PROPERTY",
"template": "rerank",
"hasContent": true,
"propertyBag": {}
},
"instance": 0
},
"baseUrl": "/solr/alfresco-year-0",
"port": 8983,
"hostName": "localhost"
},
"isMaster": true,
"lastUpdated": 1729099258001,
"lastIndexedChangeSetId": 1189,
"lastIndexedTxCommitTime": 1729098871726,
"lastIndexedTxId": 524708,
"lastIndexedChangeSetCommitTime": 1729010974050,
"propertyBag": {
"shard.key": "cm:created",
"shard.regex": "^(\\d{4})",
"coreName": "alfresco-year-29"
}
},
"ae5e918d-c602-4bb0-9e91-8dc6028bb088": {
"type": "ShardState",
"shardInstance": {
"type": "ShardInstance",
"shard": {
"type": "Shard",
"floc": {
"type": "Floc",
"storeRefs": [
"workspace://SpacesStore"
],
"numberOfShards": 32,
"shardMethod": "PROPERTY",
"template": "rerank",
"hasContent": true,
"propertyBag": {}
},
"instance": 1
},
"baseUrl": "/solr/alfresco-year-1",
"port": 8983,
"hostName": "localhost"
},
"isMaster": true,
"lastUpdated": 1729099258001,
"lastIndexedChangeSetId": 1189,
"lastIndexedTxCommitTime": 1729098871726,
"lastIndexedTxId": 524708,
"lastIndexedChangeSetCommitTime": 1729010974050,
"propertyBag": {
"shard.key": "cm:created",
"shard.regex": "^(\\d{4})",
"coreName": "alfresco-year-29"
}
}
},
".SHARD_SUBSCRIPTION": {
"8cd7436d-787a-478a-9743-6d787a778a1f": "Shard #0 subscribed on Wed Oct 16 17:20:07 UTC 2024 (timestamp = 1729099207319)",
"ae5e918d-c602-4bb0-9e91-8dc6028bb088": "Shard #1 subscribed on Wed Oct 16 17:20:08 UTC 2024 (timestamp = 1729099208026)"
}
}

View File

@@ -0,0 +1,13 @@
package com.inteligr8.alfresco.asie;
public interface Constants {
static final String QUALIFIER_ASIE = "asie";
static final String BEAN_SOLR_ADMIN_CLIENT = "search.SolrAdminClient";
static final String BEAN_SHARD_REGISTRY = "search.ShardRegistry";
static final String ATTR_SHARD_STATE = ".SHARD_STATE";
static final String ATTR_SHARD_SUBSCRIPTION = ".SHARD_SUBSCRIPTION";
static final String ATTR_ASIE = "inteligr8.asie";
static final String ATTR_UNLOADED_NODE_CORES = "unloadedNode.cores";
}

View File

@@ -0,0 +1,57 @@
package com.inteligr8.alfresco.asie.compute;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.service.SolrShardHashService;
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class SolrShardEnumeratedHashTable extends SolrShardHashTable<Object> {
/**
* For Spring use only
*/
public SolrShardEnumeratedHashTable(int shards) {
super(shards);
}
/**
* For non-Spring use
*/
public SolrShardEnumeratedHashTable(int shards, SolrShardHashService sshs) {
super(shards, sshs);
}
public SolrShardEnumeratedHashTable build(Collection<?> objs) {
for (Object obj : objs) {
int hashed = this.hashForward(obj);
Set<Object> ns = this.reverseHash.get(hashed);
if (ns == null)
this.reverseHash.put(hashed, ns = new HashSet<>());
ns.add(obj);
}
return this;
}
public SolrShardEnumeratedHashTable build(Object... objs) {
for (Object obj : objs) {
int hashed = this.hashForward(obj);
Set<Object> ns = this.reverseHash.get(hashed);
if (ns == null)
this.reverseHash.put(hashed, ns = new HashSet<>());
ns.add(obj);
}
return this;
}
}

View File

@@ -0,0 +1,43 @@
package com.inteligr8.alfresco.asie.compute;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import com.inteligr8.alfresco.asie.service.SolrShardHashService;
public class SolrShardHashTable<T> {
private final int shards;
protected Map<Integer, Set<T>> reverseHash;
@Autowired
private SolrShardHashService sshs;
/**
* For Spring use only
*/
public SolrShardHashTable(int shards) {
this.shards = shards;
this.reverseHash = new HashMap<>();
}
/**
* For non-Spring use
*/
public SolrShardHashTable(int shards, SolrShardHashService sshs) {
this(shards);
this.sshs = sshs;
}
public int hashForward(Object obj) {
return this.sshs.hash(obj, this.shards);
}
public Set<T> hashReverse(int hash) {
return this.reverseHash.get(hash);
}
}

View File

@@ -0,0 +1,43 @@
package com.inteligr8.alfresco.asie.compute;
import java.util.HashSet;
import java.util.Set;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.service.SolrShardHashService;
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class SolrShardNumericHashTable extends SolrShardHashTable<Long> {
/**
* For Spring use only
*/
public SolrShardNumericHashTable(int shards) {
super(shards);
}
/**
* For non-Spring use
*/
public SolrShardNumericHashTable(int shards, SolrShardHashService sshs) {
super(shards, sshs);
}
public SolrShardNumericHashTable build(long start, long end) {
for (long n = start; n <= end; n++) {
int hashed = this.hashForward(n);
Set<Long> ns = this.reverseHash.get(hashed);
if (ns == null)
this.reverseHash.put(hashed, ns = new HashSet<>());
ns.add(n);
}
return this;
}
}

View File

@@ -0,0 +1,45 @@
package com.inteligr8.alfresco.asie.enterprise.bootstrap;
import org.alfresco.service.cmr.attributes.AttributeService;
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.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEvent;
import org.springframework.extensions.surf.util.AbstractLifecycleBean;
import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.Constants;
/**
* This is a workaround to a bug with the Alfresco Enterprise Solr purge
* functionality, letting it fail due to a concurrency exception. This skips
* all the fancy stuff and just hard purges the shards.
*/
@Component
public class ShardPurgeService extends AbstractLifecycleBean {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
@Qualifier(Constants.QUALIFIER_ASIE)
private AttributeService attributeService;
@Value("${search.solrShardRegistry.purgeOnInit}")
private boolean enabled;
@Override
protected void onBootstrap(ApplicationEvent event) {
this.logger.info("Using workaround for Alfresco Enterprise Solr ShardRegistry purge");
this.attributeService.removeAttributes(Constants.ATTR_SHARD_STATE);
this.attributeService.removeAttributes(Constants.ATTR_SHARD_SUBSCRIPTION);
this.attributeService.removeAttributes(Constants.ATTR_ASIE);
}
@Override
protected void onShutdown(ApplicationEvent event) {
// nothing to do
}
}

View File

@@ -0,0 +1,24 @@
package com.inteligr8.alfresco.asie.enterprise.model;
import com.inteligr8.alfresco.asie.model.NodeParameterSet;
public class NodeShardParameterSet extends NodeParameterSet {
private ShardSet shardSet;
private int shardId;
public NodeShardParameterSet(String nodeHostname, int nodePort, ShardSet shardSet, int shardId) {
super(nodeHostname, nodePort);
this.shardSet = shardSet;
this.shardId = shardId;
}
public ShardSet getShardSet() {
return shardSet;
}
public int getShardId() {
return shardId;
}
}

View File

@@ -0,0 +1,23 @@
package com.inteligr8.alfresco.asie.enterprise.model;
import com.inteligr8.alfresco.asie.model.RequestParameterSet;
public class ShardParameterSet implements RequestParameterSet {
private ShardSet set;
private int id;
public ShardParameterSet(ShardSet set, int id) {
this.set = set;
this.id = id;
}
public ShardSet getSet() {
return set;
}
public int getId() {
return id;
}
}

View File

@@ -0,0 +1,115 @@
package com.inteligr8.alfresco.asie.enterprise.model;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.alfresco.repo.index.shard.Floc;
import org.alfresco.repo.index.shard.ShardMethodEnum;
import org.alfresco.repo.index.shard.ShardState;
import org.apache.commons.lang3.builder.HashCodeBuilder;
public class ShardSet {
/**
* Examples:
*
* 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;
private final boolean hasContent;
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) {
Matcher matcher = this.shardSetPattern.matcher(shardSetSpec);
if (!matcher.find())
throw new IllegalArgumentException("The shard set '" + shardSetSpec + "' is not properly formatted");
this.method = ShardMethodEnum.valueOf(matcher.group(1));
this.hasContent = ";fulltext".equals(matcher.group(2));
this.config = new HashMap<>();
for (int g = 3; g < matcher.groupCount(); g += 3)
if (matcher.group(g) != null)
this.config.put("shard." + matcher.group(g+1), matcher.group(g+2));
}
public ShardMethodEnum getMethod() {
return method;
}
public boolean hasContent() {
return hasContent;
}
public String toSpec() {
StringBuilder spec = new StringBuilder(this.method.toString());
if (this.hasContent)
spec.append(";fulltext");
for (Entry<String, String> c : this.config.entrySet()) {
if (!c.getKey().startsWith("shard."))
continue;
spec.append(';').append(c.getKey().substring(6)).append(':').append(c.getValue());
}
return spec.toString();
}
public Map<String, String> getConfig() {
return config;
}
public boolean isFor(ShardState shardState) {
return this.method.equals(shardState.getShardInstance().getShard().getFloc().getShardMethod()) &&
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;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ShardSet))
return false;
ShardSet shardSet = (ShardSet) obj;
return this.method.equals(shardSet.method) && this.config.equals(shardSet.config);
}
@Override
public int hashCode() {
if (this.hash == null) {
this.hash = new HashCodeBuilder().append(this.method).append(this.hasContent).append(this.config).build();
}
return this.hash;
}
@Override
public String toString() {
return this.toSpec();
}
}

View File

@@ -0,0 +1,129 @@
package com.inteligr8.alfresco.asie.enterprise.rest;
import java.text.NumberFormat;
import java.time.LocalDate;
import java.time.Month;
import java.time.Year;
import java.time.temporal.WeekFields;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.alfresco.repo.index.shard.ShardInstance;
import org.alfresco.repo.index.shard.ShardRegistry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.extensions.webscripts.WebScriptException;
import org.springframework.http.HttpStatus;
import com.inteligr8.alfresco.asie.Constants;
import com.inteligr8.alfresco.asie.api.CoreAdminApi;
import com.inteligr8.alfresco.asie.compute.SolrShardEnumeratedHashTable;
import com.inteligr8.alfresco.asie.compute.SolrShardHashTable;
import com.inteligr8.alfresco.asie.compute.SolrShardNumericHashTable;
import com.inteligr8.alfresco.asie.enterprise.rest.model.ShardInfo;
import com.inteligr8.alfresco.asie.enterprise.rest.model.ShardSetInfo;
import com.inteligr8.alfresco.asie.rest.AbstractAsieWebScript;
import com.inteligr8.alfresco.asie.service.SolrShardHashService;
public abstract class AbstractAsieEnterpriseWebScript extends AbstractAsieWebScript {
private final Pattern sampleTypePattern = Pattern.compile("([A-Za-z]+)([0-9]+)");
public enum SolrShardHashSampleType {
PropertyYear,
PropertyQuarter,
PropertyMonth,
PropertyWeek
}
@Autowired
@Qualifier(Constants.BEAN_SHARD_REGISTRY)
private ShardRegistry shardRegistry;
@Autowired
private SolrShardHashService sshs;
protected ShardRegistry getShardRegistry() {
return shardRegistry;
}
protected SolrShardHashTable<?> createSampleHashTable(String sampleType) {
Matcher matcher = this.sampleTypePattern.matcher(sampleType);
if (!matcher.find())
throw new WebScriptException(HttpStatus.BAD_REQUEST.value(), "The 'sampleType` is not properly formatted");
try {
SolrShardHashSampleType type = SolrShardHashSampleType.valueOf(matcher.group(1));
int shards = Integer.parseInt(matcher.group(2));
return this.createSampleHashTable(type, shards);
} catch (NumberFormatException nfe) {
// this should never happen, because of the regex
throw new WebScriptException(HttpStatus.BAD_REQUEST.value(), "The 'sampleType` number '" + matcher.group(2) + "' is not properly formatted");
} catch (IllegalArgumentException iae) {
throw new WebScriptException(HttpStatus.BAD_REQUEST.value(), "The 'sampleType` '" + matcher.group(1) + "' is not supported");
}
}
protected SolrShardHashTable<?> createSampleHashTable(SolrShardHashSampleType sampleType, int shards) {
int thisYear = Year.now().getValue();
switch (sampleType) {
case PropertyYear:
return new SolrShardNumericHashTable(shards, this.sshs).build(thisYear-40, thisYear+10);
case PropertyQuarter:
List<String> quarters = new LinkedList<>();
for (int year = thisYear - 15; year <= thisYear + 5; year++)
for (int quarter = 1; quarter <= 4; quarter++)
quarters.add(year + "-Q" + quarter);
return new SolrShardEnumeratedHashTable(shards, this.sshs).build(quarters);
case PropertyMonth:
NumberFormat monthFormat = NumberFormat.getInstance();
monthFormat.setMinimumIntegerDigits(2);
List<String> months = new LinkedList<>();
for (int year = thisYear - 15; year <= thisYear + 5; year++)
for (Month month : Month.values())
months.add(year + "-" + monthFormat.format(month.getValue()));
return new SolrShardEnumeratedHashTable(shards, this.sshs).build(months);
case PropertyWeek:
List<String> weeks = new LinkedList<>();
for (int year = thisYear - 15; year <= thisYear + 5; year++) {
int maxWeek = LocalDate.of(year, Month.DECEMBER.getValue(), 31).get(WeekFields.SUNDAY_START.weekOfYear());
for (int week = 1; week <= maxWeek; week++)
weeks.add(year + "-W" + week);
}
return new SolrShardEnumeratedHashTable(shards, this.sshs).build(weeks);
default:
throw new IllegalArgumentException();
}
}
protected void addShardHashSamples(ShardSetInfo shardSet, ShardInfo shard, SolrShardHashTable<?> sampleHashTable) {
Set<Object> existingValues = shardSet.getShardHashSamples().get(shard.getId());
if (existingValues == null) {
existingValues = new HashSet<>();
shardSet.getShardHashSamples().put(shard.getId(), existingValues);
}
Set<?> values = sampleHashTable.hashReverse(shard.getId());
if (values != null)
existingValues.addAll(values);
}
protected void addShardHashSamples(ShardInfo shard, SolrShardHashTable<?> sampleHashTable) {
this.addShardHashSamples(shard.getShardSet(), shard, sampleHashTable);
}
protected int getLatestTxId() {
return 0;
}
protected CoreAdminApi getApi(ShardInstance shard) {
return this.createApi(shard.getHostName(), shard.getPort());
}
}

View File

@@ -0,0 +1,35 @@
package com.inteligr8.alfresco.asie.enterprise.rest;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.extensions.webscripts.WebScriptResponse;
import com.inteligr8.alfresco.asie.enterprise.model.ShardSet;
public abstract class AbstractAsieNodeShardWebScript extends AbstractAsieEnterpriseWebScript {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public final void executeAuthorized(WebScriptRequest req, WebScriptResponse res) throws IOException {
this.logger.trace("execute()");
String nodeEndpoint = this.getRequiredPathParameter(req, "nodeEndpoint");
int colon = nodeEndpoint.lastIndexOf(':');
String nodeHostname = colon < 0 ? nodeEndpoint : nodeEndpoint.substring(0, colon);
int nodePort = colon < 0 ? this.getDefaultSolrPort() : Integer.parseInt(nodeEndpoint.substring(colon+1));
ShardSet shardSet = this.getRequiredPathParameter(req, "shardSet", ShardSet.class);
int shardId = this.getRequiredPathParameter(req, "shardId", Integer.class);
this.execute(req, res, nodeHostname, nodePort, shardSet, shardId);
}
protected abstract void execute(WebScriptRequest req, WebScriptResponse res,
String nodeHostname, int nodePort, ShardSet shardSet, int shardId)
throws IOException;
}

View File

@@ -0,0 +1,52 @@
package com.inteligr8.alfresco.asie.enterprise.rest;
import java.io.IOException;
import java.util.Set;
import org.alfresco.repo.index.shard.ShardState;
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.enterprise.service.ShardDiscoveryService;
public abstract class AbstractAsieNodeWebScript extends AbstractAsieEnterpriseWebScript {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private ShardDiscoveryService sds;
@Override
public final void executeAuthorized(WebScriptRequest req, WebScriptResponse res) throws IOException {
this.logger.trace("execute()");
String nodeEndpoint = this.getRequiredPathParameter(req, "nodeEndpoint");
int colon = nodeEndpoint.lastIndexOf(':');
String nodeHostname = colon < 0 ? nodeEndpoint : nodeEndpoint.substring(0, colon);
nodeHostname = nodeHostname.replace('_', '.');
int nodePort = colon < 0 ? this.getDefaultSolrPort() : Integer.parseInt(nodeEndpoint.substring(colon+1));
this.execute(req, res, nodeHostname, nodePort);
}
protected void execute(WebScriptRequest req, WebScriptResponse res, String nodeHostname, int nodePort) throws IOException {
this.logger.trace("execute({}, {})", nodeHostname, nodePort);
Set<ShardState> shardsOnNode = this.sds.findByNode(nodeHostname, nodePort);
if (shardsOnNode == null || shardsOnNode.isEmpty())
throw new WebScriptException(HttpStatus.NOT_FOUND.value(), "The ASIE node could not be found");
this.execute(req, res, shardsOnNode);
}
protected void execute(WebScriptRequest req, WebScriptResponse res, Set<ShardState> registeredNodeShards) throws IOException {
this.logger.trace("execute({})", registeredNodeShards.size());
// made to be optionally overridden
}
}

View File

@@ -0,0 +1,46 @@
package com.inteligr8.alfresco.asie.enterprise.rest;
import java.io.IOException;
import java.util.Set;
import org.alfresco.repo.index.shard.ShardState;
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.enterprise.model.ShardSet;
import com.inteligr8.alfresco.asie.enterprise.service.ShardDiscoveryService;
public abstract class AbstractAsieShardWebScript extends AbstractAsieEnterpriseWebScript {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private ShardDiscoveryService sds;
@Override
public void executeAuthorized(WebScriptRequest req, WebScriptResponse res) throws IOException {
this.logger.trace("execute()");
ShardSet shardSet = this.getRequiredPathParameter(req, "shardSet", ShardSet.class);
this.logger.debug("Parsed shard set: {}", shardSet);
int shardId = this.getRequiredPathParameter(req, "shardId", Integer.class);
try {
Set<ShardState> registeredShardNodes = this.sds.findByShard(shardSet, shardId);
if (registeredShardNodes == null || registeredShardNodes.isEmpty())
throw new WebScriptException(HttpStatus.NOT_FOUND.value(), "The ASIE shard set or shard could not be found");
this.execute(req, res, registeredShardNodes);
} catch (IllegalArgumentException iae) {
throw new WebScriptException(HttpStatus.BAD_REQUEST.value(), iae.getMessage());
}
}
protected abstract void execute(WebScriptRequest req, WebScriptResponse res, Set<ShardState> registeredShardNodes) throws IOException;
}

View File

@@ -0,0 +1,164 @@
package com.inteligr8.alfresco.asie.enterprise.rest;
import java.io.IOException;
import java.io.Serializable;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.alfresco.repo.index.shard.ShardState;
import org.alfresco.service.cmr.attributes.AttributeService;
import org.alfresco.service.cmr.attributes.AttributeService.AttributeQueryCallback;
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.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.Constants;
import com.inteligr8.alfresco.asie.api.CoreAdminApi;
import com.inteligr8.alfresco.asie.enterprise.service.ShardBackupService;
import com.inteligr8.alfresco.asie.enterprise.service.ShardStateService;
import com.inteligr8.alfresco.asie.model.NodeParameterSet;
import com.inteligr8.alfresco.asie.model.core.DisableIndexingRequest;
import com.inteligr8.solr.model.CoreMetadata;
import com.inteligr8.solr.model.core.StatusRequest;
import com.inteligr8.solr.model.core.StatusResponse;
import com.inteligr8.solr.model.core.UnloadRequest;
public abstract class AbstractUnregisterNodeWebScript<T extends NodeParameterSet> extends AbstractAsieNodeWebScript {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
@Qualifier(Constants.QUALIFIER_ASIE)
private AttributeService attrService;
@Autowired
private ShardBackupService sbs;
@Autowired
private ShardStateService sss;
protected abstract T createParameters(WebScriptRequest req, String nodeHostname, int nodePort);
@Override
protected void execute(WebScriptRequest req, WebScriptResponse res, String nodeHostname, int nodePort)
throws IOException {
T params = this.createParameters(req, nodeHostname, nodePort);
final Map<String, ShardState> guidCores = new HashMap<>();
AttributeQueryCallback callback = new AttributeQueryCallback() {
@Override
public boolean handleAttribute(Long id, Serializable value, Serializable[] keys) {
ShardState shardState = (ShardState) value;
if (!matches(params, shardState))
return true;
String guid = (String) keys[1];
guidCores.put(guid, shardState);
return true;
}
};
this.attrService.getAttributes(callback, Constants.ATTR_SHARD_STATE);
Serializable[] keys = new String[] {
Constants.ATTR_ASIE,
Constants.ATTR_UNLOADED_NODE_CORES,
nodeHostname + ":" + nodePort
};
@SuppressWarnings("unchecked")
Map<String, String> cores = (Map<String, String>) this.attrService.getAttribute(keys);
if (cores == null)
cores = new HashMap<>();
try {
for (Entry<String, ShardState> guidCore : guidCores.entrySet()) {
ShardState shardNode = guidCore.getValue();
String core = shardNode.getPropertyBag().get("coreName");
StatusResponse status = this.getCoreStatus(nodeHostname, nodePort, core);
if (status == null)
throw new WebScriptException(HttpStatus.INTERNAL_SERVER_ERROR.value(), "This should never happen");
CoreMetadata coreMetadata = status.getStatus().getCores().get(core);
if (coreMetadata == null || coreMetadata.getName() == null) {
this.logger.warn("Registered core does not actually exist on the node host; could be a DNS issue: {}:{}/solr/{}", nodeHostname, nodePort, core);
continue;
}
this.unloadCore(nodeHostname, nodePort, core);
cores.put(core, coreMetadata.getInstancePath());
String guid = guidCore.getKey();
this.removeShard(guid, shardNode);
}
} finally {
// FIXME maybe a separate tx?
this.attrService.setAttribute((Serializable) cores, keys);
}
res.setStatus(HttpStatus.OK.value());
}
protected boolean matches(T params, ShardState shardState) {
if (!params.getHostname().equalsIgnoreCase(shardState.getShardInstance().getHostName())) {
InetAddress nodeAddress = params.getAddress();
if (nodeAddress == null)
return false;
InetAddress shardNodeAddress = resolve(shardState.getShardInstance().getHostName());
if (!nodeAddress.equals(shardNodeAddress))
return false;
}
if (params.getPort() != shardState.getShardInstance().getPort())
return false;
return true;
}
private InetAddress resolve(String hostname) {
try {
return InetAddress.getByName(hostname);
} catch (UnknownHostException uhe) {
// suppress
return null;
}
}
protected StatusResponse getCoreStatus(String nodeHostname, int nodePort, String core) {
this.logger.debug("Retrieving status for core {} on ASIE node: {}", core, nodeHostname);
CoreAdminApi api = this.createApi(nodeHostname, nodePort);
return api.getStatus(new StatusRequest().withCore(core));
}
protected void unloadCore(String nodeHostname, int nodePort, String core) {
this.logger.info("Unloading core {} on ASIE node: {}", core, nodeHostname);
CoreAdminApi api = this.createApi(nodeHostname, nodePort);
api.unload(new UnloadRequest().withCore(core));
}
protected void disableIndexing(String nodeHostname, int nodePort) {
this.logger.info("Disabling indexing on ASIE node: {}", nodeHostname);
CoreAdminApi api = this.createApi(nodeHostname, nodePort);
api.disableIndexing(new DisableIndexingRequest());
}
protected void disableCoreIndexing(String nodeHostname, int nodePort, String core) {
this.logger.info("Disabling indexing on ASIE node/core: {}/{}", nodeHostname, core);
CoreAdminApi api = this.createApi(nodeHostname, nodePort);
api.disableIndexing(new DisableIndexingRequest().withCore(core));
}
protected void removeShard(String guid, ShardState shardNode) {
this.sss.clear(guid);
this.sbs.forget(shardNode);
}
}

View File

@@ -0,0 +1,32 @@
package com.inteligr8.alfresco.asie.enterprise.rest;
import java.io.IOException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.extensions.webscripts.AbstractWebScript;
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.enterprise.service.ShardBackupService;
import com.inteligr8.alfresco.asie.enterprise.service.ShardStateService;
@Component(value = "webscript.com.inteligr8.alfresco.asie.enterprise.registry.delete")
public class ClearRegistryWebScript extends AbstractWebScript {
@Autowired
private ShardBackupService sbs;
@Autowired
private ShardStateService sss;
@Override
public void execute(WebScriptRequest req, WebScriptResponse res) throws IOException {
this.sss.clear();
this.sbs.forget();
res.setStatus(HttpStatus.OK.value());
}
}

View File

@@ -0,0 +1,35 @@
package com.inteligr8.alfresco.asie.enterprise.rest;
import java.io.IOException;
import java.util.Set;
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.WebScriptResponse;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.enterprise.service.ShardBackupService;
@Component(value = "webscript.com.inteligr8.alfresco.asie.enterprise.backupNode.get")
public class GetBackupNodeWebScript extends AbstractAsieShardWebScript {
@Autowired
private ShardBackupService sbs;
@Override
public void execute(WebScriptRequest req, WebScriptResponse res, Set<ShardState> shardNodes) throws IOException {
if (shardNodes.isEmpty())
throw new WebScriptException(HttpStatus.NOT_FOUND.value(), "The ASIE shard state could not be found");
String nodeId = this.sbs.fetchNodeId(shardNodes);
res.setContentType(MediaType.APPLICATION_JSON_VALUE);
res.setContentEncoding("utf-8");
this.getObjectMapper().writeValue(res.getWriter(), nodeId);
}
}

View File

@@ -0,0 +1,39 @@
package com.inteligr8.alfresco.asie.enterprise.rest;
import java.io.IOException;
import java.util.Set;
import org.alfresco.repo.index.shard.ShardInstance;
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.WebScriptResponse;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.enterprise.rest.model.NodeInfo;
import com.inteligr8.alfresco.asie.enterprise.service.ShardAnalysisService;
@Component(value = "webscript.com.inteligr8.alfresco.asie.enterprise.leadNode.get")
public class GetLeadNodeWebScript extends AbstractAsieShardWebScript {
@Autowired
private ShardAnalysisService sas;
@Override
public void execute(WebScriptRequest req, WebScriptResponse res, Set<ShardState> shardNodesCache) throws IOException {
if (shardNodesCache.isEmpty())
throw new WebScriptException(HttpStatus.NOT_FOUND.value(), "The ASIE shard state could not be found");
ShardInstance latestNode = this.sas.computeLeadShard(shardNodesCache);
if (latestNode == null)
throw new WebScriptException(HttpStatus.NOT_FOUND.value(), "The ASIE shard state could not be found");
res.setContentType(MediaType.APPLICATION_JSON_VALUE);
res.setContentEncoding("utf-8");
this.getObjectMapper().writeValue(res.getWriter(), NodeInfo.determineId(latestNode));
}
}

View File

@@ -0,0 +1,53 @@
package com.inteligr8.alfresco.asie.enterprise.rest;
import java.io.IOException;
import java.time.Instant;
import java.time.OffsetDateTime;
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.WebScriptResponse;
import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.compute.SolrShardHashTable;
import com.inteligr8.alfresco.asie.enterprise.rest.model.NodeInfo;
import com.inteligr8.alfresco.asie.enterprise.rest.model.NodeShardInfo;
import com.inteligr8.alfresco.asie.enterprise.rest.model.ShardInfo;
import com.inteligr8.alfresco.asie.enterprise.rest.model.ShardSetInfo;
@Component(value = "webscript.com.inteligr8.alfresco.asie.enterprise.node.get")
public class GetNodeWebScript extends AbstractAsieNodeWebScript {
@Override
protected void execute(WebScriptRequest req, WebScriptResponse res, Set<ShardState> registeredNodeShards) 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);
SolrShardHashTable<?> sampleHashTable = sampleHashType == null ? null : this.createSampleHashTable(sampleHashType, maxShards);
NodeInfo node = new NodeShardInfo(registeredNode);
for (ShardState registeredNodeShard : registeredNodeShards) {
ShardInfo shard = new ShardInfo();
shard.setId(registeredNodeShard.getShardInstance().getShard().getInstance());
shard.setLatestTx(OffsetDateTime.ofInstant(Instant.ofEpochMilli(registeredNodeShard.getLastIndexedTxCommitTime()), ZoneOffset.UTC));
shard.setTxsCompleted(registeredNodeShard.getLastIndexedTxId());
shard.setShardSet(new ShardSetInfo(registeredNodeShard.getShardInstance().getShard().getFloc(), registeredNodeShard));
if (sampleHashTable != null)
this.addShardHashSamples(shard, sampleHashTable);
node.getShards().put(shard.getId(), shard);
}
res.setContentType("application/json");
res.setContentEncoding("utf-8");
this.getObjectMapper().writeValue(res.getWriter(), node);
}
}

View File

@@ -0,0 +1,70 @@
package com.inteligr8.alfresco.asie.enterprise.rest;
import java.io.IOException;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import org.alfresco.repo.index.shard.Floc;
import org.alfresco.repo.index.shard.Shard;
import org.alfresco.repo.index.shard.ShardState;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.compute.SolrShardHashTable;
import com.inteligr8.alfresco.asie.enterprise.rest.model.NodeInfo;
import com.inteligr8.alfresco.asie.enterprise.rest.model.NodeShardInfo;
import com.inteligr8.alfresco.asie.enterprise.rest.model.ShardInfo;
import com.inteligr8.alfresco.asie.enterprise.rest.model.ShardSetInfo;
@Component(value = "webscript.com.inteligr8.alfresco.asie.enterprise.nodes.get")
public class GetNodesWebScript extends AbstractAsieEnterpriseWebScript {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public void executeAuthorized(WebScriptRequest req, WebScriptResponse res) throws IOException {
this.logger.trace("execute()");
Map<Floc, Map<Shard, Set<ShardState>>> flocs = this.getShardRegistry().getFlocs();
if (flocs.isEmpty())
throw new WebScriptException(HttpStatus.NO_CONTENT.value(), "There are no ASIE shards registred with ACS");
SolrShardHashSampleType sampleHashType = this.getOptionalQueryParameter(req, "sampleHashType", SolrShardHashSampleType.class);
Map<String, NodeInfo> nodes = new TreeMap<>();
for (Entry<Floc, Map<Shard, Set<ShardState>>> floc : flocs.entrySet()) {
int maxShards = floc.getKey().getNumberOfShards();
SolrShardHashTable<?> sampleHashTable = sampleHashType == null ? null : this.createSampleHashTable(sampleHashType, maxShards);
for (Entry<Shard, Set<ShardState>> registeredShards : floc.getValue().entrySet()) {
for (ShardState registeredShardNode : registeredShards.getValue()) {
NodeInfo node = nodes.get(NodeInfo.determineId(registeredShardNode.getShardInstance()));
if (node == null) {
node = new NodeShardInfo(registeredShardNode.getShardInstance());
nodes.put(node.getId(), node);
}
ShardInfo shard = new ShardInfo(registeredShardNode);
shard.setShardSet(new ShardSetInfo(floc.getKey(), registeredShardNode));
if (sampleHashTable != null)
this.addShardHashSamples(shard, sampleHashTable);
node.getShards().put(shard.getId(), shard);
}
}
}
res.setContentType("application/json");
res.setContentEncoding("utf-8");
this.getObjectMapper().writeValue(res.getWriter(), nodes);
}
}

View File

@@ -0,0 +1,150 @@
package com.inteligr8.alfresco.asie.enterprise.rest;
import java.io.IOException;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.Arrays;
import java.util.Collection;
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.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.ShardState;
import org.alfresco.util.Pair;
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 org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.compute.SolrShardHashTable;
import com.inteligr8.alfresco.asie.enterprise.rest.model.NodeInfo;
import com.inteligr8.alfresco.asie.enterprise.rest.model.PropertyHashShardSetInfo;
import com.inteligr8.alfresco.asie.enterprise.rest.model.ShardInfo;
import com.inteligr8.alfresco.asie.enterprise.rest.model.ShardNodeInfo;
import com.inteligr8.alfresco.asie.enterprise.service.ShardDiscoveryService;
@Component(value = "webscript.com.inteligr8.alfresco.asie.enterprise.propertyHashShards.get")
public class GetPropertyHashShardsWebScript extends AbstractAsieEnterpriseWebScript {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private ShardDiscoveryService sds;
@Override
public void executeAuthorized(WebScriptRequest req, WebScriptResponse res) throws IOException {
this.logger.trace("execute()");
SolrShardHashSampleType sampleHashType = this.getRequiredPathParameter(req, "sampleHashType", SolrShardHashSampleType.class);
Integer max = this.getOptionalQueryParameter(req, "max", Integer.class);
Integer min = this.getOptionalQueryParameter(req, "min", Integer.class);
List<String> values = this.getOptionalQueryParameterAsList(req);
this.validateParameters(min, max, values);
List<PropertyHashShardSetInfo> shardSets = new LinkedList<>();
Collection<Pair<Floc, Map<Shard, Set<ShardState>>>> flocs = this.sds.findByShardMethod(ShardMethodEnum.PROPERTY);
if (flocs.isEmpty())
throw new WebScriptException(HttpStatus.NO_CONTENT.value(), "There are no property-based shards");
for (Pair<Floc, Map<Shard, Set<ShardState>>> floc : flocs) {
ShardState anyShardNode = this.getAnyShardNode(floc.getSecond());
PropertyHashShardSetInfo shardSet = new PropertyHashShardSetInfo(floc.getFirst(), anyShardNode);
shardSet.setShards(new TreeMap<>());
int maxShards = floc.getFirst().getNumberOfShards();
SolrShardHashTable<?> sampleHashTable = this.createSampleHashTable(sampleHashType, maxShards);
Map<Integer, List<Object>> shardToHashMap = new HashMap<>();
if (max != null && min != null) {
for (int i = min; i <= max; i++) {
int shardId = sampleHashTable.hashForward(i);
this.getAdd(shardToHashMap, shardId, i);
}
} else if (values != null) {
for (String value : values) {
int shardId = sampleHashTable.hashForward(value);
this.getAdd(shardToHashMap, shardId, value);
}
}
for (Entry<Shard, Set<ShardState>> shardCache : floc.getSecond().entrySet()) {
ShardInfo shard = new ShardInfo();
shard.setId(shardCache.getKey().getInstance());
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);
shard.getNodes().put(node.getId(), node);
}
List<Object> hashedValues = shardToHashMap.get(shard.getId());
if (hashedValues != null) for (Object hashedValue : hashedValues)
shardSet.getShards().put(hashedValue, shard);
}
shardSets.add(shardSet);
}
res.setContentType(MediaType.APPLICATION_JSON_VALUE);
res.setContentEncoding("utf-8");
this.getObjectMapper().writeValue(res.getWriter(), shardSets);
}
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) {
String valuesStr = this.getOptionalQueryParameter(req, "values");
if (valuesStr == null)
return null;
String[] values = valuesStr.trim().split("[ ]*[,|][ ]*");
return Arrays.asList(values);
}
private void validateParameters(Integer min, Integer max, List<String> values) {
if (max != null && min != null) {
if (min > max)
throw new WebScriptException(HttpStatus.BAD_REQUEST.value(), "The `min` parameter value may not be greater than the `max` parameter value");
} else if (values != null) {
} else {
throw new WebScriptException(HttpStatus.BAD_REQUEST.value(), "Either `min`/`max` or `values` query parameter is required");
}
}
private <K, V> void getAdd(Map<K, List<V>> map, K key, V value) {
List<V> values = map.get(key);
if (values == null) {
values = new LinkedList<>();
map.put(key, values);
}
values.add(value);
}
}

View File

@@ -0,0 +1,96 @@
package com.inteligr8.alfresco.asie.enterprise.rest;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
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 org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.compute.SolrShardEnumeratedHashTable;
import com.inteligr8.alfresco.asie.compute.SolrShardHashTable;
import com.inteligr8.alfresco.asie.compute.SolrShardNumericHashTable;
import com.inteligr8.alfresco.asie.service.SolrShardHashService;
@Component(value = "webscript.com.inteligr8.alfresco.asie.enterprise.sampleHashes.get")
public class GetSampleHashesWebScript extends AbstractAsieEnterpriseWebScript {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private SolrShardHashService sshs;
@Override
public void executeAuthorized(WebScriptRequest req, WebScriptResponse res) throws IOException {
this.logger.trace("execute()");
int shards = this.getRequiredPathParameter(req, "shards", Integer.class);
Integer max = this.getOptionalQueryParameter(req, "max", Integer.class);
Integer min = this.getOptionalQueryParameter(req, "min", Integer.class);
List<String> values = this.getOptionalQueryParameterAsList(req);
this.validateParameters(min, max, values);
Map<String, Integer> forward = new HashMap<>();
Map<Integer, Collection<? extends Object>> reverse = new HashMap<>();
SolrShardHashTable<?> table = null;
if (max != null && min != null) {
table = new SolrShardNumericHashTable(shards, this.sshs).build(min, max);
for (int i = min; i <= max; i++)
forward.put(String.valueOf(i), table.hashForward(i));
} else if (values != null) {
table = new SolrShardEnumeratedHashTable(shards, this.sshs).build(values);
for (String value : values)
forward.put(value, table.hashForward(value));
}
for (int s = 0; s < shards; s++) {
Set<?> vs = table.hashReverse(s);
if (vs != null && !vs.isEmpty()) {
reverse.put(s, vs);
} else {
reverse.put(s, Collections.emptyList());
}
}
Map<String, Map<?, ?>> response = new HashMap<>();
response.put("forward", forward);
response.put("reverse", reverse);
res.setContentType(MediaType.APPLICATION_JSON_VALUE);
res.setContentEncoding("utf-8");
this.getObjectMapper().writeValue(res.getWriter(), response);
}
private List<String> getOptionalQueryParameterAsList(WebScriptRequest req) {
String valuesStr = this.getOptionalQueryParameter(req, "values");
if (valuesStr == null)
return null;
String[] values = valuesStr.trim().split("[ ]*[,|][ ]*");
return Arrays.asList(values);
}
private void validateParameters(Integer min, Integer max, List<String> values) {
if (max != null && min != null) {
if (min > max)
throw new WebScriptException(HttpStatus.BAD_REQUEST.value(), "The `min` parameter value may not be greater than the `max` parameter value");
} else if (values != null) {
} else {
throw new WebScriptException(HttpStatus.BAD_REQUEST.value(), "Either `min`/`max` or `values` query parameter is required");
}
}
}

View File

@@ -0,0 +1,57 @@
package com.inteligr8.alfresco.asie.enterprise.rest;
import java.io.IOException;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.Set;
import java.util.TreeMap;
import org.alfresco.repo.index.shard.Shard;
import org.alfresco.repo.index.shard.ShardState;
import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.extensions.webscripts.WebScriptResponse;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.compute.SolrShardHashTable;
import com.inteligr8.alfresco.asie.enterprise.rest.model.NodeInfo;
import com.inteligr8.alfresco.asie.enterprise.rest.model.ShardInfo;
import com.inteligr8.alfresco.asie.enterprise.rest.model.ShardNodeInfo;
import com.inteligr8.alfresco.asie.enterprise.rest.model.ShardSetInfo;
@Component(value = "webscript.com.inteligr8.alfresco.asie.enterprise.shard.get")
public class GetShardWebScript extends AbstractAsieShardWebScript {
@Override
public void execute(WebScriptRequest req, WebScriptResponse res, Set<ShardState> registeredShardNodes) 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);
SolrShardHashTable<?> sampleHashTable = sampleHashType == null ? null : this.createSampleHashTable(sampleHashType, maxShards);
ShardInfo shard = new ShardInfo();
shard.setId(registeredShard.getInstance());
shard.setShardSet(new ShardSetInfo(registeredShard.getFloc(), aRegisteredShardNode));
shard.setNodes(new TreeMap<>());
if (sampleHashTable != null)
this.addShardHashSamples(shard, sampleHashTable);
for (ShardState registeredShardNode : registeredShardNodes) {
if (shard.getTxsCompleted() == null || shard.getTxsCompleted().longValue() < registeredShardNode.getLastIndexedTxId()) {
shard.setLatestTx(OffsetDateTime.ofInstant(Instant.ofEpochMilli(registeredShardNode.getLastIndexedTxCommitTime()), ZoneOffset.UTC));
shard.setTxsCompleted(registeredShardNode.getLastIndexedTxId());
}
NodeInfo node = new ShardNodeInfo(registeredShardNode);
shard.getNodes().put(node.getId(), node);
}
res.setContentType(MediaType.APPLICATION_JSON_VALUE);
res.setContentEncoding("utf-8");
this.getObjectMapper().writeValue(res.getWriter(), shard);
}
}

View File

@@ -0,0 +1,90 @@
package com.inteligr8.alfresco.asie.enterprise.rest;
import java.io.IOException;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import org.alfresco.repo.index.shard.Floc;
import org.alfresco.repo.index.shard.Shard;
import org.alfresco.repo.index.shard.ShardState;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.http.MediaType;
import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.compute.SolrShardHashTable;
import com.inteligr8.alfresco.asie.enterprise.rest.model.NodeInfo;
import com.inteligr8.alfresco.asie.enterprise.rest.model.ShardInfo;
import com.inteligr8.alfresco.asie.enterprise.rest.model.ShardNodeInfo;
import com.inteligr8.alfresco.asie.enterprise.rest.model.ShardSetInfo;
@Component(value = "webscript.com.inteligr8.alfresco.asie.enterprise.shards.get")
public class GetShardsWebScript extends AbstractAsieEnterpriseWebScript {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public void executeAuthorized(WebScriptRequest req, WebScriptResponse res) throws IOException {
this.logger.trace("execute()");
Map<Floc, Map<Shard, Set<ShardState>>> flocs = this.getShardRegistry().getFlocs();
if (flocs.isEmpty())
throw new WebScriptException(HttpStatus.NO_CONTENT.value(), "There are no ASIE shards registred with ACS");
SolrShardHashSampleType sampleHashType = this.getOptionalQueryParameter(req, "sampleHashType", SolrShardHashSampleType.class);
Map<String, ShardSetInfo> shardSets = new TreeMap<>();
for (Entry<Floc, Map<Shard, Set<ShardState>>> floc : flocs.entrySet()) {
int maxShards = floc.getKey().getNumberOfShards();
ShardState anyShardNode = this.getAnyShardNode(floc.getValue());
ShardSetInfo shardSet = new ShardSetInfo(floc.getKey(), anyShardNode);
shardSet.setShards(new TreeMap<>());
SolrShardHashTable<?> sampleHashTable = sampleHashType == null ? null : this.createSampleHashTable(sampleHashType, maxShards);
for (Entry<Shard, Set<ShardState>> registeredShard : floc.getValue().entrySet()) {
ShardInfo shard = new ShardInfo();
shard.setId(registeredShard.getKey().getInstance());
shard.setNodes(new TreeMap<>());
for (ShardState registeredShardNode : registeredShard.getValue()) {
if (shard.getTxsCompleted() == null || shard.getTxsCompleted().longValue() < registeredShardNode.getLastIndexedTxId()) {
shard.setLatestTx(OffsetDateTime.ofInstant(Instant.ofEpochMilli(registeredShardNode.getLastIndexedTxCommitTime()), ZoneOffset.UTC));
shard.setTxsCompleted(registeredShardNode.getLastIndexedTxId());
}
NodeInfo node = new ShardNodeInfo(registeredShardNode);
shard.getNodes().put(node.getId(), node);
}
if (sampleHashTable != null)
this.addShardHashSamples(shardSet, shard, sampleHashTable);
shardSet.getShards().put(shard.getId(), shard);
}
shardSets.put(shardSet.getMethodSpec(), shardSet);
}
res.setContentType(MediaType.APPLICATION_JSON_VALUE);
res.setContentEncoding("utf-8");
this.getObjectMapper().writeValue(res.getWriter(), shardSets);
}
private ShardState getAnyShardNode(Map<Shard, Set<ShardState>> shards) {
for (Set<ShardState> shardNodes : shards.values())
for (ShardState shardNode : shardNodes)
return shardNode;
return null;
}
}

View File

@@ -0,0 +1,135 @@
package com.inteligr8.alfresco.asie.enterprise.rest;
import java.io.IOException;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.alfresco.service.cmr.attributes.AttributeService;
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.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.Constants;
import com.inteligr8.alfresco.asie.api.CoreAdminApi;
import com.inteligr8.solr.model.ExceptionResponse;
import com.inteligr8.solr.model.core.CreateRequest;
import com.inteligr8.solr.model.core.ReloadRequest;
import jakarta.ws.rs.BadRequestException;
import jakarta.ws.rs.InternalServerErrorException;
@Component(value = "webscript.com.inteligr8.alfresco.asie.enterprise.nodeShard.post")
public class ReloadNodeShardWebScript extends AbstractAsieNodeWebScript {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final Pattern shardRangePattern = Pattern.compile("([0-9]+)-([0-9]+)");
@Autowired
@Qualifier(Constants.QUALIFIER_ASIE)
private AttributeService attrService;
@Override
protected void execute(WebScriptRequest req, WebScriptResponse res, final String nodeHostname, final int nodePort)
throws IOException {
String shardCore = this.getRequiredPathParameter(req, "shardCore");
int shardId = this.getRequiredPathParameter(req, "shardId", Integer.class);
String coreName = shardCore + "-" + shardId;
Serializable[] keys = new String[] {
Constants.ATTR_ASIE,
Constants.ATTR_UNLOADED_NODE_CORES,
nodeHostname + ":" + nodePort
};
Map<String, String> cores = this.fetchUnloadedCores(keys);
if (!cores.containsKey(coreName)) {
cores.putAll(this.fetchOtherCores(req));
}
boolean changed = false;
String coreInstancePath = cores.get(coreName);
if (coreInstancePath == null)
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);
CoreAdminApi api = this.createApi(nodeHostname, nodePort);
try {
api.create(new CreateRequest()
.withCore(coreName)
.withConfigDirectory(coreInstancePath));
} catch (BadRequestException bre) {
this.logger.warn("Core {} does not exist on ASIE node: {}; forgetting it", coreName, nodeHostname);
cores.remove(coreName);
changed = true;
} catch (InternalServerErrorException isee) {
ExceptionResponse response = isee.getResponse().readEntity(ExceptionResponse.class);
if (response.getError() == null || response.getError().getMessage() == null || !response.getError().getMessage().endsWith(" already exists."))
throw isee;
this.logger.warn("Core {} was already loaded on ASIE node: {}; reloading ...", coreName, nodeHostname);
api.reload(new ReloadRequest()
.withCore(coreName));
}
if (changed)
this.attrService.setAttribute((Serializable) cores, keys);
res.setStatus(HttpStatus.OK.value());
}
private Map<String, String> fetchUnloadedCores(Serializable[] keys) {
@SuppressWarnings("unchecked")
Map<String, String> cores = (Map<String, String>) this.attrService.getAttribute(keys);
if (cores == null)
cores = new HashMap<String, String>();
return cores;
}
private Map<String, String> fetchOtherCores(WebScriptRequest req) {
String coreName = this.getOptionalQueryParameter(req, "coreName");
if (coreName == null)
coreName = this.getOptionalQueryParameter(req, "core");
String shardRange = this.getOptionalQueryParameter(req, "shardRange");
Short shardCount = this.getOptionalQueryParameter(req, "shardCount", Short.class);
if (coreName == null || shardRange == null || shardCount == null)
return Collections.emptyMap();
String template = this.getOptionalQueryParameter(req, "template");
if (template == null)
template = "rerank";
Short nodeId = this.getOptionalQueryParameter(req, "nodeId", Short.class);
if (nodeId == null)
nodeId = 1;
Short nodeCount = this.getOptionalQueryParameter(req, "nodeCount", Short.class);
if (nodeCount == null)
nodeCount = 2;
String baseConfigDirectory = template + "--" + coreName + "--shards--" + shardCount + "-x-1--node--" + nodeId + "-of-" + nodeCount;
Matcher matcher = this.shardRangePattern.matcher(shardRange);
if (!matcher.find())
throw new WebScriptException(HttpStatus.BAD_REQUEST.value(), "The 'shardRange' query parameter value is not formatted as expected");
Map<String, String> cores = new HashMap<>();
short startShardId = Short.parseShort(matcher.group(1));
short endShardId = Short.parseShort(matcher.group(2));
for (short shardId = startShardId; shardId <= endShardId; shardId++) {
String shardConfigDirectory = baseConfigDirectory + "/" + coreName + "-" + shardId;
cores.put(coreName, shardConfigDirectory);
}
return cores;
}
}

View File

@@ -0,0 +1,137 @@
package com.inteligr8.alfresco.asie.enterprise.rest;
import java.io.IOException;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.alfresco.service.cmr.attributes.AttributeService;
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.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.Constants;
import com.inteligr8.alfresco.asie.api.CoreAdminApi;
import com.inteligr8.solr.model.ExceptionResponse;
import com.inteligr8.solr.model.core.CreateRequest;
import com.inteligr8.solr.model.core.ReloadRequest;
import jakarta.ws.rs.BadRequestException;
import jakarta.ws.rs.InternalServerErrorException;
@Component(value = "webscript.com.inteligr8.alfresco.asie.enterprise.node.post")
public class ReloadNodeWebScript extends AbstractAsieNodeWebScript {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final Pattern shardRangePattern = Pattern.compile("([0-9]+)-([0-9]+)");
@Autowired
@Qualifier(Constants.QUALIFIER_ASIE)
private AttributeService attrService;
@Override
protected void execute(WebScriptRequest req, WebScriptResponse res, final String nodeHostname, final int nodePort)
throws IOException {
Serializable[] keys = new String[] {
Constants.ATTR_ASIE,
Constants.ATTR_UNLOADED_NODE_CORES,
nodeHostname + ":" + nodePort
};
Map<String, String> cores = this.fetchUnloadedCores(keys);
cores.putAll(this.fetchOtherCores(req));
if (cores.isEmpty())
throw new WebScriptException(HttpStatus.NOT_FOUND.value(), "The specified node was not found or formulated");
boolean changed = false;
Iterator<Entry<String, String>> i = cores.entrySet().iterator();
while (i.hasNext()) {
Entry<String, String> core = i.next();
String coreName = core.getKey();
String coreInstancePath = core.getValue();
this.logger.info("Reloading core {} on ASIE node: {}", coreName, nodeHostname);
CoreAdminApi api = this.createApi(nodeHostname, nodePort);
try {
api.create(new CreateRequest()
.withCore(coreName)
.withConfigDirectory(coreInstancePath));
} catch (BadRequestException bre) {
this.logger.warn("Core {} does not exist on ASIE node: {}; forgetting it", coreName, nodeHostname);
i.remove();
changed = true;
} catch (InternalServerErrorException isee) {
ExceptionResponse response = isee.getResponse().readEntity(ExceptionResponse.class);
if (response.getError() == null || response.getError().getMessage() == null || !response.getError().getMessage().endsWith(" already exists."))
throw isee;
this.logger.info("Core {} was already loaded on ASIE node: {}; reloading ...", coreName, nodeHostname);
api.reload(new ReloadRequest()
.withCore(coreName));
}
}
if (changed)
this.attrService.setAttribute((Serializable) cores, keys);
res.setStatus(HttpStatus.OK.value());
}
private Map<String, String> fetchUnloadedCores(Serializable[] keys) {
@SuppressWarnings("unchecked")
Map<String, String> cores = (Map<String, String>) this.attrService.getAttribute(keys);
if (cores == null)
cores = new HashMap<String, String>();
return cores;
}
private Map<String, String> fetchOtherCores(WebScriptRequest req) {
String coreName = this.getOptionalQueryParameter(req, "coreName");
if (coreName == null)
coreName = this.getOptionalQueryParameter(req, "core");
String shardRange = this.getOptionalQueryParameter(req, "shardRange");
Short shardCount = this.getOptionalQueryParameter(req, "shardCount", Short.class);
if (coreName == null || shardRange == null || shardCount == null)
return Collections.emptyMap();
String template = this.getOptionalQueryParameter(req, "template");
if (template == null)
template = "rerank";
Short nodeId = this.getOptionalQueryParameter(req, "nodeId", Short.class);
if (nodeId == null)
nodeId = 1;
Short nodeCount = this.getOptionalQueryParameter(req, "nodeCount", Short.class);
if (nodeCount == null)
nodeCount = 2;
String baseConfigDirectory = template + "--" + coreName + "--shards--" + shardCount + "-x-1--node--" + nodeId + "-of-" + nodeCount;
Matcher matcher = this.shardRangePattern.matcher(shardRange);
if (!matcher.find())
throw new WebScriptException(HttpStatus.BAD_REQUEST.value(), "The 'shardRange' query parameter value is not formatted as expected");
Map<String, String> cores = new HashMap<>();
short startShardId = Short.parseShort(matcher.group(1));
short endShardId = Short.parseShort(matcher.group(2));
for (short shardId = startShardId; shardId <= endShardId; shardId++) {
String shardConfigDirectory = baseConfigDirectory + "/" + coreName + "-" + shardId;
cores.put(coreName, shardConfigDirectory);
}
return cores;
}
}

View File

@@ -0,0 +1,31 @@
package com.inteligr8.alfresco.asie.enterprise.rest;
import org.alfresco.repo.index.shard.ShardState;
import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.enterprise.model.NodeShardParameterSet;
import com.inteligr8.alfresco.asie.enterprise.model.ShardSet;
@Component(value = "webscript.com.inteligr8.alfresco.asie.enterprise.nodeShard.delete")
public class UnloadNodeShardWebScript extends AbstractUnregisterNodeWebScript<NodeShardParameterSet> {
@Override
protected NodeShardParameterSet createParameters(WebScriptRequest req, String nodeHostname, int nodePort) {
ShardSet shardSet = this.getRequiredPathParameter(req, "shardSet", ShardSet.class);
int shardId = this.getRequiredPathParameter(req, "shardId", Integer.class);
return new NodeShardParameterSet(nodeHostname, nodePort, shardSet, shardId);
}
@Override
protected boolean matches(NodeShardParameterSet params, ShardState shardState) {
if (!params.getShardSet().isFor(shardState))
return false;
if (params.getShardId() != shardState.getShardInstance().getShard().getInstance())
return false;
return true;
}
}

View File

@@ -0,0 +1,16 @@
package com.inteligr8.alfresco.asie.enterprise.rest;
import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.model.NodeParameterSet;
@Component(value = "webscript.com.inteligr8.alfresco.asie.enterprise.node.delete")
public class UnloadNodeWebScript extends AbstractUnregisterNodeWebScript<NodeParameterSet> {
@Override
protected NodeParameterSet createParameters(WebScriptRequest req, String nodeHostname, int nodePort) {
return new NodeParameterSet(nodeHostname, nodePort);
}
}

View File

@@ -0,0 +1,48 @@
package com.inteligr8.alfresco.asie.enterprise.rest.model;
import java.util.Map;
import java.util.TreeMap;
import org.alfresco.repo.index.shard.ShardInstance;
import com.fasterxml.jackson.annotation.JsonProperty;
public abstract class NodeInfo {
public static String determineId(ShardInstance nodeCache) {
int lastSlash = nodeCache.getBaseUrl().lastIndexOf('/');
return nodeCache.getHostName() + ":" + nodeCache.getPort() + nodeCache.getBaseUrl().substring(0, lastSlash);
}
@JsonProperty
private String id;
@JsonProperty
private Map<Integer, ShardInfo> shards;
public NodeInfo() {
}
public NodeInfo(ShardInstance nodeCache) {
this.setId(determineId(nodeCache));
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Map<Integer, ShardInfo> getShards() {
if (shards == null)
shards = new TreeMap<>();
return shards;
}
public void setShards(Map<Integer, ShardInfo> shards) {
this.shards = shards;
}
}

View File

@@ -0,0 +1,26 @@
package com.inteligr8.alfresco.asie.enterprise.rest.model;
import java.util.Map;
import org.alfresco.repo.index.shard.ShardInstance;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
@JsonInclude(Include.NON_EMPTY)
@JsonIgnoreProperties(ignoreUnknown = true)
public class NodeShardInfo extends NodeInfo {
@JsonProperty
private Map<Integer, ShardInfo> shards;
public NodeShardInfo() {
}
public NodeShardInfo(ShardInstance nodeCache) {
super(nodeCache);
}
}

View File

@@ -0,0 +1,60 @@
package com.inteligr8.alfresco.asie.enterprise.rest.model;
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.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.inteligr8.alfresco.asie.enterprise.model.ShardSet;
import com.fasterxml.jackson.annotation.JsonProperty;
@JsonInclude(Include.NON_EMPTY)
@JsonIgnoreProperties(ignoreUnknown = true)
public class PropertyHashShardSetInfo {
@JsonProperty
private String methodSpec;
@JsonProperty
private int shardCount;
@JsonProperty
private Map<Object, ShardInfo> shards;
public PropertyHashShardSetInfo() {
}
public PropertyHashShardSetInfo(Floc floc, ShardState anyShardNode) {
ShardSet shardSet = new ShardSet(floc, anyShardNode);
this.setMethodSpec(shardSet.toSpec());
this.setShardCount(floc.getNumberOfShards());
}
public String getMethodSpec() {
return this.methodSpec;
}
public void setMethodSpec(String methodSpec) {
this.methodSpec = methodSpec;
}
public int getShardCount() {
return shardCount;
}
public void setShardCount(int shardCount) {
this.shardCount = shardCount;
}
public Map<Object, ShardInfo> getShards() {
return shards;
}
public void setShards(Map<Object, ShardInfo> shards) {
this.shards = shards;
}
}

View File

@@ -0,0 +1,97 @@
package com.inteligr8.alfresco.asie.enterprise.rest.model;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.Map;
import org.alfresco.repo.index.shard.ShardState;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
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;
@JsonInclude(Include.NON_EMPTY)
@JsonIgnoreProperties(ignoreUnknown = true)
public class ShardInfo {
@JsonProperty
private int id;
@JsonProperty
private Long txsCompleted;
@JsonProperty
private Long txsRemaining;
@JsonProperty
@JsonFormat(shape = Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssXXX")
private OffsetDateTime latestTx;
@JsonProperty
private Map<String, NodeInfo> nodes;
@JsonProperty
private ShardSetInfo shardSet;
public ShardInfo() {
}
public ShardInfo(ShardState shard) {
this.setId(shard.getShardInstance().getShard().getInstance());
this.setLatestTx(OffsetDateTime.ofInstant(Instant.ofEpochMilli(shard.getLastIndexedTxCommitTime()), ZoneOffset.UTC));
this.setTxsCompleted(shard.getLastIndexedTxId());
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Long getTxsCompleted() {
return txsCompleted;
}
public void setTxsCompleted(Long txsCompleted) {
this.txsCompleted = txsCompleted;
}
public Long getTxsRemaining() {
return txsRemaining;
}
public void setTxsRemaining(Long txsRemaining) {
this.txsRemaining = txsRemaining;
}
public OffsetDateTime getLatestTx() {
return latestTx;
}
public void setLatestTx(OffsetDateTime latestTx) {
this.latestTx = latestTx;
}
public Map<String, NodeInfo> getNodes() {
return nodes;
}
public void setNodes(Map<String, NodeInfo> nodes) {
this.nodes = nodes;
}
public ShardSetInfo getShardSet() {
return shardSet;
}
public void setShardSet(ShardSetInfo shardSet) {
this.shardSet = shardSet;
}
}

View File

@@ -0,0 +1,52 @@
package com.inteligr8.alfresco.asie.enterprise.rest.model;
import java.time.Instant;
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.Shape;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
@JsonInclude(Include.NON_EMPTY)
@JsonIgnoreProperties(ignoreUnknown = true)
public class ShardNodeInfo extends NodeInfo {
@JsonProperty
private Long txsCompleted;
@JsonProperty
@JsonFormat(shape = Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssXXX")
private OffsetDateTime latestTx;
public ShardNodeInfo() {
}
public ShardNodeInfo(ShardState shard) {
super(shard.getShardInstance());
this.setLatestTx(OffsetDateTime.ofInstant(Instant.ofEpochMilli(shard.getLastIndexedTxCommitTime()), ZoneOffset.UTC));
this.setTxsCompleted(shard.getLastIndexedTxId());
}
public Long getTxsCompleted() {
return txsCompleted;
}
public void setTxsCompleted(Long txsCompleted) {
this.txsCompleted = txsCompleted;
}
public OffsetDateTime getLatestTx() {
return latestTx;
}
public void setLatestTx(OffsetDateTime latestTx) {
this.latestTx = latestTx;
}
}

View File

@@ -0,0 +1,73 @@
package com.inteligr8.alfresco.asie.enterprise.rest.model;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
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.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.inteligr8.alfresco.asie.enterprise.model.ShardSet;
import com.fasterxml.jackson.annotation.JsonProperty;
@JsonInclude(Include.NON_EMPTY)
@JsonIgnoreProperties(ignoreUnknown = true)
public class ShardSetInfo {
@JsonProperty
private String methodSpec;
@JsonProperty
private int shardCount;
@JsonProperty
private Map<Integer, ShardInfo> shards;
@JsonProperty
private Map<Integer, Set<Object>> shardHashSamples = new TreeMap<>();
public ShardSetInfo() {
}
public ShardSetInfo(Floc floc, ShardState anyShardNode) {
ShardSet shardSet = new ShardSet(floc, anyShardNode);
this.methodSpec = shardSet.toSpec();
this.setShardCount(floc.getNumberOfShards());
}
public String getMethodSpec() {
return this.methodSpec;
}
public void setMethodSpec(String methodSpec) {
this.methodSpec = methodSpec;
}
public int getShardCount() {
return shardCount;
}
public void setShardCount(int shardCount) {
this.shardCount = shardCount;
}
public Map<Integer, ShardInfo> getShards() {
return shards;
}
public void setShards(Map<Integer, ShardInfo> shards) {
this.shards = shards;
}
public Map<Integer, Set<Object>> getShardHashSamples() {
return shardHashSamples;
}
public void setShardHashSamples(Map<Integer, Set<Object>> shardHashSamples) {
this.shardHashSamples = shardHashSamples;
}
}

View File

@@ -0,0 +1,29 @@
package com.inteligr8.alfresco.asie.enterprise.service;
import java.util.Collection;
import org.alfresco.repo.index.shard.ShardInstance;
import org.alfresco.repo.index.shard.ShardState;
import org.springframework.stereotype.Component;
@Component
public class ShardAnalysisService {
public ShardInstance computeLeadShard(Collection<ShardState> shardNodesCache) {
if (shardNodesCache.isEmpty())
return null;
long latestTime = 0L;
ShardInstance latestNode = null;
for (ShardState shardNodeCache : shardNodesCache) {
if (latestTime < shardNodeCache.getLastIndexedTxCommitTime()) {
latestNode = shardNodeCache.getShardInstance();
latestTime = shardNodeCache.getLastIndexedTxCommitTime();
}
}
return latestNode;
}
}

View File

@@ -0,0 +1,102 @@
package com.inteligr8.alfresco.asie.enterprise.service;
import java.io.Serializable;
import java.util.Collection;
import org.alfresco.repo.index.shard.Shard;
import org.alfresco.repo.index.shard.ShardInstance;
import org.alfresco.repo.index.shard.ShardState;
import org.alfresco.service.cmr.attributes.AttributeService;
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.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.Constants;
import com.inteligr8.alfresco.asie.enterprise.rest.model.NodeInfo;
@Component
public class ShardBackupService {
private static final String ATTR_BACKUP_NODE = "backupNode";
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private ShardAnalysisService sas;
@Autowired
@Qualifier(Constants.QUALIFIER_ASIE)
private AttributeService attributeService;
@Value("${inteligr8.asie.backup.persistTimeMinutes}")
private int persistTimeMinutes;
public String fetchNodeId(Collection<ShardState> shardNodes) {
if (shardNodes.isEmpty())
return null;
ShardState shardNode0 = shardNodes.iterator().next();
ShardInstance node0Shard = shardNode0.getShardInstance();
Shard shard = node0Shard.getShard();
String shardKey = shard.getFloc().getShardMethod().name() + "~" + shard.getFloc().getNumberOfShards() + "~" + shard.getInstance();
PersistedNode backupNode = (PersistedNode) this.attributeService.getAttribute(Constants.ATTR_ASIE, ATTR_BACKUP_NODE, shardKey);
this.logger.debug("Found backup node: {}", backupNode);
if (backupNode == null || backupNode.isExpired()) {
ShardInstance backupShardInstance = this.sas.computeLeadShard(shardNodes);
backupNode = new PersistedNode(NodeInfo.determineId(backupShardInstance));
this.attributeService.setAttribute(backupNode, Constants.ATTR_ASIE, ATTR_BACKUP_NODE, shardKey);
}
return backupNode.getNode();
}
public void forget() {
this.attributeService.removeAttribute(Constants.ATTR_ASIE, ATTR_BACKUP_NODE);
}
public void forget(ShardState shardNode) {
ShardInstance nodeShard = shardNode.getShardInstance();
Shard shard = nodeShard.getShard();
String shardKey = shard.getFloc().getShardMethod().name() + "~" + shard.getFloc().getNumberOfShards() + "~" + shard.getInstance();
this.attributeService.removeAttribute(Constants.ATTR_ASIE, ATTR_BACKUP_NODE, shardKey);
}
private class PersistedNode implements Serializable {
private static final long serialVersionUID = 1L;
private final String node;
private long expireTimeMillis;
PersistedNode(String node) {
this.node = node;
this.reset();
}
void reset() {
this.expireTimeMillis = System.currentTimeMillis() + persistTimeMinutes * 60L * 1000L;
}
boolean isExpired() {
return this.expireTimeMillis < System.currentTimeMillis();
}
String getNode() {
return this.node;
}
@Override
public String toString() {
return "node: " + this.node + "; expires in: " + (System.currentTimeMillis() - this.expireTimeMillis) + " ms";
}
}
}

View File

@@ -0,0 +1,142 @@
package com.inteligr8.alfresco.asie.enterprise.service;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Collection;
import java.util.Collections;
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.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.enterprise.model.ShardSet;
@Component
public class ShardDiscoveryService {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
@Qualifier(Constants.BEAN_SHARD_REGISTRY)
private ShardRegistry shardRegistry;
public Set<ShardState> findByNode(String nodeHostname, int nodePort) {
Map<Floc, Map<Shard, Set<ShardState>>> flocs = this.shardRegistry.getFlocs();
if (flocs.isEmpty())
return Collections.emptySet();
Set<ShardState> shards = new HashSet<>();
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())) {
InetAddress nodeAddress = this.resolve(nodeHostname);
if (nodeAddress == null)
continue;
InetAddress shardInstanceAddress = this.resolve(shardInstance.getHostName());
if (!nodeAddress.equals(shardInstanceAddress))
continue;
}
if (nodePort == shardInstance.getPort())
shards.add(shardState);
}
}
}
return shards;
}
public Map<Shard, Set<ShardState>> findByShardSet(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()) {
if (!floc.getKey().getShardMethod().equals(shardSet.getMethod()))
continue;
if (!shardSet.getConfig().isEmpty()) {
if (floc.getValue().isEmpty())
continue;
Shard firstShard = floc.getValue().keySet().iterator().next();
Set<ShardState> firstShardStates = floc.getValue().get(firstShard);
if (firstShardStates == null || firstShardStates.isEmpty())
continue;
ShardState firstShardState = firstShardStates.iterator().next();
Map<String, String> firstShardProps = firstShardState.getPropertyBag();
if (!shardSet.isConfigurationFor(firstShardProps))
continue;
}
return floc.getValue();
}
return Collections.emptyMap();
}
public Collection<Pair<Floc, Map<Shard, Set<ShardState>>>> findByShardMethod(ShardMethodEnum shardMethod) {
Map<Floc, Map<Shard, Set<ShardState>>> flocs = this.shardRegistry.getFlocs();
if (flocs.isEmpty())
return Collections.emptyList();
this.logger.trace("Found {} shard sets", flocs.size());
List<Pair<Floc, Map<Shard, Set<ShardState>>>> filteredFlocs = new LinkedList<>();
for (Entry<Floc, Map<Shard, Set<ShardState>>> floc : flocs.entrySet()) {
if (!floc.getKey().getShardMethod().equals(shardMethod))
continue;
filteredFlocs.add(new Pair<>(floc.getKey(), floc.getValue()));
}
return filteredFlocs;
}
public Set<ShardState> findByShard(Map<Shard, Set<ShardState>> shards, int shardId) {
if (shards == null)
return null;
for (Entry<Shard, Set<ShardState>> shard : shards.entrySet()) {
if (shard.getKey().getInstance() == shardId)
return shard.getValue();
}
return Collections.emptySet();
}
public Set<ShardState> findByShard(ShardSet shardSet, int shardId) {
Map<Shard, Set<ShardState>> shards = this.findByShardSet(shardSet);
return this.findByShard(shards, shardId);
}
private InetAddress resolve(String hostname) {
try {
return InetAddress.getByName(hostname);
} catch (UnknownHostException uhe) {
return null;
}
}
}

View File

@@ -0,0 +1,60 @@
package com.inteligr8.alfresco.asie.enterprise.service;
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.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;
@Component
public class ShardStateService {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
@Qualifier(Constants.QUALIFIER_ASIE)
private AttributeService attrService;
@Autowired
@Qualifier("shardStateCache")
private SimpleCache<ShardInstance, ShardState> shardStateCache;
@Autowired
@Qualifier("shardToGuidCache")
private SimpleCache<ShardInstance, String> shardToGuidCache;
public void clear() {
this.logger.info("Removing all nodes/shards from the shard registry");
// this clears the state from the backend database
this.attrService.removeAttribute(Constants.ATTR_SHARD_STATE);
this.attrService.removeAttribute(Constants.ATTR_SHARD_SUBSCRIPTION);
// this clears the state from Hazelcast
this.shardStateCache.clear();
this.shardToGuidCache.clear();
}
public void clear(String guid) {
this.logger.info("Removing node from the shard registry: {}", guid);
ShardState shardState = (ShardState) this.attrService.getAttribute(Constants.ATTR_SHARD_STATE, guid);
// this clears the state from the backend database
this.attrService.removeAttribute(Constants.ATTR_SHARD_STATE, guid);
this.attrService.removeAttribute(Constants.ATTR_SHARD_SUBSCRIPTION, guid);
// this clears the state from Hazelcast
if (shardState != null) {
this.shardStateCache.remove(shardState.getShardInstance());
this.shardToGuidCache.remove(shardState.getShardInstance());
}
}
}

View File

@@ -0,0 +1,33 @@
package com.inteligr8.alfresco.asie.model;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class NodeParameterSet implements RequestParameterSet {
private String hostname;
private int port;
public NodeParameterSet(String hostname, int port) {
this.hostname = hostname;
this.port = port;
}
public String getHostname() {
return hostname;
}
public int getPort() {
return port;
}
public InetAddress getAddress() {
try {
return InetAddress.getByName(this.hostname);
} catch (UnknownHostException uhe) {
// suppress
return null;
}
}
}

View File

@@ -0,0 +1,5 @@
package com.inteligr8.alfresco.asie.model;
public interface RequestParameterSet {
}

View File

@@ -0,0 +1,50 @@
package com.inteligr8.alfresco.asie.provider;
import org.alfresco.service.cmr.attributes.AttributeService;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import com.inteligr8.alfresco.asie.Constants;
@Configuration
public class AttributeServiceProvider {
@Autowired
private ApplicationContext context;
/**
* This allows for the selection of the primary or first AttributeService
* registered in the Spring BeanFactory. OOTB, there are multiple
* AttributeService options and you would typically want
* 'attributeService`. But an extension could have a @Primary that we
* would want to use instead.
*
* @return An AttributeService.
*/
@Bean("asieAttributeService")
@Qualifier(Constants.QUALIFIER_ASIE)
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public AttributeService selectAttributeService() {
return this.getPrimaryOrNamed(AttributeService.class, "attributeService");
}
private <T> T getPrimaryOrNamed(Class<T> type, String beanName) {
ObjectProvider<T> provider = this.context.getBeanProvider(type);
// this will select the primary or if there is just one impl, that one impl
T t = provider.getIfUnique();
if (t == null) {
// this will select the named bean; throwing exception if there is none
t = this.context.getBean(beanName, type);
}
return t;
}
}

View File

@@ -0,0 +1,27 @@
package com.inteligr8.alfresco.asie.provider;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.inteligr8.alfresco.asie.Constants;
@Configuration
public class ObjectMapperProvider {
@Bean("asieObjectMapper")
@Qualifier(Constants.QUALIFIER_ASIE)
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public ObjectMapper createObjectMapper() {
ObjectMapper om = new ObjectMapper();
om.registerModule(new JavaTimeModule());
om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
return om;
}
}

View File

@@ -0,0 +1,227 @@
package com.inteligr8.alfresco.asie.rest;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
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.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.extensions.webscripts.AbstractWebScript;
import org.springframework.extensions.webscripts.Description.RequiredAuthentication;
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.fasterxml.jackson.databind.ObjectMapper;
import com.inteligr8.alfresco.asie.Constants;
import com.inteligr8.alfresco.asie.api.CoreAdminApi;
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;
import net.sf.acegisecurity.GrantedAuthority;
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
@Qualifier(Constants.QUALIFIER_ASIE)
private ObjectMapper objectMapper;
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 ASIE web scripts");
this.solrSharedSecret = StringUtils.trimToNull(this.solrSharedSecret);
}
@Override
public final void execute(WebScriptRequest request, WebScriptResponse response) throws IOException {
if (RequiredAuthentication.user.equals(this.getDescription().getRequiredAuthentication())) {
if (!this.isAuthorized())
throw new WebScriptException(HttpStatus.FORBIDDEN.value(), "You are not authorized for access this resource.");
}
this.executeAuthorized(request, response);
}
private boolean isAuthorized() {
if (this.authorizedAuthorities.contains(AuthenticationUtil.getFullyAuthenticatedUser()))
return true;
for (GrantedAuthority auth : AuthenticationUtil.getFullAuthentication().getAuthorities()) {
if (this.authorizedAuthorities.contains(auth.getAuthority()))
return true;
}
return false;
}
public abstract void executeAuthorized(WebScriptRequest request, WebScriptResponse response) throws IOException;
public ObjectMapper getObjectMapper() {
return objectMapper;
}
protected CoreAdminApi createApi(String hostname, int port) {
String solrBaseUrl = this.formulateSolrBaseUrl(hostname, port);
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) {
String hostname = this.getRequiredPathParameter(req, "hostname");
Integer port = this.getOptionalPathParameter(req, "port", Integer.class);
return this.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;
}
protected String getRequiredPathParameter(WebScriptRequest req, String pathParamName) {
String pathParamValue = this.getOptionalPathParameter(req, pathParamName);
if (pathParamValue == null)
throw new WebScriptException(HttpStatus.BAD_REQUEST.value(), "The '" + pathParamName + "' path parameter is required");
return pathParamValue;
}
protected <T> T getRequiredPathParameter(WebScriptRequest req, String pathParamName, Class<T> type) {
T pathParamValue = this.getOptionalPathParameter(req, pathParamName, type);
if (pathParamValue == null)
throw new WebScriptException(HttpStatus.BAD_REQUEST.value(), "The '" + pathParamName + "' path parameter is required");
return pathParamValue;
}
protected String getOptionalPathParameter(WebScriptRequest req, String pathParamName) {
return StringUtils.trimToNull(req.getServiceMatch().getTemplateVars().get(pathParamName));
}
protected <T> T getOptionalPathParameter(WebScriptRequest req, String pathParamName, Class<T> type) {
String str = StringUtils.trimToNull(req.getServiceMatch().getTemplateVars().get(pathParamName));
return this.getOptionalParameter(req, pathParamName, str, "path element", type);
}
protected String getOptionalQueryParameter(WebScriptRequest req, String queryParamName) {
return StringUtils.trimToNull(req.getParameter(queryParamName));
}
protected <T> T getOptionalQueryParameter(WebScriptRequest req, String queryParamName, Class<T> type) {
String str = StringUtils.trimToNull(req.getParameter(queryParamName));
return this.getOptionalParameter(req, queryParamName, str, "query parameter", type);
}
protected <T> T getOptionalParameter(WebScriptRequest req, String paramName, String paramValue, String paramTypeDisplay, Class<T> type) {
if (paramValue == null)
return null;
if (type.equals(String.class))
return type.cast(paramValue);
try {
try {
Constructor<T> constructor = type.getConstructor(String.class);
return constructor.newInstance(paramValue);
} catch (NoSuchMethodException nsme) {
Method method = type.getDeclaredMethod("valueOf", String.class);
@SuppressWarnings("unchecked")
T t = (T) method.invoke(null, paramValue);
return t;
}
} catch (InvocationTargetException ite) {
if (ite.getTargetException() instanceof NumberFormatException) {
throw new WebScriptException(HttpStatus.BAD_REQUEST.value(), "The `" + paramName + "` " + paramTypeDisplay + " '" + paramValue + "' must be a number");
} else {
throw new WebScriptException(HttpStatus.INTERNAL_SERVER_ERROR.value(), "An unexpected issue occurred", ite);
}
} catch (NoSuchMethodException | IllegalAccessException | InstantiationException e) {
throw new WebScriptException(HttpStatus.INTERNAL_SERVER_ERROR.value(), "An unexpected issue occurred", e);
}
}
}

View File

@@ -0,0 +1,27 @@
package com.inteligr8.alfresco.asie.service;
import java.nio.charset.Charset;
import org.apache.commons.codec.digest.MurmurHash3;
import org.springframework.stereotype.Component;
@Component
public class SolrShardHashService {
private final Charset charset = Charset.forName("utf-8");
public int hash(Object obj, int shardCount) {
String str = obj.toString();
byte[] bytes = str.getBytes(this.charset);
// From ASIE source:
// Math.abs(Hash.murmurhash3_x86_32(shardBy, 0, shardBy.length(), 66)) % shardCount
// Using Apache Commons Codec:
MurmurHash3.IncrementalHash32x86 hash = new MurmurHash3.IncrementalHash32x86();
hash.start(66);
hash.add(bytes, 0, bytes.length);
return Math.abs(hash.end()) % shardCount;
}
}

View File

@@ -0,0 +1,49 @@
<webscript xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://bitbucket.org/!api/2.0/snippets/inteligr8/AzMgbp/80fdd26a6b3769a63cdc6b54bf1f39e378545cf7/files/snippet.txt">
<!-- Naming & Organization -->
<shortname>Retrieve Backup ASIE Node for ASIE Shard</shortname>
<family>Inteligr8 ASIE</family>
<description><![CDATA[
<p>Retrieve a reference to the ASIE node that should be used for the backup of the specified ASIE shard registered with ACS.</p>
<p>The following path parameters are expected:</p>
<dl>
<dt>shardSet</dt>
<dd>A shard method combined with its distinguishing properties;
methods: MOD_ACL_ID, ACL_ID, DB_ID, DB_ID_RANGE, DATE, PROPERTY, EXPLICIT_ID;
e.g. PROPERTY;key:cm:created;regex:^d{4} or DB_ID</dd>
<dt>shardId</dt>
<dd>A number starting at 1</dd>
</dl>
<p>The response will have the following format:</p>
<pre>"hostname:port/baseUrl"</pre>
<p>This is meant to help determine which shard should be backed up.
The node is computed based on the lead shard on the first request.
That value is cached until a reques isn't made for at least a configurable amount of time.</p>
<p>The following status codes should be expected:</p>
<dl>
<dt>200</dt>
<dd>OK</dd>
<dt>204</dt>
<dd>No ASIE node/shard information available</dd>
<dt>400</dt>
<dd>The path parameters are invalid</dd>
<dt>404</dt>
<dd>The specified shard set or shard ID could not be found</dd>
</dl>
]]></description>
<!-- Endpoint Configuration -->
<url>/inteligr8/asie/shard/{shardSet}/{shardId}/backup</url>
<format default="json">any</format>
<!-- Security -->
<authentication>none</authentication>
<!-- Functionality -->
<cache>
<never>false</never>
<public>false</public>
</cache>
</webscript>

View File

@@ -0,0 +1,45 @@
<webscript xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://bitbucket.org/!api/2.0/snippets/inteligr8/AzMgbp/80fdd26a6b3769a63cdc6b54bf1f39e378545cf7/files/snippet.txt">
<!-- Naming & Organization -->
<shortname>Retrieve Lead ASIE Node for ASIE Shard</shortname>
<family>Inteligr8 ASIE</family>
<description><![CDATA[
<p>Retrieve a reference to the most current/up-to-date ASIE node for the specified ASIE shard registered with ACS.</p>
<p>The following path parameters are expected:</p>
<dl>
<dt>shardSet</dt>
<dd>A shard method combined with its distinguishing properties;
methods: MOD_ACL_ID, ACL_ID, DB_ID, DB_ID_RANGE, DATE, PROPERTY, EXPLICIT_ID;
e.g. PROPERTY;key:cm:created;regex:^d{4} or DB_ID</dd>
<dt>shardId</dt>
<dd>A number starting at 1</dd>
</dl>
<p>The response will have the following format:</p>
<pre>"hostname:port/baseUrl"</pre>
<dl>
<dt>200</dt>
<dd>OK</dd>
<dt>204</dt>
<dd>No ASIE node/shard information available</dd>
<dt>400</dt>
<dd>The path parameters are invalid</dd>
<dt>404</dt>
<dd>The specified shard set or shard ID could not be found</dd>
</dl>
]]></description>
<!-- Endpoint Configuration -->
<url>/inteligr8/asie/shard/{shardSet}/{shardId}/lead</url>
<format default="json">any</format>
<!-- Security -->
<authentication>none</authentication>
<!-- Functionality -->
<cache>
<never>false</never>
<public>false</public>
</cache>
</webscript>

View File

@@ -0,0 +1,40 @@
<webscript xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://bitbucket.org/!api/2.0/snippets/inteligr8/AzMgbp/80fdd26a6b3769a63cdc6b54bf1f39e378545cf7/files/snippet.txt">
<!-- Naming & Organization -->
<shortname>Clears ASIE Node from Registry</shortname>
<family>Inteligr8 ASIE</family>
<description><![CDATA[
<p>Clears meta-data about all ASIE shards on a single ASIE node registered with ACS.
This call will attempt to unload all shards on the ASIE node to prevent automatic registration on its next index attempt.
If that disablement fails, this call will continue to unregister all shards on the node from ACS; however, they will automatically register again on its next indexing attempt.</p>
<p>The following path parameters are expected:</p>
<dl>
<dt>nodeEndpoint</dt>
<dd>A hostname or hostname:port for the ASIE node</dd>
</dl>
<p>The following status codes should be expected:</p>
<dl>
<dt>200</dt>
<dd>OK</dd>
<dt>400</dt>
<dd>The path parameters are invalid</dd>
<dt>404</dt>
<dd>The specified ASIE node could not be found</dd>
</dl>
]]></description>
<!-- Endpoint Configuration -->
<url>/inteligr8/asie/node/{nodeEndpoint}/shards</url>
<url>/inteligr8/asie/node/{nodeEndpoint}</url>
<!-- Security -->
<authentication>admin</authentication>
<!-- Functionality -->
<cache>
<never>false</never>
<public>false</public>
</cache>
</webscript>

View File

@@ -0,0 +1,69 @@
<webscript xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://bitbucket.org/!api/2.0/snippets/inteligr8/AzMgbp/80fdd26a6b3769a63cdc6b54bf1f39e378545cf7/files/snippet.txt">
<!-- Naming & Organization -->
<shortname>Retrieves ASIE Node Information/Status</shortname>
<family>Inteligr8 ASIE</family>
<description><![CDATA[
<p>Retrieves meta-data about all shards on a single ASIE node as registred with ACS.</p>
<p>The following query parameter is supported:</p>
<dl>
<dt>nodeEndpoint</dt>
<dd>A hostname or hostname:port for the ASIE node; dots are not allowed, you may use _ (underscore) instead</dd>
<dt>sampleType</dt>
<dd>A sample hash type; Sample hash types: PropertyYear, PropertyQuarter, PropertyMonth, PropertyWeek</dd>
</dl>
<p>The response will have the following format:</p>
<pre>{
"url": string,
"hostname": string,
"shards": {
"string": { // shard ID as string
"id": number,
"txsCompleted": number,
"txsRemaining": number,
"latestTx": "datetime",
"shardSet": {
"id": "string", // generated shard set ID
"method": "string",
"methodDetail": "string",
"hasContent": boolean,
"shardCount": number,
"shardHashSamples": {
"string": [ // shard ID as string
"string": // year as string
],
...
}
}
},
...
]
}</pre>
<p>The following status codes should be expected:</p>
<dl>
<dt>200</dt>
<dd>OK</dd>
<dt>204</dt>
<dd>No ASIE shard information available</dd>
<dt>400</dt>
<dd>The path or query parameters are invalid</dd>
<dt>404</dt>
<dd>The specified ASIE node instance could not be found</dd>
</dl>
]]></description>
<!-- Endpoint Configuration -->
<url>/inteligr8/asie/node/{nodeEndpoint}?sampleHashType={sampleHashType?}</url>
<format default="json">any</format>
<!-- Security -->
<authentication>admin</authentication>
<!-- Functionality -->
<cache>
<never>false</never>
<public>false</public>
</cache>
</webscript>

View File

@@ -0,0 +1,42 @@
<webscript xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://bitbucket.org/!api/2.0/snippets/inteligr8/AzMgbp/80fdd26a6b3769a63cdc6b54bf1f39e378545cf7/files/snippet.txt">
<!-- Naming & Organization -->
<shortname>Adds ASIE Node to Registry</shortname>
<family>Inteligr8 ASIE</family>
<description><![CDATA[
<p>Loads all previously registered ASIE shards on a single ASIE node, which will eventually register with ACS.</p>
<p>The following path parameters are expected:</p>
<dl>
<dt>nodeEndpoint</dt>
<dd>A hostname or hostname:port for the ASIE node</dd>
<dt>coreName</dt>
<dd>A core name to restore in addition to known cores</dd>
<dt>shardRange</dt>
<dd>A shard range restore in addition to known shards (e.g. 0-7)</dd>
</dl>
<p>The following status codes should be expected:</p>
<dl>
<dt>200</dt>
<dd>OK</dd>
<dt>400</dt>
<dd>The path parameters are invalid</dd>
<dt>404</dt>
<dd>The specified ASIE node was not previously registered</dd>
</dl>
]]></description>
<!-- Endpoint Configuration -->
<url>/inteligr8/asie/node/{nodeEndpoint}/shards?coreName={coreName?}&amp;shardRange={shardRange?}&amp;template={template?}&amp;shardCount={shardCount?}&amp;nodeId={nodeId?}&amp;nodeCount={nodeCount?}</url>
<url>/inteligr8/asie/node/{nodeEndpoint}?coreName={coreName?}&amp;shardRange={shardRange?}&amp;template={template?}&amp;shardCount={shardCount?}&amp;nodeId={nodeId?}&amp;nodeCount={nodeCount?}</url>
<!-- Security -->
<authentication>admin</authentication>
<!-- Functionality -->
<cache>
<never>false</never>
<public>false</public>
</cache>
</webscript>

View File

@@ -0,0 +1,45 @@
<webscript xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://bitbucket.org/!api/2.0/snippets/inteligr8/AzMgbp/80fdd26a6b3769a63cdc6b54bf1f39e378545cf7/files/snippet.txt">
<!-- Naming & Organization -->
<shortname>Clears ASIE Node/Shard from Registry</shortname>
<family>Inteligr8 ASIE</family>
<description><![CDATA[
<p>Clears meta-data about a single ASIE shard on a single ASIE node registred with ACS.
This call will attempt to disable further indexing by the ASIE node of only that shard to prevent automatic registration on its next index attempt.
If that disablement fails, this call will continue to unregister the node/shard from ACS; however, it will automatically register again on its next indexing attempt.</p>
<p>The following path parameters are expected:</p>
<dl>
<dt>nodeEndpoint</dt>
<dd>A hostname or hostname:port for the ASIE node</dd>
<dt>shardSet</dt>
<dd>A shard method combined with its distinguishing properties;
methods: MOD_ACL_ID, ACL_ID, DB_ID, DB_ID_RANGE, DATE, PROPERTY, EXPLICIT_ID;
e.g. PROPERTY;key:cm:created;regex:^d{4} or DB_ID</dd>
<dt>shardId</dt>
<dd>A number starting at 1</dd>
</dl>
<p>The following status codes should be expected:</p>
<dl>
<dt>200</dt>
<dd>OK</dd>
<dt>400</dt>
<dd>The path parameters are invalid</dd>
<dt>404</dt>
<dd>The specified ASIE node, shard set, or shard ID could not be found</dd>
</dl>
]]></description>
<!-- Endpoint Configuration -->
<url>/inteligr8/asie/node/{nodeEndpoint}/shard/{shardSet}/{shardId}</url>
<!-- Security -->
<authentication>admin</authentication>
<!-- Functionality -->
<cache>
<never>false</never>
<public>false</public>
</cache>
</webscript>

View File

@@ -0,0 +1,41 @@
<webscript xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://bitbucket.org/!api/2.0/snippets/inteligr8/AzMgbp/80fdd26a6b3769a63cdc6b54bf1f39e378545cf7/files/snippet.txt">
<!-- Naming & Organization -->
<shortname>Adds ASIE Node to Registry</shortname>
<family>Inteligr8 ASIE</family>
<description><![CDATA[
<p>Loads an ASIE shard on a single ASIE node, which will eventually register with ACS.</p>
<p>The following path parameters are expected:</p>
<dl>
<dt>nodeEndpoint</dt>
<dd>A hostname or hostname:port for the ASIE node</dd>
<dt>shardCore</dt>
<dd>A core name (prefix) for the ASIE shard (e.g. alfresco)</dd>
<dt>shardId</dt>
<dd>A numeric shard ID for the ASIE shard (e.g. 0)</dd>
</dl>
<p>The following status codes should be expected:</p>
<dl>
<dt>200</dt>
<dd>OK</dd>
<dt>400</dt>
<dd>The path parameters are invalid</dd>
<dt>404</dt>
<dd>The specified ASIE node could not be found</dd>
</dl>
]]></description>
<!-- Endpoint Configuration -->
<url>/inteligr8/asie/node/{nodeEndpoint}/shard/{shardCore}/{shardId}</url>
<!-- Security -->
<authentication>admin</authentication>
<!-- Functionality -->
<cache>
<never>false</never>
<public>false</public>
</cache>
</webscript>

View File

@@ -0,0 +1,65 @@
<webscript xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://bitbucket.org/!api/2.0/snippets/inteligr8/AzMgbp/80fdd26a6b3769a63cdc6b54bf1f39e378545cf7/files/snippet.txt">
<!-- Naming & Organization -->
<shortname>Retrieve ASIE Node Information/Status</shortname>
<family>Inteligr8 ASIE</family>
<description><![CDATA[
<p>Retrieve meta-data about the ASIE nodes and their shards registered with ACS.</p>
<p>The following query parameter is supported:</p>
<dl>
<dt>sampleHashType</dt>
<dd>A sample hash type; Sample hash types: PropertyYear, PropertyQuarter, PropertyMonth, PropertyWeek</dd>
</dl>
<p>The response will have the following format:</p>
<pre>{
"nodes": {
"string": { // node URL
"id": "string",
"shards": {
"string": { // shard ID as string
"id": number,
"txsCompleted": number,
"txsRemaining": number,
"latestTx": "datetime",
"shardSet": {
"methodSpec": "string", // generated shard set ID
"shardCount": number,
"shardHashSamples": {
"string": [ // shard ID as string
"string": // year as string
],
...
}
}
},
...
}
}
}
}</pre>
<p>The following status codes should be expected:</p>
<dl>
<dt>200</dt>
<dd>OK</dd>
<dt>204</dt>
<dd>No ASIE shard information available</dd>
<dt>400</dt>
<dd>The query parameter is invalid</dd>
</dl>
]]></description>
<!-- Endpoint Configuration -->
<url>/inteligr8/asie/nodes?sampleHashType={sampleHashType?}</url>
<format default="json">any</format>
<!-- Security -->
<authentication>admin</authentication>
<!-- Functionality -->
<cache>
<never>false</never>
<public>false</public>
</cache>
</webscript>

View File

@@ -0,0 +1,72 @@
<webscript xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://bitbucket.org/!api/2.0/snippets/inteligr8/AzMgbp/80fdd26a6b3769a63cdc6b54bf1f39e378545cf7/files/snippet.txt">
<!-- Naming & Organization -->
<shortname>Retrieve ASIE Shard Hash Status</shortname>
<family>Inteligr8 ASIE</family>
<description><![CDATA[
<p>Retrieve meta-data about the ASIE shard hashes of the `PROPERTY` method registered with ACS.
Sample hashes are performed given the query parameters, which allows for reverse hashes of those samples in the response.
A min/max or an enumerated list of values must also be provided.</p>
<p>The following path and query parameters is expected or supported:</p>
<dl>
<dt>sampleHashType</dt>
<dd>PropertyYear, PropertyQuarter, PropertyMonth, PropertyWeek</dd>
<dt>shards</dt>
<dd>The total number of shards expected</dd>
<dt>min</dt>
<dd>For numerical hashes, the minimum of an integer range to compute sample hashes</dd>
<dt>max</dt>
<dd>For numerical hashes, the maximum of an integer range to compute sample hashes</dd>
<dt>values</dt>
<dd>For any hashes, a comma-delimited enumeration of values to compute sample hashes</dd>
</dl>
<p>The response will have the following format:</p>
<pre>[
{
"methodSpec": "string", // generated shard set ID
"shardCount": number
"shards": {
"string": { // hash
"id": number,
"txsCompleted": number,
"txsRemaining": number,
"latestTx": "datetime",
"nodes": {
"string": { // ASIE node URL
"url": "string",
"hostname": "string"
},
...
}
},
...
}
},
...
]</pre>
<p>The following status codes should be expected:</p>
<dl>
<dt>200</dt>
<dd>OK</dd>
<dt>204</dt>
<dd>No ASIE PROPERTY shard information available</dd>
<dt>400</dt>
<dd>The path or query parameters are invalid</dd>
</dl>
]]></description>
<!-- Endpoint Configuration -->
<url>/inteligr8/asie/shards/byHash/{sampleHashType}/{shards}?min={min?}&amp;max={max?}&amp;values={values?}</url>
<format default="json">any</format>
<!-- Security -->
<authentication>admin</authentication>
<!-- Functionality -->
<cache>
<never>false</never>
<public>false</public>
</cache>
</webscript>

View File

@@ -0,0 +1,32 @@
<webscript xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://bitbucket.org/!api/2.0/snippets/inteligr8/AzMgbp/80fdd26a6b3769a63cdc6b54bf1f39e378545cf7/files/snippet.txt">
<!-- Naming & Organization -->
<shortname>Clear ASIE Node/Shard Registry</shortname>
<family>Inteligr8 ASIE</family>
<description><![CDATA[
<p>Clears all meta-data about the ASIE shard nodes and instances registred with ACS.
This is more extreme than the OOTB purge capability, which sometimes errors rather than purging.
This will avoid those errors, clearing all shard registration information regardless of how mess up they may be.</p>
<p>If there are no shards, this will effectively do nothing.</p>
<p>The following status codes should be expected:</p>
<dl>
<dt>200</dt>
<dd>OK</dd>
</dl>
]]></description>
<!-- Endpoint Configuration -->
<url>/inteligr8/asie/shards</url>
<url>/inteligr8/asie/nodes</url>
<!-- Security -->
<authentication>admin</authentication>
<!-- Functionality -->
<cache>
<never>false</never>
<public>false</public>
</cache>
</webscript>

View File

@@ -0,0 +1,58 @@
<webscript xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://bitbucket.org/!api/2.0/snippets/inteligr8/AzMgbp/80fdd26a6b3769a63cdc6b54bf1f39e378545cf7/files/snippet.txt">
<!-- Naming & Organization -->
<shortname>Compute ASIE Sample Hash Table</shortname>
<family>Inteligr8 ASIE</family>
<description><![CDATA[
<p>Compute sample hashes.
Sample hashes are performed given the query parameters, which allows for reverse hashes of those samples in the response.
A min/max or an enumerated list of values must also be provided.</p>
<p>The following path and query parameters is expected or supported:</p>
<dl>
<dt>shards</dt>
<dd>The total number of shards expected</dd>
<dt>min</dt>
<dd>For numerical hashes, the minimum of an integer range to compute sample hashes</dd>
<dt>max</dt>
<dd>For numerical hashes, the maximum of an integer range to compute sample hashes</dd>
<dt>values</dt>
<dd>For any hashes, a comma-delimited enumeration of values to compute sample hashes</dd>
</dl>
<p>The response will have the following format:</p>
<pre>{
"forward": {
"string": number, // sample value to shard ID
...
},
"reverse": {
"number": [ // shard ID, starting at 0
"string", // sample value
...
],
...
}
}</pre>
<p>The following status codes should be expected:</p>
<dl>
<dt>200</dt>
<dd>OK</dd>
<dt>400</dt>
<dd>The path or query parameters are invalid</dd>
</dl>
]]></description>
<!-- Endpoint Configuration -->
<url>/inteligr8/asie/hash/{shards}?min={min?}&amp;max={max?}&amp;values={values?}</url>
<format default="json">any</format>
<!-- Security -->
<authentication>admin</authentication>
<!-- Functionality -->
<cache>
<never>false</never>
<public>false</public>
</cache>
</webscript>

View File

@@ -0,0 +1,73 @@
<webscript xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://bitbucket.org/!api/2.0/snippets/inteligr8/AzMgbp/80fdd26a6b3769a63cdc6b54bf1f39e378545cf7/files/snippet.txt">
<!-- Naming & Organization -->
<shortname>Retrieve ASIE Shard Information/Status</shortname>
<family>Inteligr8 ASIE</family>
<description><![CDATA[
<p>Retrieve meta-data about the specified ASIE shard registered with ACS.</p>
<p>The following path and query parameters are expected or supported:</p>
<dl>
<dt>shardSet</dt>
<dd>A shard method combined with its distinguishing properties;
methods: MOD_ACL_ID, ACL_ID, DB_ID, DB_ID_RANGE, DATE, PROPERTY, EXPLICIT_ID;
e.g. PROPERTY;key:cm:created;regex:^d{4} or DB_ID</dd>
<dt>shardId</dt>
<dd>A number starting at 1</dd>
<dt>sampleHashType</dt>
<dd>A sample hash type; Sample hash types: PropertyYear, PropertyQuarter, PropertyMonth, PropertyWeek</dd>
</dl>
<p>The response will have the following format:</p>
<pre>{
"id": number,
"txsCompleted": number,
"txsRemaining": number,
"latestTx": "datetime",
"nodes": {
"string": { // ASIE node URL
"url": "string",
"hostname": "string"
},
...
},
"shardSet": {
"id": "string", // generated shard set ID
"method": "string",
"methodDetail": "string",
"hasContent": boolean,
"shardCount": number,
"shardHashSamples": {
"string": [ // shard ID as string
"string": // year as string
],
...
}
}
}</pre>
<p>The following status codes should be expected:</p>
<dl>
<dt>200</dt>
<dd>OK</dd>
<dt>204</dt>
<dd>No ASIE shard information available</dd>
<dt>400</dt>
<dd>The parameters are invalid</dd>
<dt>404</dt>
<dd>The specified shard set or shard ID could not be found</dd>
</dl>
]]></description>
<!-- Endpoint Configuration -->
<url>/inteligr8/asie/shard/{shardSet}/{shardId}?includeSampleHashes={includeSampleHashes?}</url>
<format default="json">any</format>
<!-- Security -->
<authentication>admin</authentication>
<!-- Functionality -->
<cache>
<never>false</never>
<public>false</public>
</cache>
</webscript>

View File

@@ -0,0 +1,68 @@
<webscript xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://bitbucket.org/!api/2.0/snippets/inteligr8/AzMgbp/80fdd26a6b3769a63cdc6b54bf1f39e378545cf7/files/snippet.txt">
<!-- Naming & Organization -->
<shortname>Retrieve ASIE Shard Information/Status</shortname>
<family>Inteligr8 ASIE</family>
<description><![CDATA[
<p>Retrieve meta-data about all the ASIE shards registered with ACS.</p>
<p>The following query parameter is supported:</p>
<dl>
<dt>sampleHashType</dt>
<dd>A sample hash type; Sample hash types: PropertyYear, PropertyQuarter, PropertyMonth, PropertyWeek</dd>
</dl>
<p>The response will have the following format:</p>
<pre>{
"string": { // generated shard set ID
"methodSpec": "string", // generated shard set ID
"shardCount": number
"shards": {
"string": { // shard ID as string
"id": number,
"txsCompleted": number,
"txsRemaining": number,
"latestTx": "datetime",
"nodes": {
"string": { // ASIE node URL
"id": "string",
"txsCompleted": number,
"latestTx": "datetime",
},
...
}
},
...
},
"shardHashSamples": {
"string": [ // shard ID as string
"string": // year as string
],
...
}
}
}</pre>
<p>The following status codes should be expected:</p>
<dl>
<dt>200</dt>
<dd>OK</dd>
<dt>204</dt>
<dd>No ASIE shard information available</dd>
<dt>400</dt>
<dd>The query parameter is invalid</dd>
</dl>
]]></description>
<!-- Endpoint Configuration -->
<url>/inteligr8/asie/shards?sampleHashType={sampleHashType?}</url>
<format default="json">any</format>
<!-- Security -->
<authentication>admin</authentication>
<!-- Functionality -->
<cache>
<never>false</never>
<public>false</public>
</cache>
</webscript>

View File

@@ -0,0 +1,8 @@
# defaulting to 3 days = 60 * 24 * 3 = 4320
inteligr8.asie.backup.persistTimeMinutes=4320
inteligr8.asie.allowedAuthorities=ALFRESCO_ADMINISTRATORS
# same as solr.baseUrl, but that property is private to the Search subsystem
inteligr8.asie.basePath=/solr

View File

@@ -0,0 +1,3 @@
logger.inteligr8-asie.name=com.inteligr8.alfresco.asie
logger.inteligr8-asie.level=INFO

View File

@@ -0,0 +1,24 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Use this file for beans to be loaded in whatever order Alfresco/Spring decides -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
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">
<!-- Enable Spring annotation scanning for classes -->
<context:component-scan base-package="com.inteligr8.alfresco.asie"
name-generator="org.springframework.context.annotation.FullyQualifiedAnnotationBeanNameGenerator" />
<bean id="search.ShardRegistry" class="org.alfresco.repo.management.subsystems.SubsystemProxyFactory">
<property name="sourceApplicationContextFactory" ref="Search" />
<property name="sourceBeanName" value="search.SolrShardRegistry" />
<property name="interfaces">
<list>
<value>org.alfresco.repo.index.shard.ShardRegistry</value>
</list>
</property>
</bean>
</beans>

View File

@@ -0,0 +1,10 @@
module.id=com_inteligr8_alfresco_${project.artifactId}
module.aliases=
module.title=${project.name}
module.description=${project.description}
module.version=${module.version}
module.repo.version.min=23.0
# this is creating all sorts of problems; probably because of the non-standard versioning
module.depends.com.inteligr8.alfresco.cxf-jaxrs-platform-module=*

54
pom.xml Normal file
View File

@@ -0,0 +1,54 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.inteligr8.alfresco</groupId>
<artifactId>asie-platform-module-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>ASIE Platform Module Parent</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<maven.compiler.release>11</maven.compiler.release>
</properties>
<build>
<pluginManagement>
<plugins>
<!-- avoids log4j dependency -->
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
</plugin>
<!-- avoids struts dependency -->
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.12.1</version>
</plugin>
<!-- Force use of a new maven-dependency-plugin that doesn't download struts dependency -->
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.8.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
<modules>
<module>solr-api</module>
<module>asie-api</module>
<module>module</module>
</modules>
<repositories>
<repository>
<id>alfresco-private</id>
<url>https://artifacts.alfresco.com/nexus/content/groups/private</url>
</repository>
</repositories>
</project>

1
solr-api/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/target/

79
solr-api/pom.xml Normal file
View File

@@ -0,0 +1,79 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.inteligr8</groupId>
<artifactId>solr-api</artifactId>
<version>1.0-SNAPSHOT-solr6</version>
<packaging>jar</packaging>
<name>Apache Solr JAX-RS API</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<maven.compiler.release>11</maven.compiler.release>
<jackson.version>2.18.0</jackson.version>
</properties>
<dependencies>
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jaxb-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.jakarta.rs</groupId>
<artifactId>jackson-jakarta-rs-json-provider</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- Including for testing purposes only -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.11.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<!-- avoids log4j dependency -->
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
</plugin>
<!-- avoids struts dependency -->
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.12.1</version>
</plugin>
<!-- Force use of a new maven-dependency-plugin that doesn't download struts dependency -->
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.7.1</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

View File

@@ -0,0 +1,26 @@
package com.inteligr8.solr.api;
import com.inteligr8.solr.model.ActionResponse;
import com.inteligr8.solr.model.ResponseAction;
import com.inteligr8.solr.model.collection.AliasesResponse;
import com.inteligr8.solr.model.collection.GetAliasesRequest;
import com.inteligr8.solr.model.core.ReloadRequest;
import jakarta.ws.rs.BeanParam;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
@Path("admin/collections")
public interface CollectionAdminApi {
@GET
@Produces(MediaType.APPLICATION_JSON)
ActionResponse<ResponseAction> reload(@BeanParam ReloadRequest request);
@GET
@Produces(MediaType.APPLICATION_JSON)
AliasesResponse getAliases(@BeanParam GetAliasesRequest request);
}

View File

@@ -0,0 +1,51 @@
package com.inteligr8.solr.api;
import com.inteligr8.solr.model.EmptyResponse;
import com.inteligr8.solr.model.core.CreateRequest;
import com.inteligr8.solr.model.core.ReloadRequest;
import com.inteligr8.solr.model.core.RenameRequest;
import com.inteligr8.solr.model.core.RequestStatusRequest;
import com.inteligr8.solr.model.core.RequestStatusResponse;
import com.inteligr8.solr.model.core.StatusRequest;
import com.inteligr8.solr.model.core.StatusResponse;
import com.inteligr8.solr.model.core.SwapRequest;
import com.inteligr8.solr.model.core.UnloadRequest;
import jakarta.ws.rs.BeanParam;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
@Path("admin/cores")
public interface CoreAdminApi {
@GET
@Produces(MediaType.APPLICATION_JSON)
StatusResponse getStatus(@BeanParam StatusRequest request);
@GET
@Produces(MediaType.APPLICATION_JSON)
EmptyResponse create(@BeanParam CreateRequest request);
@GET
@Produces(MediaType.APPLICATION_JSON)
EmptyResponse reload(@BeanParam ReloadRequest request);
@GET
@Produces(MediaType.APPLICATION_JSON)
EmptyResponse rename(@BeanParam RenameRequest request);
@GET
@Produces(MediaType.APPLICATION_JSON)
EmptyResponse swap(@BeanParam SwapRequest request);
@GET
@Produces(MediaType.APPLICATION_JSON)
EmptyResponse unload(@BeanParam UnloadRequest request);
@GET
@Produces(MediaType.APPLICATION_JSON)
RequestStatusResponse getRequestStatus(@BeanParam RequestStatusRequest request);
}

View File

@@ -0,0 +1,21 @@
package com.inteligr8.solr.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;
@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;
}
}

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