So I've started setting up a simple SparkJava application from scratch with the basic goal of comparing it to a mirrored SpringBoot (with SpringData) application which I've already built. However I've been struggling with DeltaSpike (which is the alternative to SpringData chosen) setup as that adds a layer of CDI complexity which I didn't have to handle within SpringBoot.
Long story short after trying out some (probably wrong) approaches: now I'm stuck at enabling Weld (CDI) in the SparkJava Jetty container. I've taken a look on how to enable Weld for Jetty but that only got me more confused - and I'm not even sure that it's supposed to work.
What I'm getting now is: Exception in thread "main" java.lang.NoClassDefFoundError: org/jboss/weld/events/WeldEvent.
Since there are many files it's better to just share my github repo: https://github.com/vitorbmiranda/taskapp-sparkjava. Don't mind unused code and stuff like that as it's still a WIP.
A final disclaimer: apart from that error itself I feel like I may be completely missing something or my approach with SparkJava/CDI/DeltaSpike could be completely off, or my Maven dependencies could not make any sense, so any directions on that would also be appreciated. Cheers.
Update Sep 2nd: did a fresh setup on a new repo. Managed to customize the Spark embedded Jetty handler and added the Weld Listener to it.
This is how I've added the listeners:
import org.jboss.weld.environment.servlet.BeanManagerResourceBindingListener;
import org.jboss.weld.environment.servlet.Listener;
...
JettyHandler handler = new JettyHandler(matcherFilter);
...
handler.addEventListener(new BeanManagerResourceBindingListener());
handler.addEventListener(new Listener());
This is from the startup log (using TRACE level within logback.xml)
5:46:50.427 [Thread-0] DEBUG o.e.j.u.component.ContainerLifeCycle - spark.embeddedserver.jetty.JettyHandler1530180168==dftMaxIdleSec=-1 added {org.jboss.weld.environment.servlet.BeanManagerResourceBindingListener#49483272,UNMANAGED}
15:46:50.432 [Thread-0] DEBUG o.e.j.u.component.ContainerLifeCycle - spark.embeddedserver.jetty.JettyHandler1530180168==dftMaxIdleSec=-1 added {org.jboss.weld.environment.servlet.Listener#62383f62,UNMANAGED}
No "Weld" references after that. If I try to use any #Injected bean it won't work as expected (since logs didn't give me anything related to Bean/classpath lookup):
public class TestController {
#Inject
TestService testService;
public static String getTest(Request request, Response response) {
// NPE here
testService.test();
return value;
}
}
#Default
public class TestService {
public String test() {
return "123";
}
}
My pom.xml looks like:
<dependency>
<groupId>com.sparkjava</groupId>
<artifactId>spark-core</artifactId>
<version>2.9.1</version>
</dependency>
<dependency>
<groupId>org.jboss.weld.se</groupId>
<artifactId>weld-se-core</artifactId>
<version>3.1.2.Final</version>
</dependency>
<dependency>
<groupId>org.jboss.weld.servlet</groupId>
<artifactId>weld-servlet-core</artifactId>
<version>3.1.2.Final</version>
</dependency>
<dependency>
<groupId>org.jboss.weld</groupId>
<artifactId>weld-spi</artifactId>
<version>3.1.SP1</version>
</dependency>
The META-INF/beans.xml (which I would think it's not necessary, anyway) is:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
bean-discovery-mode="all">
</beans>
Related
I am trying to intercept any method in my application which is annotated with my custom developed annotation.
Initially I used the Spring AOP which works fine. But, it is not intercepting if the method call is in the same target class.
Going through the official docs, I got to know that the Spring AOP uses proxy beans for the same.
One workaround I found was to self inject the target class. But, this seems like too much fuss. Like every time I am adding my custom annotation to a method, I need to make sure that I add the #Scope annotation, set the proxyMode & self inject target class as shown here
Later I moved on to configuring and using native AspectJ.
This is my Custom annotation:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface TrackTime {
String description() default "";
}
Here is the Aspect for TrackTime annotation:
#Configuration
#Slf4j
#Aspect
public class TrackTimeServiceImpl {
#Pointcut("execution(public * *(..))")
public void methodsToBeProfiled(){
}
#Around("methodsToBeProfiled() && #annotation(x.y.z.TrackTime)")
public Object audit(ProceedingJoinPoint joinPoint) throws Exception {
//Business logic
}
}
I would like to mention here that my application is running on Jetty server.
The configuration file:
#Configuration
#EnableLoadTimeWeaving(aspectjWeaving = EnableLoadTimeWeaving.AspectJWeaving.ENABLED)
#EnableSpringConfigured
#EnableAspectJAutoProxy(proxyTargetClass = true)
public class TrackTimeConfig implements LoadTimeWeavingConfigurer {
#Override
public LoadTimeWeaver getLoadTimeWeaver() {
return new InstrumentationLoadTimeWeaver();
}
}
The aop.xml file:
Path to file: /resources/META-INF/aop.xml
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "https://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
<weaver>
<!-- only weave classes in our application-specific packages -->
<include within="in.xxx.*"/>
</weaver>
<aspects>
<!-- weave in just this aspect -->
<aspect name="in.xxx.yyy.zzz.TrackTimeServiceImpl"/>
</aspects>
</aspectj>
Relative dependencies added in parent pom.xml have been mentioned here:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.21</version>
</dependency>
My service class:
#Component
public class SomeService {
public void a(){
b();
}
#TrackTime
public void b(){
//business logic
}
}
Now when the method a() is called from the controller, even though the b() has the #TrackTime annotation, it is not intercepted.
Also, I would like to mention that I have set the following program arguments while running the application
-javaagent:/Users/xxx/.m2/repository/org/springframework/spring-instrument/5.3.6/spring-instrument-5.3.6.jar
-javaagent:/Users/xxx/.m2/repository/org/aspectj/aspectjweaver/1.9.6/aspectjweaver-1.9.6.jar
I have gone through docs, articles, followed solutions on stackoverflow. But, for the above mentioned configuration/code, it is not working as I want.
Requesting help from the community.
Thanks in advance.
I did not try to run your example locally yet, but noticed two details at first glance in your code:
You are configuring both #EnableLoadTimeWeaving (for native AspectJ) and #EnableAspectJAutoProxy (for Spring AOP), which might be conflicting with each other. Try to remove the latter and see if it fixes the problem.
In aop.xml, you are using <include within="in.xxx.*"/>. Please note that this will only include classes directly in the in.xxx package, but not classes in sub-packages. In order to include them as well, please use the double-dot notation in.xxx..*.
Feel free to report back if it is still not working with the suggested changes. I can take a closer look then.
First of all, I would like to thank #hfontanez, #void void & #kriegaex for responding and helping me out in moving forward in solving the problem statement.
So if anyone is looking out on how to intercept nested & private methods, let's have this as a one stop in configuring native AspectJ.
Please check my POC here on github for a working example.
In my case, I added the aspectjweaver JAR in my project structure and passed the arguments through VM Options in IDE.
That all !!
Nested/Private methods will now be intercepted.
Thank you,
Karthik
I'm trying to add resilience4j into my app for exponential backoff, etc.
Service
#Component
public class ResilienceService {
private static final String BACKEND_A = "backendA";
public ResilienceService() throws IOException {
testRetry();
}
#Retry(name = BACKEND_A)
public void testRetry() throws IOException {
System.out.println("Hey it's working!");
throw new IOException();
}
}
Config
resilience4j.retry.instances.backendA.maxAttempts=3
resilience4j.retry.instances.backendA.waitDuration=10s
resilience4j.retry.instances.backendA.enableExponentialBackoff=true
resilience4j.retry.instances.backendA.exponentialBackoffMultiplier=2
resilience4j.retry.instances.backendA.retryExceptions[0]=java.io.IOException
I'm trying to basically see if resilience lib will call this function 3 times. How should I think about both configuring this correctly and also testing that the retries are actually happening? I thought I could put a breakpoint on the method and see it call 3 times, but maybe I'm misunderstanding.
Aside from the comment by #M.Deinum above, you may also have caught out by resilience4j-springboot2 not depending on spring aop by default.
E.g. you might need:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
</dependencies>
To be fair, the documentation does state:
Add the Spring Boot 2 Starter of Resilience4j to your compile dependency.
The module expects that org.springframework.boot:spring-boot-starter-actuator and org.springframework.boot:spring-boot-starter-aopare already provided at runtime
I have Spring 5.0.4 application with a Dispatcher servlet config file as spring-web-servlet.xml (spring-web being Dispatcher servlet name).
To enable AOP in the application, I made the entry <aop:aspectj-autoproxy /> in spring-web-servlet.xml after including the namespace for aop.
The pom.xml has below entries:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.11</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.11</version>
</dependency>
To use AOP, I created below Aspect class:
#Aspect
#Component
public class MyAspect {
#Before("getApiPointcut()")
public void logRequest(JoinPoint joinPoint) {
//some logic here
}
#AfterReturning(value = "getApiPointcut()", returning = "returnValue")
public void logResponse(Object returnValue) {
//some logic here
}
#Pointcut("within(com.test.service.controller.*) " +
" || within(com.test.service.api.GenericService)" +
" || within(com.test.service.api.UserService)")
public void getApiPointcut() {
}
}
This class has two methods: logRequest() and logResponse().
Every API call will first go through logRequest() and before returning the response, logResponse() is called.
For the initial few requests (5-6), AOP works fine and I get the flow in MyAspect class for both the methods.
But after that, if I hit any API, flow goes through logRequest(), then the API class, and then it hangs. The API class calls the service class. But the flow never reaches the service class and hence never goes in logResponse() and the server does not return anything to the client. I checked the logs and there are no exceptions. I checked the heap memory usage too and it is not consumed fully. Even the threads are not fully occupied. (Checked these stats using jconsole).
If I disable AOP by removing <aop:aspectj-autoproxy /> from spring-web-servlet.xml file, the server returns response for all the calls without any issue.
So, the conclusion that I made is enabling the AOP is causing the server to hang but I am not able to understand why.
Any help is appreciated.
UPDATE
The APIs (controller) on which I am trying to invoke AOP are annotated with #PreAuthorize. If I comment out the #PreAuthorize annotation, the application does not hang. Uncommenting the #PreAuthorize annotation again results in 5-6 successful requests and then the application hangs.
As #PreAuthorize also uses AOP, is there any case that I need to take care of when using my own AOP with #PreAuthorize?
UPDATE 2
The #PreAuthorize method on the API is as below:
#PreAuthorize("hasAuthority('SOME_AUTH') and #myUtil.canRead(#userId) ")
If I remove the hasAuthority('SOME_AUTH') and keep the second check, the aplication works fine, but if I keep both or keep only hasAuthority() check, the application does not responds after a few successful responses.
UPDATE 3
The issue gets resolved if I annotate the API with #Transactional.
However, the code used to work without #Transactional when aop was disabled. So why does enabling AOP makes it necessary to have #Transactional on the API?
I created a dynamic java project and added this dependency:
<!-- https://mvnrepository.com/artifact/org.glassfish.jersey.containers/jersey-container-servlet-core -->
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet-core</artifactId>
<version>2.23.1</version>
</dependency>
Then i created the App class like this:
#ApplicationPath("/test")
public class App extends ResourceConfig {
public App() {
this.packages("com.test.ul");
}
}
and in the same package as the App class is in, i created this:
#Path("restaurantAPI")
public class RestaurantAPI {
#Path("/get")
#GET
public Response getRequest(#PathParam("id") String id) {
return Response.ok(id).build();
}
}
I run my server, and I call this URL:
http://localhost:8080/ULTestServer/test/restaurantAPI/get?id=3
but I get error 404
What Am I missing please ? (I always do that and it used to work)
Change
jersey-container-servlet-core
to
jersey-container-servlet
The reason is that the latter has the required component1 that allows for discovering your application without web.xml, in replace for #ApplicationPath. This is part of the servlet 3 pluggability mechanism.
The first dependency is use for non servlet 3.0 containers, where the use would have to use a web.xml to register the Jersey servlet.
1 - Here is the implementation
So, my first attempt at CDI kind of went to the dogs. I've read a ton of articles and tried a variety of simple to complex examples without success. Here is my current, simple example. What am I doing wrong?
Maven Project:
beans.xml (located in src/main/resources/META-INF)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
version="1.1"
bean-discovery-mode="all">
</beans>
Printer.java
import javax.inject.Inject;
public class Printer {
#Inject Greeting greeting;
}
Greeting.java
import javax.enterprise.inject.Default;
#Default
public class Greeting {
public void sayStuff(){ System.out.println("Stuff"); }
}
Main.java
public class Main {
public static void main( String[] args ) {
new Printer().greeting.sayStuff();
}
}
The Error
It builds fine, but on attempted run I get the error
Exception in thread "main" java.lang.NullPointerException
at com.foo.app.CDI_test.Main.main(Main.java:5)
which is precisely when I attempt to invoke sayStuff() on the greeting-property.
Why is it not being instantiated? Tutorials claim #Default to be excessive as well. I've attempted using both a custructor-injection and setter-injection, no cigar.
EDIT 1 - added pom.xml dependencies
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
<dependency>
<groupId>javax.enterprise</groupId>
<artifactId>cdi-api</artifactId>
<version>1.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.weld.se</groupId>
<artifactId>weld-se</artifactId>
<version>2.2.4.Final</version>
</dependency>
</dependencies>
EDIT 2 - version information
- Java 1.7
- Eclipse Luna 4.4.0
- IntelliJ IDEA 13.1.4
CDI runs fine in Java EE container
e.g. try a webapp, jsp/jsf page inside weblogic
In Java SE, you may need to do more
What is the easiest way to have CDI and JPA in Java SE?
http://blog.rocketscience.io/dependency-injection-with-cdi-in-java-se/
when you do new Printer() who is responsible for injecting stuff in your object?
There is no dependency between CDI/Weld and EE. It works fine in SE. In order to run it in SE though you need to start the container. This is typically done by:
public static void main( String[] args ) {
Weld weld = new Weld();
WeldContainer container = weld.initialize();
Printer printer = CDI.current().select(Printer.class).get();
printer.getGreeting().sayStuff();
}
What you cannot do is direct field access, so printer.greeting won't work, hence you need to add a getter. In addition, I use CDI.current() to access the runtime. You'll also need to add org.jboss.weld.se:weld-se to your maven dependencies. Take a look at the weld docs for reference.