initial checkin
This commit is contained in:
12
.gitignore
vendored
Normal file
12
.gitignore
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# Maven
|
||||||
|
target
|
||||||
|
pom.xml.versionsBackup
|
||||||
|
|
||||||
|
# Eclipse
|
||||||
|
.project
|
||||||
|
.classpath
|
||||||
|
.settings
|
||||||
|
.vscode
|
||||||
|
|
||||||
|
# IDEA
|
||||||
|
/.idea/
|
51
README.md
Normal file
51
README.md
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# Inteligr8 ACS Attribute Cleaner Platform Module Library
|
||||||
|
|
||||||
|
This is an Alfresco Content Services platform module that provides attribute service tools at the bootstrap and runtime of ACS.
|
||||||
|
|
||||||
|
# Features
|
||||||
|
|
||||||
|
You can enable this module by installing it and explicitly setting the following property:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
inteligr8.attrcleaner.enabled=true
|
||||||
|
```
|
||||||
|
|
||||||
|
You may also change the log level of any output from this module using the following property (default is `info`):
|
||||||
|
|
||||||
|
```ini
|
||||||
|
inteligr8.attrcleaner.log-level=info
|
||||||
|
```
|
||||||
|
|
||||||
|
## Query
|
||||||
|
|
||||||
|
At startup, you can output the contents of the ACS attribute service by setting the scope of this feature (default is `jmx`):
|
||||||
|
|
||||||
|
```ini
|
||||||
|
inteligr8.attrcleaner.feature.list.scope=jmx
|
||||||
|
```
|
||||||
|
|
||||||
|
Here are the possible values:
|
||||||
|
|
||||||
|
| Scope | Description |
|
||||||
|
| ---------------- | ----------- |
|
||||||
|
| `none` | Do not list any attributes from the attribute service. |
|
||||||
|
| `jmx` | List all JMX attributes (`.PropertyBackedBeans`) from the attribute service. |
|
||||||
|
| `shard-registry` | List all shard registry attributes (`.SHARD_STATE` and `.SHARD_SUBSCRIPTION`) from the attribute service. |
|
||||||
|
| `custom` | List all shard registry attributes in the attribute service that match the `inteligr8.attrcleaner.feature.list.keys` value. |
|
||||||
|
|
||||||
|
When using `custom`, you can query for certain keys using the following:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
inteligr8.attrcleaner.feature.list.keys=\.SHARD\_STATE,\.SHARD\_SUBSCRIPTION
|
||||||
|
```
|
||||||
|
|
||||||
|
The keys are expected to be **comma delimited** and **regular expression** patterns.
|
||||||
|
|
||||||
|
# Clear
|
||||||
|
|
||||||
|
At startup, you can clear the contents of the ACS attribute service by setting the scope of this feature (defualt is `none`). See the section on *Query* for details. Everything is the same, except the property names are as follows:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
inteligr8.attrcleaner.feature.clear.scope=
|
||||||
|
inteligr8.attrcleaner.feature.clear.keys=
|
||||||
|
```
|
161
pom.xml
Normal file
161
pom.xml
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>com.inteligr8.alfresco</groupId>
|
||||||
|
<artifactId>attribute-cleaner-platform-module</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<name>Attribute Cleaner ACS Platform Module</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>
|
||||||
|
|
||||||
|
<alfresco.sdk.version>5.2.0</alfresco.sdk.version>
|
||||||
|
<alfresco.platform.version>7.4.2</alfresco.platform.version>
|
||||||
|
<acs-platform.timeout>180000</acs-platform.timeout>
|
||||||
|
<cxf.version>3.5.5</cxf.version>
|
||||||
|
<jackson.version>2.15.0-rc1</jackson.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>
|
||||||
|
|
||||||
|
<!-- alfresco-repository makes 'runtime' scope, but need it to compile -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.woodstox</groupId>
|
||||||
|
<artifactId>woodstox-core</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</dependencyManagement>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- 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>
|
||||||
|
<dependency>
|
||||||
|
<groupId>jakarta.servlet</groupId>
|
||||||
|
<artifactId>jakarta.servlet-api</artifactId>
|
||||||
|
<version>4.0.4</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Included by pdfbox/aws; already provided by ACS -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-logging</groupId>
|
||||||
|
<artifactId>commons-logging</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>
|
||||||
|
<version>4.11.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>io.repaint.maven</groupId>
|
||||||
|
<artifactId>tiles-maven-plugin</artifactId>
|
||||||
|
<version>2.36</version>
|
||||||
|
<extensions>true</extensions>
|
||||||
|
<configuration>
|
||||||
|
<tiles>
|
||||||
|
<!-- Documentation: https://bitbucket.org/inteligr8/ootbee-beedk/src/stable/beedk-acs-platform-self-rad-tile -->
|
||||||
|
<tile>com.inteligr8.ootbee:beedk-acs-platform-self-rad-tile:[1.0.0,2.0.0)</tile>
|
||||||
|
<!-- Documentation: https://bitbucket.org/inteligr8/ootbee-beedk/src/stable/beedk-acs-search-rad-tile -->
|
||||||
|
<tile>com.inteligr8.ootbee:beedk-acs-search-rad-tile:[1.0.1,2.0.0)</tile>
|
||||||
|
<!-- Documentation: https://bitbucket.org/inteligr8/ootbee-beedk/src/stable/beedk-aps-rad-tile -->
|
||||||
|
<!-- Not much point to this without the bootstrapped processes and task implementations
|
||||||
|
<tile>com.inteligr8.ootbee:beedk-aps-rad-tile:[1.0.0,2.0.0)</tile>
|
||||||
|
-->
|
||||||
|
<!-- Documentation: https://bitbucket.org/inteligr8/ootbee-beedk/src/stable/beedk-acs-platform-module-tile -->
|
||||||
|
<tile>com.inteligr8.ootbee:beedk-acs-platform-module-tile:[1.0.0,2.0.0)</tile>
|
||||||
|
<!-- Documentation: https://bitbucket.org/inteligr8/ootbee-beedk/src/stable/beedk-acs-platform-self-it-tile
|
||||||
|
<tile>com.inteligr8.ootbee:beedk-acs-platform-self-it-tile:[1.0.0,2.0.0)</tile> -->
|
||||||
|
</tiles>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<!-- 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>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
<profiles>
|
||||||
|
<profile>
|
||||||
|
<id>windows-extended-timeout</id>
|
||||||
|
<activation>
|
||||||
|
<os>
|
||||||
|
<family>windows</family>
|
||||||
|
</os>
|
||||||
|
</activation>
|
||||||
|
<properties>
|
||||||
|
<acs-share.timeout>1200000</acs-share.timeout>
|
||||||
|
<acs-platform.timeout>2400000</acs-platform.timeout>
|
||||||
|
</properties>
|
||||||
|
</profile>
|
||||||
|
</profiles>
|
||||||
|
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>alfresco-private</id>
|
||||||
|
<url>https://artifacts.alfresco.com/nexus/content/groups/private</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
</project>
|
74
rad.ps1
Normal file
74
rad.ps1
Normal 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
rad.sh
Normal file
71
rad.sh
Normal 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!"
|
||||||
|
|
@@ -0,0 +1,220 @@
|
|||||||
|
package com.inteligr8.alfresco.attrclean;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.alfresco.service.cmr.attributes.AttributeService;
|
||||||
|
import org.alfresco.service.cmr.attributes.AttributeService.AttributeQueryCallback;
|
||||||
|
import org.alfresco.util.PropertyCheck;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.slf4j.event.Level;
|
||||||
|
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;
|
||||||
|
|
||||||
|
public abstract class AbstractBootstrapService extends AbstractLifecycleBean implements BootstrapService {
|
||||||
|
|
||||||
|
public enum Scope {
|
||||||
|
None,
|
||||||
|
JMX,
|
||||||
|
ShardRegistry,
|
||||||
|
Custom;
|
||||||
|
|
||||||
|
static Scope caseInsensitiveValueOf(String value) {
|
||||||
|
for (Scope scope : Scope.values())
|
||||||
|
if (scope.toString().equalsIgnoreCase(value))
|
||||||
|
return scope;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||||
|
private final Pattern rootKeyPattern = Pattern.compile("(.+)/.*");
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Qualifier("attributeService")
|
||||||
|
private AttributeService attributeService;
|
||||||
|
|
||||||
|
@Value("${inteligr8.attrcleaner.enabled}")
|
||||||
|
private boolean enabled;
|
||||||
|
|
||||||
|
@Value("${inteligr8.attrcleaner.log-level}")
|
||||||
|
private String logLevelRaw;
|
||||||
|
|
||||||
|
protected Level logLevel;
|
||||||
|
protected Scope scope;
|
||||||
|
protected Map<String, List<Pattern>> keyPatterns;
|
||||||
|
|
||||||
|
protected abstract String getRawScope();
|
||||||
|
|
||||||
|
protected abstract String getRawKeys();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onBootstrap(ApplicationEvent aevent) {
|
||||||
|
if (!this.enabled) {
|
||||||
|
this.logger.info("Inteligr8 Attribute Cleaner module is disabled");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.info("Inteligr8 Attribute Cleaner {} bootstrapping", this.getClass().getSimpleName());
|
||||||
|
|
||||||
|
if (this.validateAndNormalize())
|
||||||
|
this.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onShutdown(ApplicationEvent aevent) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean validateAndNormalize() {
|
||||||
|
this.logLevel = Level.valueOf(this.logLevelRaw.toUpperCase());
|
||||||
|
PropertyCheck.mandatory(this, "inteligr8.attrcleaner.log-level", this.logLevel);
|
||||||
|
|
||||||
|
this.scope = Scope.caseInsensitiveValueOf(this.getRawScope().replace("-", ""));
|
||||||
|
this.logger.trace("Attribute cleaner {} scope is {}", this.getClass().getSimpleName(), this.scope);
|
||||||
|
if (this.scope == null)
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
|
||||||
|
if (Scope.None.equals(this.scope)) {
|
||||||
|
this.logger.debug("Attribute cleaner {} feature is off", this.getClass().getSimpleName());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.keyPatterns = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
for (String keyRaw : this.getRawKeys().split(",")) {
|
||||||
|
this.logger.trace("Attribute cleaner {} key: {}", this.getClass().getSimpleName(), keyRaw);
|
||||||
|
|
||||||
|
if (keyRaw.length() == 0) {
|
||||||
|
this.logger.debug("Skipping empty key");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Matcher matcher = this.rootKeyPattern.matcher(keyRaw);
|
||||||
|
if (!matcher.find()) {
|
||||||
|
this.logger.warn("Key must have a root element; skipping: {}", keyRaw);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String rootKey = matcher.group(1).replace("\\.", ".");
|
||||||
|
|
||||||
|
Pattern keyPattern = Pattern.compile(keyRaw);
|
||||||
|
this.logger.debug("Validated key pattern: {} => {}", rootKey, keyPattern);
|
||||||
|
this.putAddToList(this.keyPatterns, rootKey, keyPattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() {
|
||||||
|
Map<Serializable[], Serializable> attributes = null;
|
||||||
|
|
||||||
|
switch (this.scope) {
|
||||||
|
case None:
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
case JMX:
|
||||||
|
attributes = this.queryJmx();
|
||||||
|
break;
|
||||||
|
case ShardRegistry:
|
||||||
|
attributes = this.queryShardRegistry();
|
||||||
|
break;
|
||||||
|
case Custom:
|
||||||
|
attributes = this.queryCustom();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.atLevel(this.logLevel).log("Queried {} Attributes: ", attributes.size());
|
||||||
|
for (Entry<Serializable[], Serializable> entry : attributes.entrySet()) {
|
||||||
|
this.execute(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void execute(Entry<Serializable[], Serializable> entry);
|
||||||
|
|
||||||
|
private Map<Serializable[], Serializable> queryJmx() {
|
||||||
|
return this.queryAttrs(".PropertyBackedBeans");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<Serializable[], Serializable> queryShardRegistry() {
|
||||||
|
Map<Serializable[], Serializable> attributes = new LinkedHashMap<>();
|
||||||
|
attributes.putAll(this.queryAttrs(".SHARD_STATE"));
|
||||||
|
attributes.putAll(this.queryAttrs(".SHARD_SUBSCRIPTION"));
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<Serializable[], Serializable> queryCustom() {
|
||||||
|
Map<Serializable[], Serializable> attributes = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
AttributeQueryCallback aqc = new AttributeQueryCallback() {
|
||||||
|
@Override
|
||||||
|
public boolean handleAttribute(Long attrId, Serializable value, Serializable[] keys) {
|
||||||
|
String keysAsStr = StringUtils.stripEnd(StringUtils.join(keys, "/"), "/");
|
||||||
|
for (Entry<String, List<Pattern>> patterns : keyPatterns.entrySet()) {
|
||||||
|
for (Pattern pattern : patterns.getValue()) {
|
||||||
|
if (pattern.matcher(keysAsStr).matches()) {
|
||||||
|
logger.debug("{} matches attribute: {}", pattern, keysAsStr);
|
||||||
|
attributes.put(keys, value);
|
||||||
|
} else {
|
||||||
|
logger.trace("{} does not match attribute: {}", pattern, keysAsStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (String rootKey : this.keyPatterns.keySet()) {
|
||||||
|
this.logger.debug("Querying for attributes with root key: {}", rootKey);
|
||||||
|
this.attributeService.getAttributes(aqc, rootKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<Serializable[], Serializable> queryAttrs(Serializable... selectKeys) {
|
||||||
|
Map<Serializable[], Serializable> attributes = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
AttributeQueryCallback aqc = new AttributeQueryCallback() {
|
||||||
|
@Override
|
||||||
|
public boolean handleAttribute(Long attrId, Serializable value, Serializable[] keys) {
|
||||||
|
logger.trace("Found attribute: {}", Arrays.toString(keys));
|
||||||
|
attributes.put(keys, value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.logger.debug("Querying for attributes with keys: {}", Arrays.toString(selectKeys));
|
||||||
|
this.attributeService.getAttributes(aqc, selectKeys);
|
||||||
|
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private <K, CV extends Collection<V>, V> void putAddToList(Map<K, CV> map, K key, V value) {
|
||||||
|
CV c = map.get(key);
|
||||||
|
if (c == null) {
|
||||||
|
c = (CV) new LinkedList<V>();
|
||||||
|
map.put(key, c);
|
||||||
|
}
|
||||||
|
c.add(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,9 @@
|
|||||||
|
package com.inteligr8.alfresco.attrclean;
|
||||||
|
|
||||||
|
public interface BootstrapService {
|
||||||
|
|
||||||
|
boolean validateAndNormalize();
|
||||||
|
|
||||||
|
void execute();
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,50 @@
|
|||||||
|
package com.inteligr8.alfresco.attrclean;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import org.alfresco.service.cmr.attributes.AttributeService;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
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.core.annotation.Order;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@Order(value = -10) // higher than average
|
||||||
|
public class ClearBootstrapService extends AbstractBootstrapService {
|
||||||
|
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(ClearBootstrapService.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Qualifier("attributeService")
|
||||||
|
private AttributeService attributeService;
|
||||||
|
|
||||||
|
@Value("${inteligr8.attrcleaner.feature.clear.scope}")
|
||||||
|
private String scopeRaw;
|
||||||
|
|
||||||
|
@Value("${inteligr8.attrcleaner.feature.clear.keys}")
|
||||||
|
private String keysRaw;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRawScope() {
|
||||||
|
return this.scopeRaw;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRawKeys() {
|
||||||
|
return this.keysRaw;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(Entry<Serializable[], Serializable> entry) {
|
||||||
|
String keysAsStr = StringUtils.join(entry.getKey(), "/");
|
||||||
|
this.logger.debug(" Removing: {}", keysAsStr);
|
||||||
|
this.attributeService.removeAttribute(entry.getKey());
|
||||||
|
this.logger.warn(" Removed: {}", keysAsStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,46 @@
|
|||||||
|
package com.inteligr8.alfresco.attrclean;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
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.core.annotation.Order;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@Order(value = -100) // higher than average
|
||||||
|
public class ListBootstrapService extends AbstractBootstrapService {
|
||||||
|
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Qualifier("attributeService")
|
||||||
|
private AttributeService attributeService;
|
||||||
|
|
||||||
|
@Value("${inteligr8.attrcleaner.feature.list.scope}")
|
||||||
|
private String scopeRaw;
|
||||||
|
|
||||||
|
@Value("${inteligr8.attrcleaner.feature.list.keys}")
|
||||||
|
private String keysRaw;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRawScope() {
|
||||||
|
return this.scopeRaw;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRawKeys() {
|
||||||
|
return this.keysRaw;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(Entry<Serializable[], Serializable> entry) {
|
||||||
|
this.logger.atLevel(this.logLevel).log(" {}: {}", entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
inteligr8.attrcleaner.enabled=false
|
||||||
|
inteligr8.attrcleaner.log-level=info
|
||||||
|
|
||||||
|
# list attributes: `none` | `jmx` | `shard-registry` | `custom`
|
||||||
|
inteligr8.attrcleaner.feature.list.scope=jmx
|
||||||
|
# when `custom`, what attributes to list
|
||||||
|
# supports regex; e.g.: \.PropertyBackedBeans/.*
|
||||||
|
inteligr8.attrcleaner.feature.list.keys=
|
||||||
|
|
||||||
|
# clear attributes: `none` | `jmx` | `shard-registry` | `custom`
|
||||||
|
inteligr8.attrcleaner.feature.clear.scope=none
|
||||||
|
# when `custom`, what attributes to clear
|
||||||
|
# supports regex; e.g.: \.PropertyBackedBeans/.*
|
||||||
|
inteligr8.attrcleaner.feature.clear.keys=
|
@@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
logger.inteligr8-jmx.name=com.inteligr8.alfresco.jmx
|
||||||
|
logger.inteligr8-jmx.level=INFO
|
@@ -0,0 +1,16 @@
|
|||||||
|
<?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 in package -->
|
||||||
|
<context:component-scan base-package="com.inteligr8.alfresco.jmx"
|
||||||
|
name-generator="org.springframework.context.annotation.FullyQualifiedAnnotationBeanNameGenerator">
|
||||||
|
<context:include-filter type="assignable" expression="com.inteligr8.alfresco.jmx.BootstrapService" />
|
||||||
|
</context:component-scan>
|
||||||
|
|
||||||
|
</beans>
|
@@ -0,0 +1,16 @@
|
|||||||
|
<?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 aps-public-rest-api -->
|
||||||
|
<context:component-scan base-package="com.inteligr8.alfresco.jmx"
|
||||||
|
name-generator="org.springframework.context.annotation.FullyQualifiedAnnotationBeanNameGenerator">
|
||||||
|
<context:exclude-filter type="assignable" expression="com.inteligr8.alfresco.jmx.BootstrapService" />
|
||||||
|
</context:component-scan>
|
||||||
|
|
||||||
|
</beans>
|
@@ -0,0 +1,4 @@
|
|||||||
|
module.id=${project.groupId}.${project.artifactId}
|
||||||
|
module.title=${project.name}
|
||||||
|
module.description=${project.description}
|
||||||
|
module.version=${project.version}
|
Reference in New Issue
Block a user