Simple #Configurable with modern spring-boot + gradle - spring

My goal is to get a bare bones aspectj+spring aop setup such that I can use #Configurable on one class. One additional restriction is that it needs to use load-time weaving as Lombok doesn't work with CTW.
The good news: I have it working!
The bad news: The console is flooded with [Xlint:cantFindType] errors. See results section below.
The Environment
I have a single class annotated with #Configurable. It is a class used by and instantiated by Jackson, hence the need for AOP. It's not very interesting so I won't show it here. It's just a normal class with the single annotation of Configurable, and an #Autowired bean inside.
SpringBootApplication
My Application class has the usual annotations:
#SpringBootApplication
#EnableSpringConfigured
#EnableLoadTimeWeaving
public class MyApplication {
build.gradle
My build.gradle has all the usual suspects. Sample:
configurations {
springinstrument
}
dependencies {
compile('org.projectlombok:lombok')
compile('org.springframework.boot:spring-boot-starter-aop')
compile("org.springframework.boot:spring-boot-starter-data-rest")
compile('org.springframework.data:spring-data-rest-hal-browser')
compile('org.springframework.boot:spring-boot-starter-data-jpa')
compile('org.springframework.boot:spring-boot-starter-security')
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.springframework.boot:spring-boot-devtools')
compile('org.springframework.plugin:spring-plugin:1.2.0.RELEASE')
..snip..
runtime('org.springframework:spring-instrument:4.+')
springinstrument "org.springframework:spring-instrument:4.+"
runtime configurations.springinstrument.dependencies
}
test.doFirst {
jvmArgs "-javaagent:${configurations.springinstrument.asPath}"
}
jvm args
I am running a JUnit test with the following args (via Intellij's run config)
-ea
-javaagent:/Users/me/.gradle/caches/modules-2/files-2.1/org.springframework/spring-instrument/4.3.3.RELEASE/5db399fa5546172b9c107817b4abaae6b379bb8c/spring-instrument-4.3.3.RELEASE.jar
aop.xml
I have a src/main/resources/META-INF/aop.xml containing:
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
<weaver options="-Xreweavable -showWeaveInfo">
<!-- only weave classes with #Configurable interface -->
<include within="#org.springframework.beans.factory.annotation.Configurable */>
</weaver>
</aspectj>
However, my suspicion is that this file is not being picked up. It doesn't matter what I put in the file, the results are always the same. Even if I put random non-valid XML.
The Test
The junit test is a simple Jackson serialize-deserialize test using the #Configurable annotated class.
The test class has the annotations:
#SpringBootTest
#RunWith(SpringRunner.class)
The Results
When running a junit test via Intellij -> It Works, but..
The LTW is actual working and my test is passing when run via Intellij with the jvm args above.
However the boot time of the app is significantly longer and the log is flooded with Xlint:cantFindType errors
Example:
2016-11-07 19:28:21.944 INFO 45213 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
[AppClassLoader#18b4aac2] error can't determine implemented interfaces of missing type org.springframework.security.ldap.authentication.LdapAuthenticationProvider
when weaving type org.springframework.security.config.annotation.authentication.configurers.ldap.LdapAuthenticationProviderConfigurer
when weaving classes
when weaving
[Xlint:cantFindType]
[AppClassLoader#18b4aac2] error can't determine implemented interfaces of missing type io.undertow.server.handlers.accesslog.AccessLogHandler
when weaving type org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainerFactory
when weaving classes
when weaving
[Xlint:cantFindType]
[AppClassLoader#18b4aac2] error can't determine implemented interfaces of missing type io.undertow.server.handlers.accesslog.AccessLogHandler
when weaving type org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainerFactory
when weaving classes
when weaving
[Xlint:cantFindType]
[AppClassLoader#18b4aac2] error can't determine implemented interfaces of missing type io.undertow.server.handlers.accesslog.AccessLogHandler
when weaving type org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainerFactory
when weaving classes
when weaving
[Xlint:cantFindType]
.. many more.. all in the org.springframework.boot package
When running a test via gradle command line -> works, sort of.
When I run the test with gradle test --tests *MyTestClass, the test fails because of a NullPointerException on.. guess what, the #Autowired bean I'm expecting to be injected automatically.
I got it working with gradle test config, huzzah! I've updated the configuration above. But.. it suffers from the same problem as running it in my IDE: the Xlint:cantFindType
So the issues:
The gradle configuration is wrong, so running the app via gradle fails. How do I fix the gradle config?
The Xlint:cantFindType errors. Is the aop.xml not being picked up? How to resolve this?
I have yet to find a simple spring-boot+gradle+ #Configurable sample that uses aop.xml

I solved the problem, and it's rather embarrassing.
It was due to the fact that the aop.xml wasn't being picked up.
The file wasn't being detected because it was in a directory named META_INF/ on the class path, not META-INF/. That's a pretty important difference.
Anyways, I've updated the question with the minimal configuration needed to get #Configurable working with a modern spring-boot and gradle setup with load time weaving for Lombok support. Hopefully it will be useful for someone.

Related

Spring boot aspectj gradle compile time weaving issue

I have a spring book application, which works fine. I would like to send email notifications to me if there are exceptions thrown from my application. I think aspectj is a good fit.
First, I tried to use Spring AOP runtime weaving, which works OK for public methods. However, I would like to get notified on private methods as well, and I have scheduled tasks. According to Spring AOP doc, runtime weaving won't work for scheduled tasks and private methods. Thus I have decided to use aspectj compile time weaving in my boot application.
I found the official aspectj gradle plugin here:
https://plugins.gradle.org/plugin/at.jku.isse.gradient-gradle
My boot application has this dependency:
compile group: 'org.springframework.boot', name: 'spring-boot-configuration-processor', version:'1.5.14.RELEASE'
My project compiles fine if I use default compiler. But if I use AspectJ compiler, it always complains:
[ant:iajc] warning Field value processing of #ConfigurationProperty meta-data is not supported
[ant:iajc] warning Hibernate JPA 2 Static-Metamodel Generator 4.3.11.Final
[ant:iajc] error at (no source information available)
D:\work\proj\build\classes\java\main\com\abc\dao\entity\Channel_.java:0::0 Internal compiler error: java.lang.Exception: java.lang.NoClassDefFoundError: org/springframework/boot/configurationprocessor/metadata/JsonMarshaller at org.aspectj.org.eclipse.jdt.internal.compiler.apt.dispatch.RoundDispatcher.handleProcessor(RoundDispatcher.java:169)
If I remove that dependency, aspectj compiles ok, I get the jar file. But then when I run my code, I got this:
Caused by: org.springframework.aop.framework.AopConfigException: Advice must be declared inside an aspect type: Offending method 'public void com.proj.aop.AspectConfiguration.afterThrowing(org.aspectj.lang.JoinPoint,java.lang.Throwable)' in class [com.proj.aop.AspectConfiguration]
And this is my Advice class:
package com.proj.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.context.annotation.Configuration;
#Aspect
#Configuration
public class AspectConfiguration {
#AfterThrowing (pointcut="execution(* org.hibernate..*.*(..)) || execution(* com.proj..*.*(..))", throwing="excep")
public void afterThrowing(JoinPoint joinPoint, Throwable excep){
System.out.println("inafterthrowing");
}
}
and my appconfig has annotation #EnableAspectJAutoProxy added.
If I replace #Configuration with #Component in my Advice class, I can run my application but the afterthrowing method is not called. Same if I remove #Configuration.
So my questions are:
Why I get the java.lang.NoClassDefFoundError: org/springframework/boot/configurationprocessor/metadata/JsonMarshaller if I have spring-boot-configuration-processor as dependency when compiled by AspectJ?
What is the correct way to use AspectJ in spring boot application and build with gradle? (use #Configuration or #Component? and why my afterthrowing is not called if I use #Component or, why I get exception if I use #Configuration?)
Thanks

Using aspectj in Hibernate entity

I have entity class like this:
package tr.com.example.model.porting
...//omitting imports
#Configurable
#Data
#Entity
public class PortOut{
public void handleRequest(Long portingId) {
processRequest(portingId);
...
}
}
And here is my aspect code in different class annotated with #Aspect and #Component:
#Around("execution(* tr.com.example..PortOut.handleRequest(..))")
public void handlePortingProcessAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
joinPoint.proceed();
log.debug(log.debug("[POM]:{} is executed with parameters:{}", joinPoint.toShortString(), joinPoint.getArgs()))
}
Normally, my aspect code works fine for other Spring managed classes such as using #Service, #Component annotation. I am using https://github.com/subes/invesdwin-instrument as a weaving agent and also using Lombok.
I tried this link(along with other so posts). But since I am using Lombok, I cannot directy compile with aspectj compiler for weaving #Configurable classes, it doesn't work with Lombok as I found out.
https://groups.google.com/forum/#!topic/project-lombok/ZkLsTZVSgD4
Shortly, what I want is using aspectj for logging some methods's arguments, but I can't use it in any Hibernate entity class, other than that it works fine. How to get it working in entity classes?
Here you find the explanation why Lombok and AspectJ do not like each other.
What is said to work though, is to delombok the Lombok-annotated source code and then normally compile the generated source code with the AspectJ compiler (e.g. via AspectJ Maven plugin, which is what I use all the time in Maven projects). Assuming you do use Maven, also the delombok step can be done with Lombok Maven plugin. If you assign the right phases to the respective plugins, it should be possible to fully automate the build process.
Disclaimer: I have never used Lombok in my whole life, but know a thing or two about AspectJ and Maven.

EnableLoadTimeWeaving Spring Boot Embedded Tomcat

Caching Aspects are not having any effects at runtime with Spring Boot and Embedded tomcat with LoadTimeWeaving enabled,but we are seeing the weaving is happening fine in the logs.
Below is the configuration, the LoadTimeWeaving is enabled along with the mode as AspectJ for Caching
#Configuration
#EnableConfigurationProperties
#EnableSpringConfigured
#EnableAspectJAutoProxy(proxyTargetClass = true)
#EnableLoadTimeWeaving
#EnableTransactionManagement
#EnableAsync
#EnableCaching(mode = AdviceMode.ASPECTJ)
public class AppConfig {
}
Spring Agent
-javaagent:../../../spring-instrument-4.3.3.RELEASE.jar
Logs
[RestartClassLoader#2aaae670] debug weaving 'uk.co.loyalty.iss.newpro.reporting.domain.refdata.gateway.RestReferenceDataGateway'
[RestartClassLoader#2aaae670] weaveinfo Join point 'method-execution(java.util.List uk.co.loyalty.iss.newpro.reporting.domain.refdata.gateway.RestReferenceDataGateway.getHierarchyLevelDefns())' in Type 'uk.co.loyalty.iss.newpro.reporting.domain.refdata.gateway.RestReferenceDataGateway' (RestReferenceDataGateway.java:118) advised by around advice from 'org.springframework.cache.aspectj.AnnotationCacheAspect' (AbstractCacheAspect.aj:64)
[RestartClassLoader#2aaae670] weaveinfo Join point 'method-execution(java.util.Map uk.co.loyalty.iss.newpro.reporting.domain.refdata.gateway.RestReferenceDataGateway.getHierarchyLevelDefinitionMap())' in Type 'uk.co.loyalty.iss.newpro.reporting.domain.refdata.gateway.RestReferenceDataGateway' (RestReferenceDataGateway.java:129) advised by around advice from '**org.springframework.cache.aspectj.AnnotationCacheAspect**' (AbstractCacheAspect.aj:64)
[RestartClassLoader#2aaae670] **debug generating class** 'uk.co.loyalty.iss.newpro.reporting.domain.refdata.gateway.RestReferenceDataGateway$AjcClosure1'
[RestartClassLoader#2aaae670] debug generating class 'uk.co.loyalty.iss.newpro.reporting.domain.refdata.gateway.RestReferenceDataGateway$AjcClosure3'
there are whole bunch of discussions around this. The weaving is happening on the RestartClassLoader, not sure is it something to do with the class loader. Have also tried adding the below,
#Bean
public LoadTimeWeaver loadTimeWeaver() throws Throwable {
InstrumentationLoadTimeWeaver loadTimeWeaver = new InstrumentationLoadTimeWeaver();
return loadTimeWeaver;
}
Please provide your suggestions.
Edit
We need Aspectj mode as we are using caching on private methods. I removed the EnableAspectJAutoProxy but still that does not help.
On further analysis on the load time weaving, I noticed the below behaviour.
I profile the application and investigated on the class loaders to see the weaving is done correct for the caching annotation to work(classes are trasformed). I noticed for some classes in the classloader ,we have a class with suffix($AjcClosure and those are transformed classes after weaving is done). So, if cachinng annotations is part of those weaved classes, then it works fine.
Then I closely looked in to the classes why some classes are weaved correctly and some are not. Then I noticed that if classes are loaded already to the class-loader before weaving happens, then that is where the weaving is not happening.
#Bean
public IAService aService(){
return new AServiceImpl();
}
In the above case, the class is AServiceImpl is loaded to the class loader only when this instance is needed (after load time weaving and caching works perfectly).
But if the same class is being initialised using #Component then it is not getting weaved.
#Service
public class AServiceImpl{
}
I think, in the above case, the class AServiceImpl is loaded to the class loader while the spring container initialises and load time weaving is trying to weave after that).
I verified this behavior again in class loader as well. So, is it an issue if aspectj tries to weave a class that was already loaded. the same problem was raised in the Spring Jira as well in the below link.
https://jira.spring.io/browse/SPR-13786
As mentioned in the above link, if I pass the aspectj weaver as java agent like the below, then all the classes are being weaved correctly. So, is it required to have two agents or do we have any other option for this.
-javaagent:../../../aspectjweaver-1.6.1.jar
-javaagent:../../../spring-instrument-4.3.3.RELEASE.jar
Yes, the weaver needs to be active before the weaving targets are loaded by the classloader.
Now, I am an AspectJ geek but not a container geek, I mainly work with Java SE and no containers. Please try to also add
-javaagent:path/to/aspectjweaver.jar
to your command line and see if it helps.

AspectJ: annotation-based logging aspect is not working in Spring Boot using compile-time weaving

In my project I use some aspects as part of my project and some as part of a common library. The first request takes about 10 seconds with the default weaving concept of Spring Boot. Thus, we try to switch to CTW (compile time weaving) using aspectj-maven-plugin 1.7.
Start-up time has been reduced now, an aspect with pointcut
#Around("execution(public com.XXXX.XXXXX.common.XXXX.APIErrorResponse *(..))")
is working, but the aspect with pointcut
#Around("#annotation(com.xxxxxx.XXX.common.xxx.Loggable)"))
is not working (I have created a #Loggable annotation to apply entry/exit logging). I get the following compiler message:
[WARNING] advice defined in com.xx.xx.xx.xx.LoggerAspect has not been applied [Xlint:adviceDidNotMatch]
I use the following configuration in my pom.xml:
Editor's remark: missing information about pom.xml should go here.
I use ajc 1.8.1 and compliance level 1.8.
My Loggable annotation:
#Retention(RetentionPolicy.CLASS)
#Target({ ElementType.METHOD, ElementType.TYPE })
public #interface Loggable {}
Any help will be appreciated.
The warning means that the fully qualified package & class name in your pointcut does not exactly match the real one in your annotation.

Spring - #Configurable classes in external JAR

I want to use spring dependency injection for my domain classes, that are possibly not created within spring context. That's why I have annotated these classes with #Configurable annotation and I try to setup load time weaving. My spring configuration is :
#SpringBootApplication
#EnableSpringConfigured
#EnableAspectJAutoProxy
#EnableCaching(mode = AdviceMode.ASPECTJ)
#EnableLoadTimeWeaving
public class WebApplication extends WebMvcConfigurerAdapter
This works fine but only if my #Configurable classes are in same JAR as my main Spring Boot application. However I want to have my #Configurable domain classes in JAR that is added as dependency for main application.
I tried this but it looks like load time weaving is not working for external JARs. Do you have any suggestions how to solve this issue?
EDIT
I have added -verbose:class to my JVM options and I have found out that classes from external JARs are loaded by class loader before LoadTimeWeaving is configured and initialized. And classes not in external JAR are loaded as they are needed i.e. after LoadTimeWeaving init.
So basically my question is :
Is it possible to initialize LTW before external JARs loading? Or is it possible to reload (or make AspectJ enhancement) classes after LTW is configured?
EDIT 2
I found out the reason why my classes from external jar are loadded before LTW init. It's because these classes are also annotated with #Entity annotation. Therefore they are loaded during hibernate initialization which occure before LTW init.
So the final question is: :D
How to assume that LTW is initialized before hibernate (and possibly other) initializations?
Ok I have a solution. :) As I sad issue was the order of spring bean initialization. So I simple had to ensure that load time weaver was initialized before hibernate. Simply injecting LoadTimeWeaver into my configuration class was the workaround:
#Autowired
private LoadTimeWeaver loadTimeWeaver;
EDIT
Another solution is to add aspectjweaver.jar as javaagent in addition to spring-instrument.

Resources