Currently I am using AspectJ Load Time Weaving to intercept the constructor of a base entity for auditing purposes. However when running the application I am getting incredibly inconsistent results revolving around the aspectOf() method that aspectJ weaves into LTW classes.
In some cases the application runs, the weaving is done correctly, and the code is functioning as expected. Other times I am met with:
java.lang.NoSuchMethodException: ca.gc.cfp.core.cfpws.repository.aspect.BaseEntityAspect.aspectOf()
Currently I am using https://github.com/subes/invesdwin-instrument to dynamically attach the instrumentation agent into the JVM so our deployment guys don't need to do any extra configuration.
My Spring application main:
@SpringBootApplication
@EntityScan(basePackages = {"ca.gc.cfp.model"})
public class CfpWsApplication {
public static void main(final String[] args) {
DynamicInstrumentationLoader.waitForInitialized();
DynamicInstrumentationLoader.initLoadTimeWeavingContext();
if (!InstrumentationLoadTimeWeaver.isInstrumentationAvailable()) {
throw new IllegalStateException(
"Instrumentation is not available AspectJ weaver will not function.");
}
SpringApplication.run(CfpWsApplication.class, args);
}
The LTW Aspect:
@Aspect
public class BaseEntityAspect {
Logger logger = LoggerFactory.getLogger(BaseEntityAspect.class);
/** Application context property needed to fetch the AuditDate bean */
@Autowired private ApplicationContext context;
@AfterReturning(
"onBaseEntityCreated() && !within(ca.gc.cfp.core.cfpws.repository.aspect.BaseEntityAspect)")
public void injectAuditTimeStamp(JoinPoint joinPoint) {
try {
AuditDate auditDate = context.getBean(AuditDate.class);
Object entityTarget = joinPoint.getTarget();
// Inject the auditing date for this Entity instance
if (entityTarget instanceof BaseEntity) {
BaseEntity baseEnt = (BaseEntity) entityTarget;
baseEnt.setAuditDate(auditDate.getAuditingTimeStamp());
}
} catch (NullPointerException e) {
logger.error(
e.getMessage()
+ " Not yet in the conext of an httpRequest, the AuditDate bean has not yet been instantiated.");
}
}
@Pointcut(
"execution(ca.gc.cfp.model.entity.BaseEntity.new(..)) && !within(ca.gc.cfp.core.cfpws.repository.aspect.BaseEntityAspect)")
public void onBaseEntityCreated() {}
}
The Aspect config class with a temporary factory method using the Aspects utils to let spring know it should ask aspectJ for the woven Aspect:
@Configuration
@EnableAspectJAutoProxy
public class AspectConfig {
/**
* Static factory for access to the load time woven aspect. This allows the aspect to be injected
* with the application context and beans from the spring IoC
*/
@Bean
public BaseEntityAspect getBaseEntityAspect() {
return Aspects.aspectOf(BaseEntityAspect.class);
}
}
aop.xml:
<aspectj>
<weaver options="-verbose -showWeaveInfo -Xreweavable -debug">
<include within="ca.gc.cfp.model" />
<include within="ca.gc.cfp.model..*" />
<include within="ca.gc.cfp.core.cfpws.repository.aspect..*"/>
</weaver>
<aspects>
<aspect name="ca.gc.cfp.core.cfpws.repository.aspect.BaseEntityAspect" />
</aspects>
</aspectj>
In a lot of cases when I run with this configuration I get the following:
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [ca.gc.cfp.core.cfpws.repository.aspect.BaseEntityAspect]: Factory method 'getBaseEntityAspect' threw exception; nested exception is org.aspectj.lang.NoAspectBoundException: Exception while initializing ca.gc.cfp.core.cfpws.repository.aspect.BaseEntityAspect: java.lang.NoSuchMethodException: ca.gc.cfp.core.cfpws.repository.aspect.BaseEntityAspect.aspectOf()
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185) ~[spring-beans-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:622) ~[spring-beans-5.1.4.RELEASE.jar:5.1.4.RELEASE]
... 19 common frames omitted
Caused by: org.aspectj.lang.NoAspectBoundException: Exception while initializing ca.gc.cfp.core.cfpws.repository.aspect.BaseEntityAspect: java.lang.NoSuchMethodException: ca.gc.cfp.core.cfpws.repository.aspect.BaseEntityAspect.aspectOf()
at org.aspectj.lang.Aspects.aspectOf(Aspects.java:50) ~[aspectjrt-1.9.4.jar:1.9.4]
at ca.gc.cfp.core.cfpws.configuration.AspectConfig.getBaseEntityAspect(AspectConfig.java:22) ~[classes/:na]
at ca.gc.cfp.core.cfpws.configuration.AspectConfig$$EnhancerBySpringCGLIB$$1cae4c58.CGLIB$getBaseEntityAspect$0(<generated>) ~[classes/:na]
at ca.gc.cfp.core.cfpws.configuration.AspectConfig$$EnhancerBySpringCGLIB$$1cae4c58$$FastClassBySpringCGLIB$$84edb9e.invoke(<generated>) ~[classes/:na]
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244) ~[spring-core-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:363) ~[spring-context-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at ca.gc.cfp.core.cfpws.configuration.AspectConfig$$EnhancerBySpringCGLIB$$1cae4c58.getBaseEntityAspect(<generated>) ~[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_211]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_211]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_211]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_211]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.1.4.RELEASE.jar:5.1.4.RELEASE]
... 20 common frames omitted
Caused by: java.lang.NoSuchMethodException: ca.gc.cfp.core.cfpws.repository.aspect.BaseEntityAspect.aspectOf()
at java.lang.Class.getDeclaredMethod(Class.java:2130) ~[na:1.8.0_211]
at org.aspectj.lang.Aspects.getSingletonOrThreadAspectOf(Aspects.java:134) ~[aspectjrt-1.9.4.jar:1.9.4]
at org.aspectj.lang.Aspects.aspectOf(Aspects.java:45) ~[aspectjrt-1.9.4.jar:1.9.4]
... 31 common frames omitted
However this is not always the case, occasionally the application runs just fine, the code executes as expected and there are no issues. This has been eluding me for a couple weeks now and I cannot seem to figure out what could be causing this code to occasionally work and occasionally not work. Could it be that CGLIB proxies are hiding the aspectOf() method from the compiler??
EDIT / UPDATE: I was able to remove the use of the above third party dependency for dynamically loading the java agent into the Spring application context. I instead used javaagent arguments and it is working fine from within my IDE. However building and running via Maven in a terminal is still causing issues. I've specified the javaagent argument via the Environment variable : MAVEN_OPTS. After doing so maven seems to be picking it up but my class still isn't being woven.