I'm having trouble injecting dao bean declared on a lib jar.
So I have a jar (mine) with a persistence context, entities and daos. Here is a dao example :
#Stateless
public class SomeDao {
#PersistenceContext
private EntityManager em;
...
}
Now I want to use this dao on my main application.
A jax-rs use case :
#Path("rs")
public class WebService{
#Inject
private SomeDao dao;
#POST
public Response doPost(){
//dao is injected but nullpointer thrown on EntityManager
dao.doSomething();
}
...
}
There is a beans.xml in both project (under META-INF/ for lib, WEB-INF/ for the web application). like this one :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
</beans>
------------- Edit ------------
I've just found out if I remove the #Stateless annotation and the producer it works. So the problem is in fact : how to inject with CDI an EJB declared on an lib jar.
Firstly, if this a jar you create and not a third party just, simply add a beans.xml in the correct location for the jar and you'll have those objects available for injection. That's the easiest way.
If it is a third part jar, your next best idea is to create a portable extension and listen to the BeforeBeanDiscovery in CDI 1.0 or AfterTypeDiscovery in CDI 1.1 and call the addAnnotatedType method to add in those classes you need from the jar. You'd create this extension in your war / ear classpath.
Related
I have a very simple Spring JPA Repository which is packed into a JAR and stored in the local maven repository.
I can use the JPA Repository in my Unit-Tests and in a little Main-Test-Routine. It gets #Autowired without any problem.
Here is the definition of my JPA-Repository:
public interface TestRepo extends CrudRepository<Seminar , Integer > {
List<Seminar> findAll();
}
And in the applicationContext.xml of the JAR:
<jpa:repositories base-package="de.dmsb.my.core"
entity-manager-factory-ref="entityManagerFactory"
transaction-manager-ref="transactionManager">
</jpa:repositories>
There a also some other Services defined in this JAR too which can be used in the JAR and in the WAR, which includes the JAR as a dependency with maven.
But as soon as a try to #Autowire the the JPA-Repository:
#Autowired
TestRepo testRepo;
I get an runtime error that there is no suitable bean defined. Other beans work - but as soon as it comes to JPA-Repositories it doesn't work.
I mean of course you cannot #Autowire a interface - and in the WAR there is not JPA-Repository bean in the ApplicationContext when I debug it - all the other beans from the JAR are there.
Any idea what the problem might be?
I have Tomcat 7.0.47 and I'm hosting a REST Easy JAXRS service. The service uses two external JARs, one that has a base repository interface and default implementation and one that creates a concrete repository derived from the base (i.e. these two JARs have a dependency).
The service works, i.e. I can send a request and get back data from the database.
Now what I'm trying to do is get the repository injected into the REST service, to do this I've changed the REST code to look like
#Path("/country")
public class CountryService {
#Inject
ICountriesRepository repository;
#GET
#Produces({"application/json", "application/xml"})
public List<Country> getCountries() throws NamingException, SQLException {
return repository.getCountries();
}
}
I've added a beans.xml file to the web application's WAR file (it's in the META-INF directory) and I've added beans.xml to both the JARS.
When I deploy the app I see the following message:
INFO: Adding scanned resource: com.mantiso.cricket.service.CountryService
but I don't see similar messages for the repository class in the JAR.
The JAR is deployed; the beans.xml file is in the JAR's META-INF directory; I've tried adding #ManagedBean to the repository class.
I'm sure I'm missing something simple, but lots of searching has turned up not a lot.
This is Tomcat 7.0.47; Weld 2.1.0; RESTEasy 3.0.5
What else should I try?
And the answer is: The beans.xml file for the web app must be in the WEB-INF directory. If it's in the META-INF directory then it's not parsed.
Although, this did appear to work OK when I tried injecting into a servlet
I have a utility jar that has a logger producer, and I am working on another project using Arquillian for testing. In the project, I have a class with #Inject Logger logger. When I run tests with Arquillian, I got an error saying that org.jboss.weld.exceptions.DeploymentException: WELD-001408 Unsatisfied dependencies for type Logger with qualifiers #Default
at injection point. I have successfully used producer from current project to produce a logger bean before, but this is the first time I have tried to use a producer from another jar (or a dependency). So, I am wondering if producers have to be in the same project or they can be in other jars. Thank you in advance.
I think see the issue-- This is not wrong with Arquillian.
Explicit jar contain the beans.xml file, that can be an empty, either it contain no version number, or contain the version number 1.1 with the bean-discovery-mode attribute set to all. such as:
For JavaEE7
<?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>
CDI will not manage and inject those beans which are annotated with
#Voted
Another one is implicit bean archive,that contains some beans annotated with a scope type, contains no beans.xml files or contains a beans.xml file with the bean-discovery-mode attribute set to annotated.
CDI can only manage and inject beans annotated with a scope type in an
implicit archive.
Refer Java Tutorial
Packaging CDI Applications
Weld-Doc
I'm trying to inject a bean located in a different jar file then the bean i'm trying to inject it into. Both beans are just basic #Stateless beans with local and remote interfaces.
If i use the normal injection
#EJB
IBean injectedBean;
or
#EJB
IBeanLocal injectedBean;
i get a NullPointerException when deploying the application.
If i use:
#EJB(mappedName="Bean")
IBean injectedBean;
or
#EJB(mappedName="Bean")
IBeanLocal injectedBean;
everything works and JBoss throws no deployment errors.
I might mention i use JBoss 5.
The bean class i'm injecting is declared as:
#Remote
public interface IBean
#Local
public interface IBeanLocal extends IBean
#Stateless(name = "Bean")
public class Bean implements IBean, IBeanLocal
My problem is that as specified in the documentation the mappedName property is vendor-specific. Is there any other way i could get this to work?
SOLVED:
I managed to solve the problem.
The problem was that i tried to deploy both jars individually which meant that each would get it's own ClassLoader in JBoss so that they couldn't find each other and would return a NullPointerException when trying to inject the bean.
The sollution was to add the jars to an ear and add an META-INF containing an application.xml looking like this:
<application xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/application_1_4.xsd"
version="1.4">
<display-name>Simple example of application</display-name>
<module>
<ejb>ejb1.jar</ejb>
</module>
<module>
<ejb>ejb2.jar</ejb>
</module>
</application>
I also had to change some JNDI lookups i made to match the new structure by adding the ear name before the classes: "ear-name/bean"
After this i just added the jars to the ear and everything deployed nicely.
You need to declare the local interface in order to have JBoss find the bean based on the interface only (assuming you're using EJB 3.0):
#Stateless(name = "Bean")
#Local ( IBeanLocal.class )
#Remote ( IBean.class )
public class Bean implements IBean, IBeanLocal { ... }
Edit: IBean is a remote interface (see comment).
Try injecting your bean with #EJB(beanName = "Bean")
Not sure if it'll work, but we had a similar issue and it was caused by the lack of the beanName attribute.
The issue I am currently experiencing is that my entityManager fails to be injected during the execution of a web application deployed within an ear.
The ear is setup as follows:
/META-INF -application.xml (EJBJar +
WAR)
-MANIFEST.MF
-weblogic-application.xml (currently just the default one that eclipse
generates)
/APP-INF
-lib
-EJBClientJar (interfaces + pojos)
-other jars
-classes
EJBJar
/META-INF
-persistence.xml
/src
-Annotated classes
WAR
-Service classes
Classes are as follows:
DAO inside EJBJar
#Stateless(mappedName="AwesomeBean")
public class Awesome implements AwesomeRemote //AwesomeRemote in EJBClientJar
{
#PersistenceContext
EntityManager em;
}
public class AwesomeService //Inside WAR
{
AwesomeRemote = context.lookup (Awesome.path.to.AwesomeRemote) //Is found
}
The call stack is like this:
War -> EJBClientJar -> EJBJar
On the EJB jar the entity manager is null. Question being how do I ensure that the PersistenceContext gets injected? The JNDI lookup succeeds on the AwesomeRemote Interface. The Interface has no annotations on it.
It's probably due to the fact that you try to inject on POJO class, not on managed component. Could you try to mark it as #Stateless and public and check whether it works?
For simple testing you can also add #WebService annotation and invoke your bean with SoapUI for example.
You should read about "packaging" part of ejb specifications.
This structure should be sufficient and work:
/
/lib
EJBClientJar (interfaces + pojos)
/META-INF
persistence.xml
application.xml
EJBJar.jar
WAR.war
As some formatting was lost, I'm not sure about what wasn't right in yours.
If this still doesn't work, try removing the only tricky part from above: put all classes from EJBClientJar in root. You the just have the most simple EAR packaging possible, it just will work.
Also, if you don't use your session bean outside a web application, you could use #Local instead of #Remote .