I have a java web application that is deployed in two EARs - one for the UI tier (contains a WAR module) and one for the business tier (contains an EJB module). Both tiers are deployed to WebSphere Application Server 7. The tiers are connected via EJB 3.0 stateless session beans. The beans are looked up via JNDI.
The two applications are currently on the same WAS server, but in the future will be deployed onto different servers. When I attempt to lookup an EJB, I get an error.
Attempting to debug the lookup error, I ran a JNDI namespace dump on the WAS server:
./dumpNameSpace.bat -host localhost -port 10031 > namespace.log
Each of my EJBs was listed with the following error:
(top)/nodes/U4752879Node01/servers/server1/ejb/au/gov/immi/emed/ejb/CrtEJB
au.gov.immi.emed.ejb.CrtEJB
ERROR: Received the following naming exception: com.ibm.ws.naming.util.InvalidObjectException: The IOR associated with the binding "CrtEJB" relative to the context "U4752879Node01Cell/nodes/U4752879Node01/servers/server1/ejb/au/gov/immi/emed/ejb" could not be resolved. If the binding is for an application object, make sure the application has started successfully. IOR: IOR:<removed>
Here is the EJB remote interface:
#Remote
public interface CrtEJB extends EMedEJB, EMedCrtService
{
}
And the bean implementation:
#Stateless(name = "CrtEJB")
#Remote(value = CrtEJB.class)
public class CrtEJBBean extends AbstractEMedEJBBean implements CrtEJB
{
...
}
And the ibm-ejb-jar-bnd.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar-bnd xmlns="http://websphere.ibm.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://websphere.ibm.com/xml/ns/javaee http://websphere.ibm.com/xml/ns/javaee/ibm-ejb-jar-bnd_1_0.xsd">
<session name="CrtEJB" simple-binding-name="ejb/au/gov/immi/emed/ejb/CrtEJB" />
...
</ejb-jar-bnd>
What is causing the error to occur? The error is the same without the ibm-ejb-jar-bnd.xml file, which I shound't need. Is this error what's causing the UI lookup to fail, or should that be working anyway? (in which case I'll post the UI error).
Related
I was wondering if EJB specifications allow to access a stateful session bean over a looked up stateful session bean.
The reason why I'm asking is, that Jboss EAP 7.0 has no problems with it, but Websphere throws a NullPointerException when I try to access the bean.
For example:
#Stateful
public class SampleServiceRoot implements SampleServiceRootRemote {
#EJB
protected SampleServiceChildLocal servicechild;
#Override
public SampleServiceChildLocal getServiceChild(){
return servicechild;
}
}
#Stateful
public class SampleServiceChild implements SampleServiceChildLocal,SampleServiceChildRemote{
#Override
public void anyMethod(){
//DO Anything
}
}
When I do a remote lookup to the SampleServiceRootRemote and call "getServiceChild()" and try to call "anyMethod()" on it, it works on JBoss EAP 7.0 but on Websphere I get a NullPointerException.
So I was wondering if this is a Bug in Websphere or is it forbidden by EJB Specification and I was just lucky with JBoss EAP 7.0?
The EJB specification does require this scenario to work, depending on some configuration options; there are configuration options that may disable it.
The fact that you are seeing a NullPointerException indicates that WebSphere is not aware of the #EJB annotation on the field in the SampleServiceRoot class. Per the EJB specification, an instance of SampleServiceRoot cannot be created if the #EJB annotation cannot be resolved. Since an instance of SampleServiceRoot has been created, then one of the following has likely occurred:
1 - The application performed a new SampleServiceRoot rather than looking it up in JNDI. This doesn't sound like your problem, but good to double check.
2 - The application contains an ejb-jar.xml with the setting metadata-complete="true". When this is set, WebSphere will not look for annotations, and so will not see or process the #EJB annotation. Either change the setting to "false" or add the <ejb-ref> or <ejb-local-ref> to the ejb-jar.xml file.
3 - The application does not have metadata-complete="true", however when the application is deployed to WebSphere the option to set metadata-complete was selected.
This option will change the metadata-complete setting to "true". Stop using this option, or add the <ejb-ref> or <ejb-local-ref> to the ejb-jar.xml file.
4 - The EJB is contained in a WAR module at level 2.4 or older. In WebSphere, annotations for older modules are not processed.
5 - The application includes a copy of the javax.ejb.EJB class. WebSphere provides the javax.ejb.EJB class, and it is loaded by the WebSphere runtime classloader. If the application also contains the javax.ejb.EJB class on the application classpath, then another instance will be loaded by the application classloader, and it will not match the instance used by the EJB Container. There should be a warning in the logs if this has occurred.
So yes, your scenario is required to be supported; however the specification does allow configurations that disable it. You just need to identify which configuration / packaging option has caused WebSphere to not see the #EJB annotation.
Thanks for the answer,
we tried to move the declarations of the Local-Interface to the Remote Interface of the SampleServiceChild. Also we did not use #EJB annotation. We managed it with doing a lookup of the SampleServiceChild over the InitialContext.
Now it works
I have an EJB which is exposed by both local and remote interfaces
package com.sam.enqueue;
import javax.ejb.Local;
import javax.ejb.Remote;
import javax.ejb.Singleton;
#Singleton
#Local(SamEnqueueLocal.class)
#Remote(SamEnqueueRemote.class)
public class SamEnqueue implements SamEnqueueRemote, SamEnqueueLocal {
}
// remote interface
package com.sam.enqueue;
import javax.ejb.Remote;
#Remote
public interface SamEnqueueRemote {
}
// local interface
package com.sam.enqueue;
#Local
public interface SamEnqueueLocal {
}
My app container is websphere 8.0 and I am not overriding the default JNDI names which the server assigns.
During server startup I get the following default bindings in logs:
CNTR0167I: The server is binding the com.sam.enqueue.SamEnqueueRemote interface of the SamEnqueue enterprise bean in the SAM_ENQUEUE.jar module of the SAM_ENQUEUE application. The binding location is: ejb/SAM_ENQUEUE/SAM_ENQUEUE.jar/SamEnqueue#com.sam.enqueue.SamEnqueueRemote
CNTR0167I: The server is binding the com.sam.enqueue.SamEnqueueRemote interface of the SamEnqueue enterprise bean in the SAM_ENQUEUE.jar module of the SAM_ENQUEUE application. The binding location is: com.sam.enqueue.SamEnqueueRemote
CNTR0167I: The server is binding the com.sam.enqueue.SamEnqueueRemote interface of the SamEnqueue enterprise bean in the SAM_ENQUEUE.jar module of the SAM_ENQUEUE application. The binding location is: java:global/SAM_ENQUEUE/SamEnqueue!com.sam.enqueue.SamEnqueueRemote
CNTR0167I: The server is binding the com.sam.enqueue.SamEnqueueLocal interface of the SamEnqueue enterprise bean in the SAM_ENQUEUE.jar module of the SAM_ENQUEUE application. The binding location is: ejblocal:SAM_ENQUEUE/SAM_ENQUEUE.jar/SamEnqueue#com.sam.enqueue.SamEnqueueLocal
CNTR0167I: The server is binding the com.sam.enqueue.SamEnqueueLocal interface of the SamEnqueue enterprise bean in the SAM_ENQUEUE.jar module of the SAM_ENQUEUE application. The binding location is: ejblocal:com.sam.enqueue.SamEnqueueLocal
CNTR0167I: The server is binding the com.sam.enqueue.SamEnqueueLocal interface of the SamEnqueue enterprise bean in the SAM_ENQUEUE.jar module of the SAM_ENQUEUE application. The binding location is: java:global/SAM_ENQUEUE/SamEnqueue!com.sam.enqueue.SamEnqueueLocal
The lookup class is just a simple java class in a different EAR in the same server with the following code:
Context ctx = new InitialContext();
Object local = ctx.lookup("java:global/SAM_ENQUEUE/SamEnqueue!com.sam.enqueue.SamEnqueueLocal");
SamEnqueueLocal samEnqueue = (SamEnqueueLocal) local;
The lookup is working with any of the three JNDI names for the local but it's not getting cast to SamEnqueueLocal. The exception trace is:
SystemErr R java.lang.ClassCastException: com.sam.enqueue.EJSLocal0SGSamEnqueue_cf56ba6f incompatible with com.sam.enqueue.SamEnqueueLocal
... rest ommited
I've made a shared library and put the stub of destination EAR in it. The library is the classpath of the source lookup EAR with the Classes loaded with local class loader first (parent last) policy. The library is not isolated. If I remove the stub, I get a java.lang.ClassNotFoundException: com.sam.enqueue.SamEnqueueLocal as expected.
Update:
While using Dependency Injection:
#EJB(lookup="ejblocal:com.sam.enqueue.SamEnqueueLocal")
private SamEnqueueLocal samEnqueueLocal;
The error I get is:
javax.ejb.EJBException: Injection failure; nested exception is: java.lang.IllegalArgumentException: Can not set com.sam.enqueue.SamEnqueueLocal field com.some.SomeBean.samEnqueueLocal to com.sam.enqueue.EJSLocal0SGSamEnqueue_cf56ba6f
Caused by: java.lang.IllegalArgumentException: Can not set com.sam.enqueue.SamEnqueueLocal field com.some.SomeBean.samEnqueueLocal to com.sam.enqueue.EJSLocal0SGSamEnqueue_cf56ba6f
So it's basically the same.
You are getting java.lang.ClassCastException because you are retrieving a reference to an EJB that exists in a different class loader than the deployment unit (ejb-jar, war, etc) that is trying to inject it.
Using local EJB references between applications is vendor dependent if possible at all. You may be able to deploy your SamEnqueue bean in a separate EJB module and try to reference it via a manifest Class-Path: entry from each application. Be sure that there are no copies of SamEnqueueLocal in either EAR file.
Alternatively, just use the SamEnqueueRemote interface.
Refer to chapter 8 of the Java EE specification for more information.
See the "Local client views" section of the EJB modules topic in the knowledge center:
The EJB specification only requires local client views to be supported
for EJBs packaged within the same application. This includes local
homes, local business interfaces, and the no-interface view.
WebSphere® Application Server permits access to local client views to
EJBs packaged within a separate application with some restrictions
The local interface and all parameter, return, and exception types used by the local interface must be visible to the class loader of both the calling application and the target EJB application. You can ensure this by either using a shared library associated with a server class loader or by using an isolated shared library associated with both applications. Read the Creating shared libraries topic for more information.
...
From the link provided by in bkail's answer, These are the steps that I followed to make it work.
Take out the Local and Remote interfaces i.e. SamEnqueueRemote and SamEnqueueLocal from my source EJB jar and package then into a separate jar file. Although just taking out the Local interface will also work.
Make a shared library and put this jar into it. The shared library must be isolated so that same version of class is loaded by caller and callee.
In the caller EAR, get a reference to the local interface with either a lookup or injection.
Deploy both the caller and callee to the server and make sure to add the shared library in the classpath of both the EARs.
One of the approaches mentioned in this link is similar.
One way to prevent this is to use a remote interface. As Paul mentioned, there is optimization that happens when the caller and callee are in the same JVM, so it's not as expensive as if they're in separate JVMs. The ORB has classloader machinery that ensures the classes for the caller and callee are loaded using classloaders compatible with each side of the call.
Option 2, including the ejb jar within the new ear, won't solve the problem. Even though the classes will be available in both classloaders, the object passed by-reference from callee back to caller will still be instanciated using the other application's classloader and will not be type-assignable. Same with Option 3.
The 2nd way to make it work is to place the classes used by caller and callee in a "WAS shared library" and configure both applications to use that shared library. The subject of shared libraries and how to configure them is described in the WAS InfoCenter documentation...search on "shared library."
The 3rd way to make it work, which is the least desirable of the three, is to change the WAS server classloader policy to "one classloader per server." The default, as I mentioned at the top, is "one classloader per application (EAR file)." Changing to one classloader per server ensures that everything gets loaded by the same classloader and is thus type-compatible, but deprives your applications of the isolation/security benefits that come from having each app in its own classloader.
I have problem in configuring JNDI lookup for ejb3.1 and weblogic 12c, jdk1.6
#Remote
public interface Bank{
public String accounts();
}
#Stateless(name="BankSession")
public class BankSessionBean implements Bank{
#Override
public String accounts() {
//////// }
ejb-jar.xml:
<enterprise-beans>
<session>
<display-name>BankSession</display-name>
<ejb-name>BankSession</ejb-name>
<business-remote>com.examples.Bank</business-remote>
<ejb-class>com.examples.BankSessionBean</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Bean</transaction-type>
</session>
<enterprise-beans>
weblogic-ejb-jar.xml:
<weblogic-enterprise-bean>
<ejb-name>BankSession</ejb-name>
<jndi-name>BankSession</jndi-name>
</weblogic-enterprise-bean>
client code:
When i start using Jndi Look up using this syntax
java:comp/env/ejb/BankSession
Its giving following error.
javax.naming.NameNotFoundException: While trying to lookup 'java:comp/env/ejb/BankSession' didn't find subcontext.
Here ejbbean and client code runs on different jvm's.
Which Jndi look up should i use ?
java:comp/env/ejb/BankSession'
java:global/applicationName/moduleName/BankSession
java:module/BankSession
java:app/moduleName/BankSession
java:comp/env/ejb/BankSession is working fine with ejb 3.0 and oc4j server.
When i migrate to ejb3.1 and weblogic 12c, its not working.
I even tried without using weblogic deployment xml files. Same issue encountered.
How should i configure my Jndi here ? Please help as i am facing this issue since long time.
For a lookup in the form of java:comp/env/... you need an EJB ref in the deployment descriptor of the component that does the lookup!
java:comp/env/... is always relative to the component that does the lookup.
A lookup name that is independent of the actual component that does the lookup and works from anywhere inside your application is 4.: java:app/moduleName/...
java:global/appName/... will fail if you decide to rename your ear file one day.
java:module/... only works inside a component that is part of the same module.
So 4. is IMO the best alternative.
Since:
the #EJB doesn't work on desktop apps, because there's no container,
Weblogic doesn't expose through JNDI the names of the EJB's (3.0),
I didn't find anything about it in the official documentation for a few days now,
does anyone know how I could consume an EJB from a desktop app?
I got hints that i have to modify web.xml... but then what web.xml, there's more of them and at least some of them break my app.
Clarification:
I have a WebLogic server running, NOT exposing some EJB's inside an EAR hosted on that server. Specifically, the server is not exposing the names of EJB3.0 Session beans, while it DOES expose the names of some other EJB2.1 names.
How do i get a reference to the EJB3.0 objects, hosted on my server, from my desktop app?
FURTHER Clarification:
Weblogic Specifically does not expose the names of EJB3.0 within the JNDI, because the specifications of EJB3.0 don't require it.
To answer my own question (got it after some effort), to expose an EJB3.0 object through a remote interface, in the Weblogic 11g server, these steps must be taken:
This is what my ejb-jar.xml says about the bean RolyBean:
<enterprise-beans>
<session id="Session_Roly">
<display-name>Roly</display-name>
<ejb-name>Roly</ejb-name>
<remote>com.medicon.server.RolyRemote</remote>
<ejb-class>com.medicon.server.RolyBean</ejb-class>
<session-type>Stateless</session-type>
</session>
</enterprise-beans>
This is what the weblogic-ejb-jar.xml (in the same dir) says:
<wls:weblogic-enterprise-bean>
<wls:ejb-name>Roly</wls:ejb-name>
<wls:jndi-name>Roly</wls:jndi-name>
</wls:weblogic-enterprise-bean>
These 2 deployment descriptors seem to be the only 2 places where it's required to specify exposure of the EJB through JNDI.
I'm writing my first Java EE (EJB + Servlets etc.) application (please note: I'm using Eclipse).
I'm stuck with a problem with EntityManager injection not working, and having some difficulties finding why due to my Java EE (and Java in general) noobness.
Here is my persistence.xml file - I think this is mostly correct, since I can launch the HSQL database manager from the JMX console and my PUBLIC.USER table shows up correctly.
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" 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_1_0.xsd">
<persistence-unit name="MyPu">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:/DefaultDS</jta-data-source>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/>
<property name="hibernate.hbm2ddl.auto" value="create-drop"/>
</properties>
</persistence-unit>
</persistence>
Here's my servlet code:
[...]
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws
String id = request.getParameter("username");
String password = request.getParameter("password");
UserManagerBean um = new UserManagerBean();
um.register(username, password);
RequestDispatcher dispatcher=getServletContext().getRequestDispatcher("/index.jsp");
dispatcher.forward(request, response);
}
And here's my UserManagerBean Class:
//bunch of imports
import myPackage.UserManager;
public #Stateful class UserManagerBean implements UserManager {
#PersistenceContext(unitName="MyPu")
private EntityManager persistManager;
public void register(String username, String password) {
User user = new User(userame, password);
persistManager.persist(user);
persistManager.flush();
}
}
The persistManager.persist(user) line throws a NullPointerException.
From my own searching I understood that this is happening because, since I'm calling new() on UserManagerBean the injection from the #PersistenceContext annotation never takes place and persistManager never gets bound.
If so, it is evident that I'm missing something about the proper usage of EJBs.
What is the right way to instantiate my bean? What's with the interfaces? I'm not entirely sure if my bean should be stateful or stateless :\
Additional info:
I changed code in my servlet, from
UserManagerBean um = new UserManagerBean();
to
#EJB
private UserManagerBean um;
in the appropriate place. Now um is the NullPointer.
Quoting Referencing EJB3 Session Beans from non-EJB3 Beans from the JBoss documentation:
JBoss Application Server 4.2.2 implemented EJB3 functionality by way of an EJB MBean container running as a plugin in the JBoss Application Server. This had certain implications for application development.
The EJB3 plugin injects references to an EntityManager and #EJB references from one EJB object to another. However this support is limited to the EJB3 MBean and the JAR files it manages. Any JAR files which are loaded from a WAR (such as Servlets, JSF backing beans, and so forth) do not undergo this processing. The Java 5 Enterprise Edition standard specifies that a Servlet can reference a Session Bean through an #EJB annotated reference, this was not implemented in JBoss Application Server 4.2.2.
So even though the Java EE 5 specification stipulates a wider scope of the #EJB annotation, JBoss 4.2.2 doesn't support it. In fact JBoss 4.2.2 is a transitional version that doesn't claim full Java EE 5 compliance.
Consequently, either:
Stick with you actual version of JBoss but use a JDNI lookup to get your bean - or -
Swith to JBoss 5 AS that fully supports the entire Java 5 Enterprise Edition specification (but has horrible startup performances) - or -
Swith to another Java EE 5 application server like GlassFish v2 (or even v3) or WebLogic 10. I'd go with GFv3.
As Petar Minchev said in the comments above #EJB doesn't work in servlets with JBoss 4.2.
In your WAR (in the WEB-INF directory) you'll need to modify two files:
web.xml:
<ejb-local-ref>
<ejb-ref-name>ejb/UserManager</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>
<local-home/>
<local>myPackage.UserManager</local>
</ejb-local-ref>
jboss-web.xml:
<?xml version='1.0' encoding='ISO-8859-1'?>
<!DOCTYPE jboss-web PUBLIC "-//JBoss//DTD Web Application 2.3//EN" "http://www.jboss.org/j2ee/dtd/jboss-web_3_2.dtd">
<jboss-web>
<ejb-local-ref>
<ejb-ref-name>ejb/UserManager</ejb-ref-name>
<local-jndi-name>[earname]/UserManagerBean/local</local-jndi-name>
</ejb-local-ref>
</jboss-web>
You can then get an instance of your EJB by doing:
UserManagerBean um = (UserManager)new InitialContext().lookup( "java:comp/env/ejb/UserManager" );
As per the comments, it turns out that you indeed are using an appserver which doesn't support it at all -as I expected. To resolve this, you have 3 options:
Use "good old" XML config files.
Upgrade the server (there's already a stable 5.1.0).
Replace the server (I can recommend Glassfish v3 to be the most up-to-date).
To enable dependency injection on your project make sure that you have the file "beans.xml" and it is properly located.
For WAR packaging the location is src/main/webapp/WEB-INF/
For JAR and EAR packaging the location is src/main/resources/META-INF/
If you have several maven modules you must have it on each module.
More information about beans.xml