Accessing ejb in ear from war via local interface - java

Is it possible to access ejb that resides inside ear file, from a war using local interface?
Both deployed under same app server.
The ejb:
#Stateless(name="FirstDemoEJB",mappedName = "FirstDemoEJB/Local")
public class FirstSessionBean implements FirstSessionBeanRemote, FirstSessionBeanLocal {
}
Now, in test servlet (war):
InitialContext context = new InitialContext();
Object ob = context.lookup("java:comp/env/FirstDemoEJB/Local");
Fails on:
javax.naming.NameNotFoundException: While trying to look up
comp/env/FirstDemoEJB/Local in /app/webapp/FirstWebEJBTest/12292093.;
But remote lookup works fine:
Object ob = context.lookup("FirstDemoEJB/Local#org.weblogic.FirstSessionBeanRemote");

You should use container dependency injection to access the ejb from the servlet. Most likely, the problem is that you have the jndi lookup path incorrect. Using the container dependency injection relieves you of knowing the jndi naming scheme.
public class MyServlet extends HttpServlet {
#EJB(mappedName = "FirstDemoEJB/Local")
private FirstSessionBeanLocal firstSessionBeanLocal;
}

Related

Referencing EJB Local home from a POJO class of a separate web application

I am trying to port 2 EJB modules in my application from EJB2.1 to EJB3.0. I am using the Eclipse Kepler IDE and regenerated the session beans using an EJB3.0 configuration. I am not using an ejb-jar.xml because in EJB 3.0 that is supposed to be redundant. I have instead used annotations for marking my bean as Stateless and specifying the Local and Local Home Interfaces. I have still kept the Local Home interface since I wanted the basic structure of my project to be similar to what it was in EJB2.1. I have also done away with the xml bindings for the EJB while migrating.
We are using a WAS 7 application server for deployment and while the EJB is getting successfully deployed without errors, I am getting a naming Exception while looking up my Local Home interface from a separate POJO class of a different web application it is required in. I basically want to call the create() method of the Local Home interface after referencing the EJB Local Home. Adding code samples below:
Session Bean:
#Stateless
#Local(AccessLDAPSessionLocal.class)
#LocalHome(AccessLDAPSessionLocalHome.class)
public class AccessLDAPSessionBean implements AccessLDAPSessionLocal {
//Business Logic
}
Local Interface:
public interface AccessLDAPSessionLocal {
//business Interface
}
Local Home Interface:
public interface AccessLDAPSessionLocalHome extends EJBLocalHome {
public AccessLDAPSessionLocal create() throws CreateException;
}
Pojo class referencing the Local Home interface:
public static AccessLDAPSessionLocal getAccessLDAPSessionBean() throws NamingException, CreateException {
if (accessLDAPSessionBean == null) {
InitialContext context = new InitialContext();
Object obj = context.lookup("java:global/AccessLDAP/AccessLDAPSessionBean!com.ibm.asset.hrportal.core.ejb.ldap.AccessLDAPSessionLocalHome");
accessLDAPSessionBean = ((AccessLDAPSessionLocalHome) obj).create();
}
return accessLDAPSessionBean;
}
Also my Local and Local Home interfaces are inside my EJB client which I use as a jar file, while my Session Bean is inside the actual EJB which is used as an EAR.
Following is the error I am getting:
NamingException::javax.naming.NameNotFoundException: Name global not found in context "java:".
Am I missing some configuration resulting in the failure of JNDI lookup? Any help would be gratefully appreciated. Thanks in advance.
WebSphere Application Server 7.0 is only an implementation of EJB 3.0, but the java:global namespace wasn't added until EJB 3.1, which wasn't implemented in WebSphere Application Server until 8.0. As with all EJB 3.0 implementations, you will need to lookup a vendor-specific binding name. You can find the WebSphere Application Server binding name by looking at the CNTR0167I messages in SystemOut.log. See the EJB 3.0 application bindings overview topic in the Knowledge Center if you would like to customize this binding name.
Regardless, it is not a best practice to directly lookup EJBs by their binding name. Instead, you should use an EJB reference. In EJB 3.0, that means using an annotation like this in an EE managed object (such as a servlet or another EJB):
#EJB
private AccessLDAPSessionLocalHome home;
In this case, the EJB container is required to find a target EJB within the same application that contains the EJB reference, so you do not need to explicitly configure a target binding name for the EJB reference.
If you need to access the EJB reference from a utility class rather than an EE managed class, then declare the EJB reference with a name on a managed class (such as a servlet or another EJB), and look it up from the utility class:
#EJB(name = "ejb/accessHome", beanInterface = AccessLDAPSessionLocalHome.class)
public class MyServlet { ... }
public class MyUtility {
...
InitialContext context = new InitialContext();
Object obj = context.lookup("java:comp/env/ejb/accessHome");
...
}
You can configure multiple such EJB references on the same managed EE class using the #EJBs annotation:
#EJBs({
#EJB(name = "ejb/accessHome", beanInterface = AccessLDAPSessionLocalHome.class),
#EJB(name = "ejb/other" beanInterface = Other.class)
})
public class MyServlet { ... }
If your EJB is packaged in a separate EAR, then note that this is not a portable configuration. See the "Local client views" section of the EJB modules topic in the Knowledge Center. Additionally, you will need to explicitly configure a binding name for the EJB reference.
I think the way you are looking up the ejb is not correct. The JNDI name would be something like "java:comp/env/". ejb-ref-name would be part of your web.xml
Also, you will need to give providerURL and factoryName to the context object before doing the lookup.

Root resource from other .jar file cannot be looked up in JAX-RS application

I am trying to build an application ear file with the following structure:
app.ear
--> lib
-- app-domain.jar
-- app-api.jar
-- app-common.jar
...
--> META-INF
-- application.xml
-- glassfish-application.xml
-- MANIFEST.MF
-- app-ejb.jar
-- app-rs.war
The app-api.jar file contains my remote interfaces like
#Remote
public interface LanguageService {
/**
* #return all languages known to the system
*/
List<Language> loadLanguages();
The implementation is contained in the app-ejb.jar file and looks like this:
#Stateless
#Remote(LanguageService.class)
#Path("/language")
public class LanguageServiceImpl extends ValidatingService implements LanguageService {
#PersistenceContext(unitName = "kcalculator")
EntityManager em;
#GET
#Produces("application/json")
#Override
public List<Language> loadLanguages() {
CriteriaQuery<Language> query = createLoadLanguageQuery();
return em.createQuery(query).getResultList();
}
And finally I want to provide this as an JAX-RS web service and thus have my implementation of the javax.rs.Application class in the app-rs.war file, which looks like this:
#ApplicationPath("/resources")
public class MyApplication extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> s = new HashSet<Class<?>>();
s.add(LanguageServiceImpl.class);
return s;
}
This deploys without any problem, the application class is also detected. However, when i finally access the web service an internal server error occurs due to a NPE.
The LanguageServiceImpl cannot be looked up, the log contains the following entry:
Caused by: javax.naming.NameNotFoundException: No object bound to name java:module/LanguageServiceImpl!com.kcalculator.ejb.LanguageServiceImpl
at com.sun.enterprise.naming.impl.GlassfishNamingManagerImpl.lookup(GlassfishNamingManagerImpl.java:741)
at com.sun.enterprise.naming.impl.GlassfishNamingManagerImpl.lookup(GlassfishNamingManagerImpl.java:715)
at com.sun.enterprise.naming.impl.JavaURLContext.lookup(JavaURLContext.java:167)
at com.sun.enterprise.naming.impl.SerialContext.lookup(SerialContext.java:471)
... 63 more
Hence the file is considered a Pojo, and so the reference to the entity manager is not initialized, which finally results in the Nullpointer exception.
I am kinda stuck, as annotating the bean class and giving it a mapped name is not working. Putting my application class into the ejb.jar file does not solve the problem either.
Can anyone point out what i am missing here?
Additional comment:
What I found out in the meantime: If I add a stateless session bean to my app-rs.war file and register it in MyApplication, it works without any problem. There injecting the LanguageService works, too. So it seems the problem is related to the fact that the service implementing bean class is located in another artifact.
The problem could be that you have an EJB with a remote interface.
JAX-RS 1.1 states in 6.2 that JAX-RS annotations only need to be supported on no-interface beans and local interfaces:
JAX-RS annotations MAY be applied to a bean’s local interface or directly to a no-interface bean.
As indicated in one of the previous comments, a working solution was found by moving the session beans to the web archive as well. Thus the separation between the ejb .jar file and the disclosing web service containing project is gone, however it seems rational to have the services in the artifact that is also supposed to provide the web services.
Thanks for the hints, however it is still not clear to me (according to the specification) why the initially described approach should not be feasible (but i realized it isn't...).

Startup scheduler after compleate deploying

I have webproject on Java EE 5 and Websphere 7.0
I need to create scheduler and start it after deploy application.
What I tried:
I create EJB with interface:
#Local
public interface ISchedulerBean {
public void executeTimer(Timer timer);
public void createTimer();
}
stateless session bean implements this interface. Method createTimer creates timer instance from TimerService. This part of code work fine.
Also I need to call method createTimer() after application deploy.
I tried:
Add listener servletContext:
public class SchedulerInitialiserContextListener implements ServletContextListener {
//service to lookup ejb
private WebServiceLocator webServiceLocator;
private SchedulerService schedulerService;
public SchedulerInitialiserContextListener() {
webServiceLocator = new WebServiceLocator();
schedulerService = webServiceLocator.getSchedulerService();
}
public void contextDestroyed(ServletContextEvent ctx) {
}
public void contextInitialized(ServletContextEvent ctx) {
schedulerService.createTimer();
}
}
create Servlet with 1 where inject SchedulerBean in init() method and call createTimer.
But this does not work, because at first webspere deploy web application, call listeners, initialize servlets, and only then deploy ejb.jar in which all ejb are located. I can see that in webshpere log file. So when I try get ejb throw #EJB annotation or lookup I get Exception, beacause ejb has not been found.
May be is other approach to start timer after deploy ejb module or change deploy order?
You can set the startup order of your modules.
Assuming you develop with Rational Application Developer do the following:
Right click your ear project and click Java EE -> Open WebSphere Application Server Deployment.
Look for the Application section, you will see all the modules and you can set the Start weight for each one.
Make sure your web project containing the Context Listener has the largest value an you should be fine.
This operation creates an ibmconfig directory under your ear project's META-INF, make sure you package it in your build process

Lookup EJB using InitialContext on Weblogic 10.x.x

Could you please tell me how to lookup EJB on Weblogic?
I have following bean:
#Stateless
#EJB(name = "DataAccess", beanInterface = DataAccessLocal.class)
public class DataAccess implements DataAccessLocal {
...
}
I need this bean in other class which is not part of managed content (just simple class), so I guess it should be done like this:
DataAccessLocal dataAccess = DataAccessLocal.class.cast((new InitialContext()).lookup("%SOME_JNDI_NAME%"));
The question is what should be used as %SOME_JNDI_NAME% in case of Weblogic 10.x.x AS?
Any help will be appreciated.
I would update your EJB class to look like this:
#Stateless(name="DataAccessBean", mappedName="ejb/DataAccessBean")
#Remote(DataAccessRemote.class)
#Local(DataAccessLocal.class)
public class DataAccess implements DataAccessLocal, DataAccessRemote {
...
}
Looking up the EJB from a class deployed in the same EAR (using the local interface):
InitialContext ctx = new InitialContext(); //if not in WebLogic container then you need to add URL and credentials.
// use <MAPPED_NAME>
Objet obj = ctx.lookup("java:comp/env/ejb/DataAccessBean");
EJB injection is usually preferred, and you can do it as follows:
#EJB(name="DataAccessBean")
DataAccessLocal myDataAccessBean;
If you are trying to use the EJB remotely then you will need to use the remote interface and the following JNDI name:
DataAccessBean#<package>.DataAccessRemote

ejb lookup failing with NamingException

I've added the following in my web.xml:
<ejb-ref>
<ejb-ref-name>ejb/userManagerBean</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>
<home>gha.ywk.name.entry.ejb.usermanager.UserManagerHome</home>
<remote>what should go here??</remote>
</ejb-ref>
The following java code is giving me NamingException:
public UserManager getUserManager () throws HUDException {
String ROLE_JNDI_NAME = "ejb/userManagerBean";
try {
Properties props = System.getProperties();
Context ctx = new InitialContext(props);
UserManagerHome userHome = (UserManagerHome) ctx.lookup(ROLE_JNDI_NAME);
UserManager userManager = userHome.create();
WASSSecurity user = userManager.getUserProfile("user101", null);
return userManager;
} catch (NamingException e) {
log.error("Error Occured while getting EJB UserManager" + e);
return null;
} catch (RemoteException ex) {
log.error("Error Occured while getting EJB UserManager" + ex);
return null;
} catch (CreateException ex) {
log.error("Error Occured while getting EJB UserManager" + ex);
return null;
}
}
The code is used inside the container. By that I mean that the .WAR is deployed on the server (Sun Application Server).
StackTrace (after jsight's suggestion):
>Exception occurred in target VM: com.sun.enterprise.naming.java.javaURLContext.<init>(Ljava/util/Hashtable;Lcom/sun/enterprise/naming/NamingManagerImpl;)V
java.lang.NoSuchMethodError: com.sun.enterprise.naming.java.javaURLContext.<init>(Ljava/util/Hashtable;Lcom/sun/enterprise/naming/NamingManagerImpl;)V
at com.sun.enterprise.naming.java.javaURLContextFactory.getObjectInstance(javaURLContextFactory.java:32)
at javax.naming.spi.NamingManager.getURLObject(NamingManager.java:584)
at javax.naming.spi.NamingManager.getURLContext(NamingManager.java:533)
at javax.naming.InitialContext.getURLOrDefaultInitCtx(InitialContext.java:279)
at javax.naming.InitialContext.lookup(InitialContext.java:351)
at gov.hud.pih.eiv.web.EjbClient.EjbClient.getUserManager(EjbClient.java:34)
I think you want to access an EJB application (known as EJB module) from a web application in Sun Application Server, right ?
ok, let's go.
When you deploy an EJB into an application server, the application server gives it an address - known as global JNDI address - as a way you can access it (something like your address). It changes from an application server to another.
In JBoss Application Server, you can see global JNDI address (after starting it up) in the following address
http://127.0.0.1:8080/jmx-console/HtmlAdaptor
In Sun Application Server, if you want to see global JNDI address (after starting it up), do the following
Access the admin console in the following address
http://127.0.0.1:4848/asadmin
And click JNDI browsing
If your EJB IS NOT registered right there, there is something wrong
EJB comes in two flavors: EJB 2.1 and EJB 3.0. So what is the difference ?
Well, well, well...
Let's start with EJB 2.1
Create a Home interface
It defines methods for CREATING, destroying, and finding local or remote EJB objects. It acts as life cycle interfaces for the EJB objects. All home interfaces have to extend standard interface javax.ejb.EJBHome - if you a using a remote ejb object - or javax.ejb.EJBLocalHome - if you are using a local EJB object.
// a remote EJB object - extends javax.ejb.EJBHome
// a local EJB object - extends javax.ejb.EJBLocalHome
public interface MyBeanRemoteHome extends javax.ejb.EJBHome {
MyBeanRemote create() throws javax.ejb.CreateException, java.rmi.RemoteException;
}
Application Server will create Home objects as a way you can obtain an EJB object, nothing else.
Take care of the following
A session bean’s remote home interface MUST DEFINE ONE OR MORE create<METHOD> methods.
A stateless session bean MUST DEFINE exactly one <METHOD> method with no arguments.
...
throws clause MUST INCLUDE javax.ejb.CreateException
...
If your Home interface extends javax.ejb.EJBHome, throws clauses MUST INCLUDE the java.rmi.RemoteException. If it extends javax.ejb.EJBLocalHome, MUST NOT INCLUDE the java.rmi.RemoteException.
...
Each create method of a stateful session bean MUST BE NAMED create<METHOD>, and it
must match one of the Init methods or ejbCreate<METHOD> methods defined in the session
bean class. The matching ejbCreate<METHOD> method MUST HAVE THE SAME NUMBER AND TYPES OF ARGUMENTS. The create method for a stateless session bean MUST BE NAMED create but need not have a matching “ejbCreate” method.
Now create an business interface in order to define business logic in our EJB object
// a remote EJB object - extends javax.ejb.EJBObject
// a local EJB object - extends javax.ejb.EJBLocalObject
public interface MyBeanRemote extends javax.ejb.EJBObject {
void doSomething() throws java.rmi.RemoteException;
}
Now take care of the following
If you are using a remote EJB object, remote interface methods MUST NOT EXPOSE local interface types or local home interface types.
...
If your Home interface extends javax.ejb.EJBObject, throws clauses MUST INCLUDE the java.rmi.RemoteException. If it extends javax.ejb.EJBLocalObject, MUST NOT INCLUDE the java.rmi.RemoteException.
Now our EJB
public class MyBean implements javax.ejb.SessionBean {
// why create method ? Take a special look at EJB Home details (above)
public void create() {
System.out.println("create");
}
public void doSomething() throws java.rmi.RemoteException {
// some code
};
}
Now take care of the following
It MUST IMPLEMENTS javax.ejb.SessionBean. It defines four methods - not shown above: setSessionContext, ejbRemove, ejbPassivate, and ejbActivate.
Notice our bean DOES NOT IMPLEMENT our business interface because of EJB specification says:
For each method defined in the interface, there must be a matching method in the session bean’s class. The matching method must have:
The same name
The same number and types of arguments, and the same return type.
All the exceptions defined in the throws clause of the matching method of the session
bean class must be defined in the throws clause of the method of the local interface.
And YOU HAVE TO DECLARE a ejb-jar.xml file according to
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar 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/ejb-jar_2_1.xsd" version="2.1">
<enterprise-beans>
<session>
<ejb-name>HelloWorldEJB</ejb-name>
<home>br.com.MyBeanRemoteHome</home>
<remote>br.com.MyBeanRemote</remote>
<local-home>br.com.MyBeanLocalHome</local-home>
<local>br.com.MyBeanLocal</local>
<ejb-class>br.com.MyBean</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
</session>
</enterprise-beans>
</ejb-jar>
If you do not have a local EJB object remove from the deployment descriptor above
<local-home>br.com.MyBeanLocalHome</local-home>
<local>br.com.MyBeanLocal</local>
If you do not have a remote EJB object remove from the deployment descriptor above
<home>br.com.MyBeanRemoteHome</home>
<remote>br.com.MyBeanRemote</remote>
And put in META-INF directory
Our jar file will contain the following
/META-INF/ejb-jar.xml
br.com.MyBean.class
br.com.MyBeanRemote.class
br.com.MyBeanRemoteHome.class
Now our EJB 3.0
// or #Local
// You can not put #Remote and #Local at the same time
#Remote
public interface MyBean {
void doSomething();
}
#Stateless
public class MyBeanStateless implements MyBean {
public void doSomething() {
}
}
Nothing else,
In JBoss put jar file in
<JBOSS_HOME>/server/default/deploy
In Sun Application Server access (after starting it up) admin console
http://127.0.0.1:4848/asadmin
And access EJB Modules in order to deploy your ejb-jar file
As you have some problems when deploying your application in NetBeans, i suggest the following
Create a simple Java library PROJECT (a simple jar without a main method)
Add /server/default/lib (contains jar files in order you retrieve your EJB's) jar files to your Java application whether you are using JBoss (I do not know which directory in Sun Application Server)
Implement code above
Now create another war PROJECT
Add our project created just above and add <JBOSS_HOME>/client (contains jar files in order to access our EJB's). Again i do not know which directory in Sun Application Server. Ckeck out its documentation.
See its global mapping address as shown in the top of the answer
And implement the following code in your Servlet or something else whether you are using JBoss
public static Context getInitialContext() throws javax.naming.NamingException {
Properties p = new Properties();
p.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
p.put(Context.URL_PKG_PREFIXES, " org.jboss.naming:org.jnp.interfaces");
p.put(Context.PROVIDER_URL, "jnp://127.0.0.1:1099");
return new javax.naming.InitialContext(p);
}
Or the following whether you are using Sun Application Server - put the file appserv-rt.jar (I do not know which past contain appserv-rt.jar in Sun Application Server) in your classpath
public static Context getInitialContext() throws javax.naming.NamingException {
return new javax.naming.InitialContext();
}
In order to access your EJB in our Servlet or something else
MyBeanRemote myBean = (MyBeanRemote) getInitialContext().lookup(<PUT_EJB_GLOBAL_ADDRESS_RIGHT_HERE>);
myBean.doSomething();
regards,
Last two answers are both correct in that they are things you need to change/fix. But the NoSuchMethodError you see is not from your code, nor from things trying to find your code (would produce some kind of NoClassDefFoundException, I think, were this the case). This looks more like incompatible versions of the JNDI provider provided by the container, and what the JNDI implementation in the Java library wants. That's a pretty vague answer, but, would imagine it is solvable by perhaps upgrading your application server, and, ensuring you aren't deploying possibly-stale copies of infrastructure classes related to JNDI with your app, that might interfere.
First, fix your web.xml and add the Remote Interface in it:
<ejb-ref>
<description>Sample EJB</description>
<ejb-ref-name>SampleBean</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>
<home>com.SampleHome</home>
<remote>com.Sample</remote> <!-- the remote interface goes here -->
</ejb-ref>
Then, regarding the java.lang.NoSuchMethodError, Sean is right, you have a mismatch between the version of the app server "client library" you are using inside NetBeans and the app server version (server-side). I can't tell you exactly which JARs you need to align though, refer to the Sun Application Server documentation.
PS: This is not a direct answer to the problem but I don't think you're currently passing any useful properties when creating your initial context with the results of the call to System.getProperties(), there is nothing helpful in these properties to define the environment of a context (e.g. the initial context factory). Refer to the InitialContext javadocs for more details.

Categories