initial checkin

This commit is contained in:
2023-04-04 13:59:33 -04:00
commit 2c93275a48
13 changed files with 506 additions and 0 deletions

9
.gitignore vendored Normal file
View File

@@ -0,0 +1,9 @@
# Maven
target
pom.xml.versionsBackup
# Eclipse
.settings
.project
.classpath

59
README.md Normal file
View File

@@ -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 `<aop:aspectj-autoproxy />` 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 `<aop:include>`, the problem persists. If you try to use `<aop:config>` 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
<aspectj>
<aspects>
<aspect name="com.myco.alfresco.mymodule.aspect.MyAspect" />
</aspects>
</aspectj>
```
### 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
<acs-platform.tomcat.opts>-javaagent:/var/lib/tomcat/dev/lib/aspectjweaver-${aspectj.version}.jar</acs-platform.tomcat.opts>
```

95
pom.xml Normal file
View File

@@ -0,0 +1,95 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.inteligr8.alfresco</groupId>
<artifactId>aspectj-platform-module</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>AspectJ ACS Platform Module</name>
<description>A module to add AspectJ annotation support to Alfresco Content Services modules.</description>
<scm>
<connection>scm:git:https://bitbucket.org/inteligr8/aspectj-platform-module.git</connection>
<developerConnection>scm:git:git@bitbucket.org:inteligr8/aspectj-platform-module.git</developerConnection>
<url>https://bitbucket.org/inteligr8/aspectj-platform-module</url>
</scm>
<organization>
<name>Inteligr8</name>
<url>https://www.inteligr8.com</url>
</organization>
<developers>
<developer>
<id>brian.long</id>
<name>Brian Long</name>
<email>brian@inteligr8.com</email>
<url>https://twitter.com/brianmlong</url>
</developer>
</developers>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<alfresco.sdk.version>4.2.0</alfresco.sdk.version>
<alfresco.platform.version>6.2.0-ga</alfresco.platform.version>
<aspectj.version>1.9.4</aspectj.version>
<acs-platform.tomcat.opts>-javaagent:/var/lib/tomcat/dev/lib/aspectjweaver-${aspectj.version}.jar</acs-platform.tomcat.opts>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.alfresco</groupId>
<artifactId>acs-community-packaging</artifactId>
<version>${alfresco.platform.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-repository</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>io.repaint.maven</groupId>
<artifactId>tiles-maven-plugin</artifactId>
<version>2.33</version>
<extensions>true</extensions>
<configuration>
<tiles>
<!-- Documentation: https://bitbucket.org/inteligr8/ootbee-beedk/src/stable/beedk-acs-platform-self-rad-tile -->
<tile>com.inteligr8.ootbee:beedk-acs-platform-self-rad-tile:[1.0.0,1.1.0)</tile>
<!-- Documentation: https://bitbucket.org/inteligr8/ootbee-beedk/src/stable/beedk-acs-platform-module-tile -->
<tile>com.inteligr8.ootbee:beedk-acs-platform-module-tile:[1.0.0,1.1.0)</tile>
<!-- Documentation: https://bitbucket.org/inteligr8/ootbee-beedk/src/stable/beedk-acs-platform-self-it-tile -->
<tile>com.inteligr8.ootbee:beedk-acs-platform-self-it-tile:[1.0.0,1.1.0)</tile>
</tiles>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>alfresco-public</id>
<url>https://artifacts.alfresco.com/nexus/content/groups/public</url>
</repository>
</repositories>
</project>

74
rad.ps1 Normal file
View File

@@ -0,0 +1,74 @@
function discoverArtifactId {
$script:ARTIFACT_ID=(mvn -q -Dexpression=project"."artifactId -DforceStdout help:evaluate)
}
function rebuild {
echo "Rebuilding project ..."
mvn process-classes
}
function start_ {
echo "Rebuilding project and starting Docker containers to support rapid application development ..."
mvn -Drad process-classes
}
function start_log {
echo "Rebuilding project and starting Docker containers to support rapid application development ..."
mvn -Drad "-Ddocker.showLogs" process-classes
}
function stop_ {
discoverArtifactId
echo "Stopping Docker containers that supported rapid application development ..."
docker container ls --filter name=${ARTIFACT_ID}-*
echo "Stopping containers ..."
docker container stop (docker container ls -q --filter name=${ARTIFACT_ID}-*)
echo "Removing containers ..."
docker container rm (docker container ls -aq --filter name=${ARTIFACT_ID}-*)
}
function tail_logs {
param (
$container
)
discoverArtifactId
docker container logs -f (docker container ls -q --filter name=${ARTIFACT_ID}-${container})
}
function list {
discoverArtifactId
docker container ls --filter name=${ARTIFACT_ID}-*
}
switch ($args[0]) {
"start" {
start_
}
"start_log" {
start_log
}
"stop" {
stop_
}
"restart" {
stop_
start_
}
"rebuild" {
rebuild
}
"tail" {
tail_logs $args[1]
}
"containers" {
list
}
default {
echo "Usage: .\rad.ps1 [ start | start_log | stop | restart | rebuild | tail {container} | containers ]"
}
}
echo "Completed!"

71
rad.sh Normal file
View File

@@ -0,0 +1,71 @@
#!/bin/sh
discoverArtifactId() {
ARTIFACT_ID=`mvn -q -Dexpression=project.artifactId -DforceStdout help:evaluate`
}
rebuild() {
echo "Rebuilding project ..."
mvn process-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!"

View File

@@ -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<String> basePackages = this.determineBasePackages();
this.logger.debug("Scanning packages for Aspects: {}", basePackages);
Collection<BeanDefinition> 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<String> determineBasePackages() {
List<String> 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<BeanDefinition> findAspectBeanDefinitions(Collection<String> basePackages) {
Set<BeanDefinition> 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;
}
}

View File

@@ -0,0 +1,16 @@
<aspectj>
<weaver options="-verbose">
<exclude within="java..*" />
<exclude within="javax..*" />
<exclude within="com.vaadin..*" />
<exclude within="com.googlecode..*" />
<exclude within="freemarker..*" />
<exclude within="io.undertow..*" />
<exclude within="org.alfresco..*" />
<exclude within="org.apache..*" />
<exclude within="org.hotswap..*" />
<exclude within="org.python..*" />
<exclude within="org.springframework..*" />
<exclude within="org.xnio..*" />
</weaver>
</aspectj>

View File

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

View File

@@ -0,0 +1 @@
log4j.logger.com.inteligr8.alfresco.aspectj=info

View File

@@ -0,0 +1,17 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Use this file for beans to be loaded in whatever order Alfresco/Spring decides -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- Enable Spring annotation scanning for classes in package -->
<context:component-scan base-package="com.inteligr8.alfresco.aspectj" />
<!-- Mixing AspectJ with Alfresco's use of aopalliance is not possible in Spring; must use AspectJ native configuration -->
</beans>

View File

@@ -0,0 +1,4 @@
module.id=${project.artifactId}
module.title=${project.name}
module.description=${project.description}
module.version=${project.version}

View File

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

View File

@@ -0,0 +1,63 @@
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<beans>
<!--
To support hot reloading of server side Javascript files in Share, we have to turn on development mode.
This setting will tell the Rhinoscript Processor not to compile and cache the JS files.
Cool, we can now change server side JS files and have the changes picked up,
without having to restart or refresh web scripts.
But… Due to a known bug in the Surf framework (ALF-9970) this will break the admin consoles in Share.
Override this bean and disable javascript compilation so that webscripts can be hot reloaded.
We have changed the 'compile' property from true to false.
-->
<bean id="javaScriptProcessor" class="org.alfresco.repo.jscript.RhinoScriptProcessor" init-method="register">
<property name="name">
<value>javascript</value>
</property>
<property name="extension">
<value>js</value>
</property>
<!-- Do not "compile javascript and cache compiled scripts" -->
<property name="compile">
<value>false</value>
</property>
<!-- allow sharing of sealed scopes for performance -->
<!-- disable to give each script it's own new scope which can be extended -->
<property name="shareSealedScopes">
<value>true</value>
</property>
<property name="scriptService">
<ref bean="scriptService"/>
</property>
<!-- Creates ScriptNodes which require the ServiceRegistry -->
<property name="serviceRegistry">
<ref bean="ServiceRegistry"/>
</property>
<property name="storeUrl">
<value>${spaces.store}</value>
</property>
<property name="storePath">
<value>${spaces.company_home.childname}</value>
</property>
</bean>
</beans>