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.
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
Edit: I uploaded a minimal example. You can build with mvn clean install and after deployment, open the following urls:
http://localhost:8080/jaxrs-test/unqualified -> response: Hello there: de.test.SomeBean#........
http://localhost:8080/jaxrs-test/qualified -> response: Hello there: null
Tested on wildfly-23.0.0.Final with openjdk11.
Original quesion:
Consider the classes:
App.java - Jaxrs application:
#ApplicationPath("/api")
public class App extends Application {
}
Q.java - qualifier annotation:
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface Q {
}
X.java - some simple class:
public class X {
public String getX(){
return "some x";
}
}
Foo.java - jaxrs resource:
#Path("/foo")
#Q
public class Foo {
#Inject
private X test;
#Path("bar")
#GET
public String getBar() {
return test.getX();
}
}
The pom contains the following dependencies (and nothing else relevant):
<dependencies>
<dependency>
<groupId>javax.enterprise</groupId>
<artifactId>cdi-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.1.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
After building this application and deploying it to a wildfly server, a reques to
http://localhost:8080/app/api/foo/bar
leads to a null pointer exception, because test in the Foo resource is null.
The request and injection of X works perfectly when removing the qualifier annotation.
Why is that? I could not find anything in the documentation suggesting that.
Is there any way to get CDI working in a qualified jaxrs-resource class?
With qualified I mean any qualifier besides #Named, since the latter does not prevent the class to get the qualifier #Default.
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>
I am trying to understand the behaviour of #PropertySource annotation when not using #Autowiring and Spring Environment class. I am trying to use #Value to inject values from a properties file at runtime. From the book that I am reading and from the online sources it is required to have a static bean - PropertySourcesPlaceholderConfigurer configured in order for this to work. But for me the #Value works without PropertySourcesPlaceholderConfigurer static bean as well. Can someone point me to the right direction as to whats happening here. May be I am missing something very basic. When do we need PropertySourcesPlaceholderConfigurer and when not?
Below is the code that I am trying out -
package com.nilaysundarkar.demos;
public class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
AppConfig.java -
package com.nilaysundarkar.demos;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
#Configuration
#PropertySource("classpath:/com/nilaysundarkar/demos/app.properties")
public class AppConfig {
// This bean does not make any difference, or does it?
/*#Bean
public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer(){
return new PropertySourcesPlaceholderConfigurer();
}*/
#Bean
public Person person(#Value("${person.name}") String name){
Person person = new Person();
person.setName(name);
return person;
}
}
Bootstrap -
package com.nilaysundarkar.demos;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class App {
public static void main(String[] args){
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
Person person = context.getBean(Person.class);
System.out.println(person.getName());
((AnnotationConfigApplicationContext)context).close();
}
}
properties file - app.properties -
person.name=John Doe
pom -
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.nilaysundarkar.demos</groupId>
<artifactId>demos-runtime-injections</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
</dependencies>
When I run App.java -
In spring boot, and spring in general, application.properties (and application.yml since spring boot) can be placed in src/main/resources and it is picked automatically by the spring environment. That means that any property from this files will be loaded to your Environment and will be ready for injection using #Value.
You can use PropertySourcesPlaceholderConfigurer in order to register more property sources like foo.properties, [NAME].properties and so on in order for the spring Environment to add them.
When you use #PropertySource you register another propery file to your spring Environment so you dont need to use the custom PropertySourcesPlaceholderConfigurer to register it again. #PropertySource make it easier to register property files that do not require some special loading like a file in your file system etc.
As long as you use the default locations (application.properties) you don't need to register a custom bean of this type.
EDIT:
Example for PropertySourcesPlaceholderConfigurer with the same functionality as #PropertySource. The example is based on a foo.properties file residing in src/main/resources:
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
configurer.setLocation(new ClassPathResource("foo.properties"));
return configurer;
}
Took Privotal's Spring Core course months ago (probably Feb/2018) and the student handout (Version 5.0.a) explicitly tells you that a static PropertySourcesPlaceholderConfigurer bean must be declared in order for the ${}placeholders to be resolved when using Spring Core. But, when I tested this behavior omitting the creation of the bean in question it worked as If I’d created the bean resolving the placeholders. After that, I contacted my course instructor because I thought there was something wrong with my code, but he later confirmed that my code to be "good". He proceeded to contact Pivotal and we got an official answer :
Properties files registered via #PropertySource are automatically
added to the Environment in
org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClass,
SourceClass) while processing #Configuration classes.
So, apparently Pivotal acknowledge that the documentation on this is poor, and have filed a new JIRA.
BTW, this only applies to Spring Core 4.3+ as Spring Boot creates this bean for you automatically.
EDIT:
If you're taking the certification test, it's not clear when will Pivotal make the update, but the odds of this particular issue showing up on the exam are minuscule (in case it does appears, you can appeal)
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