Using JSR-330 annotations with Jersey 2 + Spring - jersey-2.0

I have been trying to use Spring 4 with Jersey 2 and notice that using the jersey-spring3 extension there is no way to make #Named annotated resources managed by Spring. They must be annotated with #Component to be managed by Spring.
If a resource is annotated with #Named HK2 seems to "take over" managing that resource.
Resource
import com.example.jersey.spring.Greeting;
import java.util.logging.Logger;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
#Named
#Singleton
#Path("resource")
public class Resource {
private static final Logger logger = Logger.getLogger("Resource");
public void init() {
logger.info("Creating -> " + this + " injected with -> " + greeting);
}
#Inject
private Greeting greeting;
#GET
#Produces(MediaType.TEXT_PLAIN)
public String getIt() {
logger.info("Working on " + this + " Greeting " + greeting);
return "Got it!";
}
}
With the following Spring applicationContext
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"
default-init-method="init">
<context:component-scan base-package="com.example.jersey.webapp"/>
<bean id="greeting" class="com.example.jersey.spring.Greeting"/>
</beans>
Results in the following log messages being printed on start-up
Oct 18, 2017 3:11:44 PM org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor <init>
INFO: JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
Oct 18, 2017 3:11:44 PM com.example.jersey.webapp.Resource init
INFO: Creating -> com.example.jersey.webapp.Resource#6e199bab injected with -> com.example.jersey.spring.Greeting#533b3005
Oct 18, 2017 3:11:45 PM org.springframework.web.context.ContextLoader initWebApplicationContext
INFO: Root WebApplicationContext: initialization completed in 249 ms
And after a couple of GET calls.
Oct 18, 2017 3:11:56 PM com.example.jersey.webapp.Resource getIt
INFO: Working on com.example.jersey.webapp.Resource#40328bbc Greeting com.example.jersey.spring.Greeting#533b3005
Oct 18, 2017 3:12:03 PM com.example.jersey.webapp.Resource getIt
INFO: Working on com.example.jersey.webapp.Resource#40328bbc Greeting com.example.jersey.spring.Greeting#533b3005
So it appears Spring does create the bean but that is not the one thats used to service the requests. However if instead of Named, Component is used the entire lifecycle is managed by Spring.
Is there a way to use the standard #Named annotation and ensure Spring manages the lifecycle of resource completely?

I don't think that there is a way to disable HK2.
From latest Jersey-2 Userguide:
the resources must themselves be managed by Spring, by annotating with #Component, #Service, #Controller or #Repository
Spring recommends the use of the more specific annotations #Service, #Controller and #Repository.
From the Spring 4.3.12 Documentation §7.10.1:
#Component and further stereotype annotations
The #Repository annotation is a marker for any class that fulfills the
role or stereotype of a repository (also known as Data Access Object
or DAO). Among the uses of this marker is the automatic translation of
exceptions as described in Section 20.2.2, “Exception translation”.
Spring provides further stereotype annotations: #Component, #Service,
and #Controller. #Component is a generic stereotype for any
Spring-managed component. #Repository, #Service, and #Controller are
specializations of #Component for more specific use cases, for
example, in the persistence, service, and presentation layers,
respectively. Therefore, you can annotate your component classes with
#Component, but by annotating them with #Repository, #Service, or
#Controller instead, your classes are more properly suited for
processing by tools or associating with aspects. For example, these
stereotype annotations make ideal targets for pointcuts. It is also
possible that #Repository, #Service, and #Controller may carry
additional semantics in future releases of the Spring Framework. Thus,
if you are choosing between using #Component or #Service for your
service layer, #Service is clearly the better choice. Similarly, as
stated above, #Repository is already supported as a marker for
automatic exception translation in your persistence layer.

Related

Eclipse MicroProfile Metrics with SOAP-based web services

Isn’t it possible to use Eclipse MicroProfile Metrics with SOAP-based web services on Payara Server 5.193.1? #Counted and #Timed don’t seem to work with #WebService and #WebMethod? Although, #Metric works. Is this by design or is it an issue?
Here is my code:
Interface:
package nl.tent.laboratory.emp.metrics;
import javax.jws.WebMethod;
import javax.jws.WebService;
#WebService
public interface MyWebService {
#WebMethod
String sayHello();
}
Implementation:
package nl.tent.laboratory.emp.metrics;
import javax.jws.WebService;
import org.eclipse.microprofile.metrics.annotation.Counted;
#WebService(endpointInterface = "nl.tent.laboratory.emp.metrics.MyWebService")
public class MyWebServiceImpl implements MyWebService {
// #Inject
// #Metric
// Counter counter;
public MyWebServiceImpl() {
super();
}
#Counted(name = "myCounter")
#Override
public String sayHello() {
// counter.inc();
return "Hello Marc!";
}
}
#Counted
and #Timed
are method interceptors and work only on CDI beans. #Metric
injects metrics objects and works where injection is supported, including Servlets and Web services.
In Payara Server, a web service object is implemented as a servlet by default. Servlets can inject CDI beans but they aren't CDI beans themselves and CDI interceptors don't work on them.
You need to turn your WS into a CDI bean (e.g. with #RequestScoped) or EJB (#Stateless) to enable the Metrics interceptors.

Multiple persistence units in Wildfly?

Is it possible to have two persistence units in a Wildfly (9.0.2) application?
I get "WFLYJPA0061: Persistence unitName was not specified and there are 2 persistence unit definitions in application deployment deployment "jasper-web.war". Either change the application deployment to have only one persistence unit definition or specify the unitName for each reference to a persistence unit."
I have unitName specified in the #PeristenceContext annotations. I read somewhere I could disable
<!--
<subsystem xmlns="urn:jboss:domain:jpa:1.1">
<jpa default-datasource="" default-extended-persistence-inheritance="DEEP"/>
</subsystem>
-->
in standalone.xml. This removed the error message, but also disabled injection of entityManagers (null pointer referencing them in code)
I have tried to split the persistence units over two different ejb-jars, each with their own persistence.xml, but as they're still included in the same war, Wildfly still complains.
The the two persistence units are used with hibernate, one with a postgresql database and one with a ucanaccess driver for ms access. They both work separately.
here an example of what works in our wildfly 8.x/9.x ejb app:
First of all define all the classes for each persistence-unit in the persistence.xml, unlisted classes can be turned off to disable autodiscovery.
persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="primary">
<jta-data-source>java:jboss/datasources/primary_ds</jta-data-source>
<class>whatever.primary.model.SomeEntity</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties> ... </properties>
</persistence-unit>
<persistence-unit name="secondary">
<jta-data-source>java:jboss/datasources/secondary_ds</jta-data-source>
<class>whatever.secondary.model.AnotherEntity</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties> ... </properties>
</persistence-unit>
</persistence>
If you use JBoss Developer Studio ignore the warning (it is only an eclipse flaw):
Multiple persistence units defined - only the first persistence unit will be recognized
Resources.java
package whatever.util;
import javax.annotation.Resource;
import javax.enterprise.inject.Produces;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.sql.DataSource;
public class Resources {
#Produces
#PersistenceContext(unitName = "primary")
private EntityManager emPrimary;
#Produces
#PersistenceContext(unitName = "secondary")
private EntityManager emSecondary;
#Produces
#Resource(lookup = "java:jboss/datasources/primary_ds")
private DataSource dsPrimary;
#Produces
#Resource(lookup = "java:jboss/datasources/secondary_ds")
private DataSource dsSecodnary;
}
Dao primary example
package whatever.dao;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
#Stateless
public class DaoPrimaryExample {
#PersistenceContext(unitName = "primary")
private EntityManager em;
public void create(SomeEntity entity) {
em.persist(entity);
}
}
Dao secondary example
package whatever.dao;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
#Stateless
public class DaoSecondaryExample {
#PersistenceContext(unitName = "secondary")
private EntityManager em;
public void create(AnotherEntity entity) {
em.persist(entity);
}
}
IMPORTANT: If you plan to use booth persistence units in same transaction than XA datasources should be used.
Adding this option for a persitence unit in the persistence.xml fixed the issue for me:
<property name="wildfly.jpa.default-unit" value="true"/>
As John Ament pointed out the error message actually indicates an injection point without unitName (that I had forgotten about in my source...) As soon as I got rid of that, everything worked as it should. I also got a bit confused googling this problem, when I actually found some old issues in jboss AS where this actually seems to have been a problem, once.

Override Built-in Principal Bean

In Java EE 6, there is a built in bean type javax.security.Principal which is available for injection like #Inject Principal principal;.
This is by default and I'm wondering if there is a way to change it, say, by supplying a producer method instead.
As it stands, writing a producer method will cause a deployment exception.
Example Producer:
my.package;
import javax.security.Principal;
public class MyProducer {
...
#Produces
public Principal obtainPrincipal() {
return getMyPrincipal();
}
}
The exception:
org.jboss.weld.exceptions.DeploymentException: WELD-001409 Ambiguous dependencies for type [Principal] with qualifiers [#Default] at injection point [[parameter 1] of [constructor] #Inject public my.package.MyType(Principal, SomeOtherType)]. Possible dependencies [[Producer Method [Principal] with qualifiers [#Any #Default] declared as [[method] #Produces #SessionScoped protected my.package.MyProducer.obtainPrincipal()], Built-in Bean [java.security.Principal] with qualifiers [#Default]]]
at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:278)
at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:244)
at org.jboss.weld.bootstrap.Validator.validateBean(Validator.java:107)
at org.jboss.weld.bootstrap.Validator.validateRIBean(Validator.java:127)
at org.jboss.weld.bootstrap.Validator.validateBeans(Validator.java:346)
at org.jboss.weld.bootstrap.Validator.validateDeployment(Validator.java:331)
at org.jboss.weld.bootstrap.WeldBootstrap.validateBeans(WeldBootstrap.java:366)
at org.jboss.as.weld.WeldContainer.start(WeldContainer.java:83)
at org.jboss.as.weld.services.WeldService.start(WeldService.java:76)
at org.jboss.msc.service.ServiceControllerImpl$StartTask.startService(ServiceControllerImpl.java:1811)
at org.jboss.msc.service.ServiceControllerImpl$StartTask.run(ServiceControllerImpl.java:1746)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:722)
So the question is - is there any way to replace the default built in types in CDI?
To give you an idea of why I need this, my project is switching from JAAS to Apache Shiro. There is a good amount of existing code that does something based on the injected principal. Shiro in a web environment however, only wraps the HttpServlet request / response and overrides the appropriate security related methods. That however does not propagate to CDI, which in that case always returns the anonymous principal.
Update
I also tried the following:
my.package;
import javax.security.Principal;
#Alternative
public class MyPrincipal implements Principal {
...
#Override
public String getName() {
return getMyPrincipalName();
}
}
Then enabling the alternative in beans.xml:
<?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/javaeehttp://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
<alternatives>
<class>my.package.MyPrincipal</class>
</alternatives>
</beans>
In this case, there are no deployment errors, but the injected principal is still the default one, not my enabled alternative.
The guys at Weld helped me out. My deployment structure was something like:
-- app.war
|-- module0.jar (beans.xml)
|-- module1.jar (beans.xml)
|-- ...
|-- other-lib.jar
But in CDI 1.0, alternatives are anabled per bean archive only. So, adding
<alternatives>
<class>my.package.MyPrincipal</class>
</alternatives>
in each beans.xml makes it all work.

Interceptors are not intercepting

I am having problems setting up a Java EE 6 CDI interceptors. I am using embedded glassfish, I have specified the interceptor in beans.xml in the web application.
<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">
<interceptors>
<class>ServiceInterceptor</class>
</interceptors>
</beans>
I am trying to secure this bean:
#Named
//#Stateless
#RequestScoped
public class SecuredMethodJSFBean /*implements Serializable*/{
#Inject
protected SecuredMethodSample securedMethodSample;
/*
#CurrentUser
#SessionScoped
#Inject
protected RuntimePrincipalAware principal;
//protected JSFLoginBean jsfLoginBean;
*/
public SecuredMethodJSFBean()
{
super();
System.out.println("creating secured method jsf bean");
}
#Secured("adfadfafd")
public void doSomething()
{
//System.out.println("\n\n\n\nprincipal:" + principal);
//System.out.println("principal:" + jsfLoginBean.getPrincipal());
//securedMethodSample.doSomething(jsfLoginBean.getPrincipal().getName());
//return(jsfLoginBean.getPrincipal().getName());
//securedMethodSample.doSomething(principal.getName());
//return(principal.getName());
//return("secured-method");
securedMethodSample.doSomething("testing ...");
}
}
What do I need to do to get my interceptors running?
Also, I am trying to use interceptors to intercept method invocations on beans used by servlets. Since those beans are beans, I should be able to intercept them. However, I am unable to do so. I was originally trying to intercept method invocations in servlets directly, but they're not CDI beans so that does not make sense.
Thanks,
Walter
"I have specified the interceptor in beans.xml in the web application"
Is #Secured defined in another project / jar? In that case you need to enable it in that beans.xml.
To answer my question better, I did the following:
I made the project ejb instead of jar (inside maven).
I created an ejb-jar.xml file specifying / declaring my interceptor (src/main/resources/META-INF).
in projects using this interceptor, I specified the type as ejb to it would pick it up correctly.
Walter

How to get Container Managed Transactions (CMT) working with EJB 3.1, Hibernate 3.6, JPA 2.0 , JBoss and MySQL

I was trying to get CMT working with JPA EntityManagers and EJBs, but came up with the error below. (stack trance truncated):
Caused by: java.lang.RuntimeException: **Could not resolve #EJB reference: [EJB Reference: beanInterface 'com.mydomain.beans.TestBean2', beanName 'testBean2', mappedName 'null', lookupName 'null',** owning unit 'AbstractVFSDeploymentContext#2008455195{vfs:///Users/willtardy/Documents/workspace/.metadata/.plugins/org.jboss.ide.eclipse.as.core/JBoss_6.0_Runtime_Server1300532851414/deploy/mydomainWeb.war}']
for environment entry: env/com.mydomain.action.SearchAction/testBean in unit AbstractVFSDeploymentContext#2008455195{vfs:///Users/willtardy/Documents/workspace/.metadata/.plugins/org.jboss.ide.eclipse.as.core/JBoss_6.0_Runtime_Server1300532851414/deploy/mydomainWeb.war}
My classes:
Servlet that access the Session Bean:
public class SearchActionExample extends Action {
#EJB
private static TestBeanServiceInterface testBean;
public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
testBean.addSource("TEST SOURCE NAME", 88, 99);
Service service = testBean.findService("HBA", "MEL");
return mapping.findForward("success");
}
}
Remote interface:
#Remote
public interface TestBeanServiceInterface {
// Source is my own custom entity
void addSource(String sourceName, int newthreadsleeptime, int maxactivehttpclients);
// Service is my own Custom entity
Service findService(String departureAirportCode, String arrivalAirportCode);
}
Stateless Session Bean definition:
#Stateless
public class TestBeanService implements TestBeanServiceInterface {
#PersistenceContext(unitName="mydomainJPA")
private EntityManager em;
public void addSource(String sourceName, int newthreadsleeptime, int maxactivehttpclients) {
Source source = new Source();
source.setName(sourceName);
source.setNewThreadSleepTime(newthreadsleeptime);
source.setMaxActiveHttpClients(maxactivehttpclients);
em.persist(source);
}
public Service findService(String departureAirportCode, String arrivalAirportCode) {
String queryString = "from Service where departureairportcode = '" + departureAirportCode + "' and arrivalairportcode = '" + arrivalAirportCode + "'";
Service service = (Service)em.createQuery(queryString).getSingleResult();
return service;
}
}
file persistnce.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" version="2.0">
<persistence-unit name="mydomainJPA" transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:/MySqlDS</jta-data-source>
<class>com.mydomain.entities.Service</class>
<class>com.mydomain.entities.Source</class>
<properties>
<property name="hibernate.query.factory_class" value="org.hibernate.hql.classic.ClassicQueryTranslatorFactory"/>
<property name="hibernate.archive.autodetection" value="class"/>
<property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.JBossTransactionManagerLookup"/>
<property name="hibernate.current_session_context_class" value="jta"/>
</properties>
</persistence-unit>
When it says "cannot resolve reference", where else can I define the beans? ejb-jar.xml isn't needed with EJB3. Is there some other config file that I'm missing?
UPDATE:
I have updated the code segments above so that the bean is created as the interface type instead, as per the answer below.
Do the EJBs need to be defined or mapped in web.xml?
Assuming that a reference is required in web.xml, I have added an EJB ref to web.xml (see below), but now I'm receiving a new error (see below)
lines added to web.xml:
<ejb-ref>
<ejb-ref-name>ejb/TestBeanEJBname</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>
<home>com.mydomain.action.TestBeanService</home>
<remote>com.mydomain.action.TestBeanServiceInterface</remote>
</ejb-ref>
new error message now being received:
12:11:00,980 ERROR [org.jboss.kernel.plugins.dependency.AbstractKernelController] Error installing to PostClassLoader: name=vfs:///Users/willtardy/Documents/workspace/.metadata/.plugins/org.jboss.ide.eclipse.as.core/JBoss_6.0_Runtime_Server1300532851414/deploy/purejetWeb.war state=ClassLoader mode=Manual requiredState=PostClassLoader: org.jboss.deployers.spi.DeploymentException: java.lang.IllegalStateException: Failed to find ContainerDependencyMetaData for interface: au.com.purejet.action.TestBeanServiceInterface
Caused by: java.lang.IllegalStateException: Failed to find ContainerDependencyMetaData for interface: com.mydomain.action.TestBeanServiceInterface
at org.jboss.deployment.MappedReferenceMetaDataResolverDeployer.resolveEjbInterface(MappedReferenceMetaDataResolverDeployer.java:1255) [:6.0.0.Final]
at org.jboss.deployment.MappedReferenceMetaDataResolverDeployer.resolveEjbRefs(MappedReferenceMetaDataResolverDeployer.java:1099) [:6.0.0.Final]
at org.jboss.deployment.MappedReferenceMetaDataResolverDeployer.resolve(MappedReferenceMetaDataResolverDeployer.java:807) [:6.0.0.Final]
at org.jboss.deployment.MappedReferenceMetaDataResolverDeployer.internalDeploy(MappedReferenceMetaDataResolverDeployer.java:181) [:6.0.0.Final]
... 39 more
Update:
"Local" interface works just fine (i.e. doesn't have to be Remote)
I got it to work by deploying within an Enterprise Application Project within Eclipse. No references to beans are required within web.xml, ejb-jar.xml, or application.xml.
Contents of application.xml within EAR being deployed to Jboss:
<?xml version="1.0" encoding="UTF-8"?>
<application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:application="http://java.sun.com/xml/ns/javaee/application_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_6.xsd" id="Application_ID" version="6">
<display-name>myprojects</display-name>
<module>
<web>
<web-uri>myproject.war</web-uri>
<context-root>myproject</context-root>
</web>
</module>
<module>
<ejb>myprojectsEJB.jar</ejb>
</module>
</application>
SessionBean class:
#Stateless
#Local(SessionBeanLocal.class)
public class SessionBean implements SessionBeanLocal {
#PersistenceContext(unitName="JPAtestProjectPersistenceUnit")
private EntityManager em;
Interface class:
#Local
public interface SessionBeanLocal {
TestTiger addTestTiger(String testTigerName);
MOST IMPORTANT change that got things working: inside the class that holds the session been local variable, a setting was required for the container (JBoss AS) to create the bean:
#EJB()
private TestBean3Local beanVariable;
public void setBeanVariable(TestBean3Local beanVariable) {
System.out.println("=====\n\nSET BEAN VARIABE SETTER WAS CALLED. (BY CONTAINER?) \n\n=======");
this.beanVariable = beanVariable;
}
You need to inject the remote interface and not the Bean
public class SearchActionExample extends Action {
#EJB
private static TestBean2Remote testBean;
public class SearchActionExample extends Action {
#EJB
private static TestBeanServiceInterface testBean;
Don't do injections into static field, injections are instance members and happen when object is created, whereas static field is a class member. This is most probably the cause for exception.
I have obtained a working solution:
#Local interface works just fine (i.e. doesn't have to be Remote)
No references to beans are required within web.xml, ejb-jar.xml, application.xml, or any jboss config file.
I got it to work by deploying within an "Enterprise Application Project" (EAP) within Eclipse. This project contains "Deployment Assembly" that contains the .jar containing JPA Entity Classes, and another .jar that contains other business-logic classes. The EAP has those two projects PLUS the EJB project and the "Dynamic Web Project" (creates a .war) for a total of 4 projects on it's build path. Jboss AS tool within Eclipse publishes/deploys the EAP to the Jboss server. Contents of application.xml within EAP being deployed to Jboss:
<?xml version="1.0" encoding="UTF-8"?>
<application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:application="http://java.sun.com/xml/ns/javaee/application_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_6.xsd" id="Application_ID" version="6">
<display-name>myprojects</display-name>
<module>
<web>
<web-uri>myproject.war</web-uri>
<context-root>myproject</context-root>
</web>
</module>
<module>
<ejb>myprojectsEJB.jar</ejb>
</module>
</application>
Local Interface class:
package com.myproject.beans;
import javax.ejb.Local;
import com.myproject.entities.Lion;
#Local
public interface SessionBeanLocal {
Lion addLion(String lionName);
}
SessionBean class:
package com.myproject.beans;
import javax.ejb.Local;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import com.myproject.Lion;
#Stateless
#Local(SessionBeanLocal.class)
public class SessionBean implements SessionBeanLocal {
#PersistenceContext(unitName="PersistenceUnitNameInPersistenceXML")
private EntityManager em;
public Lion addLion(String lionName) {
Lion lion = new Lion(lionName);
em.persist(lion);
}
MOST IMPORTANT change that got things working: inside the class that holds the session been variable (e.g. inside a Struts action servlet, but could be any servlet), a setter method was required for the container (JBoss AS) to create the bean:
#EJB()
private SessionBeanLocal bean;
public void setBean(SessionBeanLocal bean) {
System.out.println("setBean setter was called by container (e.g. Jboss)");
this.bean = bean;
}
public exampleStrutsServletMethod(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
PrintWriter out = response.getWriter();
Lion lion = bean.addLion("Simba"); // this will persist the Lion within the persistence-context (and auto-generate an Id), and the container will manage when it's flushed to the database
out.print("<html>LION ID = " + lion.getLionId() + "<html>");
}
file persistnce.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" version="2.0">
<persistence-unit name="PersistenceUnitNameInPersistenceXML" transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:/MySqlDS</jta-data-source>
<properties>
</properties>
</persistence-unit>
mysql-dx.xml (in directory jboss-server-dir/server/default/deploy):
<?xml version="1.0" encoding="UTF-8"?>
<datasources>
<local-tx-datasource>
<jndi-name>MySqlDS</jndi-name>
<connection-url>jdbc:mysql://localhost:3306/myProjectDatabase</connection-url>
<driver-class>com.mysql.jdbc.Driver</driver-class>
<user-name>username</user-name>
<password>mypassword</password>
<exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter</exception-sorter-class-name>
<metadata>
<type-mapping>mySQL</type-mapping>
</metadata>
</local-tx-datasource>
</datasources>
NOTE: Classes do not need to be defined in persistence.xml (via "< class >") if "Persistence Class Management" is set to "Discover annotated classes automatically" in "Java Persistence" project property panel for the Eclipse JPA project (i.e. the project that containers your JPA 2.0 Entity classes and persistence.xml)
NOTE: This solution is based on: EJB3.1, Eclipse Helios SR2, Hibernate 3.6, JPA 2.0, JBoss 6, MySQL 5.5.10
NOTE: Regarding "Container Managed Transactions" (CMT). The Hibernate manual references them, and indicates that you need to set persistence.xml properties such as "hibernate.transaction.factory_class" to value of: "org.hibernate.transaction.CMTTransactionFactory". This is not the case if you are using JPA EntityManager instead of native hibernate. I didn't required any such custom CMT properties in persistence.xml. This is where Hibernate gets confusing, between the two ways to implement it (i.e. SessionFactory vs EntityManager). Please feel free to comment more on this part of my solution as I'm still just wrapping my head around it! Will