From 2c93275a4888e29a4237c614f9e58de79b3ad890 Mon Sep 17 00:00:00 2001 From: "Brian M. Long" Date: Tue, 4 Apr 2023 13:59:33 -0400 Subject: [PATCH] initial checkin --- .gitignore | 9 ++ README.md | 59 ++++++++++++ pom.xml | 95 +++++++++++++++++++ rad.ps1 | 74 +++++++++++++++ rad.sh | 71 ++++++++++++++ .../aspectj/AspectJSpringAdapter.java | 86 +++++++++++++++++ src/main/resources/META-INF/aop.xml | 16 ++++ .../alfresco-global.properties | 3 + .../log4j.properties | 1 + .../module-context.xml | 17 ++++ .../module.properties | 4 + .../alfresco/extension/debug-log4j.properties | 8 ++ .../disable-webscript-caching-context.xml | 63 ++++++++++++ 13 files changed, 506 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 pom.xml create mode 100644 rad.ps1 create mode 100644 rad.sh create mode 100644 src/main/java/com/inteligr8/alfresco/aspectj/AspectJSpringAdapter.java create mode 100644 src/main/resources/META-INF/aop.xml create mode 100644 src/main/resources/alfresco/module/com.inteligr8.alfresco.aspectj-platform-module/alfresco-global.properties create mode 100644 src/main/resources/alfresco/module/com.inteligr8.alfresco.aspectj-platform-module/log4j.properties create mode 100644 src/main/resources/alfresco/module/com.inteligr8.alfresco.aspectj-platform-module/module-context.xml create mode 100644 src/main/resources/alfresco/module/com.inteligr8.alfresco.aspectj-platform-module/module.properties create mode 100644 src/test/resources/alfresco/extension/debug-log4j.properties create mode 100644 src/test/resources/alfresco/extension/disable-webscript-caching-context.xml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..620a11b --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +# Maven +target +pom.xml.versionsBackup + +# Eclipse +.settings +.project +.classpath + diff --git a/README.md b/README.md new file mode 100644 index 0000000..75a7823 --- /dev/null +++ b/README.md @@ -0,0 +1,59 @@ +# AspectJ Module for Alfresco Content Services + +This module provides AspectJ support to Alfresco Content Services. + +## Why? + +### AspectJ vs AOP Alliance + +Alfresco Content Services rests on top of the Spring Framework. It is an older version and the code was original written before 2010. Although it uses the principles behind AspectJ, it does not use the modern implementation of it. It instead is using the AOP Alliance API, a stripped down version of AspectJ (`aspectjrt`), and `spring-aop`. Although these libraries seem to support annotation-based AspectJ implementations, you will quickly run into problems. + +### AspectJ in Spring AOP + +In order to enable `spring-aop` for AspectJ, you typically include `` in the Spring context (ACS module `module-context.xml`). When you do this, it will try to pick up all the AOP Alliance `MethodInterceptor` implementations too. It winds up doing way more than it should and ACS startup errors ensue. + +If you try to narrow it using ``, the problem persists. If you try to use `` you'll still have the same problem. There seems to be no way to use AspectJ annotations with ACS using the `spring-aop` module. + +### AspectJ outside Spring AOP, but in Spring + +So this module enables AspectJ as if you were not use Spring. It then assumes your `@Aspect` objects are Spring beans and loads them dynamically. This prevents conflicts with ACS libraries while giving you the ability to use AspectJ annotations. + +## Using + +This library will handle nearly all the requirements to get AspectJ working in ACS. The following are the exceptions and notes you need to be aware of. + +### `@Aspect` + +You can use the `@Aspect` annotation as you would think. However, you cannot add a `@Component` or similar annotation to the class. The class must NOT be defined as a Spring bean. We need to let AspectJ instantiate the class; not Spring. This module will side-load the instance as a Spring bean, including any use of @PostConstruct or @Autowired as one would expect. + +### `aop.xml` + +AspectJ without Spring AOP requires you to define your aspects. This module includes an `aop.xml` that deals with excluding ACS libraries. You just need to define your aspects and maybe exclude some more libraries to prevent them from being picked up. + +Create a file in your `src/main/resources` or similar path, called: `META-INF/aop.xml`. It should have something like the following contents. You cannot use wildcards to capture your aspects. + +```xml + + + + + +``` + +### Maven Project + +If you are developing your module in Maven, you can include this project as a dependency. That will get you access to the AspectJ API for compiling your module. Do not include the library in your module AMP. This is automatically handled by the Order Of The Bee SDK. The Alfresco SDK may need a scope of `provided`. + +### AspectJ Agent + +This is the most complicated requirement of this module. AspectJ needs to use an agent at startup to enable its functionality. You will need the following property provided at runtime: + +``` +-javaagent:/path/to/aspectjweaver-${aspectj.version}.jar +``` + +How you do this is very highly dependent on your runtime environment. When using the OOTB SDK, you can provide the following Maven property to enable AspectJ in your RAD or IT testing: + +```xml +-javaagent:/var/lib/tomcat/dev/lib/aspectjweaver-${aspectj.version}.jar +``` diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..bead99f --- /dev/null +++ b/pom.xml @@ -0,0 +1,95 @@ + + 4.0.0 + + com.inteligr8.alfresco + aspectj-platform-module + 1.0-SNAPSHOT + jar + + AspectJ ACS Platform Module + A module to add AspectJ annotation support to Alfresco Content Services modules. + + + scm:git:https://bitbucket.org/inteligr8/aspectj-platform-module.git + scm:git:git@bitbucket.org:inteligr8/aspectj-platform-module.git + https://bitbucket.org/inteligr8/aspectj-platform-module + + + Inteligr8 + https://www.inteligr8.com + + + + brian.long + Brian Long + brian@inteligr8.com + https://twitter.com/brianmlong + + + + + UTF-8 + 8 + 8 + + 4.2.0 + 6.2.0-ga + 1.9.4 + -javaagent:/var/lib/tomcat/dev/lib/aspectjweaver-${aspectj.version}.jar + + + + + + org.alfresco + acs-community-packaging + ${alfresco.platform.version} + pom + import + + + + + + + org.alfresco + alfresco-repository + provided + + + org.aspectj + aspectjweaver + ${aspectj.version} + + + + + + + io.repaint.maven + tiles-maven-plugin + 2.33 + true + + + + com.inteligr8.ootbee:beedk-acs-platform-self-rad-tile:[1.0.0,1.1.0) + + com.inteligr8.ootbee:beedk-acs-platform-module-tile:[1.0.0,1.1.0) + + com.inteligr8.ootbee:beedk-acs-platform-self-it-tile:[1.0.0,1.1.0) + + + + + + + + + alfresco-public + https://artifacts.alfresco.com/nexus/content/groups/public + + + \ No newline at end of file diff --git a/rad.ps1 b/rad.ps1 new file mode 100644 index 0000000..61bcb2f --- /dev/null +++ b/rad.ps1 @@ -0,0 +1,74 @@ + +function discoverArtifactId { + $script:ARTIFACT_ID=(mvn -q -Dexpression=project"."artifactId -DforceStdout help:evaluate) +} + +function rebuild { + echo "Rebuilding project ..." + mvn process-classes +} + +function start_ { + echo "Rebuilding project and starting Docker containers to support rapid application development ..." + mvn -Drad process-classes +} + +function start_log { + echo "Rebuilding project and starting Docker containers to support rapid application development ..." + mvn -Drad "-Ddocker.showLogs" process-classes +} + +function stop_ { + discoverArtifactId + echo "Stopping Docker containers that supported rapid application development ..." + docker container ls --filter name=${ARTIFACT_ID}-* + echo "Stopping containers ..." + docker container stop (docker container ls -q --filter name=${ARTIFACT_ID}-*) + echo "Removing containers ..." + docker container rm (docker container ls -aq --filter name=${ARTIFACT_ID}-*) +} + +function tail_logs { + param ( + $container + ) + + discoverArtifactId + docker container logs -f (docker container ls -q --filter name=${ARTIFACT_ID}-${container}) +} + +function list { + discoverArtifactId + docker container ls --filter name=${ARTIFACT_ID}-* +} + +switch ($args[0]) { + "start" { + start_ + } + "start_log" { + start_log + } + "stop" { + stop_ + } + "restart" { + stop_ + start_ + } + "rebuild" { + rebuild + } + "tail" { + tail_logs $args[1] + } + "containers" { + list + } + default { + echo "Usage: .\rad.ps1 [ start | start_log | stop | restart | rebuild | tail {container} | containers ]" + } +} + +echo "Completed!" + diff --git a/rad.sh b/rad.sh new file mode 100644 index 0000000..8c1e390 --- /dev/null +++ b/rad.sh @@ -0,0 +1,71 @@ +#!/bin/sh + +discoverArtifactId() { + ARTIFACT_ID=`mvn -q -Dexpression=project.artifactId -DforceStdout help:evaluate` +} + +rebuild() { + echo "Rebuilding project ..." + mvn process-test-classes +} + +start() { + echo "Rebuilding project and starting Docker containers to support rapid application development ..." + mvn -Drad process-test-classes +} + +start_log() { + echo "Rebuilding project and starting Docker containers to support rapid application development ..." + mvn -Drad -Ddocker.showLogs process-test-classes +} + +stop() { + discoverArtifactId + echo "Stopping Docker containers that supported rapid application development ..." + docker container ls --filter name=${ARTIFACT_ID}-* + echo "Stopping containers ..." + docker container stop `docker container ls -q --filter name=${ARTIFACT_ID}-*` + echo "Removing containers ..." + docker container rm `docker container ls -aq --filter name=${ARTIFACT_ID}-*` +} + +tail_logs() { + discoverArtifactId + docker container logs -f `docker container ls -q --filter name=${ARTIFACT_ID}-$1` +} + +list() { + discoverArtifactId + docker container ls --filter name=${ARTIFACT_ID}-* +} + +case "$1" in + start) + start + ;; + start_log) + start_log + ;; + stop) + stop + ;; + restart) + stop + start + ;; + rebuild) + rebuild + ;; + tail) + tail_logs $2 + ;; + containers) + list + ;; + *) + echo "Usage: ./rad.sh [ start | start_log | stop | restart | rebuild | tail {container} | containers ]" + exit 1 +esac + +echo "Completed!" + diff --git a/src/main/java/com/inteligr8/alfresco/aspectj/AspectJSpringAdapter.java b/src/main/java/com/inteligr8/alfresco/aspectj/AspectJSpringAdapter.java new file mode 100644 index 0000000..1cf955f --- /dev/null +++ b/src/main/java/com/inteligr8/alfresco/aspectj/AspectJSpringAdapter.java @@ -0,0 +1,86 @@ +package com.inteligr8.alfresco.aspectj; + +import java.util.Collection; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Properties; +import java.util.Set; + +import javax.annotation.PostConstruct; + +import org.aspectj.lang.Aspects; +import org.aspectj.lang.annotation.Aspect; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; +import org.springframework.core.type.filter.AnnotationTypeFilter; +import org.springframework.stereotype.Component; + +@Component +public class AspectJSpringAdapter { + + private final Logger logger = LoggerFactory.getLogger(AspectJSpringAdapter.class); + private final String basePackagePropertySuffix = "aspectj.scanPackages"; + + @Autowired + private ApplicationContext applicationContext; + + @Autowired + @Qualifier("global-properties") + private Properties globalProps; + + @PostConstruct + public void init() { + Collection basePackages = this.determineBasePackages(); + this.logger.debug("Scanning packages for Aspects: {}", basePackages); + + Collection aspectBeanDefs = this.findAspectBeanDefinitions(basePackages); + + for (BeanDefinition aspectBeanDef : aspectBeanDefs) { + this.logger.debug("Configuring aspect as bean: {}", aspectBeanDef.getBeanClassName()); + + try { + Object aspect = Aspects.aspectOf(Class.forName(aspectBeanDef.getBeanClassName())); + String beanName = "aspectj." + aspect.getClass().getSimpleName(); + Object bean = this.applicationContext.getAutowireCapableBeanFactory().initializeBean(aspect, beanName); + this.applicationContext.getAutowireCapableBeanFactory().autowireBean(bean); + this.logger.debug("Wrapped aspect in bean: {} => {}", beanName, aspectBeanDef.getBeanClassName()); + } catch (ClassNotFoundException cnfe) { + this.logger.warn("Failed to properly register aspect: " + aspectBeanDef.getBeanClassName(), cnfe); + } + } + } + + protected Collection determineBasePackages() { + List basePackages = new LinkedList<>(); + + Enumeration propNames = this.globalProps.propertyNames(); + while (propNames.hasMoreElements()) { + String propName = (String) propNames.nextElement(); + if (propName.endsWith(this.basePackagePropertySuffix)) { + basePackages.add(this.globalProps.getProperty(propName).trim()); + } + } + + return basePackages; + } + + protected Collection findAspectBeanDefinitions(Collection basePackages) { + Set beanDefs = new HashSet<>(); + + ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false); + provider.addIncludeFilter(new AnnotationTypeFilter(Aspect.class)); + + for (String basePackage : basePackages) + beanDefs.addAll(provider.findCandidateComponents(basePackage)); + + return beanDefs; + } + +} diff --git a/src/main/resources/META-INF/aop.xml b/src/main/resources/META-INF/aop.xml new file mode 100644 index 0000000..fa56fd1 --- /dev/null +++ b/src/main/resources/META-INF/aop.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/alfresco/module/com.inteligr8.alfresco.aspectj-platform-module/alfresco-global.properties b/src/main/resources/alfresco/module/com.inteligr8.alfresco.aspectj-platform-module/alfresco-global.properties new file mode 100644 index 0000000..6f97fa3 --- /dev/null +++ b/src/main/resources/alfresco/module/com.inteligr8.alfresco.aspectj-platform-module/alfresco-global.properties @@ -0,0 +1,3 @@ + +# To scan Java packages, add a unique property with the suffix: `aspectj.scanPackages` +# e.g. mymodule.aspectj.scanPackages=com.myco.alfresco.mymodule diff --git a/src/main/resources/alfresco/module/com.inteligr8.alfresco.aspectj-platform-module/log4j.properties b/src/main/resources/alfresco/module/com.inteligr8.alfresco.aspectj-platform-module/log4j.properties new file mode 100644 index 0000000..a18a30e --- /dev/null +++ b/src/main/resources/alfresco/module/com.inteligr8.alfresco.aspectj-platform-module/log4j.properties @@ -0,0 +1 @@ +log4j.logger.com.inteligr8.alfresco.aspectj=info diff --git a/src/main/resources/alfresco/module/com.inteligr8.alfresco.aspectj-platform-module/module-context.xml b/src/main/resources/alfresco/module/com.inteligr8.alfresco.aspectj-platform-module/module-context.xml new file mode 100644 index 0000000..1985690 --- /dev/null +++ b/src/main/resources/alfresco/module/com.inteligr8.alfresco.aspectj-platform-module/module-context.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + diff --git a/src/main/resources/alfresco/module/com.inteligr8.alfresco.aspectj-platform-module/module.properties b/src/main/resources/alfresco/module/com.inteligr8.alfresco.aspectj-platform-module/module.properties new file mode 100644 index 0000000..3f654aa --- /dev/null +++ b/src/main/resources/alfresco/module/com.inteligr8.alfresco.aspectj-platform-module/module.properties @@ -0,0 +1,4 @@ +module.id=${project.artifactId} +module.title=${project.name} +module.description=${project.description} +module.version=${project.version} diff --git a/src/test/resources/alfresco/extension/debug-log4j.properties b/src/test/resources/alfresco/extension/debug-log4j.properties new file mode 100644 index 0000000..da0cbc6 --- /dev/null +++ b/src/test/resources/alfresco/extension/debug-log4j.properties @@ -0,0 +1,8 @@ +# Module debugging +log4j.logger.com.inteligr8.alfresco.aspectj=trace + +# WebScript debugging +log4j.logger.org.springframework.extensions.webscripts.ScriptLogger=debug + +# non-WebScript JavaScript execution debugging +log4j.logger.org.alfresco.repo.jscript.ScriptLogger=debug diff --git a/src/test/resources/alfresco/extension/disable-webscript-caching-context.xml b/src/test/resources/alfresco/extension/disable-webscript-caching-context.xml new file mode 100644 index 0000000..07829ea --- /dev/null +++ b/src/test/resources/alfresco/extension/disable-webscript-caching-context.xml @@ -0,0 +1,63 @@ + + + + + + + + javascript + + + js + + + + false + + + + + true + + + + + + + + + + ${spaces.store} + + + ${spaces.company_home.childname} + + + + +