version upgrade; minor refactor
This commit is contained in:
BIN
metadata.keystore
Normal file
BIN
metadata.keystore
Normal file
Binary file not shown.
23
pom.xml
23
pom.xml
@@ -10,6 +10,14 @@
|
|||||||
|
|
||||||
<name>Annotations ACS Platform Module</name>
|
<name>Annotations ACS Platform Module</name>
|
||||||
<description>A module to support annotation-based development for Alfresco Content Services modules.</description>
|
<description>A module to support annotation-based development for Alfresco Content Services modules.</description>
|
||||||
|
<url>https://bitbucket.org/inteligr8/annotations-platform-module</url>
|
||||||
|
|
||||||
|
<licenses>
|
||||||
|
<license>
|
||||||
|
<name>GNU GENERAL PUBLIC LICENSE, Version 3, 29 June 2007</name>
|
||||||
|
<url>https://www.gnu.org/licenses/lgpl-3.0.txt</url>
|
||||||
|
</license>
|
||||||
|
</licenses>
|
||||||
|
|
||||||
<scm>
|
<scm>
|
||||||
<connection>scm:git:https://bitbucket.org/inteligr8/annotations-platform-module.git</connection>
|
<connection>scm:git:https://bitbucket.org/inteligr8/annotations-platform-module.git</connection>
|
||||||
@@ -34,9 +42,10 @@
|
|||||||
<maven.compiler.source>8</maven.compiler.source>
|
<maven.compiler.source>8</maven.compiler.source>
|
||||||
<maven.compiler.target>8</maven.compiler.target>
|
<maven.compiler.target>8</maven.compiler.target>
|
||||||
|
|
||||||
<alfresco.sdk.version>4.2.0</alfresco.sdk.version>
|
<alfresco.sdk.version>4.8.0</alfresco.sdk.version>
|
||||||
<alfresco.platform.version>6.2.0-ga</alfresco.platform.version>
|
<alfresco.platform.version>7.4.2</alfresco.platform.version>
|
||||||
<aspectj.version>1.9.4</aspectj.version>
|
<alfresco.platform.war.version>22.22</alfresco.platform.war.version>
|
||||||
|
<aspectj.version>1.9.19</aspectj.version>
|
||||||
<acs-platform.tomcat.opts>-javaagent:/var/lib/tomcat/dev/lib/aspectjweaver-${aspectj.version}.jar</acs-platform.tomcat.opts>
|
<acs-platform.tomcat.opts>-javaagent:/var/lib/tomcat/dev/lib/aspectjweaver-${aspectj.version}.jar</acs-platform.tomcat.opts>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
@@ -63,7 +72,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.inteligr8.alfresco</groupId>
|
<groupId>com.inteligr8.alfresco</groupId>
|
||||||
<artifactId>aspectj-platform-module</artifactId>
|
<artifactId>aspectj-platform-module</artifactId>
|
||||||
<version>1.0-SNAPSHOT</version>
|
<version>1.0.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
@@ -77,11 +86,11 @@
|
|||||||
<configuration>
|
<configuration>
|
||||||
<tiles>
|
<tiles>
|
||||||
<!-- Documentation: https://bitbucket.org/inteligr8/ootbee-beedk/src/stable/beedk-acs-platform-self-rad-tile -->
|
<!-- Documentation: https://bitbucket.org/inteligr8/ootbee-beedk/src/stable/beedk-acs-platform-self-rad-tile -->
|
||||||
<tile>com.inteligr8.ootbee:beedk-acs-platform-self-rad-tile:[1.0.0,1.1.0)</tile>
|
<tile>com.inteligr8.ootbee:beedk-acs-platform-self-rad-tile:[1.1.0,1.2.0)</tile>
|
||||||
<!-- Documentation: https://bitbucket.org/inteligr8/ootbee-beedk/src/stable/beedk-acs-platform-module-tile -->
|
<!-- Documentation: https://bitbucket.org/inteligr8/ootbee-beedk/src/stable/beedk-acs-platform-module-tile -->
|
||||||
<tile>com.inteligr8.ootbee:beedk-acs-platform-module-tile:[1.0.0,1.1.0)</tile>
|
<tile>com.inteligr8.ootbee:beedk-acs-platform-module-tile:[1.1.0,1.2.0)</tile>
|
||||||
<!-- Documentation: https://bitbucket.org/inteligr8/ootbee-beedk/src/stable/beedk-acs-platform-self-it-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,1.1.0)</tile>
|
<tile>com.inteligr8.ootbee:beedk-acs-platform-self-it-tile:[1.1.0,1.2.0)</tile>
|
||||||
</tiles>
|
</tiles>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
2
rad.sh
2
rad.sh
@@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/bin/sh
|
||||||
|
|
||||||
discoverArtifactId() {
|
discoverArtifactId() {
|
||||||
ARTIFACT_ID=`mvn -q -Dexpression=project.artifactId -DforceStdout help:evaluate | sed 's/\x1B\[[0-9;]\{1,\}[A-Za-z]//g'`
|
ARTIFACT_ID=`mvn -q -Dexpression=project.artifactId -DforceStdout help:evaluate | sed 's/\x1B\[[0-9;]\{1,\}[A-Za-z]//g'`
|
||||||
|
@@ -5,10 +5,20 @@ import java.lang.annotation.Retention;
|
|||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This annotation tells the framework to execute the annotated method
|
||||||
|
* asynchronously. The execution may be performed any number of ways,
|
||||||
|
* including a threaded execution or through a queuing service.
|
||||||
|
*/
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target(ElementType.METHOD)
|
@Target(ElementType.METHOD)
|
||||||
public @interface Asynchronous {
|
public @interface Asynchronous {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the execution is guaranteed.
|
||||||
|
*
|
||||||
|
* @return `true` if guaranteed; `false` otherwise
|
||||||
|
*/
|
||||||
boolean durable() default true;
|
boolean durable() default true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,15 @@
|
|||||||
package com.inteligr8.alfresco.annotations;
|
package com.inteligr8.alfresco.annotations;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface provides a way to specify a user for expected authorizations.
|
||||||
|
*
|
||||||
|
* @see com.inteligr8.alfresco.annotations.Authorized
|
||||||
|
*/
|
||||||
public interface Authorizable {
|
public interface Authorizable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return An ACS user ID.
|
||||||
|
*/
|
||||||
String authorizeAsUser();
|
String authorizeAsUser();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -5,10 +5,33 @@ import java.lang.annotation.Retention;
|
|||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This annotation tells the framework to execute the annotated method
|
||||||
|
* inside an ACS authorized context. This is how the execution can be elevated
|
||||||
|
* to a service account or de-escalated to a user account.
|
||||||
|
*
|
||||||
|
* If the authorization is expected to be the same (the same user), then
|
||||||
|
* another layer of authorization is **not** added.
|
||||||
|
*
|
||||||
|
* Use the Authorizable interface to provide a dynamic user ID for the
|
||||||
|
* authorization context.
|
||||||
|
*
|
||||||
|
* @see com.inteligr8.alfresco.annotations.Authorizable
|
||||||
|
*/
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target(ElementType.METHOD)
|
@Target(ElementType.METHOD)
|
||||||
public @interface Authorized {
|
public @interface Authorized {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The user ID to use for the authorization context.
|
||||||
|
*
|
||||||
|
* It is important to note that if the Authorizable interface is
|
||||||
|
* implemented, then the value returned from its method will take
|
||||||
|
* precedence over this one. This capability is useful for to support
|
||||||
|
* dynamic user authorization contexts.
|
||||||
|
*
|
||||||
|
* @return An ACS user ID; empty will be treated as `system`.
|
||||||
|
*/
|
||||||
String value() default "";
|
String value() default "";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -5,6 +5,14 @@ import java.lang.annotation.Retention;
|
|||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This annotation tells the framework to execute the annotated method
|
||||||
|
* inside an ACS `system` authorized context. This is the highest privileged
|
||||||
|
* execution.
|
||||||
|
*
|
||||||
|
* If the authorization is expected to be the same (remains `system`), then
|
||||||
|
* another layer of authorization is **not** added.
|
||||||
|
*/
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target(ElementType.METHOD)
|
@Target(ElementType.METHOD)
|
||||||
public @interface AuthorizedAsSystem {
|
public @interface AuthorizedAsSystem {
|
||||||
|
@@ -5,6 +5,11 @@ import java.lang.annotation.Retention;
|
|||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This annotation tells the framework to skip execution if the annotated
|
||||||
|
* parameter or any annotated method parameter is a child association that is
|
||||||
|
* not primary.
|
||||||
|
*/
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target({
|
@Target({
|
||||||
ElementType.METHOD,
|
ElementType.METHOD,
|
||||||
|
@@ -5,6 +5,12 @@ import java.lang.annotation.Retention;
|
|||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This annotation tells the framework to skip execution if the annotated
|
||||||
|
* parameter or any annotated method parameter is a node reference and it does
|
||||||
|
* not exist. Unless cached, this will result in the framework consulting with
|
||||||
|
* the database.
|
||||||
|
*/
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target({
|
@Target({
|
||||||
ElementType.METHOD,
|
ElementType.METHOD,
|
||||||
|
@@ -5,6 +5,15 @@ import java.lang.annotation.Retention;
|
|||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This annotation tells the framework to skip execution if the annotated
|
||||||
|
* parameter or any annotated method parameter is a node reference and it does
|
||||||
|
* not have the specified aspect. Unless cached, this will result in the
|
||||||
|
* framework consulting with the database.
|
||||||
|
*
|
||||||
|
* This includes support for checking the child node reference of a
|
||||||
|
* parent-child association and both the source/target of a peer association.
|
||||||
|
*/
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target({
|
@Target({
|
||||||
ElementType.METHOD,
|
ElementType.METHOD,
|
||||||
@@ -12,6 +21,9 @@ import java.lang.annotation.Target;
|
|||||||
})
|
})
|
||||||
public @interface IfNodeHasAspect {
|
public @interface IfNodeHasAspect {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return An ACS aspect in the Alfresco QName prefixed format (e.g. `cm:auditable`).
|
||||||
|
*/
|
||||||
String aspect() default "";
|
String aspect() default "";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -5,6 +5,15 @@ import java.lang.annotation.Retention;
|
|||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This annotation tells the framework to skip execution if the annotated
|
||||||
|
* parameter or any annotated method parameter is a node reference and it is
|
||||||
|
* not of the specified type. Unless cached, this will result in the framework
|
||||||
|
* consulting with the database.
|
||||||
|
*
|
||||||
|
* This includes support for checking the child node reference of a
|
||||||
|
* parent-child association and both the source/target of a peer association.
|
||||||
|
*/
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target({
|
@Target({
|
||||||
ElementType.METHOD,
|
ElementType.METHOD,
|
||||||
@@ -12,6 +21,9 @@ import java.lang.annotation.Target;
|
|||||||
})
|
})
|
||||||
public @interface IfNodeOfType {
|
public @interface IfNodeOfType {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return An ACS node type in the Alfresco QName prefixed format (e.g. `cm:content`).
|
||||||
|
*/
|
||||||
String type() default "";
|
String type() default "";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -5,6 +5,10 @@ import java.lang.annotation.Retention;
|
|||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This annotation tells the framework to skip execution if the annotated
|
||||||
|
* parameter or any annotated method parameter is `null`.
|
||||||
|
*/
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target({
|
@Target({
|
||||||
ElementType.METHOD,
|
ElementType.METHOD,
|
||||||
|
@@ -7,7 +7,7 @@ import java.lang.annotation.Target;
|
|||||||
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target(ElementType.METHOD)
|
@Target(ElementType.METHOD)
|
||||||
public @interface ClusterSynchronized {
|
public @interface JobSynchronized {
|
||||||
|
|
||||||
String value() default "";
|
String value() default "";
|
||||||
|
|
@@ -5,20 +5,48 @@ import java.lang.annotation.Retention;
|
|||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This annotation tells the framework to execute the annotated method inside a
|
||||||
|
* pool of threads.
|
||||||
|
*/
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target(ElementType.METHOD)
|
@Target(ElementType.METHOD)
|
||||||
public @interface Threaded {
|
public @interface Threaded {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return A name for the thread pool.
|
||||||
|
*/
|
||||||
String name() default "";
|
String name() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return A number of threads to execute.
|
||||||
|
*/
|
||||||
int threads() default 1;
|
int threads() default 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return A maximum number of threads to execute at any one time; the thread pool size.
|
||||||
|
*/
|
||||||
int concurrency() default 0;
|
int concurrency() default 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return A Java thread priority for all the threads.
|
||||||
|
*/
|
||||||
int priority() default Thread.NORM_PRIORITY;
|
int priority() default Thread.NORM_PRIORITY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the calling thread should wait for all the threads to complete.
|
||||||
|
*
|
||||||
|
* @return `true` to wait; `false` to return immediately.
|
||||||
|
*/
|
||||||
boolean join() default false;
|
boolean join() default false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How long the calling thread should wait before returning. If a timeout
|
||||||
|
* is reached, a TimeoutException will be thrown. If this is not desired,
|
||||||
|
* then `join()` should return `false`.
|
||||||
|
*
|
||||||
|
* @return A number of milliseconds to wait; 0 waits indefinitely
|
||||||
|
*/
|
||||||
long joinWaitMillis() default 0L;
|
long joinWaitMillis() default 0L;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -5,16 +5,41 @@ import java.lang.annotation.Retention;
|
|||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This annotation tells the framework to wrap the annotated method inside an
|
||||||
|
* ACS API retryable transaction. This may be used in conjunction with the
|
||||||
|
* Spring Transactional annotation.
|
||||||
|
*
|
||||||
|
* @see org.springframework.transaction.annotation.Transactional
|
||||||
|
*/
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target(ElementType.METHOD)
|
@Target(ElementType.METHOD)
|
||||||
public @interface TransactionalRetryable {
|
public @interface TransactionalRetryable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return A number of retries; -1 for ACS API default (unlimited).
|
||||||
|
* @see RetryingTransactionHelper#setMaxRetries(int)
|
||||||
|
*/
|
||||||
int maxRetries() default -1;
|
int maxRetries() default -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return A minimum number of milliseconds to wait between retries; -1 for ACS API default (200 ms).
|
||||||
|
* @see RetryingTransactionHelper#setMinRetryWaitMs(int)
|
||||||
|
*/
|
||||||
int minRetryWaitInMillis() default -1;
|
int minRetryWaitInMillis() default -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return A maximum number of milliseconds to wait between retries; -1 for ACS API default (2000 ms).
|
||||||
|
* @see RetryingTransactionHelper#setMaxRetryWaitMs(int)
|
||||||
|
*/
|
||||||
int maxRetryWaitInMillis() default -1;
|
int maxRetryWaitInMillis() default -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return A number of milliseconds to progressively add to the wait after each attempt; -1 for ACS API default (100 ms).
|
||||||
|
* @see RetryingTransactionHelper#setRetryWaitIncrementMs(int)
|
||||||
|
*/
|
||||||
int incRetryWaitInMillis() default -1;
|
int incRetryWaitInMillis() default -1;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -2,11 +2,21 @@ package com.inteligr8.alfresco.annotations.aspect;
|
|||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
import org.aspectj.lang.ProceedingJoinPoint;
|
import org.aspectj.lang.ProceedingJoinPoint;
|
||||||
import org.aspectj.lang.reflect.MethodSignature;
|
import org.aspectj.lang.reflect.MethodSignature;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public class AbstractMethodAspect<A extends Annotation> extends AbstractWarnOnceService {
|
public abstract class AbstractMethodAspect<A extends Annotation> extends AbstractWarnOnceService {
|
||||||
|
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||||
|
|
||||||
|
private Set<Pair<String, String>> warned = new HashSet<>();
|
||||||
|
|
||||||
protected A getAnnotation(ProceedingJoinPoint joinPoint, Class<A> annotationClass, boolean warnReturn, boolean warnThrows) {
|
protected A getAnnotation(ProceedingJoinPoint joinPoint, Class<A> annotationClass, boolean warnReturn, boolean warnThrows) {
|
||||||
Method method = this.getMethod(joinPoint, annotationClass, warnReturn, warnThrows);
|
Method method = this.getMethod(joinPoint, annotationClass, warnReturn, warnThrows);
|
||||||
@@ -50,4 +60,25 @@ public class AbstractMethodAspect<A extends Annotation> extends AbstractWarnOnce
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected boolean validate(ProceedingJoinPoint joinPoint, Class<A> annotationClass,
|
||||||
|
Collection<Class<? extends Annotation>> ignoredAnnotationClasses, Collection<Class<? extends Annotation>> disallowedAnnotationClasses) {
|
||||||
|
Method method = this.getMethod(joinPoint, annotationClass, false, false);
|
||||||
|
|
||||||
|
for (Class<? extends Annotation> a : ignoredAnnotationClasses) {
|
||||||
|
if (method.isAnnotationPresent(a)) {
|
||||||
|
if (this.warned.add(Pair.of(a.getName(), annotationClass.getName())))
|
||||||
|
this.logger.warn("@{} cannot be used on the same method as @{}; ignoring annotation", a.getSimpleName(), annotationClass.getSimpleName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Class<? extends Annotation> a : disallowedAnnotationClasses) {
|
||||||
|
if (method.isAnnotationPresent(a)) {
|
||||||
|
this.logger.error("@{} cannot be used on the same method as @{}", a.getSimpleName(), annotationClass.getSimpleName());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -6,7 +6,7 @@ import java.util.Set;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public class AbstractWarnOnceService {
|
public abstract class AbstractWarnOnceService {
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||||
|
|
||||||
|
@@ -14,10 +14,13 @@ import org.springframework.beans.factory.annotation.Qualifier;
|
|||||||
import com.inteligr8.alfresco.annotations.Asynchronous;
|
import com.inteligr8.alfresco.annotations.Asynchronous;
|
||||||
import com.inteligr8.alfresco.annotations.service.AsyncService;
|
import com.inteligr8.alfresco.annotations.service.AsyncService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This aspect implements the @Asynchronous annotation.
|
||||||
|
*
|
||||||
|
* @see com.inteligr8.alfresco.annotations.Asynchronous
|
||||||
|
*/
|
||||||
@Aspect
|
@Aspect
|
||||||
@DeclarePrecedence("com.inteligr8.alfresco.annotations.aspect.AsyncAspect, *")
|
@DeclarePrecedence("com.inteligr8.alfresco.annotations.aspect.AsyncAspect, *")
|
||||||
//@Order(Ordered.HIGHEST_PRECEDENCE + Byte.MAX_VALUE)
|
|
||||||
//@Component
|
|
||||||
public class AsyncAspect extends AbstractMethodAspect<Asynchronous> {
|
public class AsyncAspect extends AbstractMethodAspect<Asynchronous> {
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||||
|
@@ -18,10 +18,14 @@ import com.inteligr8.alfresco.annotations.Authorizable;
|
|||||||
import com.inteligr8.alfresco.annotations.Authorized;
|
import com.inteligr8.alfresco.annotations.Authorized;
|
||||||
import com.inteligr8.alfresco.annotations.AuthorizedAsSystem;
|
import com.inteligr8.alfresco.annotations.AuthorizedAsSystem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This aspect implements the Authorized and AuthorizedAsSystem annotations.
|
||||||
|
*
|
||||||
|
* @see com.inteligr8.alfresco.annotations.Authorized
|
||||||
|
* @see com.inteligr8.alfresco.annotations.AuthorizedAsSystem
|
||||||
|
*/
|
||||||
@Aspect
|
@Aspect
|
||||||
@DeclarePrecedence("com.inteligr8.alfresco.annotations.aspect.AuthorizedAspect, com.inteligr8.alfresco.annotations.aspect.RetryingTransactionAspect")
|
@DeclarePrecedence("com.inteligr8.alfresco.annotations.aspect.AuthorizedAspect, com.inteligr8.alfresco.annotations.aspect.RetryingTransactionAspect")
|
||||||
//@Order(Ordered.HIGHEST_PRECEDENCE + Short.MAX_VALUE)
|
|
||||||
//@Component
|
|
||||||
public class AuthorizedAspect {
|
public class AuthorizedAspect {
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||||
@@ -73,7 +77,7 @@ public class AuthorizedAspect {
|
|||||||
|
|
||||||
Authorized runAsAnnotation = method.getAnnotation(Authorized.class);
|
Authorized runAsAnnotation = method.getAnnotation(Authorized.class);
|
||||||
String runAs = StringUtils.trimToNull(runAsAnnotation.value());
|
String runAs = StringUtils.trimToNull(runAsAnnotation.value());
|
||||||
if (runAs != null) {
|
if (runAs != null && runAs.length() > 0) {
|
||||||
this.logger.trace("The @Authorized method '{}' must run as: {}", method, runAs);
|
this.logger.trace("The @Authorized method '{}' must run as: {}", method, runAs);
|
||||||
return runAs;
|
return runAs;
|
||||||
}
|
}
|
||||||
|
@@ -12,8 +12,12 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
import com.inteligr8.alfresco.annotations.IfChildAssociationIsPrimary;
|
import com.inteligr8.alfresco.annotations.IfChildAssociationIsPrimary;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This aspect implements the IfChildAssociationIsPrimary annotation.
|
||||||
|
*
|
||||||
|
* @see com.inteligr8.alfresco.annotations.IfChildAssociationIsPrimary
|
||||||
|
*/
|
||||||
@Aspect
|
@Aspect
|
||||||
//@Component
|
|
||||||
public class ChildIsPrimaryAspect extends AbstractMethodOrParameterAspect<IfChildAssociationIsPrimary> {
|
public class ChildIsPrimaryAspect extends AbstractMethodOrParameterAspect<IfChildAssociationIsPrimary> {
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||||
|
@@ -14,13 +14,16 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import com.inteligr8.alfresco.annotations.ClusterSynchronized;
|
import com.inteligr8.alfresco.annotations.JobSynchronized;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This aspect implements the JobSynchronized annotation.
|
||||||
|
*
|
||||||
|
* @see com.inteligr8.alfresco.annotations.JobSynchronized
|
||||||
|
*/
|
||||||
@Aspect
|
@Aspect
|
||||||
@DeclarePrecedence("com.inteligr8.alfresco.annotations.aspect.RetryingTransactionAspect, com.inteligr8.alfresco.annotations.aspect.ClusterSynchronizedAspect")
|
@DeclarePrecedence("com.inteligr8.alfresco.annotations.aspect.RetryingTransactionAspect, com.inteligr8.alfresco.annotations.aspect.JobLockAspect")
|
||||||
//@Component
|
public class JobLockAspect extends AbstractMethodAspect<JobSynchronized> {
|
||||||
//@Order(Ordered.LOWEST_PRECEDENCE - Byte.MAX_VALUE)
|
|
||||||
public class ClusterSynchronizedAspect extends AbstractMethodAspect<ClusterSynchronized> {
|
|
||||||
|
|
||||||
private static final String NS = "http://inteligr8.com/alfresco/model";
|
private static final String NS = "http://inteligr8.com/alfresco/model";
|
||||||
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||||
@@ -28,33 +31,33 @@ public class ClusterSynchronizedAspect extends AbstractMethodAspect<ClusterSynch
|
|||||||
@Autowired
|
@Autowired
|
||||||
private JobLockService jobLockService;
|
private JobLockService jobLockService;
|
||||||
|
|
||||||
@Pointcut("@annotation(com.inteligr8.alfresco.annotations.ClusterSynchronized) && execution(* *(..))")
|
@Pointcut("@annotation(com.inteligr8.alfresco.annotations.JobSynchronized) && execution(* *(..))")
|
||||||
public void isClusterSyncAnnotated() {
|
public void isJobSyncAnnotated() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Around("isClusterSyncAnnotated()")
|
@Around("isJobSyncAnnotated()")
|
||||||
public Object clusterSync(ProceedingJoinPoint joinPoint) throws Throwable {
|
public Object jobSync(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||||
this.logger.trace("clusterSync({})", joinPoint);
|
this.logger.trace("jobSync({})", joinPoint);
|
||||||
|
|
||||||
Method method = this.getMethod(joinPoint, ClusterSynchronized.class, false, false);
|
Method method = this.getMethod(joinPoint, JobSynchronized.class, false, false);
|
||||||
ClusterSynchronized clusterSync = method.getAnnotation(ClusterSynchronized.class);
|
JobSynchronized clusterSync = method.getAnnotation(JobSynchronized.class);
|
||||||
|
|
||||||
QName lockQName = this.getLockQName(clusterSync, method);
|
QName lockQName = this.getLockQName(clusterSync, method);
|
||||||
|
|
||||||
this.logger.debug("Acquiring cluster lock: {}", lockQName);
|
this.logger.debug("Acquiring job lock: {}", lockQName);
|
||||||
String lockToken = this.jobLockService.getLock(lockQName, clusterSync.lockTimeoutInMillis(),
|
String lockToken = this.jobLockService.getLock(lockQName, clusterSync.lockTimeoutInMillis(),
|
||||||
clusterSync.acquireWaitBetweenRetriesInMillis(), clusterSync.acquireMaxRetries());
|
clusterSync.acquireWaitBetweenRetriesInMillis(), clusterSync.acquireMaxRetries());
|
||||||
try {
|
try {
|
||||||
this.logger.trace("Acquired cluster lock: {}", lockQName);
|
this.logger.trace("Acquired job lock: {}", lockQName);
|
||||||
|
|
||||||
return joinPoint.proceed();
|
return joinPoint.proceed();
|
||||||
} finally {
|
} finally {
|
||||||
this.logger.debug("Releasing cluster lock: {}", lockQName);
|
this.logger.debug("Releasing job lock: {}", lockQName);
|
||||||
this.jobLockService.releaseLock(lockToken, lockQName);
|
this.jobLockService.releaseLock(lockToken, lockQName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private QName getLockQName(ClusterSynchronized clusterSync, Method method) {
|
private QName getLockQName(JobSynchronized clusterSync, Method method) {
|
||||||
String lockName = StringUtils.trimToNull(clusterSync.value());
|
String lockName = StringUtils.trimToNull(clusterSync.value());
|
||||||
if (lockName != null) {
|
if (lockName != null) {
|
||||||
return QName.createQNameWithValidLocalName(NS, lockName);
|
return QName.createQNameWithValidLocalName(NS, lockName);
|
@@ -1,16 +1,12 @@
|
|||||||
package com.inteligr8.alfresco.annotations.aspect;
|
package com.inteligr8.alfresco.annotations.aspect;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedHashSet;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.alfresco.service.cmr.dictionary.ClassDefinition;
|
import org.alfresco.service.cmr.dictionary.ClassDefinition;
|
||||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||||
import org.alfresco.service.cmr.repository.AssociationRef;
|
|
||||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
|
||||||
import org.alfresco.service.cmr.repository.NodeRef;
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
import org.alfresco.service.cmr.repository.NodeService;
|
import org.alfresco.service.cmr.repository.NodeService;
|
||||||
import org.alfresco.service.namespace.QName;
|
import org.alfresco.service.namespace.QName;
|
||||||
@@ -25,12 +21,16 @@ import org.slf4j.LoggerFactory;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
|
||||||
import com.inteligr8.alfresco.annotations.NodeAspectConstrainable;
|
|
||||||
import com.inteligr8.alfresco.annotations.IfNodeHasAspect;
|
import com.inteligr8.alfresco.annotations.IfNodeHasAspect;
|
||||||
|
import com.inteligr8.alfresco.annotations.NodeAspectConstrainable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This aspect implements the IfNodeHasAspect annotation.
|
||||||
|
*
|
||||||
|
* @see com.inteligr8.alfresco.annotations.IfNodeHasAspect
|
||||||
|
*/
|
||||||
@Aspect
|
@Aspect
|
||||||
@DeclarePrecedence("com.inteligr8.alfresco.annotations.aspect.RetryingTransactionAspect, com.inteligr8.alfresco.annotations.aspect.NodeAspectAspect")
|
@DeclarePrecedence("com.inteligr8.alfresco.annotations.aspect.RetryingTransactionAspect, com.inteligr8.alfresco.annotations.aspect.NodeAspectAspect")
|
||||||
//@Component
|
|
||||||
public class NodeAspectAspect extends QNameBasedAspect<IfNodeHasAspect> {
|
public class NodeAspectAspect extends QNameBasedAspect<IfNodeHasAspect> {
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||||
@@ -129,35 +129,4 @@ public class NodeAspectAspect extends QNameBasedAspect<IfNodeHasAspect> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private Collection<NodeRef> extractNodeRefs(Object obj) {
|
|
||||||
if (obj instanceof NodeRef) {
|
|
||||||
NodeRef nodeRef = (NodeRef) obj;
|
|
||||||
return Collections.singleton(nodeRef);
|
|
||||||
} else if (obj instanceof ChildAssociationRef) {
|
|
||||||
ChildAssociationRef childAssocRef = (ChildAssociationRef) obj;
|
|
||||||
return Collections.singleton(childAssocRef.getChildRef());
|
|
||||||
} else if (obj instanceof AssociationRef) {
|
|
||||||
AssociationRef assocRef = (AssociationRef) obj;
|
|
||||||
return Arrays.asList(assocRef.getSourceRef(), assocRef.getTargetRef());
|
|
||||||
} else if (obj instanceof Collection<?>) {
|
|
||||||
Set<NodeRef> nodeRefs = new LinkedHashSet<>();
|
|
||||||
for (Object o : ((Collection<?>) obj)) {
|
|
||||||
Collection<NodeRef> subNodeRefs = this.extractNodeRefs(o);
|
|
||||||
if (subNodeRefs != null)
|
|
||||||
nodeRefs.addAll(subNodeRefs);
|
|
||||||
}
|
|
||||||
return nodeRefs;
|
|
||||||
} else if (obj instanceof Object[]) {
|
|
||||||
Set<NodeRef> nodeRefs = new LinkedHashSet<>();
|
|
||||||
for (Object o : ((Object[]) obj)) {
|
|
||||||
Collection<NodeRef> subNodeRefs = this.extractNodeRefs(o);
|
|
||||||
if (subNodeRefs != null)
|
|
||||||
nodeRefs.addAll(subNodeRefs);
|
|
||||||
}
|
|
||||||
return nodeRefs;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,16 +1,11 @@
|
|||||||
package com.inteligr8.alfresco.annotations.aspect;
|
package com.inteligr8.alfresco.annotations.aspect;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.LinkedHashSet;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.alfresco.service.cmr.dictionary.ClassDefinition;
|
import org.alfresco.service.cmr.dictionary.ClassDefinition;
|
||||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||||
import org.alfresco.service.cmr.repository.AssociationRef;
|
|
||||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
|
||||||
import org.alfresco.service.cmr.repository.NodeRef;
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
import org.alfresco.service.cmr.repository.NodeService;
|
import org.alfresco.service.cmr.repository.NodeService;
|
||||||
import org.alfresco.service.namespace.QName;
|
import org.alfresco.service.namespace.QName;
|
||||||
@@ -28,9 +23,13 @@ import org.springframework.beans.factory.annotation.Value;
|
|||||||
import com.inteligr8.alfresco.annotations.IfNodeOfType;
|
import com.inteligr8.alfresco.annotations.IfNodeOfType;
|
||||||
import com.inteligr8.alfresco.annotations.NodeTypeConstrainable;
|
import com.inteligr8.alfresco.annotations.NodeTypeConstrainable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This aspect implements the IfNodeOfType annotation.
|
||||||
|
*
|
||||||
|
* @see com.inteligr8.alfresco.annotations.IfNodeOfType
|
||||||
|
*/
|
||||||
@Aspect
|
@Aspect
|
||||||
@DeclarePrecedence("com.inteligr8.alfresco.annotations.aspect.RetryingTransactionAspect, com.inteligr8.alfresco.annotations.aspect.NodeTypeAspect")
|
@DeclarePrecedence("com.inteligr8.alfresco.annotations.aspect.RetryingTransactionAspect, com.inteligr8.alfresco.annotations.aspect.NodeTypeAspect")
|
||||||
//@Component
|
|
||||||
public class NodeTypeAspect extends QNameBasedAspect<IfNodeOfType> {
|
public class NodeTypeAspect extends QNameBasedAspect<IfNodeOfType> {
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||||
@@ -129,35 +128,4 @@ public class NodeTypeAspect extends QNameBasedAspect<IfNodeOfType> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private Collection<NodeRef> extractNodeRefs(Object obj) {
|
|
||||||
if (obj instanceof NodeRef) {
|
|
||||||
NodeRef nodeRef = (NodeRef) obj;
|
|
||||||
return Collections.singleton(nodeRef);
|
|
||||||
} else if (obj instanceof ChildAssociationRef) {
|
|
||||||
ChildAssociationRef childAssocRef = (ChildAssociationRef) obj;
|
|
||||||
return Collections.singleton(childAssocRef.getChildRef());
|
|
||||||
} else if (obj instanceof AssociationRef) {
|
|
||||||
AssociationRef assocRef = (AssociationRef) obj;
|
|
||||||
return Arrays.asList(assocRef.getSourceRef(), assocRef.getTargetRef());
|
|
||||||
} else if (obj instanceof Collection<?>) {
|
|
||||||
Set<NodeRef> nodeRefs = new LinkedHashSet<>();
|
|
||||||
for (Object o : ((Collection<?>) obj)) {
|
|
||||||
Collection<NodeRef> subNodeRefs = this.extractNodeRefs(o);
|
|
||||||
if (subNodeRefs != null)
|
|
||||||
nodeRefs.addAll(subNodeRefs);
|
|
||||||
}
|
|
||||||
return nodeRefs;
|
|
||||||
} else if (obj instanceof Object[]) {
|
|
||||||
Set<NodeRef> nodeRefs = new LinkedHashSet<>();
|
|
||||||
for (Object o : ((Object[]) obj)) {
|
|
||||||
Collection<NodeRef> subNodeRefs = this.extractNodeRefs(o);
|
|
||||||
if (subNodeRefs != null)
|
|
||||||
nodeRefs.addAll(subNodeRefs);
|
|
||||||
}
|
|
||||||
return nodeRefs;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -12,9 +12,12 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
import com.inteligr8.alfresco.annotations.IfNotNull;
|
import com.inteligr8.alfresco.annotations.IfNotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This aspect implements the IfNotNull annotation.
|
||||||
|
*
|
||||||
|
* @see com.inteligr8.alfresco.annotations.IfNotNull
|
||||||
|
*/
|
||||||
@Aspect
|
@Aspect
|
||||||
//@Order(Ordered.HIGHEST_PRECEDENCE + 64)
|
|
||||||
//@Component
|
|
||||||
public class NotNullAspect {
|
public class NotNullAspect {
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||||
|
@@ -22,9 +22,13 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
|
|
||||||
import com.inteligr8.alfresco.annotations.IfNodeExists;
|
import com.inteligr8.alfresco.annotations.IfNodeExists;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This aspect implements the IfNodeExists annotation.
|
||||||
|
*
|
||||||
|
* @see com.inteligr8.alfresco.annotations.IfNodeExists
|
||||||
|
*/
|
||||||
@Aspect
|
@Aspect
|
||||||
@DeclarePrecedence("com.inteligr8.alfresco.annotations.aspect.RetryingTransactionAspect, com.inteligr8.alfresco.annotations.aspect.OperableNodeAspect")
|
@DeclarePrecedence("com.inteligr8.alfresco.annotations.aspect.RetryingTransactionAspect, com.inteligr8.alfresco.annotations.aspect.OperableNodeAspect")
|
||||||
//@Component
|
|
||||||
public class OperableNodeAspect extends AbstractMethodOrParameterAspect<IfNodeExists> {
|
public class OperableNodeAspect extends AbstractMethodOrParameterAspect<IfNodeExists> {
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||||
|
@@ -1,14 +1,20 @@
|
|||||||
package com.inteligr8.alfresco.annotations.aspect;
|
package com.inteligr8.alfresco.annotations.aspect;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
|
|
||||||
import org.alfresco.repo.cache.DefaultSimpleCache;
|
import org.alfresco.repo.cache.DefaultSimpleCache;
|
||||||
import org.alfresco.repo.cache.SimpleCache;
|
import org.alfresco.repo.cache.SimpleCache;
|
||||||
|
import org.alfresco.service.cmr.repository.AssociationRef;
|
||||||
|
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
import org.alfresco.service.namespace.NamespaceService;
|
import org.alfresco.service.namespace.NamespaceService;
|
||||||
import org.alfresco.service.namespace.QName;
|
import org.alfresco.service.namespace.QName;
|
||||||
import org.alfresco.service.namespace.QNamePattern;
|
import org.alfresco.service.namespace.QNamePattern;
|
||||||
@@ -76,6 +82,37 @@ public abstract class QNameBasedAspect<T extends Annotation> extends AbstractMet
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Collection<NodeRef> extractNodeRefs(Object obj) {
|
||||||
|
if (obj instanceof NodeRef) {
|
||||||
|
NodeRef nodeRef = (NodeRef) obj;
|
||||||
|
return Collections.singleton(nodeRef);
|
||||||
|
} else if (obj instanceof ChildAssociationRef) {
|
||||||
|
ChildAssociationRef childAssocRef = (ChildAssociationRef) obj;
|
||||||
|
return Collections.singleton(childAssocRef.getChildRef());
|
||||||
|
} else if (obj instanceof AssociationRef) {
|
||||||
|
AssociationRef assocRef = (AssociationRef) obj;
|
||||||
|
return Arrays.asList(assocRef.getSourceRef(), assocRef.getTargetRef());
|
||||||
|
} else if (obj instanceof Collection<?>) {
|
||||||
|
Set<NodeRef> nodeRefs = new LinkedHashSet<>();
|
||||||
|
for (Object o : ((Collection<?>) obj)) {
|
||||||
|
Collection<NodeRef> subNodeRefs = this.extractNodeRefs(o);
|
||||||
|
if (subNodeRefs != null)
|
||||||
|
nodeRefs.addAll(subNodeRefs);
|
||||||
|
}
|
||||||
|
return nodeRefs;
|
||||||
|
} else if (obj instanceof Object[]) {
|
||||||
|
Set<NodeRef> nodeRefs = new LinkedHashSet<>();
|
||||||
|
for (Object o : ((Object[]) obj)) {
|
||||||
|
Collection<NodeRef> subNodeRefs = this.extractNodeRefs(o);
|
||||||
|
if (subNodeRefs != null)
|
||||||
|
nodeRefs.addAll(subNodeRefs);
|
||||||
|
}
|
||||||
|
return nodeRefs;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public interface QNameBasedCallback<T> {
|
public interface QNameBasedCallback<T> {
|
||||||
|
@@ -21,10 +21,24 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
|
|
||||||
import com.inteligr8.alfresco.annotations.TransactionalRetryable;
|
import com.inteligr8.alfresco.annotations.TransactionalRetryable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This aspect implements the @Transactional and @TransactionalRetryable
|
||||||
|
* annotations.
|
||||||
|
*
|
||||||
|
* Most notably, it implements the Spring @Transactional annotation, so it
|
||||||
|
* works when used within ACS modules. Both could be used; or just either one.
|
||||||
|
* Each situation has a different meaning.
|
||||||
|
*
|
||||||
|
* - Without @TransactionalRetryable, it will not automatically retry due to
|
||||||
|
* expected concurrency issues.
|
||||||
|
* - Without @Transactional, it will be like a readonly
|
||||||
|
* @Transactional(SUPPORTS)
|
||||||
|
*
|
||||||
|
* @see org.springframework.transaction.annotation.Transactional
|
||||||
|
* @see com.inteligr8.alfresco.annotations.TransactionalRetryable
|
||||||
|
*/
|
||||||
@Aspect
|
@Aspect
|
||||||
@DeclarePrecedence("com.inteligr8.alfresco.annotations.aspect.AuthorizedAspect, com.inteligr8.alfresco.annotations.aspect.RetryingTransactionAspect")
|
@DeclarePrecedence("com.inteligr8.alfresco.annotations.aspect.AuthorizedAspect, com.inteligr8.alfresco.annotations.aspect.RetryingTransactionAspect")
|
||||||
//@Component
|
|
||||||
//@Order(Ordered.LOWEST_PRECEDENCE - Short.MAX_VALUE)
|
|
||||||
public class RetryingTransactionAspect {
|
public class RetryingTransactionAspect {
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||||
|
@@ -9,6 +9,7 @@ import java.util.concurrent.BlockingQueue;
|
|||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.aspectj.lang.ProceedingJoinPoint;
|
import org.aspectj.lang.ProceedingJoinPoint;
|
||||||
@@ -24,10 +25,13 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
|||||||
import com.inteligr8.alfresco.annotations.Threadable;
|
import com.inteligr8.alfresco.annotations.Threadable;
|
||||||
import com.inteligr8.alfresco.annotations.Threaded;
|
import com.inteligr8.alfresco.annotations.Threaded;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This aspect implements the @Threaded annotation.
|
||||||
|
*
|
||||||
|
* @see com.inteligr8.alfresco.annotations.Threaded
|
||||||
|
*/
|
||||||
@Aspect
|
@Aspect
|
||||||
@DeclarePrecedence("com.inteligr8.alfresco.annotations.aspect.AsyncAspect, com.inteligr8.alfresco.annotations.aspect.ThreadedAspect, *")
|
@DeclarePrecedence("com.inteligr8.alfresco.annotations.aspect.AsyncAspect, com.inteligr8.alfresco.annotations.aspect.ThreadedAspect, *")
|
||||||
//@Order(Ordered.HIGHEST_PRECEDENCE + Byte.MAX_VALUE)
|
|
||||||
//@Component
|
|
||||||
public class ThreadedAspect extends AbstractMethodAspect<Threaded> {
|
public class ThreadedAspect extends AbstractMethodAspect<Threaded> {
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||||
@@ -53,6 +57,7 @@ public class ThreadedAspect extends AbstractMethodAspect<Threaded> {
|
|||||||
this.logger.trace("threaded({})", joinPoint);
|
this.logger.trace("threaded({})", joinPoint);
|
||||||
|
|
||||||
Threaded threaded = this.getAnnotation(joinPoint, Threaded.class, true, true);
|
Threaded threaded = this.getAnnotation(joinPoint, Threaded.class, true, true);
|
||||||
|
|
||||||
MergedThreadConfiguration threadConfig = new MergedThreadConfiguration(joinPoint, threaded);
|
MergedThreadConfiguration threadConfig = new MergedThreadConfiguration(joinPoint, threaded);
|
||||||
|
|
||||||
ThreadFactoryBuilder tfbuilder = new ThreadFactoryBuilder()
|
ThreadFactoryBuilder tfbuilder = new ThreadFactoryBuilder()
|
||||||
@@ -86,13 +91,16 @@ public class ThreadedAspect extends AbstractMethodAspect<Threaded> {
|
|||||||
|
|
||||||
if (threaded.join()) {
|
if (threaded.join()) {
|
||||||
long waitMillis = threaded.joinWaitMillis() == 0L ? 300000L : threaded.joinWaitMillis();
|
long waitMillis = threaded.joinWaitMillis() == 0L ? 300000L : threaded.joinWaitMillis();
|
||||||
do {
|
while (true) {
|
||||||
this.logger.debug("Blocking this thread until subthreads finish: {}", Thread.currentThread().getId());
|
this.logger.debug("Blocking this thread until subthreads finish: {}", Thread.currentThread().getId());
|
||||||
if (threadExecutor.awaitTermination(waitMillis, TimeUnit.MILLISECONDS)) {
|
if (!threadExecutor.awaitTermination(waitMillis, TimeUnit.MILLISECONDS)) {
|
||||||
|
if (threaded.joinWaitMillis() > 0L)
|
||||||
|
throw new TimeoutException();
|
||||||
|
} else {
|
||||||
this.logger.trace("Subthreads finished; unblocking this thread: {}", Thread.currentThread().getId());
|
this.logger.trace("Subthreads finished; unblocking this thread: {}", Thread.currentThread().getId());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} while (threaded.joinWaitMillis() == 0L);
|
}
|
||||||
|
|
||||||
this.logger.debug("Subthreads running: {}; unblocking this thread: {}", threadExecutor.getActiveCount(), Thread.currentThread().getId());
|
this.logger.debug("Subthreads running: {}; unblocking this thread: {}", threadExecutor.getActiveCount(), Thread.currentThread().getId());
|
||||||
}
|
}
|
||||||
|
@@ -17,6 +17,9 @@ import org.alfresco.service.transaction.TransactionService;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This bean implements a standard TransactionManager for ACS.
|
||||||
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class AlfrescoTransactionManager implements TransactionManager {
|
public class AlfrescoTransactionManager implements TransactionManager {
|
||||||
|
|
||||||
|
@@ -30,7 +30,6 @@ import javax.jms.MessageConsumer;
|
|||||||
import javax.jms.MessageProducer;
|
import javax.jms.MessageProducer;
|
||||||
import javax.jms.Queue;
|
import javax.jms.Queue;
|
||||||
import javax.jms.Session;
|
import javax.jms.Session;
|
||||||
import javax.transaction.TransactionManager;
|
|
||||||
|
|
||||||
import org.alfresco.model.ContentModel;
|
import org.alfresco.model.ContentModel;
|
||||||
import org.alfresco.repo.cache.SimpleCache;
|
import org.alfresco.repo.cache.SimpleCache;
|
||||||
@@ -48,8 +47,8 @@ import org.alfresco.service.namespace.NamespaceService;
|
|||||||
import org.alfresco.service.namespace.QName;
|
import org.alfresco.service.namespace.QName;
|
||||||
import org.alfresco.service.transaction.TransactionService;
|
import org.alfresco.service.transaction.TransactionService;
|
||||||
import org.alfresco.util.Pair;
|
import org.alfresco.util.Pair;
|
||||||
import org.apache.activemq.ActiveMQXAConnectionFactory;
|
import org.apache.activemq.ActiveMQConnectionFactory;
|
||||||
import org.apache.activemq.jms.pool.XaPooledConnectionFactory;
|
import org.apache.activemq.jms.pool.PooledConnectionFactory;
|
||||||
import org.aspectj.lang.ProceedingJoinPoint;
|
import org.aspectj.lang.ProceedingJoinPoint;
|
||||||
import org.aspectj.lang.reflect.MethodSignature;
|
import org.aspectj.lang.reflect.MethodSignature;
|
||||||
import org.quartz.JobKey;
|
import org.quartz.JobKey;
|
||||||
@@ -79,7 +78,7 @@ import com.inteligr8.alfresco.annotations.service.AsyncProcessException;
|
|||||||
import com.inteligr8.alfresco.annotations.service.AsyncService;
|
import com.inteligr8.alfresco.annotations.service.AsyncService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class provides integration with MQ for the asynchronous filing of nodes.
|
* This class provides integration with MQ for the asynchronous method executions.
|
||||||
*
|
*
|
||||||
* @author brian@inteligr8.com
|
* @author brian@inteligr8.com
|
||||||
*/
|
*/
|
||||||
@@ -98,7 +97,6 @@ public class MqAsyncService extends AbstractLifecycleBean implements AsyncServic
|
|||||||
protected int workerThreads;
|
protected int workerThreads;
|
||||||
|
|
||||||
@Value("${inteligr8.async.mq.url}")
|
@Value("${inteligr8.async.mq.url}")
|
||||||
//@Value("${messaging.broker.url}")
|
|
||||||
protected String url;
|
protected String url;
|
||||||
|
|
||||||
@Value("${inteligr8.async.mq.username}")
|
@Value("${inteligr8.async.mq.username}")
|
||||||
@@ -137,12 +135,9 @@ public class MqAsyncService extends AbstractLifecycleBean implements AsyncServic
|
|||||||
@Autowired
|
@Autowired
|
||||||
protected TransactionService txService;
|
protected TransactionService txService;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
protected TransactionManager txManager;
|
|
||||||
|
|
||||||
private String hostname;
|
private String hostname;
|
||||||
|
|
||||||
private XaPooledConnectionFactory factory;
|
private PooledConnectionFactory factory;
|
||||||
|
|
||||||
private SimpleCache<Pair<Class<?>, String>, Method> methodCache;
|
private SimpleCache<Pair<Class<?>, String>, Method> methodCache;
|
||||||
|
|
||||||
@@ -164,12 +159,11 @@ public class MqAsyncService extends AbstractLifecycleBean implements AsyncServic
|
|||||||
this.hostname = "unknown";
|
this.hostname = "unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
ActiveMQXAConnectionFactory factory = new ActiveMQXAConnectionFactory(this.url);
|
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(this.url);
|
||||||
|
|
||||||
XaPooledConnectionFactory pool = new XaPooledConnectionFactory();
|
PooledConnectionFactory pool = new PooledConnectionFactory();
|
||||||
pool.setConnectionFactory(factory);
|
pool.setConnectionFactory(factory);
|
||||||
pool.setMaxConnections(this.maxConnections);
|
pool.setMaxConnections(this.maxConnections);
|
||||||
pool.setTransactionManager(this.txManager);
|
|
||||||
pool.start();
|
pool.start();
|
||||||
|
|
||||||
this.factory = pool;
|
this.factory = pool;
|
||||||
|
@@ -33,7 +33,8 @@ import com.inteligr8.alfresco.annotations.service.AsyncProcessException;
|
|||||||
import com.inteligr8.alfresco.annotations.service.AsyncService;
|
import com.inteligr8.alfresco.annotations.service.AsyncService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class provides a non-persistent alternative to MQ for asynchronous execution.
|
* This class provides a non-persistent alternative to MQ for asynchronous method
|
||||||
|
* execution.
|
||||||
*
|
*
|
||||||
* @author brian@inteligr8.com
|
* @author brian@inteligr8.com
|
||||||
*/
|
*/
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
<aspect name="com.inteligr8.alfresco.annotations.aspect.AsyncAspect" />
|
<aspect name="com.inteligr8.alfresco.annotations.aspect.AsyncAspect" />
|
||||||
<aspect name="com.inteligr8.alfresco.annotations.aspect.AuthorizedAspect" />
|
<aspect name="com.inteligr8.alfresco.annotations.aspect.AuthorizedAspect" />
|
||||||
|
|
||||||
<aspect name="com.inteligr8.alfresco.annotations.aspect.ClusterSynchronizedAspect" />
|
<aspect name="com.inteligr8.alfresco.annotations.aspect.JobLockAspect" />
|
||||||
<aspect name="com.inteligr8.alfresco.annotations.aspect.OperableNodeAspect" />
|
<aspect name="com.inteligr8.alfresco.annotations.aspect.OperableNodeAspect" />
|
||||||
<aspect name="com.inteligr8.alfresco.annotations.aspect.NodeTypeAspect" />
|
<aspect name="com.inteligr8.alfresco.annotations.aspect.NodeTypeAspect" />
|
||||||
<aspect name="com.inteligr8.alfresco.annotations.aspect.NodeAspectAspect" />
|
<aspect name="com.inteligr8.alfresco.annotations.aspect.NodeAspectAspect" />
|
||||||
|
@@ -0,0 +1,2 @@
|
|||||||
|
logger.inteligr8-annotations.name=com.inteligr8.alfresco.annotations
|
||||||
|
logger.inteligr8-annotations.level=info
|
@@ -1,4 +1,9 @@
|
|||||||
module.id=${project.artifactId}
|
module.id=${project.groupId}.${project.artifactId}
|
||||||
module.title=${project.name}
|
module.title=${project.name}
|
||||||
module.description=${project.description}
|
module.description=${project.description}
|
||||||
module.version=${project.version}
|
module.version=${project.version}
|
||||||
|
|
||||||
|
module.repo.version.min=6.0
|
||||||
|
#module.repo.version.max=
|
||||||
|
|
||||||
|
module.depends.aspectj-platform-module=1.0-*
|
||||||
|
@@ -6,37 +6,52 @@ import org.slf4j.LoggerFactory;
|
|||||||
import org.springframework.context.ApplicationEvent;
|
import org.springframework.context.ApplicationEvent;
|
||||||
import org.springframework.extensions.surf.util.AbstractLifecycleBean;
|
import org.springframework.extensions.surf.util.AbstractLifecycleBean;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class ClusterSynchronizedTest extends AbstractLifecycleBean {
|
public class JobSynchronizedTest extends AbstractLifecycleBean {
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onBootstrap(ApplicationEvent event) {
|
protected void onBootstrap(ApplicationEvent event) {
|
||||||
MutableInt threadsRun = new MutableInt();
|
MutableInt threadsRun = new MutableInt();
|
||||||
|
this.simpleLock();
|
||||||
this.threadThenLock(threadsRun);
|
this.threadThenLock(threadsRun);
|
||||||
|
this.threadAndLock(threadsRun);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onShutdown(ApplicationEvent event) {
|
protected void onShutdown(ApplicationEvent event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Threaded(name = "cluster-sync", threads = 5, join = true)
|
@JobSynchronized
|
||||||
@Transactional
|
private void simpleLock() {
|
||||||
@ClusterSynchronized
|
this.logger.debug("simpleLock()");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Threaded(name = "job-sync", threads = 5, join = true)
|
||||||
|
@JobSynchronized
|
||||||
|
private void threadAndLock(MutableInt threadsRun) {
|
||||||
|
this.lock(threadsRun);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Threaded(name = "job-sync", threads = 5, join = true)
|
||||||
private void threadThenLock(MutableInt threadsRun) {
|
private void threadThenLock(MutableInt threadsRun) {
|
||||||
this.lock(threadsRun);
|
this.lock(threadsRun);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JobSynchronized
|
||||||
private void lock(MutableInt threadsRun) {
|
private void lock(MutableInt threadsRun) {
|
||||||
|
this.locked(threadsRun);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void locked(MutableInt threadsRun) {
|
||||||
int t = threadsRun.intValue();
|
int t = threadsRun.intValue();
|
||||||
this.logger.debug("After start of a mutually exclusive execution block: {}", t);
|
this.logger.debug("After start of a mutually exclusive execution block: {}", t);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Thread.sleep(200L);
|
Thread.sleep(100L);
|
||||||
} catch (InterruptedException ie) {
|
} catch (InterruptedException ie) {
|
||||||
}
|
}
|
||||||
|
|
@@ -6,3 +6,6 @@ log4j.logger.org.springframework.extensions.webscripts.ScriptLogger=debug
|
|||||||
|
|
||||||
# non-WebScript JavaScript execution debugging
|
# non-WebScript JavaScript execution debugging
|
||||||
log4j.logger.org.alfresco.repo.jscript.ScriptLogger=debug
|
log4j.logger.org.alfresco.repo.jscript.ScriptLogger=debug
|
||||||
|
|
||||||
|
# Module importing
|
||||||
|
#log4j.logger.org.alfresco.repo.module.ImporterModuleComponent=trace
|
||||||
|
@@ -0,0 +1,12 @@
|
|||||||
|
# Module debugging
|
||||||
|
logger.inteligr8-annotations.level=trace
|
||||||
|
|
||||||
|
# WebScript debugging
|
||||||
|
logger.springframework-extensions-webscripts-ScriptLogger.level=debug
|
||||||
|
|
||||||
|
# non-WebScript JavaScript execution debugging
|
||||||
|
logger.alfresco-repo-jscript-ScriptLogger.level=debug
|
||||||
|
|
||||||
|
# Module importing
|
||||||
|
#logger.alfresco-repo-module-importer.name=org.alfresco.repo.module.ImporterModuleComponent
|
||||||
|
#logger.alfresco-repo-module-importer.level=trace
|
Reference in New Issue
Block a user