How to use weaven with aspectJ in compiletime in spring project - spring

We are using Spring and we used Spring AOP. Due to the nature of Spring AOP which uses Proxy we reached the limitation of it when tring to warp join point on call inside a call.
i.e
aspect on B execution will not run if A is being call
public void A(){
B()
}
public void B(){
}
In order to solves this issue we are using ApsectJ weaven in compile time.
Which is work good. But then, the issue is make it play nice with Spring Bean i.e let the Autowired work in side the aspect class.
Pom.xml Maven plugin
<!-- AspectJ configuration -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.7</version>
<configuration>
<complianceLevel>1.8</complianceLevel>
<source>1.8</source>
<target>1.8</target>
<showWeaveInfo>true</showWeaveInfo>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
EDIT
duplicate of Spring autowired bean for #Aspect aspect is null
info on how to get aspectj to work with maven

Use AspectJ in compile time and make sure spring autowired magic will work
According to the AspectJ doc aspectOf Chapter. In order to some module to known that aspect is an aspect of something one should use aspectOf. Spring has the feature
<bean id="a" class="com.someinterface.A" factory-method="aspectOf"></bean>
This will result the A of above to be a Spring Bean and as a bonus Spring will know that this is an aspect of some other code. This is enough for Spring to use the Autowire magic inside of an aspect.
NOTE that using aspectOf requires xml configuration. I tried to get the same result with #Configurable but it did not work. if some one has some info on that it will great. :)
Bonus - Use Spring AOP proxy for aspect(in run time)
Set spring to scan #Aspect and make it a spring bean
<context:component-scan base-package="com.centure" >
<context:include-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect"/>
</context:component-scan>
in this case every thing will work out of the box
private SomeService service;
public SomeService getService() {
return service;
}
#Autowired
public void setService(SomeService) {
this.service = service;
}
#Aspect
public class myAspect {
#Pointcut("execution(public * com.myinterface.save(..))")
public void save() {
}
#Around("myAspect () && args(thearg)")
public Object doBasicProfiling(ProceedingJoinPoint pjp, TheObject thearg)
throws Throwable {
Object retVal = null;
try {
retVal = pjp.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
return retVal;
}

Related

Apache Camel SOAP/CXF request to Server (Spring Boot)

I am trying to make requests to dedicated WSDL server with the help of Apache Camel CXF.
I have the WSDL URL:
http://www.learnwebservices.com/services/tempconverter?wsdl
I've made the Java classes of WSDL using the cxf-codegen-plugin:
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-codegen-plugin</artifactId>
<version>3.4.2</version>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<goals>
<goal>wsdl2java</goal>
</goals>
<configuration>
<wsdlOptions>
<wsdlOption>
<wsdl>${basedir}/src/main/resources/wsdl/tempconverter.wsdl</wsdl>
<packagenames>
<packagename>office.planet.integrations.merlion</packagename>
</packagenames>
</wsdlOption>
</wsdlOptions>
</configuration>
</execution>
</executions>
</plugin>
I have the following Camel route:
#Component
public class MerlionRoute
extends RouteBuilder {
#Override
public void configure() throws Exception {
from("direct:celsius-to-fahrenheit")
.process(exchange -> {
System.out.println("HELLO!!!!!");
CelsiusToFahrenheitRequest c = new CelsiusToFahrenheitRequest();
c.setTemperatureInCelsius(Double.valueOf(exchange.getIn().getHeader("num").toString()));
exchange.getIn().setBody(c);
})
.setHeader(CxfConstants.OPERATION_NAME, constant("CelsiusToFahrenheit"))
.setHeader(CxfConstants.OPERATION_NAMESPACE, constant("{{endpoint.namespace}}"))
.to("cxf:bean:cxfConvertTemp")
.process(exchange -> {
System.out.println("WE ARE HERE");
MessageContentsList response = (MessageContentsList) exchange.getIn().getBody();
CelsiusToFahrenheitResponse r = (CelsiusToFahrenheitResponse) response.get(0);
exchange.getIn().setBody("Temp in Farenheit: "+r.getTemperatureInFahrenheit());
System.out.println(r.getTemperatureInFahrenheit());
})
.end();
}
}
The Bean class of the EndPoint:
#Configuration
public class CxfBeans {
#Value("${endpoint.wsdl}")
private String SOAP_URL;
#Bean(name = "cxfConvertTemp")
public CxfEndpoint buildCxfEndpoint() {
CxfEndpoint cxf = new CxfEndpoint();
cxf.setAddress(SOAP_URL);
cxf.setServiceClass(TempConverterEndpoint.class);
cxf.setWsdlURL(SOAP_URL);
return cxf;
}
}
And the WSDL endpoint:
endpoint.wsdl=http://www.learnwebservices.com/services/tempconverter?wsdl
endpoint.namespace=http://learnwebservices.com/services/tempconverter
When I am launching the project, my route starts, but nothing happens.
Only this I can see:
2022-03-31 18:41:44.933 INFO 44313 --- [ restartedMain] o.a.c.w.s.f.ReflectionServiceFactoryBean : Creating Service {http://learnwebservices.com/services/tempconverter}TempConverterEndpointService from WSDL: http://www.learnwebservices.com/services/tempconverter?wsdl
How shall I request the data from WSDL server within the Camel CXF? What am I doing wrong?
Your route needs to be triggered. As it stands, nothing calls your from endpoint "direct:celsius-to-fahrenheit", and thus indeed, nothing happens.
Assuming you want this route to be triggered only once, you could define your from endpoint as "timer://celsius-to-fahrenheit?repeatCount=1".
See Camel Timer component.

How to test OSGi declarative services using JUnit and tycho-surefire-plugin?

Tried to test an OSGi service using JUnit and the tycho-surefire-plugin.
Configuration of the plugin
<plugin>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-surefire-plugin</artifactId>
<version>${tycho.version}</version>
<configuration>
<showEclipseLog>true</showEclipseLog>
<dependencies>
<dependency>
<type>p2-installable-unit</type>
<artifactId>org.eclipse.equinox.ds</artifactId>
</dependency>
</dependencies>
</configuration>
</plugin>
Testcase (logging statements etc. omitted). The test class is contained in it's own OSGi bundle, separated from the code under test.
#Component(name = "LdapConnectionConfigurationServiceTest", immediate = true)
public class LdapConnectionConfigurationServiceTest {
private LdapConnectionConfiguration testObject;
#Reference
public void bindTestObject(final LdapConnectionConfiguration testObject) {
this.testObject = testObject;
}
public void unbindTestObject(final LdapConnectionConfiguration testObject) {
this.testObject = null;
}
#Test
public void testLdapPort() {
assertEquals(10389, testObject.getLdapPort());
}
}
Tycho starts an OSGi container, the test bundle, starts the LdapConnectionConfigurationServiceTest service and properly injects the testObject.
Subsequently JUnit runs this test case, but creates another instance of this class. Which doesn't get the testObject injected, so I'm getting NullPointerExceptions.
Don't know what I'm missing... What I want is running the test case against an injected service provided by the OSGi framework.

Aspectj BootstrapMethodError when using Java 8 stream API

so here i am - running a spring application with spring roo behind.
i use to cut my controllers into aspects, so my main controller will look like this:
#Controller
#RequestMapping("/apples")
#SessionAttributes(types = {Apple.class})
public class AppleController {
}
and other aspects extend its functionality like:
privileged aspect AppleController_Basics {
#RequestMapping(value = "/allApples", produces = "text/html", method=RequestMethod.GET)
public String AppleController.allApples(Model model) {
...
return "apples/list";
}
}
Now when i try to use Java 8 stream API within the aspect like:
apples.stream().filter(a -> a.isSweet()).collect(Collectors.toList());
i am facing the following exception:
org.springframework.web.util.NestedServletException: Handler processing failed; nested exception is java.lang.BootstrapMethodError: java.lang.NoSuchMethodError: com.apple.web.AppleController.lambda$0(Lcom/apple/model/Apple;)Z
when i use stream API for another entity than Apple itself, i get a slight different exception:
org.springframework.web.util.NestedServletException: Handler processing failed; nested exception is java.lang.BootstrapMethodError: java.lang.IllegalAccessError: tried to access method com.apple.web.AppleController.lambda$0(Lcom/apple/security/AppleEater;)Z from class com.apple.web.aspects.AppleController_Basics
when using forEach i get OutOfMemoryError::
apples.forEach(System.out::println);
org.springframework.web.util.NestedServletException: Handler processing failed; nested exception is java.lang.OutOfMemoryError: Java heap space
when i use those expressions in the main class, everything works fine.
the plugin looks like this:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.9</version>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.10</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.8.10</version>
</dependency>
</dependencies>
<executions>
<execution>
<phase>process-sources</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
<configuration>
<complianceLevel>1.8</complianceLevel>
<outxml>true</outxml>
<aspectLibraries>
<aspectLibrary>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</aspectLibrary>
</aspectLibraries>
<source>1.8</source>
<target>1.8</target>
<showWeaveInfo>true</showWeaveInfo>
<weaveWithAspectsInMainSourceFolder>false</weaveWithAspectsInMainSourceFolder>
</configuration>
</plugin>
i tried diffrent things to change my aspectj plugin configuration in order to make it work - without success. i appreciate any hint or help as i am really confused right now, pls dont hate <3
javap -c -p AppleController.class
public java.lang.String allApples(org.springframework.ui.Model);
Code:
0: aload_0
1: aload_1
2: invokestatic #528 // Method com/apple/web/aspects/AppleController_Basics.ajc$interMethod$com_apple_web_aspects_AppleController_Basics$com_apple_web_AppleController$allApples:(Lcom/apple/web/AppleController;Lorg/springframework/ui/Model;)Ljava/lang/String;
5: areturn
This is obviously an AspectJ compiler bug or shortcoming. I have created a bug ticket for it.
Here is the (non-Spring) test case I have extracted from your code:
package de.scrum_master.app;
public class Apple {
private String type;
private boolean sweet;
public Apple(String type, boolean sweet) {
this.type = type;
this.sweet = sweet;
}
public String getType() {
return type;
}
public boolean isSweet() {
return sweet;
}
}
package de.scrum_master.app;
import java.util.Arrays;
import java.util.List;
public class AppleController {
private static final List<Apple> APPLES =
Arrays.asList(new Apple("Granny Smith", false), new Apple("Golden Delicious", true));
public static void main(String[] args) {
AppleController appleController = new AppleController();
System.out.println("Named: " + appleController.namedApples(APPLES, "Smith"));
System.out.println("Sweet: " + appleController.sweetApples(APPLES));
System.out.println("Sour: " + appleController.sourApples(APPLES));
}
}
package de.scrum_master.aspect;
import java.util.List;
import java.util.stream.Collectors;
import java.util.function.Predicate;
import de.scrum_master.app.Apple;
import de.scrum_master.app.AppleController;
public privileged aspect AppleControllerITDAspect {
public List<Apple> AppleController.namedApples(List<Apple> apples, String subString) {
// Anonymous subclass works
return apples.stream().filter(new Predicate<Apple>() {
#Override
public boolean test(Apple a) {
return a.getType().contains(subString);
}
}).collect(Collectors.toList());
}
public List<Apple> AppleController.sweetApples(List<Apple> apples) {
// Method reference works
return apples.stream().filter(Apple::isSweet).collect(Collectors.toList());
}
public List<Apple> AppleController.sourApples(List<Apple> apples) {
// Lambda causes IllegalAccessError
return apples.stream().filter(a -> !a.isSweet()).collect(Collectors.toList());
}
}
The console log looks like this:
Named: [de.scrum_master.app.Apple#6f496d9f]
Sweet: [de.scrum_master.app.Apple#4e50df2e]
Exception in thread "main" java.lang.BootstrapMethodError: java.lang.IllegalAccessError: tried to access method de.scrum_master.app.AppleController.lambda$0(Lde/scrum_master/app/Apple;)Z from class de.scrum_master.aspect.AppleControllerITDAspect
at de.scrum_master.aspect.AppleControllerITDAspect.ajc$interMethod$de_scrum_master_aspect_AppleControllerITDAspect$de_scrum_master_app_AppleController$sourApples(AppleControllerITDAspect.aj:28)
at de.scrum_master.app.AppleController.sourApples(AppleController.java:1)
at de.scrum_master.aspect.AppleControllerITDAspect.ajc$interMethodDispatch1$de_scrum_master_aspect_AppleControllerITDAspect$de_scrum_master_app_AppleController$sourApples(AppleControllerITDAspect.aj)
at de.scrum_master.app.AppleController.main(AppleController.java:14)
Caused by: java.lang.IllegalAccessError: tried to access method de.scrum_master.app.AppleController.lambda$0(Lde/scrum_master/app/Apple;)Z from class de.scrum_master.aspect.AppleControllerITDAspect
at java.lang.invoke.MethodHandleNatives.resolve(Native Method)
at java.lang.invoke.MemberName$Factory.resolve(Unknown Source)
at java.lang.invoke.MemberName$Factory.resolveOrFail(Unknown Source)
at java.lang.invoke.MethodHandles$Lookup.resolveOrFail(Unknown Source)
at java.lang.invoke.MethodHandles$Lookup.linkMethodHandleConstant(Unknown Source)
at java.lang.invoke.MethodHandleNatives.linkMethodHandleConstant(Unknown Source)
... 4 more
In the aspect above you can also see a temporary workaround: use a method reference or a classical anonymous subclass instead of a lambda.
Background info: The AspectJ compiler AJC is a regularly updated fork of the Eclipse Java compiler ECJ (AspectJ is also an official Eclipse project, BTW). So maybe the bug is in ECJ, but probably rather in AJC.
Now that method:
lambda$0(Lcom/apple/model/Apple;)Z
is actually the de-sugar of your lambda a -> a.isSweet(), which will look like this:
private static boolean lambda$0(Apple s){
return s.isSweet();
}
This method is generated by the compiler. Unless you are using some weird compiler, this would have to be a bug in aspectj.
You can check that the method is there in AppleController by invoking the command to decompile your .class file:
javap -c -p AppleController.class
where the output should be something like this:
private static boolean lambda$0(com.model.apple.Apple);
Code:
0: aload_0
1: invokevirtual #9 // Method isSweet:()Z
4: ireturn
If this method is indeed there (the javac did his job correctly), you theoretically can not get a java.lang.NoSuchMethodError, which means that
aspectj is doing something very funny in the version that you are using.
I highly doubt this last paragraph, but just in case...
On the other hand if you de-compile (javap command) and you do not see the lambda$0 method, but one called lambda$main$0 for example, it means you are compiling with jdk-9 or some non-obvious Eclipse compiler.

Unable to run generated jar from spring-boot jersey

Im unable to run the generated jar file with my spring-boot with jersey project.
exception that i encounter is:
Error starting Tomcat context. Exception: org.springframework.beans.factory.UnsatisfiedDependencyException. Message: Error creating bean with name 'org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration': Unsatisfied dependency expressed through constructor parameter 1
Project runs properly when it's done via IDE (running the Main class) or when using spring-boot:run
Here are the details of the current setup:
Packaging:
jar
dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
<version>1.5.1.RELEASE</version>
</dependency>
my jersey configuration (ResourceConfig) is set to scan packages
#Component
public class JerseyConfiguration extends ResourceConfig {
public JerseyConfiguration() {
packages(true, "com.my.base.jaxrs.packages");
}
}
spring-boot-maven-plugin configured as:
org.springframework.boot
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
I also did not use the spring-boot-starter-parent but added the spring-boot-dependencies as indicated in the docs.
This is more of a workaround than an actual valid solution to use
packages(true, "my.package");
in reference to Anton's answer, i settled with this solution with the limitation that it requires resources with class level #Path or #Provider annotation:
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
provider.addIncludeFilter(new AnnotationTypeFilter(Path.class));
provider.addIncludeFilter(new AnnotationTypeFilter(Provider.class));
provider.findCandidateComponents("my.package.here").forEach(beanDefinition -> {
try {
LOGGER.info("registering {} to jersey config", beanDefinition.getBeanClassName());
register(Class.forName(beanDefinition.getBeanClassName()));
} catch (ClassNotFoundException e) {
LOGGER.warn("Failed to register: {}", beanDefinition.getBeanClassName());
}
});
I had this problem, I did not want to complicate things too much so I just registered all my jersey controllers individually.
#Configuration
public class JerseyConfig extends ResourceConfig {
JerseyConfig() {
// my old version that does not play well with spring boot fat jar
/*
packages(
"com.mycompany.api.resources"
);
*/
register(com.mycompany.api.resources.FooController.class);
register(com.mycompany.api.resources.BarController.class);
}
NOTE: I would not recommend this for large projects with many files, it will very quickly become long and unreadable and tedious to maintain.
That said, it is a working solution and you will be able to run your jar with the usual java -jar my-project.jar command.
Alternatively you could do,
#Configuration
public class JerseyConfig extends ResourceConfig {
JerseyConfig() {
BeanConfig beanConfig = new BeanConfig();
beanConfig.setResourcePackage("com.mycompany.api.resources");
}
}

SpringBoot Couchbase Integration

I want to make a filterable list of my UserTask entity with the QueryDslPredicateExecutor interface, so the parameters given in the query string will be autoprocessed into a Predicate.
I have the following classes/interfaces
public interface UserTaskQuerydslRepository extends CrudRepository<UserTask, String>,
QueryDslPredicateExecutor<UserTask>, QuerydslBinderCustomizer<QUserTask> {
#Override
default void customize(QuerydslBindings bindings, QUserTask userTask) {
...
}
}
UserTask is my class that represents the (couchbase) model
#QueryEntity
#Document(expiry = 0)
public class UserTask {
#Id
private String id;
...
}
If i annotate this class with #QueryEntity then Maven generates the QUserTask class for me
#Generated("com.mysema.query.codegen.EntitySerializer")
public class QUserTask extends EntityPathBase<UserTask> {
private static final long serialVersionUID = 493434469L;
public static final QUserTask userTask = new QUserTask("userTask");
public final StringPath id = createString("id");
...
public QUserTask(String variable) {
super(UserTask.class, forVariable(variable));
}
public QUserTask(Path<? extends UserTask> path) {
super(path.getType(), path.getMetadata());
}
public QUserTask(PathMetadata<?> metadata) {
super(UserTask.class, metadata);
}
}
To generate QUserTask i added the following lines to pom.xml
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>1.1.3</version>
<executions>
<execution>
<goals>
<goal>process</goal>
</goals>
<configuration>
<outputDirectory>target/generated-sources/apt</outputDirectory>
<processor>com.mysema.query.apt.jpa.JPAAnnotationProcessor</processor>
<processor>com.mysema.query.apt.QuerydslAnnotationProcessor</processor>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>com.mysema.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>3.4.3</version>
</dependency>
</dependencies>
</plugin>
In the project we have both JPA entities and couchbase entities, that's why i have the JPAAnnotationProcessor there.
If i run the application like this i get the following error:
org.springframework.data.mapping.PropertyReferenceException: No
property findAll found for type UserTask!
I tried to annotate my UserTaskQuerydslRepository with #NoRepositoryBean, it solved my findAll problem, but when i tries to #Inject this repository to a Resource (or controller, JHipster calls it Resource) i get the following error
No qualifying bean of type [.UserTaskQuerydslRepository]
found for dependency: expected at least 1 bean which qualifies as
autowire candidate for this dependency. Dependency annotations:
{#javax.inject.Inject()}
Can anyone help me what did I do wrong?
As #mp911de said in his comment, Spring Data Couchbase doesn't have support for QueryDsl, which explains why the bean cannot be created.
I can see where your confusion comes from when reading the doc. Chapter 5 is the content common to all Spring Data store implementations. All store documentations have one chapter with that same content, which generically talk about the repository basics. So it can mention things that are not in a particular implementation.
The first sentence of the section you linked even hints at it:
Several Spring Data modules offer integration with Querydsl via QueryDslPredicateExecutor.
Several, but not the Spring Data Couchbase module unfortunately.
2016. 07. 11. : After some research, and according to answers by #mp911de, and #simon-baslé we know that Spring Data Couchbase doesn't have support for QueryDsl yet.
I found a workaround for the problem that i wanted to solve (dynamic querying, aka. filters on a list and make it pageable)
https://github.com/TeamWanari/couchbase-query-executor

Resources