Getting EJB module name dynamically for JNDI on EAP 7 - jboss

How can we get the EJB module name dynamically for JNDI on EAP 7
we have looked into the post on stack overflow here to get the module name
We don't want to override in ejb-jar.xml.
We don't want to customise the module name in maven so it is
fixed.
Nor we looking are looping at looping as we are currently
implemented
URL url = clazz.getResource("");
String [] strArray = url.getPath().split("/");
for (int i=0; i<strArray.length; i++){
if (strArray[i].contains(".jar")){
moduleName = strArray[i].substring(0, strArray[i].length() - 4);
break;
}
}
jndiName = "java:app/"+moduleName+"/"+clazz.getSimpleName()+"!"+clazz.getCanonicalName();
Is there a cleaner way
java:app/<module-name>/<bean-name>!<fully-qualified-bean-interface-name>

Overriding EJB module name by using
ejb-jar.xml
<?xml version="1.1" encoding="UTF-8"?>
<ejb-jar 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/ejb-jar_3_1.xsd"
version="3.1">
<module-name>my-module</module-name>
</ejb-jar>
For application name overriding use application name in application.xml
The above solution helps in making application versioning information separate from its dependency for example while creating JNDI name to look up remote EJB in EAP 7.

Related

Error Injecting the JPA Entity Manager in WebSphere Liberty

I have inherited a legacy application that initially was built with WebSphere 6.1 and then was migrated to WebSphere 8.0 running with JPA 2.0 and openJPA without issues. We are migrating to WebSphere Liberty for strategic reasons. We first tested on WebSphere Classic 8.5.5.8 and JPA and the entity manger has no issues there. However, on Liberty 8.5.5.8 I get the following exception:
javax.ejb.EJBException: The java:comp/env/com.xxx.xxxx.service.CHServiceBean/em reference of type javax.persistence.EntityManager for the CHServiceBean component in the CHServiceEJB.jar module of the CHServiceEAR application cannot be resolved.
at com.ibm.wsspi.injectionengine.InjectionBinding.getInjectionObject(InjectionBinding.java:1493)
.....
[err] Caused by:
[err] javax.ejb.EJBException: The java:comp/env/com.xxxx.xxxx.service.CHServiceBean/em reference of type javax.persistence.EntityManager for the CHServiceBean component in the CHServiceEJB.jar module of the CHServiceEAR application cannot be resolved.
[err] at com.ibm.wsspi.injectionengine.InjectionBinding.getInjectionObject(InjectionBinding.java:1493)
[err] at [internal classes]
I had another EJB injection issue that was resolved through configuration of the binding files, however I am unable to resolve this issue. I have two applications that each have their own EAR files but both run in the same Liberty JVM. Application A runs the front end/UI logic while Application B is the back-end EJB / JPA interfaces. In the project facets the JPA application is set to 2.0 (I wanted 2.1 but based on another thread JPA 2.0 and EJB 3.1 are as high as I can go at the moment...See my other thread topic here -->Eclipse Juno and JPA 2.1 support).
Here is my server.xml file:
<server description="new server">
<!-- Enable features -->
<featureManager>
<feature>javaee-7.0</feature>
<feature>localConnector-1.0</feature>
<feature>distributedMap-1.0</feature>
<feature>adminCenter-1.0</feature>
<feature>ssl-1.0</feature>
<feature>usr:webCacheMonitor-1.0</feature>
<feature>webCache-1.0</feature>
<feature>ldapRegistry-3.0</feature>
</featureManager>
<!-- Admin Center Config Start -->
<!-- To access this server from a remote client add a host attribute to the following element, e.g. host="*" -->
<httpEndpoint host="*" httpPort="9080" httpsPort="9443" id="defaultHttpEndpoint"/>
<keyStore id="defaultKeyStore" password="xxxxxx"/>
<basicRegistry id="basic">
<user name="admin" password="xxxxx"/>
<user name="nonadmin" password="xxxxxx"/>
</basicRegistry>
<administrator-role>
<user>admin</user>
</administrator-role>
<remoteFileAccess>
<writeDir>${server.config.dir}</writeDir>
</remoteFileAccess>
<!-- Automatically expand WAR files and EAR files -->
<applicationManager autoExpand="true"/>
<applicationMonitor updateTrigger="mbean"/>
<enterpriseApplication id="CHNewCHRDMEAR" location="CHNewCHRDMEAR.ear" name="CHNewCHRDMEAR">
<application-bnd>
<security-role name="AllAuthenticated">
<special-subject type="ALL_AUTHENTICATED_USERS"/>
</security-role>
</application-bnd>
</enterpriseApplication>
<enterpriseApplication id="CHServiceEAR" location="CHServiceEAR.ear" name="CHServiceEAR"/>
<!-- JAAS Authentication Alias (Global) Config -->
<authData id="dbUser" password="{xor}MzhmJT06ajI=" user="dbUser"/>
<!-- JDBC Driver and Datasource Config -->
<library id="DB2JCC4Lib">
<fileset dir="C:\DB2\Jars" includes="db2jcc4.jar db2jcc_license_cisuz.jar"/>
</library>
<dataSource containerAuthDataRef="dbUser" id="CHTEST2" jndiName="jdbc/nextgen" type="javax.sql.XADataSource">
<jdbcDriver libraryRef="DB2JCC4Lib"/>
<properties.db2.jcc databaseName="CHTEST2" password="{xor}MzhmJT06ajI=" portNumber="60112" serverName="server.com" sslConnection="false" user="dbUser"/>
<containerAuthData password="{xor}MzhmJT06ajI=" user="dbUser"/>
</dataSource>
<dataSource id="CHTEST2_RO" jndiName="jdbc/nextgen_RO" type="javax.sql.XADataSource">
<jdbcDriver libraryRef="DB2JCC4Lib"/>
<properties.db2.jcc databaseName="CHTEST2" password="{xor}MzhmJT06ajI=" portNumber="60112" serverName="server.com" sslConnection="false" user="dbUser"/>
<containerAuthData password="{xor}MzhmJT06ajI=" user="dbUser"/>
</dataSource>
<!-- More in file, but no included...-->
</server>
Here is my persistence.xml file:
<?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="CHService" transaction-type="JTA">
<jta-data-source>jdbc/nextgen</jta-data-source>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="openjpa.jdbc.TransactionIsolation" value="read-uncommitted" /></properties></persistence-unit>
<persistence-unit name="CHServiceRO" transaction-type="JTA">
<jta-data-source>jdbc/nextgen_RO</jta-data-source>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="openjpa.jdbc.TransactionIsolation" value="read-uncommitted" />
</properties>
</persistence-unit>
</persistence>
I *believe that we are relying solely on injection to get the context for JPA jndi lookups but that is because I don't see in our code any call to an initial context for any JPA specific JNDI names. Below are my two anotated session beans from the EJB project:
a. The CHService Bean:
#Stateless
#TransactionManagement(TransactionManagementType.CONTAINER)
#Local({ CHServiceLocal.class })
#Remote({ CHServiceRemote.class })
#Interceptors({ CHServiceLog.class })
#Resources({
#Resource(name = "jdbc/nextgen", mappedName = "jdbc/nextgen", authenticationType = AuthenticationType.APPLICATION, shareable = true, type = javax.sql.DataSource.class),
#Resource(name = "services/cache/CHBluepages", mappedName = "services/cache/CHBluepages", authenticationType = AuthenticationType.APPLICATION, shareable = true, type = com.ibm.websphere.cache.DistributedMap.class),
#Resource(name = "services/cache/CHGeneric", mappedName = "services/cache/CHGeneric", authenticationType = AuthenticationType.APPLICATION, shareable = true, type = com.ibm.websphere.cache.DistributedMap.class) })
public class CHServiceBean extends AbstractCHServiceImpl implements
CHService {
#PersistenceContext(unitName = "CHService")
private EntityManager em;
b. The CHServiceRO bean:
#Stateless
#TransactionManagement(TransactionManagementType.CONTAINER)
#Local({CHServiceLocalRO.class})
#Remote({CHServiceRemoteRO.class})
#Interceptors({CHServiceROLog.class})
#Resources({
#Resource(name="jdbc/nextgen_RO", mappedName="jdbc/nextgen_RO", authenticationType=AuthenticationType.APPLICATION, shareable=true, type=javax.sql.DataSource.class),
#Resource(name="jdbc/nextgen", mappedName="jdbc/nextgen", authenticationType=AuthenticationType.APPLICATION, shareable=true, type=javax.sql.DataSource.class),
#Resource(name="services/cache/CHBluepages", mappedName="services/cache/CHBluepages", authenticationType=AuthenticationType.APPLICATION, shareable=true, type=com.ibm.websphere.cache.DistributedMap.class),
#Resource(name="services/cache/CHGeneric", mappedName="services/cache/CHGeneric", authenticationType=AuthenticationType.APPLICATION, shareable=true, type=com.ibm.websphere.cache.DistributedMap.class)
})
public class CHServiceBeanRO implements CHServiceRO {
#PersistenceContext (unitName="CHServiceRO") private EntityManager em;
private CHServiceBase ch;
#PostConstruct
private void init() { ch = new CHServiceBase(em); }
Here is a snippet from the Web.xml of the front-end application calling the JPA application:
<resource-ref id="ResourceRef_1436377001246">
<res-ref-name>jdbc/nextgen</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Application</res-auth>
<res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>
<resource-ref id="ResourceRef_1436377001247">
<res-ref-name>jdbc/nextgen_RO</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Application</res-auth>
<res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>
Based on the post from Gas on this topic: java.lang.ClassCastException,Getting Entitymanager Via JNDI Lookup
I also tried updating the web.xml with the following entries:
<persistence-unit-ref>
<persistence-unit-ref-name>chJPA</persistence-unit-ref-name>
<persistence-unit-name>CHService</persistence-unit-name>
</persistence-unit-ref>
<persistence-unit-ref>
<persistence-unit-ref-name>chJPA_RO</persistence-unit-ref-name>
<persistence-unit-name>CHServiceRO</persistence-unit-name>
</persistence-unit-ref>
and the Bean code with:
#PersistenceContext(name = "chJPA", unitName = "CHService")
and
#PersistenceContext (name="chJPA_RO", unitName="CHServiceRO")
Got the same error just with a different jndi name, ie The java:comp/env/chJPA reference of type javax.persistence.EntityManager for the CHServiceBean com.......etc etc.
Lastly, per this post: Error while accessing EntityManager - openjpa - WAS liberty profile
It seems that maybe I can't have the full JavaEE 7 feature and run JPA 2.0? Please advise!
As in the post you are referring to - you cannot have <feature>javaee-7.0</feature> together with JPA 2.0, as it enables 2.1, thats why you have conflicts.
So you have 2 options:
either use Java EE7 and JPA 2.1
or just enable required Java EE 6 features and then use JPA 2.0
Since you are migrating from WAS 8.0, which doesn't support Java EE7 for now, easier choice might be to use the second option.
So try to remove javee-7.0 feature, and add ejbLite-3.1 and jpa-2.0 and whatever you need more.
You are correct that you cannot have javaee-7.0 and the JPA 2.0 feature, as it enables the JPA 2.1 feature. So the answer Gas gave is correct.
I just wanted to point out since you said that you do want to go to JPA 2.1 eventually, once you work out your eclipse issues, that you should use the WebSphere Application Server Migration Toolkit to identify application changes needed when migrating from JPA 2.0 to JPA 2.1. The Liberty JPA 2.0 implementation is built on OpenJPA, whereas the JPA 2.1 implemtation is built on EclipseLink. The migration toolkit is downloadable for free on wasdev: https://developer.ibm.com/wasdev/downloads/#asset/tools-WebSphere_Application_Server_Migration_Toolkit

How to specify the context for an EJB in JBOSS

I've got some legacy code that runs in JBoss which looks something like:
#Stateless
#Remote(MyClass.class)
#RemoteBinding(jndiBinding = "app/Service")
public class myServiceBean {
// Some methods
}
When I look up the Bean in JNDI I have to use a string that looks like:
ear-name/Service/remote
I'm OK with the last two parts of the name, but I have an issue with "ear-file" being used as the context name. Because I build with Maven by default ear-file will contain the version number (which I do want for traceability), however it means I would have to change all the references to the service each time I issue a new version of the service!
How do I force the the context to something other than the name of the ear-file?
You can set up your ear name in application.xml like this:
<application-name>ear-name</application-name>
So you can have ear-namexxx.ear but your lookups will be made to ear-name
You can do the same for ejb modules as well:
<ejb-jar>
<module-name>ejb-name</module-name>
</ejb-jar>
Hope it helps!
Source https://developer.jboss.org/thread/158207

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

How to order deployment of EJBs and JMS queue config in JBoss 5?

I'm using JBoss [EAP] 5.0.0.GA and I have an EAR which contains an EJB jar that contains some MDBs which depend on the existence of the JMS queues that they use. When I configured the queues in .../server/all/deploy/messaging/myqueues-service.xml there was no problem.
However, I wanted to configure the queues in the EAR file to avoid having to make changes to the JBoss config directly. No problem, I put my myqueues-service.xml file into the root of the EAR and added the reference to my jboss-app.xml as follows:
<jboss-app>
<module-order>strict</module-order>
<loader-repository>
seam.jboss.org:loader=my-ear.ear
</loader-repository>
<module>
<service>myqueues-service.xml</service>
</module>
</jboss-app>
However, when I do that, JBoss loads the EJB jar (contained in my-ear.ear) first and then configures the JMS Queues afterwards. This results in errors when the MDB are loaded:
12:16:02,714 WARN [JmsActivation] Failure in jms activation org.jboss.resource.adapter.jms.inflow.JmsActivationSpec#13a59e .....
javax.naming.NameNotFoundException: MyQueue not bound
It's not a huge problem, as later on the MDBs successfully reconnect to JMS:
12:16:12,698 INFO [JmsActivation] Attempting to reconnect org.jboss.resource.adapter.jms.inflow.JmsActivationSpec#f91ad5
12:16:12,823 INFO [JmsActivation] Reconnected with messaging provider.
But I'd really like to avoid having any errors, and in order to do that I need a way to force JBoss to configure the JMS queues first, before loading the EJB jar. Is there any way to do this? For reference, here's the application.xml for the EAR:
<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_5.xsd" version="5">
<display-name>my-ear</display-name>
<module>
<ejb>my-ejb.jar</ejb>
</module>
<module>
<web>
<web-uri>my.war</web-uri>
<context-root>myroot</context-root>
</web>
</module>
</application>
Any suggestions appreciated.
Ok, jaikiran pai on the community.jboss.org forums helped me out. The solution is to add the JMS Queue as a dependency on the MDB. In my case I used the #Depends annotation:
#MessageDriven(activationConfig = {
#ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
#ActivationConfigProperty(propertyName = "destination", propertyValue = "queue/MyQueue") })
#Depends("jboss.messaging.destination:service=Queue,name=MyQueue")
public class MyMessageListener implements MessageListener {
...
}
You could do the same using jboss.xml if you aren't using annotations.