Can I use CDI constructor injection for EJBs? - java

I want to do something like this:
#Stateless
public class GreeterEjb {
private final Greeter greeter;
#Inject
public GreeterEjb(Greeter greeter) {
this.greeter = greeter;
}
public String greet() {
return greeter.greet();
}
}
I tried it with Glassfish 3.1.1 and JBoss 7.0.2 with mixed results. Under some circumstances it works, under other circumstances it doesn't.
See this thread in the Glassfisch forum if you are interested in the details.
The EJB 3.1 spec, section 4.9.2 Bean Classes says:
The class must have a public constructor that takes no parameters.
That sounds like constructor injection is not allowed for EJBs.
BUT the CDI spec says at the start of section 3 that Session Beans are supported by CDI. Section 3.2 then talks at length about CDI and EJBs but never mentions anything about constructor injection not working. Which makes me think that it should be allowed.
So, do the specs allow CDI constructor injection for EJBs or not?

Kris and Pete Muir have finally convinced me: The EJB must have a public no-arg constructor even if another constructor is used for injection. Weird to use two constructors at the same time, but it works. Thanks guys.
Successfully tested on Glassfish 3.1.1, JBoss 7.0.2 and TomEE 1.0.0-beta-2.
#Stateless
public class GreeterEjb {
private final Greeter greeter;
#Inject
public GreeterEjb(Greeter greeter) {
this.greeter = greeter;
}
// public no-arg constructor required for EJBs
// injection still works fine with the #Inject constructor
public GreeterEjb() {
this.greeter = null;
}
public String greet() {
return greeter.greet();
}
}

Constructor injection of EJBs is required in Java EE 6 ONLY IF CDI is enabled for the jar. If this not working in an appserver, file a bug.
Please also file an issue here - http://java.net/jira/browse/EJB_SPEC - to have the EJB language spec fixed (it's wrong).
This is tested in the CDITCK - https://github.com/jboss/cdi-tck/blob/master/impl/src/main/java/org/jboss/cdi/tck/tests/implementation/enterprise/definition/ExplicitConstructorSessionBean.java - but not for no-interface-views, so please raise an issue in https://issues.jboss.org/browse/CDITCK and we can add a test for your case.

Related

CDI injection source not available in Jersey sub-resource

I'm using CDI in a Jersey app. On root resources, CDI injection works as expected, but whenever I return a sub-resource, the CDI injection sources are not available.
My root resource with sub-resource locator:
#Path("")
public class MyResource {
#Inject #Named("name") // works
private String name;
#Context
private ResourceContext context;
#Path("test2")
public Object test2() {
return MySubResource.class;
//return context.getResource(MySubResource.class); // this does not work either
}
}
The sub-resource:
public class MySubResource {
#Inject #Named("name") // error
private String name;
#GET
public Response test() {
return Response.ok("Name in sub resource: " + name).build();
}
}
Error:
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=String,parent=MySubResource,qualifiers={#javax.inject.Named(value=name)},position=-1,optional=false,self=false,unqualified=null,1235803160)
I'm using org.glassfish.jersey.ext.cdi:jersey-cdi1x and Weld dependencies, running on Undertow, with the Weld servlet listener added to the deployment.
Again, the same injection on the root resource does work. The #Named("name") String is produced by an #ApplicationScoped producer.
Is this not supposed to work? What am I missing?
Minimal example Maven project available here:
https://gitlab.com/Victor8321/jersey-sub-resource-cdi
Note: An open issue for this exists, but not sure what the official stance on this is: https://java.net/jira/browse/JERSEY-3184
As pointed out in https://github.com/eclipse-ee4j/jersey/issues/3456, adding a dummy #Path("xyz") to the sub-resource class is a "fix". However, that exposes your sub-resource under the dummy path.
Injecting an instance just through CDI works as well (#Inject Instance<MySubResource> ..), but then Jersey-managed resources aren't available for injection, such as #Context HttpServletRequest.
I've found 2 other approaches that fully work (both CDI injection and JAX-RS injection) and have no side effects (as with #Path):
Annotate the sub resource class with #Provider.
register() the sub resource class in the ResourceConfig (or Application).
Both approaches seem to work because they make Jersey - and in turn, CDI - aware of the class.
Note: I've updated my example project accordingly for future reference.

Can #Inject be used in a pojo

I am trying to use and understand CDI, when I use #Inject in a simple pojo class, it throws me NPE.
example
Greeting.java
public Class Greeting {
public String greet() {
System.out.println("Hello");
}
}
Test.java
import javax.inject.Inject;
public class Test {
#Inject
private Greeting greeting;
public void testGreet() {
greeting.testGreet();
}
}
When I call testGreet() it throws NPE, why is the greeting instance null. Does #Inject way of adding dependency only be used in container managed bean?
Note: jar is not the problem here.
TL;DR:
#Inject-annotated fields are only populated for container-instantiated beans.
Long version:
The CDI container provides you a lot of utilities for easily injecting dependencies to your beans, but it doesn't work by magic. The container can only populate the annotated fields of a client bean if the client bean itself was instantiated by the container. When the container is instantiating the object the sequence of events is as follows:
Your bean's constructor is called.
#Inject-annotated fields (and some other
annotations, #PersistenceContext and #EJB for instance) are
populated.
#PostConstruct-annotated no-args method is called.
Your bean is finished.
You're facing a classic bootstrapping problem, how to move from non-container-managed code into container-managed code. Your options are:
Get access to an instance of BeanManager from your JavaEE container via JNDI lookup. This is technical and a bit clumsy.
Use a CDI extension library such as Apache DeltaSpike. (Example: BeanProvider.getContextualReference(Test.class, false);)
Modify your application to start in a situation where you can inject your Test class rather than calling new Test();. This can be done for example by setting up a startup singleton ejb, which calls your test in it's #PostConstruct-annotated initialisation.
Hope this helps.
You need a JavaEE container, and than you need to define Greeting and Test as managed beans. After that you can inject one in another.
Try to take a look at:
https://docs.oracle.com/javaee/6/tutorial/doc/girch.html
Your class should be implemented from Serializable for being injectable as a "CDI Bean"

Ejb returns java.lang.NullPointerException

I am newbie in cdi and these are my first steps.
I have a bean in ejb module:
#Stateless
public class TestBean {
public String getIt(){
return "test";
}
}
I have a POJO in war module (I tried with #EJB and #Inject - same result)
public class SaveAction extends Action{
#EJB
private TestBean bean;
#Override
public void execute(){
....
String test = bean.getIt(); //HERE I GET java.lang.NullPointerException
...
}
}
Both war and ejb are inside ear. In log I see
EJB5181:Portable JNDI names for EJB TestBean:
[java:global/example.com/my-ejb/TestBean!com.example.TestBean,
java:global/example.com/my-ejb/TestBean]]]
From that I conclude that bean is initialized - but I can't find it. What am I doing wrong?
CDI and other dependency injection containers don't use magic! It's just ordinary java code that cannot do more or less than any other java code written anywhere. So it is impossible for a framework to do injection when an object is instantiated directly via new:
SaveAction action = new SaveAction();
// don't expect any injection has happened - it can't! no magic!
// action.bean is still null here!
The framework does not have any idea that an object like SaveAction has been instantiated. (Therefore it would be necessary to somehow inform the framework about the newly created object - but neither the constructor nor the 'new' statement do this! Just think one minute about how you would write such a framework code! It's not possible!* ).
To make injection work, the object must be created by the container instead! Otherwise it is NOT managed! (See also chapter 3.7 of the Web Beans specification (JSR 299)).
The best way to do this is to let the container inject the object into another already managed bean. It seems this just deferes the problem, but there are some already managed beans in your application, like the servlet!
Suggestion: Make your SaveAction CDI aware (e.g. annotate it with #Default) and let it be injected into your servlet!
Tutorials:
http://middlewaremagic.com/jboss/?p=1063
http://hudson.jboss.org/jenkins/job/JBoss-AS7-Docs/lastSuccessfulBuild/artifact/guides/developer-getting-started-guide/target/docbook/publish/en-US/html/helloworld.html
*) In theory it should be possible using aspect oriented programming or instrumentation to manipulate the constructors of beans to notify the container if they are invoked. But that's a very complex concept with many unsolved issues I think.

Get rid of Guice Dependency

I have following class
public class TransactionalTest {
#javax.inject.Inject
EntityManager em;
#com.google.inject.persist.Transactional
public void insertSomeData() {
Preferences p0 = new Preferences();
p0.setTemplatename("pref 01");
em.persist(p0);
}
}
I am using javax.inject.Inject instead of com.google.inject.Inject.
What is the correct annotation for Transactional, thus I have no dependencies to Guice?
From my memories, you will have to write your own annotation.
However, if you're in a JavaEE system, youc an use EJB TransactionAttribute. There also exist some CDI extensiosn, like Seam Solder or Apache DeltaSpike that may in a distant future provide such code outside of JavaEE containers.

CDI Injection of an EJB leads to NullPointerException

I am new to Java EE 6 and CDI. I have read a couple of tutorials and the weld documentation. However something that should work from my understanding doesn't so I need help.
I have the following situation. I created a Java EE 6 Application with NetBeans 7.0.1 using the maven archetype supplied with the IDE and I deploy to GlassFish 3.1 also supplied by the IDE.
The beans.xml is located in the META-INF directory of my EJB jar.
I have created a class that works soley as a producer class for my EJB Artifacts (and EntityManager)
#Stateless
public class EjbArtifactProducer {
#PersistenceContext(unitName = "trackProfiler-PU")
private EntityManager em;
#EJB
private UserFacadeLocal userFacade;
#EJB
private AuthServiceLocal authService;
#EJB
private NewsEntryFacadeLocal newsEntryFacade;
#EJB
private RoleFacadeLocal roleFacade;
#EJB
private TrackCommentFacade trackCommentFacade;
#EJB
private TrackFacade trackFacade;
#EJB
private TrackTypeFacade trackTypeFacade;
#EJB
private WaypointFacadeLocal waypointFacade;
#Produces
public AuthServiceLocal getAuthService() {
return authService;
}
#Produces
public EntityManager getEm() {
return em;
}
#Produces
public NewsEntryFacadeLocal getNewsEntryFacade() {
return newsEntryFacade;
}
#Produces
public RoleFacadeLocal getRoleFacade() {
return roleFacade;
}
#Produces
public TrackCommentFacade getTrackCommentFacade() {
return trackCommentFacade;
}
#Produces
public TrackFacade getTrackFacade() {
return trackFacade;
}
#Produces
public TrackTypeFacade getTrackTypeFacade() {
return trackTypeFacade;
}
#Produces
public UserFacadeLocal getUserFacade() {
return userFacade;
}
#Produces
public WaypointFacadeLocal getWaypointFacade() {
return waypointFacade;
}
}
I tried to apply the #Produces annotation directly to the fields an on methods as shown above.
However the following does not inject anything in another EJB
#Inject
private NewsEntryFacadeLocal newsEntryFacade;
This is done in a stateless session ejb but when I try to access newsEntryFacade in any of my business methods a NullPointerException is thrown. So clearly no Injection is happening or my producers produce null references.
Am I missing something? Or should this work according to CDI/Weld?
Strangely it seems to work that way when I try to #Inject EJBs into the web application part (however i needed an extra producer class in my .war for this to work, is this as it should be?).
EDIT: The project works with an ant build (generated by NetBeans). Are there issues with the Maven archetype provided by NetBeans? It seems that with the Maven archetype there are some issues with CDI injection between the war and ejb modules. I found that if I had separate producers in the web and ejb module Glassfish generates a deployment error stating that there are two indistinguishable implementations of an interface. But when I remove the producer in the web module Weld complains that the EJB i want to inject into my beans in the web module cannot be resolved. Also with the Ant build EJBs can be #Injected without a producer while the maven build needs producer fields on a class. I can't explain how this could happen. After all the final deployment should be more or less equal, shouldn't it?
If you want to use #Inject then annotate it as #Named #ApplicationScoped, otherwise use #EJB when injecting your singleton.
Jordan Denison is correct. You're trying to #Inject and EJB, but you be using #EJB for EJBs. You're EJB class is probably annotated with #Stateless or something. #Inject should be used on session beans that annotated with #Named and some sort of scope.
Hard to tell whats going wrong but what definitely did not work for us is to use CDI between class loader boundaries. For example if your application is packaged as an ear file you would have your ejbs in an jar file and your webapp in your war file. In this case you can not use CDI to inject your ejbs in your web layer. The problem is that the jar and the war is loaded by different class loaders. Maybe newer CDI implementations behave different but at least JBoss 6 and Glassfish had this problem.
Try to put #Named into EjbArtifactProducer. Also, if the produces is this kind of simple, I think it's better to remove it too (else, you should do one more change).
you are mixing two different concepts... use CDI as backing beans for JSF. (CDI in Web Container) and use EJB and JPA in the Business Layer... the CDI layer can inject a EJB to call the specific business method.
in this case you have a clean separation on concerns.
BTW: you do not need any EJB interfaces at all! use only interfaces if you have a requirements to communicate from remote... (#Remote). with the #LocalBean annotation you can inject directly the EJB itself..
if you have e clean separation of layers each with his own concern i think you better find the reason for this NullPointerException.. and i think your NullPointerException does not exists any more after this...
Layering:
Web Browser --> JSF Facelet --> CDI Backing Bean --> EJB Service(s) --> EntityManager

Categories

Resources