version upgrade; minor refactor

This commit is contained in:
2024-08-07 18:05:43 -04:00
parent afcfbbc61a
commit dc916ecdd6
37 changed files with 375 additions and 142 deletions

BIN
metadata.keystore Normal file

Binary file not shown.

23
pom.xml
View File

@@ -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
View File

@@ -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'`

View File

@@ -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;
} }

View File

@@ -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();
} }

View File

@@ -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 "";
} }

View File

@@ -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 {

View File

@@ -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,

View File

@@ -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,

View File

@@ -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 "";
} }

View File

@@ -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 "";
} }

View File

@@ -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,

View File

@@ -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 "";

View File

@@ -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;
} }

View File

@@ -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;
} }

View File

@@ -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;
}
} }

View File

@@ -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());

View File

@@ -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());

View File

@@ -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;
} }

View File

@@ -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());

View File

@@ -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);

View File

@@ -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;
}
}
} }

View File

@@ -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;
}
}
} }

View File

@@ -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());

View File

@@ -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());

View File

@@ -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> {

View File

@@ -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());

View File

@@ -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());
} }

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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
*/ */

View File

@@ -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" />

View File

@@ -0,0 +1,2 @@
logger.inteligr8-annotations.name=com.inteligr8.alfresco.annotations
logger.inteligr8-annotations.level=info

View File

@@ -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-*

View File

@@ -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) {
} }

View File

@@ -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

View File

@@ -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