9 Commits

Author SHA1 Message Date
01d2f5ce23 fix v1.1.x pom 2024-11-01 08:35:38 -04:00
8a0db9f11d Merge branch 'develop' into feature/community 2024-11-01 08:35:03 -04:00
57293c6efe fix v1.1.x poms 2024-11-01 08:34:57 -04:00
567d971e16 added old module name as alias 2024-10-31 15:17:42 -04:00
3e544c125b initial community-module (incomplete/breaking) 2024-10-31 14:55:42 -04:00
e2c4224554 v1.1.x pom 2024-10-31 14:48:38 -04:00
dc13bc09de refactored; split enterprise from generic 2024-10-31 14:48:17 -04:00
ae4f664ba7 fix project URL 2024-10-29 09:27:34 -04:00
0474d27362 deploy all artifacts 2024-10-29 09:25:56 -04:00
100 changed files with 1864 additions and 696 deletions

View File

@@ -2,6 +2,13 @@
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.1-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>
<groupId>com.inteligr8.alfresco</groupId>
<artifactId>asie-api</artifactId>
@@ -12,12 +19,6 @@
<description>Alfresco Search &amp; Insight Engine JAX-RS API</description>
<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>
<maven.deploy.skip>true</maven.deploy.skip>
<alfresco.platform.version>6.0.0</alfresco.platform.version>
</properties>
@@ -44,28 +45,6 @@
<artifactId>alfresco-repository</artifactId>
</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.8.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
<repositories>
<repository>

86
community-module/pom.xml Normal file
View File

@@ -0,0 +1,86 @@
<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.1-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>
<artifactId>asie-community-platform-module</artifactId>
<packaging>jar</packaging>
<name>ASIE Platform Module for ACS Community</name>
<properties>
<alfresco.sdk.version>5.2.0</alfresco.sdk.version>
<alfresco.platform.version>23.3.0</alfresco.platform.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.alfresco</groupId>
<artifactId>acs-community-packaging</artifactId>
<version>${alfresco.platform.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.inteligr8.alfresco</groupId>
<artifactId>asie-shared</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<!-- Needed by this module, but provided by ACS -->
<dependency>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-repository</artifactId>
<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>
<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-public</id>
<url>https://artifacts.alfresco.com/nexus/content/groups/public</url>
</repository>
</repositories>
</project>

View File

@@ -0,0 +1,28 @@
package com.inteligr8.alfresco.asie.provider;
import org.alfresco.repo.index.shard.ShardRegistry;
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.inteligr8.alfresco.asie.Constants;
@Configuration
public class ShardRegistryProvider extends AbstractProvider<ShardRegistry> {
/**
* This allows for the selection of the primary or first ShardRegistry
* registered in the Spring BeanFactory.
*
* @return A ShardRegistry.
*/
@Bean(Constants.BEAN_SHARD_REGISTRY)
@Qualifier(Constants.QUALIFIER_ASIE)
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public ShardRegistry selectBean() {
return this.getPrimary(ShardRegistry.class);
}
}

View File

@@ -0,0 +1,80 @@
package com.inteligr8.alfresco.asie.service;
import java.io.Serializable;
import java.util.Arrays;
import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.repo.index.shard.ShardInstance;
import org.alfresco.repo.index.shard.ShardState;
import org.alfresco.service.cmr.attributes.AttributeService;
import org.alfresco.service.cmr.attributes.AttributeService.AttributeQueryCallback;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.Constants;
import com.inteligr8.alfresco.asie.enterprise.EnterpriseConstants;
@Component
public class ShardStateService implements com.inteligr8.alfresco.asie.spi.ShardStateService {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
@Qualifier(Constants.QUALIFIER_ASIE)
private AttributeService attrService;
@Autowired
@Qualifier(Constants.BEAN_SHARD_STATE_CACHE)
private SimpleCache<ShardInstance, ShardState> shardStateCache;
public void clear() {
this.logger.info("Removing all nodes/shards from the shard registry");
// this clears the state from the backend database
this.attrService.removeAttributes(EnterpriseConstants.ATTR_SHARD_STATE);
this.attrService.removeAttributes(EnterpriseConstants.ATTR_SHARD_SUBSCRIPTION);
this.attrService.removeAttributes(Constants.ATTR_ASIE);
// this clears the state from Hazelcast
this.shardStateCache.clear();
this.shardToGuidCache.clear();
}
public void remove(Serializable... keys) {
if (keys.length == 0)
throw new IllegalArgumentException();
this.logger.info("Removing from the shard registry: {}", Arrays.toString(keys));
Serializable[] shardStateKeys = keys;
Serializable[] shardSubKeys;
if (EnterpriseConstants.ATTR_SHARD_STATE.equals(keys[0])) {
shardSubKeys = ArrayUtils.clone(keys);
shardSubKeys[0] = EnterpriseConstants.ATTR_SHARD_SUBSCRIPTION;
} else {
shardStateKeys = ArrayUtils.addFirst(keys, EnterpriseConstants.ATTR_SHARD_STATE);
shardSubKeys = ArrayUtils.addFirst(keys, EnterpriseConstants.ATTR_SHARD_SUBSCRIPTION);
}
ShardState shardState = (ShardState) this.attrService.getAttribute(shardStateKeys);
// this clears the state from the backend database
this.attrService.removeAttribute(shardStateKeys);
this.attrService.removeAttribute(shardSubKeys);
// this clears the state from Hazelcast
if (shardState != null) {
this.shardStateCache.remove(shardState.getShardInstance());
this.shardToGuidCache.remove(shardState.getShardInstance());
}
}
public void iterate(AttributeQueryCallback callback) {
this.attrService.getAttributes(callback, EnterpriseConstants.ATTR_SHARD_STATE);
}
}

View File

@@ -0,0 +1,363 @@
package com.inteligr8.alfresco.asie.service;
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.OptionalInt;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.repo.index.shard.Floc;
import org.alfresco.repo.index.shard.Shard;
import org.alfresco.repo.index.shard.ShardInstance;
import org.alfresco.repo.index.shard.ShardMethodEnum;
import org.alfresco.repo.index.shard.ShardRegistry;
import org.alfresco.repo.index.shard.ShardState;
import org.alfresco.repo.search.impl.QueryParserUtils;
import org.alfresco.repo.search.impl.parsers.AlfrescoFunctionEvaluationContext;
import org.alfresco.repo.search.impl.parsers.CMISLexer;
import org.alfresco.repo.search.impl.parsers.FTSLexer;
import org.alfresco.repo.search.impl.parsers.FTSParser;
import org.alfresco.repo.search.impl.parsers.FTSQueryParser;
import org.alfresco.repo.search.impl.querymodel.Conjunction;
import org.alfresco.repo.search.impl.querymodel.Constraint;
import org.alfresco.repo.search.impl.querymodel.Disjunction;
import org.alfresco.repo.search.impl.querymodel.FunctionalConstraint;
import org.alfresco.repo.search.impl.querymodel.QueryEngine;
import org.alfresco.repo.search.impl.querymodel.QueryModelFactory;
import org.alfresco.repo.search.impl.querymodel.QueryOptions;
import org.alfresco.repo.search.impl.querymodel.QueryOptions.Connective;
import org.alfresco.repo.search.impl.querymodel.impl.BaseConstraint;
import org.alfresco.repo.search.impl.querymodel.impl.lucene.LuceneQueryBuilderComponent;
import org.alfresco.service.cmr.attributes.AttributeService;
import org.alfresco.service.cmr.attributes.AttributeService.AttributeQueryCallback;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.search.SearchParameters;
import org.alfresco.service.cmr.search.SearchParameters.Operator;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.antlr.runtime.ANTLRStringStream;
import org.antlr.runtime.CharStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.tree.CommonTree;
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;
import com.inteligr8.alfresco.asie.model.Node;
import com.inteligr8.alfresco.asie.model.ShardSet;
@Component
public class SolrShardRegistry extends AbstractLifecycleBean implements ShardRegistry {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final Pattern coreShardPattern = Pattern.compile("(.+)-[0-9]+");
@Autowired
private ShardStateService sss;
@Autowired
@Qualifier(Constants.QUALIFIER_ASIE)
private AttributeService attrService;
@Autowired
private NamespaceService namespaceService;
@Autowired
@Qualifier(Constants.BEAN_SHARD_STATE_CACHE)
private SimpleCache<ShardInstance, ShardState> onlineShardCache;
@Autowired
@Qualifier(Constants.BEAN_OFFILINE_SHARD_STATE_CACHE)
private SimpleCache<ShardInstance, ShardState> offlineShardCache;
@Autowired
@Qualifier(Constants.BEAN_CORE_EXPLICIT_CACHE)
private SimpleCache<String, QName> coreExplicitIdCache;
@Autowired
@Qualifier(Constants.BEAN_FLOC_CACHE)
private SimpleCache<Floc, Boolean> flocCache;
@Value("${inteligr8.asie.registerUnknownShardOffline}")
private boolean registerOffline;
@Value("${inteligr8.asie.offlineIdleShardInSeconds}")
private int offlineIdleShardInSeconds;
@Value("${inteligr8.asie.forgetOfflineShardInSeconds}")
private int forgetOfflineShardInSeconds;
@Override
protected void onBootstrap(ApplicationEvent event) {
this.attrService.getAttributes(new AttributeQueryCallback() {
@Override
public boolean handleAttribute(Long id, Serializable value, Serializable[] keys) {
switch ((String) keys[2]) {
case Constants.ATTR_STATE:
ShardState shardNodeState = (ShardState) value;
ShardInstance shardNode = shardNodeState.getShardInstance();
cacheShard(shardNode, shardNodeState, (String) keys[1]);
if (ShardMethodEnum.EXPLICIT_ID.toString().equals(shardNodeState.getPropertyBag().get("shard.method"))) {
String coreName = shardNode.getShard().getFloc().getPropertyBag().get("coreName");
if (coreName != null && !coreExplicitIdCache.contains(coreName)) {
String property = shardNodeState.getPropertyBag().get("shard.key");
QName propertyQname = QName.createQName(property, namespaceService);
logger.debug("Mapping core to explicit ID: {} => {}", coreName, propertyQname);
coreExplicitIdCache.put(coreName, propertyQname);
}
}
return true;
default:
return true;
}
}
}, Constants.ATTR_ASIE_NODES);
}
@Override
protected void onShutdown(ApplicationEvent event) {
}
protected void cacheShard(ShardInstance shardNode, ShardState shardNodeState, String nodeId) {
SimpleCache<ShardInstance, ShardState> shardCache = this.onlineShardCache;
ShardState cachedShardNodeState = this.onlineShardCache.get(shardNode);
if (cachedShardNodeState == null) {
cachedShardNodeState = this.offlineShardCache.get(shardNode);
shardCache = this.offlineShardCache;
}
if (cachedShardNodeState == null) {
Boolean online = (Boolean) this.attrService.getAttribute(Constants.ATTR_ASIE_NODES, nodeId, Constants.ATTR_ONLINE);
if (online != null) {
if (online.booleanValue()) {
this.onlineShardCache.put(shardNode, cachedShardNodeState);
} else {
this.offlineShardCache.put(shardNode, cachedShardNodeState);
}
} else {
if (this.registerOffline) {
this.offlineShardCache.put(shardNode, cachedShardNodeState);
} else {
this.onlineShardCache.put(shardNode, cachedShardNodeState);
}
}
} else if (cachedShardNodeState.getLastIndexedTxId() < shardNodeState.getLastIndexedTxId()) {
shardCache.put(shardNode, shardNodeState);
}
}
protected void fixFlocPropertyBag(ShardState shardNodeState) {
Floc floc = shardNodeState.getShardInstance().getShard().getFloc();
if (floc.getPropertyBag().isEmpty()) {
for (Entry<String, String> prop : shardNodeState.getPropertyBag().entrySet()) {
if (prop.getKey().startsWith("shard.")) {
floc.getPropertyBag().put(prop.getKey(), prop.getValue());
} else if (prop.getKey().equals("coreName")) {
String coreName = this.extractCoreName(prop.getValue());
if (coreName != null)
floc.getPropertyBag().put(prop.getKey(), coreName);
}
}
}
}
protected String extractCoreName(String coreShardName) {
Matcher matcher = coreShardPattern.matcher(coreShardName);
if (!matcher.matches())
return null;
return matcher.group(1);
}
@Override
public void registerShardState(ShardState shardNodeState) {
ShardInstance shardNode = shardNodeState.getShardInstance();
Node node = new Node(shardNode);
this.fixFlocPropertyBag(shardNodeState);
this.cacheShard(shardNode, shardNodeState, node.getId());
}
@Override
public Map<Floc, Map<Shard, Set<ShardState>>> getFlocs() {
Map<Floc, Map<Shard, Set<ShardState>>> flocs = new HashMap<>();
for (ShardInstance shardNode : this.onlineShardCache.getKeys()) {
Floc floc = shardNode.getShard().getFloc();
Map<Shard, Set<ShardState>> shards = flocs.get(floc);
if (shards == null)
flocs.put(floc, shards = new HashMap<>());
Set<ShardState> shardNodeStates = shards.get(shardNode.getShard());
if (shardNodeStates == null)
shards.put(shardNode.getShard(), shardNodeStates = new HashSet<>());
ShardState shardNodeState = this.onlineShardCache.get(shardNode);
if (shardNodeState != null) // in case it was removed during the looping (very rare)
shardNodeStates.add(shardNodeState);
}
return flocs;
}
@Override
public void purge() {
this.sss.clear();
}
@Override
public void purgeAgedOutShards() {
long onlineExpired = System.currentTimeMillis() - this.offlineIdleShardInSeconds * 1000L;
long offlineExpired = System.currentTimeMillis() - this.forgetOfflineShardInSeconds * 1000L;
for (ShardInstance shardNode : this.onlineShardCache.getKeys()) {
ShardState shardNodeState = this.onlineShardCache.get(shardNode);
if (shardNodeState.getLastUpdated() < onlineExpired) {
this.logger.warn("Taking shard offline: {}", shardNode);
this.onlineShardCache.remove(shardNode);
this.offlineShardCache.put(shardNode, shardNodeState);
}
}
for (ShardInstance shardNode : this.offlineShardCache.getKeys()) {
ShardState shardNodeState = this.offlineShardCache.get(shardNode);
if (shardNodeState.getLastUpdated() < offlineExpired) {
this.logger.info("Forgetting about already offline shard: {}", shardNode);
this.offlineShardCache.remove(shardNode);
}
}
}
@Override
public QName getExplicitIdProperty(String coreName) {
return this.coreExplicitIdCache.get(coreName);
}
@Override
public Set<Integer> getShardInstanceList(String coreName) {
Set<Integer> shardIds = new HashSet<>();
for (ShardInstance shardNode : this.onlineShardCache.getKeys()) {
shardIds.add(shardNode.getShard().getInstance());
}
return shardIds;
}
@Override
public OptionalInt getShardInstanceByTransactionTimestamp(String coreId, long txnTimestamp) {
throw new UnsupportedOperationException();
}
@Override
public List<ShardInstance> getIndexSlice(SearchParameters searchParameters) {
for (Floc floc : this.flocCache.getKeys()) {
Set<Integer> shardIds = new HashSet<>();
switch (floc.getShardMethod()) {
case EXPLICIT_ID:
String property = floc.getPropertyBag().get("shard.key");
// check filters and other parameters
if (searchParameters.getQuery() != null) {
SearchTerm term = this.extractPropertySearchTeam(searchParameters, property);
if (term != null && term.operator.equals("=")) {
try {
shardIds.add(Integer.parseInt(term.value));
} catch (NumberFormatException nfe) {
// skip
}
}
}
break;
}
}
searchParameters.get
// TODO Auto-generated method stub
return null;
}
private SearchTerm extractPropertySearchTeam(SearchParameters searchParameters, String property) {
switch (searchParameters.getLanguage()) {
case SearchService.LANGUAGE_CMIS_ALFRESCO:
case SearchService.LANGUAGE_CMIS_STRICT:
case SearchService.LANGUAGE_INDEX_CMIS:
case SearchService.LANGUAGE_SOLR_CMIS:
return this.extractCmisPropertySearchTerm(searchParameters, property, "=");
case SearchService.LANGUAGE_FTS_ALFRESCO:
case SearchService.LANGUAGE_INDEX_ALFRESCO:
case SearchService.LANGUAGE_INDEX_FTS_ALFRESCO:
case SearchService.LANGUAGE_LUCENE:
case SearchService.LANGUAGE_SOLR_ALFRESCO:
case SearchService.LANGUAGE_SOLR_FTS_ALFRESCO:
return this.extractFtsPropertySearchTerm(searchParameters, "=@" + property);
default:
return null;
}
}
@Autowired
private QueryEngine queryEngine;
@Autowired
private DictionaryService dictionaryService;
private SearchTerm extractFtsPropertySearchTerm(SearchParameters searchParameters, String field) {
// TODO include filter and other possible constraints
if (searchParameters.getQuery() == null)
return null;
CharStream cs = new ANTLRStringStream(searchParameters.getQuery());
FTSLexer lexer = new FTSLexer(cs);
CommonTokenStream tokens = new CommonTokenStream(lexer);
FTSParser parser = new FTSParser(tokens);
parser.setDefaultFieldConjunction(searchParameters.getDefaultFTSOperator().equals(Operator.AND));
parser.setMode(searchParameters.getDefaultFTSOperator().equals(Operator.AND) ? FTSParser.Mode.DEFAULT_CONJUNCTION : FTSParser.Mode.DEFAULT_DISJUNCTION);
CommonTree ftsNode = (CommonTree) parser.ftsQuery().getTree();
}
private SearchTerm extractCmisPropertySearchTerm(SearchParameters searchParameters, String field, String operator) {
// TODO include filter and other possible constraints
if (searchParameters.getQuery() == null)
return null;
CharStream cs = new ANTLRStringStream(searchParameters.getQuery());
CMISLexer lexer = new CMISLexer();
CommonTokenStream tokens = new CommonTokenStream(lexer);
FTSParser parser = new FTSParser(tokens);
parser.setDefaultFieldConjunction(searchParameters.getDefaultFTSOperator().equals(Operator.AND));
parser.setMode(searchParameters.getDefaultFTSOperator().equals(Operator.AND) ? FTSParser.Mode.DEFAULT_CONJUNCTION : FTSParser.Mode.DEFAULT_DISJUNCTION);
CommonTree ftsNode = (CommonTree) parser.ftsQuery().getTree();
}
private class SearchTerm {
private String field;
private String operator;
private String value;
}
}

View File

@@ -0,0 +1,28 @@
inteligr8.asie.registerUnknownShardOffline=false
inteligr8.asie.idleShardExpirationInSeconds=${}
# maxItems needs to be greater than total shards, including HA instances
cache.offlineShardStateSharedCache.tx.maxItems=1024
cache.offlineShardStateSharedCache.tx.statsEnabled=${caches.tx.statsEnabled}
cache.offlineShardStateSharedCache.maxItems=1024
cache.offlineShardStateSharedCache.timeToLiveSeconds=1800
cache.offlineShardStateSharedCache.maxIdleSeconds=0
cache.offlineShardStateSharedCache.cluster.type=fully-distributed
cache.offlineShardStateSharedCache.backup-count=1
cache.offlineShardStateSharedCache.eviction-policy=LRU
cache.offlineShardStateSharedCache.merge-policy=com.hazelcast.spi.merge.PutIfAbsentMergePolicy
cache.offlineShardStateSharedCache.readBackupData=false
cache.coreExplicitIdSharedCache.tx.maxItems=1024
cache.coreExplicitIdSharedCache.tx.statsEnabled=${caches.tx.statsEnabled}
cache.coreExplicitIdSharedCache.maxItems=1024
cache.coreExplicitIdSharedCache.timeToLiveSeconds=1800
cache.coreExplicitIdSharedCache.maxIdleSeconds=0
cache.coreExplicitIdSharedCache.cluster.type=fully-distributed
cache.coreExplicitIdSharedCache.backup-count=1
cache.coreExplicitIdSharedCache.eviction-policy=LRU
cache.coreExplicitIdSharedCache.merge-policy=com.hazelcast.spi.merge.PutIfAbsentMergePolicy
cache.coreExplicitIdSharedCache.readBackupData=false

View File

@@ -0,0 +1,18 @@
<?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">
<bean name="offlineShardStateCache" factory-bean="cacheFactory" factory-method="createCache">
<constructor-arg value="cache.offlineShardStateSharedCache" />
</bean>
<bean name="coreExplicitIdCache" factory-bean="cacheFactory" factory-method="createCache">
<constructor-arg value="cache.coreExplicitIdSharedCache" />
</bean>
</beans>

12
enterprise-module/.gitignore vendored Normal file
View File

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

View File

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

122
enterprise-module/pom.xml Normal file
View File

@@ -0,0 +1,122 @@
<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.1-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>
<artifactId>asie-enterprise-platform-module</artifactId>
<packaging>jar</packaging>
<name>ASIE Platform Module for ACS Enterprise</name>
<properties>
<alfresco.sdk.version>5.2.0</alfresco.sdk.version>
<alfresco.platform.version>23.3.0</alfresco.platform.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.alfresco</groupId>
<artifactId>acs-community-packaging</artifactId>
<version>${alfresco.platform.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Provided by cxf-jaxrs-platform-module, but packaged due to shared -->
<dependency>
<groupId>com.inteligr8</groupId>
<artifactId>common-rest-client</artifactId>
<scope>provided</scope>
</dependency>
<!-- Provided by cxf-jaxrs-platform-module, but packaged due to solr-api -->
<dependency>
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jakarta-xmlbind-annotations</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.inteligr8.alfresco</groupId>
<artifactId>asie-shared</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Needed by this module, but provided by ACS -->
<dependency>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-repository</artifactId>
<scope>provided</scope>
</dependency>
<!-- Alfresco Modules required to use this module -->
<dependency>
<groupId>com.inteligr8.alfresco</groupId>
<artifactId>cxf-jaxrs-platform-module</artifactId>
<version>1.3.1-acs-v23.3</version>
<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>
<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-public</id>
<url>https://artifacts.alfresco.com/nexus/content/groups/public</url>
</repository>
</repositories>
</project>

74
enterprise-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
enterprise-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,10 @@
package com.inteligr8.alfresco.asie.enterprise;
public interface EnterpriseConstants {
static final String BEAN_SHARD_REGISTRY = "search.ShardRegistry";
static final String ATTR_SHARD_STATE = ".SHARD_STATE";
static final String ATTR_SHARD_SUBSCRIPTION = ".SHARD_SUBSCRIPTION";
}

View File

@@ -1,16 +1,14 @@
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;
import com.inteligr8.alfresco.asie.spi.ShardStateService;
/**
* This is a workaround to a bug with the Alfresco Enterprise Solr purge
@@ -18,28 +16,26 @@ import com.inteligr8.alfresco.asie.Constants;
* all the fancy stuff and just hard purges the shards.
*/
@Component
public class ShardPurgeService extends AbstractLifecycleBean {
public class ShardPurgeOnInitService extends AbstractLifecycleBean {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
@Qualifier(Constants.QUALIFIER_ASIE)
private AttributeService attributeService;
private ShardStateService sss;
@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);
if (this.enabled) {
this.logger.debug("Purge on initialization is enabled with 'search.solrShardRegistry.purgeOnInit'");
this.sss.clear();
}
}
@Override
protected void onShutdown(ApplicationEvent event) {
// nothing to do
}
}

View File

@@ -0,0 +1,30 @@
package com.inteligr8.alfresco.asie.enterprise.provider;
import org.alfresco.repo.index.shard.ShardRegistry;
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.inteligr8.alfresco.asie.Constants;
import com.inteligr8.alfresco.asie.enterprise.EnterpriseConstants;
import com.inteligr8.alfresco.asie.provider.AbstractProvider;
@Configuration
public class ShardRegistryProvider extends AbstractProvider<ShardRegistry> {
/**
* This allows for the selection of the primary or first ShardRegistry
* registered in the Spring BeanFactory.
*
* @return A ShardRegistry.
*/
@Bean(Constants.BEAN_SHARD_REGISTRY)
@Qualifier(Constants.QUALIFIER_ASIE)
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public ShardRegistry selectBean() {
return this.getPrimaryOrNamed(ShardRegistry.class, EnterpriseConstants.BEAN_SHARD_REGISTRY);
}
}

View File

@@ -0,0 +1,84 @@
package com.inteligr8.alfresco.asie.enterprise.service;
import java.io.Serializable;
import java.util.Arrays;
import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.repo.index.shard.ShardInstance;
import org.alfresco.repo.index.shard.ShardState;
import org.alfresco.service.cmr.attributes.AttributeService;
import org.alfresco.service.cmr.attributes.AttributeService.AttributeQueryCallback;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.Constants;
import com.inteligr8.alfresco.asie.enterprise.EnterpriseConstants;
@Component
public class ShardStateService implements com.inteligr8.alfresco.asie.spi.ShardStateService {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
@Qualifier(Constants.QUALIFIER_ASIE)
private AttributeService attrService;
@Autowired
@Qualifier(Constants.BEAN_SHARD_STATE_CACHE)
private SimpleCache<ShardInstance, ShardState> shardStateCache;
@Autowired
@Qualifier(Constants.BEAN_SHARD_GUID_CACHE)
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.removeAttributes(EnterpriseConstants.ATTR_SHARD_STATE);
this.attrService.removeAttributes(EnterpriseConstants.ATTR_SHARD_SUBSCRIPTION);
this.attrService.removeAttributes(Constants.ATTR_ASIE);
// this clears the state from Hazelcast
this.shardStateCache.clear();
this.shardToGuidCache.clear();
}
public void remove(Serializable... keys) {
if (keys.length == 0)
throw new IllegalArgumentException();
this.logger.info("Removing from the shard registry: {}", Arrays.toString(keys));
Serializable[] shardStateKeys = keys;
Serializable[] shardSubKeys;
if (EnterpriseConstants.ATTR_SHARD_STATE.equals(keys[0])) {
shardSubKeys = ArrayUtils.clone(keys);
shardSubKeys[0] = EnterpriseConstants.ATTR_SHARD_SUBSCRIPTION;
} else {
shardStateKeys = ArrayUtils.addFirst(keys, EnterpriseConstants.ATTR_SHARD_STATE);
shardSubKeys = ArrayUtils.addFirst(keys, EnterpriseConstants.ATTR_SHARD_SUBSCRIPTION);
}
ShardState shardState = (ShardState) this.attrService.getAttribute(shardStateKeys);
// this clears the state from the backend database
this.attrService.removeAttribute(shardStateKeys);
this.attrService.removeAttribute(shardSubKeys);
// this clears the state from Hazelcast
if (shardState != null) {
this.shardStateCache.remove(shardState.getShardInstance());
this.shardToGuidCache.remove(shardState.getShardInstance());
}
}
public void iterate(AttributeQueryCallback callback) {
this.attrService.getAttributes(callback, EnterpriseConstants.ATTR_SHARD_STATE);
}
}

View File

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

View File

@@ -6,10 +6,6 @@
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" />

View File

@@ -0,0 +1,10 @@
module.id=com_inteligr8_alfresco_${project.artifactId}
module.aliases=com_inteligr8_alfresco_asie-platform-module
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=*

View File

@@ -1,241 +0,0 @@
<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>
<!-- 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.3.1-acs-v23.3</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>
<profiles>
<profile>
<id>ossrh-release</id>
<properties>
<maven.deploy.skip>true</maven.deploy.skip>
</properties>
<build>
<plugins>
<plugin>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<id>source</id>
<phase>package</phase>
<goals><goal>jar-no-fork</goal></goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<executions>
<execution>
<id>javadoc</id>
<phase>package</phase>
<goals><goal>jar</goal></goals>
<configuration>
<show>public</show>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-gpg-plugin</artifactId>
<executions>
<execution>
<id>sign</id>
<phase>verify</phase>
<goals><goal>sign</goal></goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.sonatype.plugins</groupId>
<artifactId>nexus-staging-maven-plugin</artifactId>
<version>1.7.0</version>
<extensions>true</extensions>
<configuration>
<serverId>ossrh</serverId>
<nexusUrl>https://s01.oss.sonatype.org/</nexusUrl>
<autoReleaseAfterClose>true</autoReleaseAfterClose>
</configuration>
<executions>
<execution>
<id>ossrh-deploy</id>
<phase>deploy</phase>
<goals><goal>deploy</goal></goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<repositories>
<repository>
<id>alfresco-private</id>
<url>https://artifacts.alfresco.com/nexus/content/groups/private</url>
</repository>
</repositories>
</project>

View File

@@ -1,13 +0,0 @@
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

@@ -1,29 +0,0 @@
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

@@ -1,60 +0,0 @@
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

@@ -1,8 +0,0 @@
# 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

72
pom.xml
View File

@@ -5,12 +5,12 @@
<groupId>com.inteligr8.alfresco</groupId>
<artifactId>asie-platform-module-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>ASIE Platform Module Parent</name>
<description>An Alfresco Content Services Platform Module for expanded Alfresco Search Services &amp; Insight Engine integration.</description>
<url>https://bitbucket.org/inteligr8/attribute-cleaner-platform-module</url>
<url>https://bitbucket.org/inteligr8/asie-platform-module</url>
<licenses>
<license>
@@ -70,6 +70,72 @@
<modules>
<module>solr-api</module>
<module>asie-api</module>
<module>module</module>
<module>shared</module>
<module>enterprise-module</module>
<module>community-module</module>
</modules>
<profiles>
<profile>
<id>ossrh-release</id>
<properties>
<maven.deploy.skip>true</maven.deploy.skip>
</properties>
<build>
<plugins>
<plugin>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<id>source</id>
<phase>package</phase>
<goals><goal>jar-no-fork</goal></goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<executions>
<execution>
<id>javadoc</id>
<phase>package</phase>
<goals><goal>jar</goal></goals>
<configuration>
<show>public</show>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-gpg-plugin</artifactId>
<executions>
<execution>
<id>sign</id>
<phase>verify</phase>
<goals><goal>sign</goal></goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.sonatype.plugins</groupId>
<artifactId>nexus-staging-maven-plugin</artifactId>
<version>1.7.0</version>
<extensions>true</extensions>
<configuration>
<serverId>ossrh</serverId>
<nexusUrl>https://s01.oss.sonatype.org/</nexusUrl>
<autoReleaseAfterClose>true</autoReleaseAfterClose>
</configuration>
<executions>
<execution>
<id>ossrh-deploy</id>
<phase>deploy</phase>
<goals><goal>deploy</goal></goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

12
shared/.gitignore vendored Normal file
View File

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

1
shared/README.md Normal file
View File

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

73
shared/pom.xml Normal file
View File

@@ -0,0 +1,73 @@
<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.1-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>
<artifactId>asie-shared</artifactId>
<packaging>jar</packaging>
<name>ASIE Shared Library for Platform Modules</name>
<properties>
<alfresco.sdk.version>5.2.0</alfresco.sdk.version>
<alfresco.platform.version>23.3.0</alfresco.platform.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.alfresco</groupId>
<artifactId>acs-community-packaging</artifactId>
<version>${alfresco.platform.version}</version>
<type>pom</type>
<scope>import</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-repository</artifactId>
<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>
<repositories>
<repository>
<id>alfresco-public</id>
<url>https://artifacts.alfresco.com/nexus/content/groups/public</url>
</repository>
</repositories>
</project>

74
shared/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
shared/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,23 @@
package com.inteligr8.alfresco.asie;
public interface Constants {
static final String QUALIFIER_ASIE = "asie";
// OOTB
static final String BEAN_SHARD_STATE_CACHE = "shardStateCache";
static final String BEAN_SHARD_GUID_CACHE = "shardToGuidCache";
static final String BEAN_OFFILINE_SHARD_STATE_CACHE = "offlineShardStateCache";
static final String BEAN_CORE_EXPLICIT_CACHE = "coreExplicitIdCache";
static final String BEAN_OBJECT_MAPPER = "asie.ObjectMapper";
static final String BEAN_ATTRIBUTE_SERVICE = "asie.AttributeService";
static final String BEAN_SHARD_REGISTRY = "asie.ShardRegistry";
static final String ATTR_ASIE = "inteligr8.asie";
static final String ATTR_ASIE_NODES = "inteligr8.asie.nodes";
static final String ATTR_STATE = "state";
static final String ATTR_ONLINE = "online";
static final String ATTR_UNLOADED = "unloadedNode.cores";
}

View File

@@ -0,0 +1,56 @@
package com.inteligr8.alfresco.asie.model;
import java.io.Serializable;
import org.alfresco.repo.index.shard.ShardInstance;
public class Node implements Serializable {
private static final long serialVersionUID = -8834744746109388928L;
private final String id;
private final ShardInstance shardNode;
public Node(ShardInstance shardNode) {
this.shardNode = shardNode;
this.id = this.getHostname() + ":" + this.getPort() + this.getPath();
}
public String getId() {
return this.id;
}
public String getHostname() {
return this.shardNode.getHostName();
}
public int getPort() {
return this.shardNode.getPort();
}
public String getPath() {
// baseUrl is to the shard; we want to the node, so exclude the core
int lastSlash = this.shardNode.getBaseUrl().lastIndexOf('/');
return this.shardNode.getBaseUrl().substring(0, lastSlash);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Node))
return false;
Node node = (Node) obj;
return this.id.equals(node.id);
}
@Override
public int hashCode() {
return this.id.hashCode();
}
@Override
public String toString() {
return this.id;
}
}

View File

@@ -1,6 +1,4 @@
package com.inteligr8.alfresco.asie.enterprise.model;
import com.inteligr8.alfresco.asie.model.NodeParameterSet;
package com.inteligr8.alfresco.asie.model;
public class NodeShardParameterSet extends NodeParameterSet {

View File

@@ -1,6 +1,4 @@
package com.inteligr8.alfresco.asie.enterprise.model;
import com.inteligr8.alfresco.asie.model.RequestParameterSet;
package com.inteligr8.alfresco.asie.model;
public class ShardParameterSet implements RequestParameterSet {

View File

@@ -1,4 +1,4 @@
package com.inteligr8.alfresco.asie.enterprise.model;
package com.inteligr8.alfresco.asie.model;
import java.util.HashMap;
import java.util.Map;

View File

@@ -0,0 +1,34 @@
package com.inteligr8.alfresco.asie.provider;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
public abstract class AbstractProvider<T> {
@Autowired
private ApplicationContext context;
public abstract T selectBean();
protected <U extends T> U getPrimaryOrNamed(Class<U> type, String beanName) {
ObjectProvider<U> provider = this.context.getBeanProvider(type);
// this will select the primary or if there is just one impl, that one impl
U u = provider.getIfUnique();
if (u == null) {
// this will select the named bean; throwing exception if there is none
u = this.context.getBean(beanName, type);
}
return u;
}
protected <U extends T> U getPrimary(Class<U> type) {
ObjectProvider<U> provider = this.context.getBeanProvider(type);
// this will select the primary or if there is just one impl, that one impl
return provider.getObject();
}
}

View File

@@ -1,11 +1,8 @@
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;
@@ -13,10 +10,7 @@ import org.springframework.context.annotation.Scope;
import com.inteligr8.alfresco.asie.Constants;
@Configuration
public class AttributeServiceProvider {
@Autowired
private ApplicationContext context;
public class AttributeServiceProvider extends AbstractProvider<AttributeService> {
/**
* This allows for the selection of the primary or first AttributeService
@@ -27,24 +21,11 @@ public class AttributeServiceProvider {
*
* @return An AttributeService.
*/
@Bean("asieAttributeService")
@Bean(Constants.BEAN_ATTRIBUTE_SERVICE)
@Qualifier(Constants.QUALIFIER_ASIE)
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public AttributeService selectAttributeService() {
public AttributeService selectBean() {
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

@@ -14,7 +14,7 @@ import com.inteligr8.alfresco.asie.Constants;
@Configuration
public class ObjectMapperProvider {
@Bean("asieObjectMapper")
@Bean(Constants.BEAN_OBJECT_MAPPER)
@Qualifier(Constants.QUALIFIER_ASIE)
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public ObjectMapper createObjectMapper() {

View File

@@ -1,4 +1,4 @@
package com.inteligr8.alfresco.asie.enterprise.rest;
package com.inteligr8.alfresco.asie.rest;
import java.io.IOException;
@@ -7,9 +7,9 @@ import org.slf4j.LoggerFactory;
import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.extensions.webscripts.WebScriptResponse;
import com.inteligr8.alfresco.asie.enterprise.model.ShardSet;
import com.inteligr8.alfresco.asie.model.ShardSet;
public abstract class AbstractAsieNodeShardWebScript extends AbstractAsieEnterpriseWebScript {
public abstract class AbstractAsieNodeShardWebScript extends AbstractAsieShardableWebScript {
private final Logger logger = LoggerFactory.getLogger(this.getClass());

View File

@@ -1,4 +1,4 @@
package com.inteligr8.alfresco.asie.enterprise.rest;
package com.inteligr8.alfresco.asie.rest;
import java.io.IOException;
import java.util.Set;
@@ -12,9 +12,9 @@ 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;
import com.inteligr8.alfresco.asie.service.ShardDiscoveryService;
public abstract class AbstractAsieNodeWebScript extends AbstractAsieEnterpriseWebScript {
public abstract class AbstractAsieNodeWebScript extends AbstractAsieShardableWebScript {
private final Logger logger = LoggerFactory.getLogger(this.getClass());

View File

@@ -1,4 +1,4 @@
package com.inteligr8.alfresco.asie.enterprise.rest;
package com.inteligr8.alfresco.asie.rest;
import java.io.IOException;
import java.util.Set;
@@ -12,10 +12,10 @@ 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;
import com.inteligr8.alfresco.asie.model.ShardSet;
import com.inteligr8.alfresco.asie.service.ShardDiscoveryService;
public abstract class AbstractAsieShardWebScript extends AbstractAsieEnterpriseWebScript {
public abstract class AbstractAsieShardWebScript extends AbstractAsieShardableWebScript {
private final Logger logger = LoggerFactory.getLogger(this.getClass());

View File

@@ -1,4 +1,4 @@
package com.inteligr8.alfresco.asie.enterprise.rest;
package com.inteligr8.alfresco.asie.rest;
import java.text.NumberFormat;
import java.time.LocalDate;
@@ -24,12 +24,11 @@ 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.rest.model.ShardInfo;
import com.inteligr8.alfresco.asie.rest.model.ShardSetInfo;
import com.inteligr8.alfresco.asie.service.SolrShardHashService;
public abstract class AbstractAsieEnterpriseWebScript extends AbstractAsieWebScript {
public abstract class AbstractAsieShardableWebScript extends AbstractAsieWebScript {
private final Pattern sampleTypePattern = Pattern.compile("([A-Za-z]+)([0-9]+)");
@@ -41,7 +40,7 @@ public abstract class AbstractAsieEnterpriseWebScript extends AbstractAsieWebScr
}
@Autowired
@Qualifier(Constants.BEAN_SHARD_REGISTRY)
@Qualifier(Constants.QUALIFIER_ASIE)
private ShardRegistry shardRegistry;
@Autowired

View File

@@ -1,13 +1,9 @@
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;
@@ -15,12 +11,7 @@ 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;
@@ -31,7 +22,6 @@ 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 {
@@ -81,30 +71,12 @@ public abstract class AbstractAsieWebScript extends AbstractWebScript implements
}
@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);
protected Set<String> getAuthorities() {
return this.authorizedAuthorities;
}
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 ObjectMapper getObjectMapper() {
return this.objectMapper;
}
protected CoreAdminApi createApi(String hostname, int port) {
@@ -164,64 +136,5 @@ public abstract class AbstractAsieWebScript extends AbstractWebScript implements
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

@@ -1,16 +1,18 @@
package com.inteligr8.alfresco.asie.enterprise.rest;
package com.inteligr8.alfresco.asie.rest;
import java.io.IOException;
import java.io.Serializable;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
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.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -22,10 +24,9 @@ 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.alfresco.asie.service.ShardBackupService;
import com.inteligr8.alfresco.asie.spi.ShardStateService;
import com.inteligr8.solr.model.CoreMetadata;
import com.inteligr8.solr.model.core.StatusRequest;
import com.inteligr8.solr.model.core.StatusResponse;
@@ -52,7 +53,7 @@ public abstract class AbstractUnregisterNodeWebScript<T extends NodeParameterSet
throws IOException {
T params = this.createParameters(req, nodeHostname, nodePort);
final Map<String, ShardState> guidCores = new HashMap<>();
final List<Pair<Serializable[], ShardState>> matchingCores = new LinkedList<>();
AttributeQueryCallback callback = new AttributeQueryCallback() {
@Override
@@ -61,17 +62,16 @@ public abstract class AbstractUnregisterNodeWebScript<T extends NodeParameterSet
if (!matches(params, shardState))
return true;
String guid = (String) keys[1];
guidCores.put(guid, shardState);
matchingCores.add(Pair.of(keys, shardState));
return true;
}
};
this.attrService.getAttributes(callback, Constants.ATTR_SHARD_STATE);
this.sss.iterate(callback);
Serializable[] keys = new String[] {
Constants.ATTR_ASIE,
Constants.ATTR_UNLOADED_NODE_CORES,
Constants.ATTR_UNLOADED,
nodeHostname + ":" + nodePort
};
@@ -80,8 +80,8 @@ public abstract class AbstractUnregisterNodeWebScript<T extends NodeParameterSet
if (cores == null)
cores = new HashMap<>();
try {
for (Entry<String, ShardState> guidCore : guidCores.entrySet()) {
ShardState shardNode = guidCore.getValue();
for (Pair<Serializable[], ShardState> matchingCore : matchingCores) {
ShardState shardNode = matchingCore.getValue();
String core = shardNode.getPropertyBag().get("coreName");
StatusResponse status = this.getCoreStatus(nodeHostname, nodePort, core);
@@ -90,14 +90,13 @@ public abstract class AbstractUnregisterNodeWebScript<T extends NodeParameterSet
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;
} else {
this.unloadCore(nodeHostname, nodePort, core);
cores.put(core, coreMetadata.getInstancePath());
}
this.unloadCore(nodeHostname, nodePort, core);
cores.put(core, coreMetadata.getInstancePath());
String guid = guidCore.getKey();
this.removeShard(guid, shardNode);
this.sss.remove(matchingCore.getKey());
this.sbs.forget(shardNode);
}
} finally {
// FIXME maybe a separate tx?
@@ -143,22 +142,5 @@ public abstract class AbstractUnregisterNodeWebScript<T extends NodeParameterSet
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,105 @@
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.Set;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.apache.commons.lang3.StringUtils;
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 net.sf.acegisecurity.GrantedAuthority;
public abstract class AbstractWebScript extends org.springframework.extensions.webscripts.AbstractWebScript {
protected abstract Set<String> getAuthorities();
@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);
}
protected boolean isAuthorized() {
if (this.getAuthorities().contains(AuthenticationUtil.getFullyAuthenticatedUser()))
return true;
for (GrantedAuthority auth : AuthenticationUtil.getFullAuthentication().getAuthorities()) {
if (this.getAuthorities().contains(auth.getAuthority()))
return true;
}
return false;
}
public abstract void executeAuthorized(WebScriptRequest request, WebScriptResponse response) throws IOException;
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

@@ -1,4 +1,4 @@
package com.inteligr8.alfresco.asie.enterprise.rest;
package com.inteligr8.alfresco.asie.rest;
import java.io.IOException;
@@ -9,10 +9,10 @@ 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;
import com.inteligr8.alfresco.asie.service.ShardBackupService;
import com.inteligr8.alfresco.asie.spi.ShardStateService;
@Component(value = "webscript.com.inteligr8.alfresco.asie.enterprise.registry.delete")
@Component(value = "webscript.com.inteligr8.alfresco.asie.registry.delete")
public class ClearRegistryWebScript extends AbstractWebScript {
@Autowired

View File

@@ -1,4 +1,4 @@
package com.inteligr8.alfresco.asie.enterprise.rest;
package com.inteligr8.alfresco.asie.rest;
import java.io.IOException;
import java.util.Set;
@@ -12,9 +12,10 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import com.inteligr8.alfresco.asie.enterprise.service.ShardBackupService;
import com.inteligr8.alfresco.asie.model.Node;
import com.inteligr8.alfresco.asie.service.ShardBackupService;
@Component(value = "webscript.com.inteligr8.alfresco.asie.enterprise.backupNode.get")
@Component(value = "webscript.com.inteligr8.alfresco.asie.backupNode.get")
public class GetBackupNodeWebScript extends AbstractAsieShardWebScript {
@Autowired
@@ -25,11 +26,11 @@ public class GetBackupNodeWebScript extends AbstractAsieShardWebScript {
if (shardNodes.isEmpty())
throw new WebScriptException(HttpStatus.NOT_FOUND.value(), "The ASIE shard state could not be found");
String nodeId = this.sbs.fetchNodeId(shardNodes);
Node node = this.sbs.fetchNode(shardNodes);
res.setContentType(MediaType.APPLICATION_JSON_VALUE);
res.setContentEncoding("utf-8");
this.getObjectMapper().writeValue(res.getWriter(), nodeId);
this.getObjectMapper().writeValue(res.getWriter(), node.getId());
}
}

View File

@@ -1,4 +1,4 @@
package com.inteligr8.alfresco.asie.enterprise.rest;
package com.inteligr8.alfresco.asie.rest;
import java.io.IOException;
import java.util.Set;
@@ -13,27 +13,27 @@ 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;
import com.inteligr8.alfresco.asie.model.Node;
import com.inteligr8.alfresco.asie.spi.ShardDiscoveryService;
@Component(value = "webscript.com.inteligr8.alfresco.asie.enterprise.leadNode.get")
@Component(value = "webscript.com.inteligr8.alfresco.asie.leadNode.get")
public class GetLeadNodeWebScript extends AbstractAsieShardWebScript {
@Autowired
private ShardAnalysisService sas;
private ShardDiscoveryService sds;
@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);
ShardInstance latestNode = this.sds.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));
this.getObjectMapper().writeValue(res.getWriter(), new Node(latestNode).getId());
}
}

View File

@@ -1,4 +1,4 @@
package com.inteligr8.alfresco.asie.enterprise.rest;
package com.inteligr8.alfresco.asie.rest;
import java.io.IOException;
import java.time.Instant;
@@ -13,12 +13,12 @@ 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;
import com.inteligr8.alfresco.asie.rest.model.NodeInfo;
import com.inteligr8.alfresco.asie.rest.model.NodeShardInfo;
import com.inteligr8.alfresco.asie.rest.model.ShardInfo;
import com.inteligr8.alfresco.asie.rest.model.ShardSetInfo;
@Component(value = "webscript.com.inteligr8.alfresco.asie.enterprise.node.get")
@Component(value = "webscript.com.inteligr8.alfresco.asie.node.get")
public class GetNodeWebScript extends AbstractAsieNodeWebScript {
@Override

View File

@@ -1,4 +1,4 @@
package com.inteligr8.alfresco.asie.enterprise.rest;
package com.inteligr8.alfresco.asie.rest;
import java.io.IOException;
import java.util.Map;
@@ -18,13 +18,14 @@ 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;
import com.inteligr8.alfresco.asie.model.Node;
import com.inteligr8.alfresco.asie.rest.model.NodeInfo;
import com.inteligr8.alfresco.asie.rest.model.NodeShardInfo;
import com.inteligr8.alfresco.asie.rest.model.ShardInfo;
import com.inteligr8.alfresco.asie.rest.model.ShardSetInfo;
@Component(value = "webscript.com.inteligr8.alfresco.asie.enterprise.nodes.get")
public class GetNodesWebScript extends AbstractAsieEnterpriseWebScript {
@Component(value = "webscript.com.inteligr8.alfresco.asie.nodes.get")
public class GetNodesWebScript extends AbstractAsieShardableWebScript {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@@ -47,7 +48,8 @@ public class GetNodesWebScript extends AbstractAsieEnterpriseWebScript {
for (Entry<Shard, Set<ShardState>> registeredShards : floc.getValue().entrySet()) {
for (ShardState registeredShardNode : registeredShards.getValue()) {
NodeInfo node = nodes.get(NodeInfo.determineId(registeredShardNode.getShardInstance()));
String nodeId = new Node(registeredShardNode.getShardInstance()).getId();
NodeInfo node = nodes.get(nodeId);
if (node == null) {
node = new NodeShardInfo(registeredShardNode.getShardInstance());
nodes.put(node.getId(), node);

View File

@@ -1,4 +1,4 @@
package com.inteligr8.alfresco.asie.enterprise.rest;
package com.inteligr8.alfresco.asie.rest;
import java.io.IOException;
import java.time.Instant;
@@ -30,14 +30,14 @@ 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;
import com.inteligr8.alfresco.asie.rest.model.NodeInfo;
import com.inteligr8.alfresco.asie.rest.model.PropertyHashShardSetInfo;
import com.inteligr8.alfresco.asie.rest.model.ShardInfo;
import com.inteligr8.alfresco.asie.rest.model.ShardNodeInfo;
import com.inteligr8.alfresco.asie.service.ShardDiscoveryService;
@Component(value = "webscript.com.inteligr8.alfresco.asie.enterprise.propertyHashShards.get")
public class GetPropertyHashShardsWebScript extends AbstractAsieEnterpriseWebScript {
@Component(value = "webscript.com.inteligr8.alfresco.asie.propertyHashShards.get")
public class GetPropertyHashShardsWebScript extends AbstractAsieShardableWebScript {
private final Logger logger = LoggerFactory.getLogger(this.getClass());

View File

@@ -1,4 +1,4 @@
package com.inteligr8.alfresco.asie.enterprise.rest;
package com.inteligr8.alfresco.asie.rest;
import java.io.IOException;
import java.util.Arrays;
@@ -24,8 +24,8 @@ 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 {
@Component(value = "webscript.com.inteligr8.alfresco.asie.sampleHashes.get")
public class GetSampleHashesWebScript extends AbstractAsieShardableWebScript {
private final Logger logger = LoggerFactory.getLogger(this.getClass());

View File

@@ -1,4 +1,4 @@
package com.inteligr8.alfresco.asie.enterprise.rest;
package com.inteligr8.alfresco.asie.rest;
import java.io.IOException;
import java.time.Instant;
@@ -15,12 +15,12 @@ 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;
import com.inteligr8.alfresco.asie.rest.model.NodeInfo;
import com.inteligr8.alfresco.asie.rest.model.ShardInfo;
import com.inteligr8.alfresco.asie.rest.model.ShardNodeInfo;
import com.inteligr8.alfresco.asie.rest.model.ShardSetInfo;
@Component(value = "webscript.com.inteligr8.alfresco.asie.enterprise.shard.get")
@Component(value = "webscript.com.inteligr8.alfresco.asie.shard.get")
public class GetShardWebScript extends AbstractAsieShardWebScript {
@Override

View File

@@ -1,4 +1,4 @@
package com.inteligr8.alfresco.asie.enterprise.rest;
package com.inteligr8.alfresco.asie.rest;
import java.io.IOException;
import java.time.Instant;
@@ -22,13 +22,13 @@ 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;
import com.inteligr8.alfresco.asie.rest.model.NodeInfo;
import com.inteligr8.alfresco.asie.rest.model.ShardInfo;
import com.inteligr8.alfresco.asie.rest.model.ShardNodeInfo;
import com.inteligr8.alfresco.asie.rest.model.ShardSetInfo;
@Component(value = "webscript.com.inteligr8.alfresco.asie.enterprise.shards.get")
public class GetShardsWebScript extends AbstractAsieEnterpriseWebScript {
@Component(value = "webscript.com.inteligr8.alfresco.asie.shards.get")
public class GetShardsWebScript extends AbstractAsieShardableWebScript {
private final Logger logger = LoggerFactory.getLogger(this.getClass());

View File

@@ -1,4 +1,4 @@
package com.inteligr8.alfresco.asie.enterprise.rest;
package com.inteligr8.alfresco.asie.rest;
import java.io.IOException;
import java.io.Serializable;
@@ -28,7 +28,7 @@ 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")
@Component(value = "webscript.com.inteligr8.alfresco.asie.nodeShard.post")
public class ReloadNodeShardWebScript extends AbstractAsieNodeWebScript {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@@ -47,7 +47,7 @@ public class ReloadNodeShardWebScript extends AbstractAsieNodeWebScript {
Serializable[] keys = new String[] {
Constants.ATTR_ASIE,
Constants.ATTR_UNLOADED_NODE_CORES,
Constants.ATTR_UNLOADED,
nodeHostname + ":" + nodePort
};

View File

@@ -1,4 +1,4 @@
package com.inteligr8.alfresco.asie.enterprise.rest;
package com.inteligr8.alfresco.asie.rest;
import java.io.IOException;
import java.io.Serializable;
@@ -30,7 +30,7 @@ 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")
@Component(value = "webscript.com.inteligr8.alfresco.asie.node.post")
public class ReloadNodeWebScript extends AbstractAsieNodeWebScript {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@@ -45,7 +45,7 @@ public class ReloadNodeWebScript extends AbstractAsieNodeWebScript {
throws IOException {
Serializable[] keys = new String[] {
Constants.ATTR_ASIE,
Constants.ATTR_UNLOADED_NODE_CORES,
Constants.ATTR_UNLOADED,
nodeHostname + ":" + nodePort
};

View File

@@ -1,13 +1,13 @@
package com.inteligr8.alfresco.asie.enterprise.rest;
package com.inteligr8.alfresco.asie.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;
import com.inteligr8.alfresco.asie.model.NodeShardParameterSet;
import com.inteligr8.alfresco.asie.model.ShardSet;
@Component(value = "webscript.com.inteligr8.alfresco.asie.enterprise.nodeShard.delete")
@Component(value = "webscript.com.inteligr8.alfresco.asie.nodeShard.delete")
public class UnloadNodeShardWebScript extends AbstractUnregisterNodeWebScript<NodeShardParameterSet> {
@Override

View File

@@ -1,11 +1,11 @@
package com.inteligr8.alfresco.asie.enterprise.rest;
package com.inteligr8.alfresco.asie.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")
@Component(value = "webscript.com.inteligr8.alfresco.asie.node.delete")
public class UnloadNodeWebScript extends AbstractUnregisterNodeWebScript<NodeParameterSet> {
@Override

View File

@@ -1,4 +1,4 @@
package com.inteligr8.alfresco.asie.enterprise.rest.model;
package com.inteligr8.alfresco.asie.rest.model;
import java.util.Map;
import java.util.TreeMap;
@@ -6,13 +6,9 @@ import java.util.TreeMap;
import org.alfresco.repo.index.shard.ShardInstance;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.inteligr8.alfresco.asie.model.Node;
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;
@@ -24,7 +20,7 @@ public abstract class NodeInfo {
}
public NodeInfo(ShardInstance nodeCache) {
this.setId(determineId(nodeCache));
this.setId(new Node(nodeCache).getId());
}
public String getId() {

View File

@@ -1,4 +1,4 @@
package com.inteligr8.alfresco.asie.enterprise.rest.model;
package com.inteligr8.alfresco.asie.rest.model;
import java.util.Map;

View File

@@ -1,4 +1,4 @@
package com.inteligr8.alfresco.asie.enterprise.rest.model;
package com.inteligr8.alfresco.asie.rest.model;
import java.util.Map;
@@ -8,7 +8,7 @@ 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.inteligr8.alfresco.asie.model.ShardSet;
import com.fasterxml.jackson.annotation.JsonProperty;
@JsonInclude(Include.NON_EMPTY)

View File

@@ -1,4 +1,4 @@
package com.inteligr8.alfresco.asie.enterprise.rest.model;
package com.inteligr8.alfresco.asie.rest.model;
import java.time.Instant;
import java.time.OffsetDateTime;

View File

@@ -1,4 +1,4 @@
package com.inteligr8.alfresco.asie.enterprise.rest.model;
package com.inteligr8.alfresco.asie.rest.model;
import java.time.Instant;
import java.time.OffsetDateTime;

View File

@@ -1,4 +1,4 @@
package com.inteligr8.alfresco.asie.enterprise.rest.model;
package com.inteligr8.alfresco.asie.rest.model;
import java.util.Map;
import java.util.Set;
@@ -10,7 +10,7 @@ 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.inteligr8.alfresco.asie.model.ShardSet;
import com.fasterxml.jackson.annotation.JsonProperty;
@JsonInclude(Include.NON_EMPTY)

View File

@@ -1,4 +1,4 @@
package com.inteligr8.alfresco.asie.enterprise.service;
package com.inteligr8.alfresco.asie.service;
import java.io.Serializable;
import java.util.Collection;
@@ -15,16 +15,17 @@ 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;
import com.inteligr8.alfresco.asie.model.Node;
import com.inteligr8.alfresco.asie.spi.ShardDiscoveryService;
@Component
public class ShardBackupService {
public class ShardBackupService implements com.inteligr8.alfresco.asie.spi.ShardBackupService {
private static final String ATTR_BACKUP_NODE = "backupNode";
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private ShardAnalysisService sas;
private ShardDiscoveryService sds;
@Autowired
@Qualifier(Constants.QUALIFIER_ASIE)
@@ -33,7 +34,7 @@ public class ShardBackupService {
@Value("${inteligr8.asie.backup.persistTimeMinutes}")
private int persistTimeMinutes;
public String fetchNodeId(Collection<ShardState> shardNodes) {
public Node fetchNode(Collection<ShardState> shardNodes) {
if (shardNodes.isEmpty())
return null;
@@ -46,8 +47,8 @@ public class ShardBackupService {
this.logger.debug("Found backup node: {}", backupNode);
if (backupNode == null || backupNode.isExpired()) {
ShardInstance backupShardInstance = this.sas.computeLeadShard(shardNodes);
backupNode = new PersistedNode(NodeInfo.determineId(backupShardInstance));
ShardInstance backupShardInstance = this.sds.computeLeadShard(shardNodes);
backupNode = new PersistedNode(new Node(backupShardInstance));
this.attributeService.setAttribute(backupNode, Constants.ATTR_ASIE, ATTR_BACKUP_NODE, shardKey);
}
@@ -70,12 +71,12 @@ public class ShardBackupService {
private class PersistedNode implements Serializable {
private static final long serialVersionUID = 1L;
private static final long serialVersionUID = 4105196543023419818L;
private final String node;
private final Node node;
private long expireTimeMillis;
PersistedNode(String node) {
PersistedNode(Node node) {
this.node = node;
this.reset();
}
@@ -88,7 +89,7 @@ public class ShardBackupService {
return this.expireTimeMillis < System.currentTimeMillis();
}
String getNode() {
Node getNode() {
return this.node;
}

View File

@@ -1,4 +1,4 @@
package com.inteligr8.alfresco.asie.enterprise.service;
package com.inteligr8.alfresco.asie.service;
import java.net.InetAddress;
import java.net.UnknownHostException;
@@ -25,16 +25,33 @@ 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;
import com.inteligr8.alfresco.asie.model.ShardSet;
@Component
public class ShardDiscoveryService {
public class ShardDiscoveryService implements com.inteligr8.alfresco.asie.spi.ShardDiscoveryService {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
@Qualifier(Constants.BEAN_SHARD_REGISTRY)
@Qualifier(Constants.QUALIFIER_ASIE)
private ShardRegistry shardRegistry;
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;
}
public Set<ShardState> findByNode(String nodeHostname, int nodePort) {
Map<Floc, Map<Shard, Set<ShardState>>> flocs = this.shardRegistry.getFlocs();
@@ -114,11 +131,11 @@ public class ShardDiscoveryService {
return filteredFlocs;
}
public Set<ShardState> findByShard(Map<Shard, Set<ShardState>> shards, int shardId) {
public <T> Set<T> filterByShard(Map<Shard, Set<T>> shards, int shardId) {
if (shards == null)
return null;
for (Entry<Shard, Set<ShardState>> shard : shards.entrySet()) {
for (Entry<Shard, Set<T>> shard : shards.entrySet()) {
if (shard.getKey().getInstance() == shardId)
return shard.getValue();
}
@@ -128,7 +145,7 @@ public class ShardDiscoveryService {
public Set<ShardState> findByShard(ShardSet shardSet, int shardId) {
Map<Shard, Set<ShardState>> shards = this.findByShardSet(shardSet);
return this.findByShard(shards, shardId);
return this.filterByShard(shards, shardId);
}
private InetAddress resolve(String hostname) {

View File

@@ -0,0 +1,17 @@
package com.inteligr8.alfresco.asie.spi;
import java.util.Collection;
import org.alfresco.repo.index.shard.ShardState;
import com.inteligr8.alfresco.asie.model.Node;
public interface ShardBackupService {
Node fetchNode(Collection<ShardState> shardNodes);
void forget();
void forget(ShardState shardNode);
}

View File

@@ -0,0 +1,72 @@
package com.inteligr8.alfresco.asie.spi;
import java.util.Collection;
import java.util.Map;
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.ShardState;
import org.alfresco.util.Pair;
import com.inteligr8.alfresco.asie.model.ShardSet;
public interface ShardDiscoveryService {
/**
* Determine the lead shard in the specified node/shard snapshot metadata.
*
* @param shardNodes A collection of snapshot metadata.
* @return A single node/shard holding the latest snapshot metadata.
*/
ShardInstance computeLeadShard(Collection<ShardState> shardNodes);
/**
* Find the latest snapshot of each shard on the specified node.
*
* @param nodeHostname The hostname of a ASIE node.
* @param nodePort The port of an ASIE node.
* @return A set of the latest snapshot metadata of shards.
*/
Set<ShardState> findByNode(String nodeHostname, int nodePort);
/**
* Find the shards, their nodes, and the latest snapshot of each within the
* specified shard set.
*
* @param shardSet A shard set.
* @return A map of shards to sets of the latest snapshot metadata of those shards and their nodes.
*/
Map<Shard, Set<ShardState>> findByShardSet(ShardSet shardSet);
/**
* Find the shards, their nodes, and the latest snapshot of each using the
* specified shard method.
*
* @param shardMethod A shard method.
* @return A collection of maps of shards to sets of the latest snapshot metadata of those shards and their nodes.
*/
Collection<Pair<Floc, Map<Shard, Set<ShardState>>>> findByShardMethod(ShardMethodEnum shardMethod);
/**
* Filter the latest snapshot of each shard.
*
* @param shards A map of shards to sets of the latest snapshot metadata of those shards and their nodes.
* @param shardId A 0-based index of a shard.
* @return A set of the latest snapshot metadata of shards.
*/
<T> Set<T> filterByShard(Map<Shard, Set<T>> shards, int shardId);
/**
* Find the latest snapshot of each shard and their nodes within the
* specified shard set.
*
* @param shardSet A shard set.
* @param shardId A 0-based index of a shard.
* @return A set of the latest snapshot metadata of shards.
*/
Set<ShardState> findByShard(ShardSet shardSet, int shardId);
}

View File

@@ -0,0 +1,18 @@
package com.inteligr8.alfresco.asie.spi;
import java.io.Serializable;
import org.alfresco.service.cmr.attributes.AttributeService.AttributeQueryCallback;
public interface ShardStateService {
/**
* Clears the shard state.
*/
void clear();
void remove(Serializable... keys);
void iterate(AttributeQueryCallback callback);
}

View File

@@ -0,0 +1,22 @@
# 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
# Overrides of alfresco-repository.jar/alfresco/caches.properties
cache.shardStateSharedCache.tx.maxItems=0
cache.shardStateSharedCache.tx.statsEnabled=${caches.tx.statsEnabled}
cache.shardStateSharedCache.maxItems=0
cache.shardStateSharedCache.timeToLiveSeconds=1800
cache.shardStateSharedCache.maxIdleSeconds=0
cache.shardStateSharedCache.cluster.type=fully-distributed
cache.shardStateSharedCache.backup-count=1
cache.shardStateSharedCache.eviction-policy=LRU
cache.shardStateSharedCache.merge-policy=com.hazelcast.spi.merge.PutIfAbsentMergePolicy
cache.shardStateSharedCache.readBackupData=false

View File

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

View File

@@ -0,0 +1,14 @@
<?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" />
</beans>

View File

@@ -2,6 +2,13 @@
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.1-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>
<groupId>com.inteligr8</groupId>
<artifactId>solr-api</artifactId>
@@ -11,12 +18,6 @@
<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>
<maven.deploy.skip>true</maven.deploy.skip>
<jackson.version>2.18.0</jackson.version>
</properties>
@@ -33,7 +34,7 @@
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jaxb-annotations</artifactId>
<artifactId>jackson-module-jakarta-xmlbind-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
@@ -47,26 +48,4 @@
<version>${jackson.version}</version>
</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.8.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>