I have a EJB defined as this:
package com.foo;
#Stateless (mappedName="HelloWorld")
public class HelloWorldBean implements HelloWorld, HelloWorldLocal
....
When it's deployed to Weblogic (WL), it gets the name myBean. I'm not sure if this is important.
I try to call the bean with this code:
Hashtable ht = new Hashtable();
ht.put(Context.INITIAL_CONTEXT_FACTORY,"weblogic.jndi.WLInitialContextFactory");
ht.put(Context.PROVIDER_URL, "t3://localhost:7001");
ic = new InitialContext(ht);
tp = (HelloWorld) ic.lookup("HelloWorld#com.foo.HelloWorldBean");
Anyone know why I get the following error?
javax.naming.NameNotFoundException: While trying to lookup 'HelloWorld#com.foo.HelloWorldBean' didn't find subcontext 'HelloWorld#com'.
Resolved '' [Root exception is javax.naming.NameNotFoundException: While trying
to lookup 'HelloWorld#com.foo.HelloWorldBean' didn't find
subcontext 'HelloWorld#com'. Resolved '']; remaining name 'HelloWorld#com/foo/HelloWorldBean'
To lookup a Remote Interface of a Session Bean with multiple Remote Business interfaces (e.g.com.acme.FooBusiness1, com.acme.FooBusiness2), you need to lookup a name derived from the combination of the target ejb's global JNDI name (the mappedName() in #Stateless) and the specific Remote Business Interface, separated by a "#":
InitialContext ic = new InitialContext();
FooBusiness1 bean1 = (FooBusiness1) ic.lookup("FooEJB#com.acme.FooBusiness1");
FooBusiness2 bean2 = (FooBusiness2) ic.lookup("FooEJB#com.acme.FooBusiness2");
In the typical case of a bean only having one Remote Business Interface, this fully-qualified form is not needed. In that case, the bean's JNDI name can be used directly :
FooBusiness bean = (FooBusiness) ic.lookup("FooEJB");
That was the theoretical part. Now the practice. In your case, from what I can see, you are accessing the EJB from Weblogic so I'd rather use the no-arg InitialContext() constructor (and use a jndi.properties configuration file for other environments) but this is just a side note. Then, you should look up com.foo.HelloWorld, the Remote Interface, not com.foo.HelloWorldBean, the implementation:
InitialContext ic = new InitialContext();
(HelloWorld) ic.lookup("HelloWorld#com.foo.HelloWorld");
And if your bean has only one Remote Business Interface, this should work:
(HelloWorld) ic.lookup("HelloWorld");
Related
I have a working Glassfish 5 setup inside of Eclipse.
I have modules set up like this:
common
contains remote interface for singleton bean with #remote and #local annotation
included in all other modules as a pom dependency
core
contains a singleton bean that implements the interface in common
intended to contain service classes stored in JNDI that allow any number of clients to lookup and store data and/or access other service logic
builds a war file that deploys onto GlassFish
desktop
does an InitialContext lookup of the singleton bean. (I intend to abstract this into the Service Lookup design pattern).
intended to be a desktop client that remotely accesses beans stored on the server.
Right now, I'm just trying to get it to remotely access the service in core and print some text to the console proving it worked.
I think my problem stems from a misunderstanding of how to store a custom bean in JNDI in the first place. I looked on Google, but I mostly find articles and answers to questions telling me to add name and mappedName to the Singleton annotation or they only show how to add a predefined bean into JDNI such as a Boolean or String, and not something custom defined.
My bean is defined like this:
#Singleton(name = "BookService", mappedName = "ejb/BookService")
public class BookServiceImpl implements BookService {
private static final long serialVersionUID = 1L;
which results in this on the Glassfish server:
Core Server Screenshot
but nothing appears in JNDI:
JNDI Screenshot
The client does it's InitialContext lookup like this (I've tried multiple ways of writing the JNDI name):
BookService bookService = (BookService) initialContext.lookup("java:global/core/BookService");
using these configurations (I defined my domain with a base port of 8000, so every port is 8000 + something):
glashfishEnvironmentTable = new Hashtable<String, String>();
glashfishEnvironmentTable.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.enterprise.naming.impl.SerialInitContextFactory");
glashfishEnvironmentTable.put(Context.STATE_FACTORIES,
"com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl");
glashfishEnvironmentTable.put(Context.URL_PKG_PREFIXES, "com.sun.enterprise.naming");
//glashfishEnvironmentTable.put(Context.PROVIDER_URL, "ejbd://localhost:8080/");
// on a different host than the appserver
glashfishEnvironmentTable.put("org.omg.CORBA.ORBInitialHost", "localhost");
// optional. Defaults to 3700. Only needed if target orb port is not 3700.
glashfishEnvironmentTable.put("org.omg.CORBA.ORBInitialPort", "8037");
I get an error like this when I run it:
Exception in thread "main" javax.naming.CommunicationException: Communication exception for SerialContext[myEnv={org.omg.CORBA.ORBInitialPort=8037, java.naming.factory.initial=com.sun.enterprise.naming.impl.SerialInitContextFactory, org.omg.CORBA.ORBInitialHost=localhost, java.naming.factory.url.pkgs=com.sun.enterprise.naming, java.naming.factory.state=com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl} [Root exception is java.rmi.MarshalException: CORBA MARSHAL 1330446343 No; nested exception is:
org.omg.CORBA.MARSHAL: FINE: 00810007: Underflow in BufferManagerReadStream after last fragment in message vmcid: OMG minor code: 7 completed: No]
I think my InitialContext is configured properly because when I manually define the bean in JNDI like this (The ServiceFactory doesn't do anything, because I don't know how to properly extend ObjectFactory):
Bean defined in JNDI
I get this:
Exception in thread "main" java.lang.ClassCastException: javax.naming.Reference cannot be cast to common.service.BookService
Is there simple fix to this I am missing, or am I mixing oil and water trying to get an EJB Singleton into JNDI? Or am I missing something big like an EJBHome or a Servlet?
Do I need to extend the factory class to add my service classes into JDNI, or should ObjectFactory work? If I must extend the factory class, how would I go about it?
I hope I've defined the scope of the question well enough, but this is all new to me, so if anybody has experience implementing something like this, I appreciate any input that gets me closer to doing it right.
I figured it out. I did two things I shouldn't have.
I forgot to maven install my common module before building my war file... doh!
It seems that the #Remote and the #Local annotations cannot be in the same interface or it will fail (which is a shame, because I was trying to set it up so I didn't have to have two nearly identical interfaces).
Here are some more details in case someone else has a similar issue and needs to see something that works (I haven't done #Local here, because I haven't tried it yet. It should be trivial to add though):
For remote execution, you need an interface annotated with #Remote that extends Serializable (Serialization is required for Client/Server interface).
import java.io.Serializable;
public interface Remote extends Serializable{
}
import javax.ejb.Remote;
#Remote
public interface BookServiceRemote extends Remote {
public String readBook();
}
Your war file needs to contain the BookServiceImpl
import javax.ejb.Singleton;
import us.boggs.template.common.service.BookServiceRemote;
#Singleton(name = "BookService")
public class BookServiceImpl implements BookServiceRemote {
private static final long serialVersionUID = 1L;
#Override
public String readBook() {
return "Read the book.";
}
}
Your client pom.xml must include the common module and this:
<dependency>
<groupId>org.glassfish.main.appclient</groupId>
<artifactId>gf-client</artifactId>
<version>5.1.0</version>
</dependency>
Your client main method can use this by creating an InitialContext and doing a lookup(My base port was 8000, so my ORBInitialPort is 8000+37=8037):
private static Hashtable<String, String> glashfishEnvironmentTable;
static {
glashfishEnvironmentTable = new Hashtable<String, String>();
glashfishEnvironmentTable.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.enterprise.naming.impl.SerialInitContextFactory");
glashfishEnvironmentTable.put(Context.STATE_FACTORIES,
"com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl");
glashfishEnvironmentTable.put(Context.URL_PKG_PREFIXES, "com.sun.enterprise.naming");
glashfishEnvironmentTable.put("org.omg.CORBA.ORBInitialHost", "localhost");
// optional. Defaults to 3700. Only needed if target orb port is not 3700.
glashfishEnvironmentTable.put("org.omg.CORBA.ORBInitialPort", "8037");
}
InitialContext initialContext = new InitialContext(glashfishEnvironmentTable);
BookServiceRemote bookService = (BookServiceRemote) initialContext.lookup("java:global/core/BookService");
System.out.println(bookService.readBook());
I'm trying to load in a property file provided by JNDI which should be platform independent. I know I can do it in the following ways, dependent on the platform:
For Weblogic:
Properties properties = new Properties();
properties.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
InitialContext context = new InitialContext(properties);
context.lookup(propertiesLocation);
For Tomcat:
Context context = new InitialContext();
Context envCtx = (Context) context.lookup("java:comp/env");
final Object lookup = envCtx.lookup(propertiesLocation);
The core problem is that in Tomcat the prefix java:comp/env/ is needed. Since Spring is able to load all this platform independently, I tried looking into the possibilities of Spring loading my JNDI resources.
I found out I can use the JndiTemplate of Spring in the following way:
JndiTemplate jndiTemplate = new JndiTemplate();
Object lookup = jndiTemplate.lookup(propertiesLocation);
This is still platform dependent however, needing to use java:comp/env as a prefix during the lookup on tomcat. Looking further on StackOverflow and in the Spring javadocs, I found the class JndiLocatorSupport, which has the following:
JNDI names may or may not include the "java:comp/env/" prefix expected by J2EE applications when accessing a locally mapped (ENC - Environmental Naming Context) resource. If it doesn't, the "java:comp/env/" prefix will be prepended if the "resourceRef" property is true (the default is false) and no other scheme (e.g. "java:") is given.
So I created a JndiObjectFactoryBean which extends JndiLocatorSupport, enabled setResourceRef but it doesn't seem to append the prefix.
Core problem:
When using the following code:
JndiObjectFactoryBean factoryBean = new JndiObjectFactoryBean();
factoryBean.setResourceRef(true);
Object lookup = factoryBean.getJndiTemplate().lookup(propertiesLocation);
I'd expect it to have the same effect as:
JndiObjectFactoryBean factoryBean = new JndiObjectFactoryBean();
Object lookup = factoryBean.getJndiTemplate().lookup("java:comp/env/" + propertiesLocation);
But it doesn't. It seems to have no effect at all. But if I look through the source code, it does go like this:
JndiObjectFactoryBean.lookup() -> JndiObjectLocator.lookup() -> JndiLocatorSupport.lookup(), which does call the right methods.
JndiObjectFactoryBean factoryBean = new JndiObjectFactoryBean();
factoryBean.setResourceRef(true);
Object lookup = factoryBean.getJndiTemplate().lookup(propertiesLocation);
and
JndiTemplate jndiTemplate = new JndiTemplate();
Object lookup = jndiTemplate.lookup(propertiesLocation);
Are the same with regards to the lookup. The first is only a very complex way to obtain a JndiTemplate. All the settings you do are for the JndiObjectFactoryBean NOT for the internal JndiTemplate. Basically your whole approach doesn't add anything.
Instead use a JndiLocatorDelegate and let that do the lookup (don't try to get the JndiTemplate!).
JndiLocatorDelegate jndi = JndiLocatorDelegate.createDefaultResourceRefLocator();
Object lookup = jndi.lookup(propertiesLocation);
This will by default do a lookup in java:comp/env and if not found do a fallback to a plain propertiesLocation (what you passed in).
For various reasons I need to perform a manual lookup of SessionContext. In JBoss5, the solution
InitialContext initialContext = new InitialContext();
SessionContext sessionContext = (SessionContext) initialContext.lookup("java:comp/EJBContext");
has served med well, but from JBoss 7 I instead get a
javax.naming.NameNotFoundException: EJBContext -- service jboss.naming.context.java.global.EJBContext
Has something changed in how context is looked up in JBoss 7.2, or is my deployment lacking anything vital? For reference, standard injection works fine, this is the only lookup that fails. Or am I doing something terribly wrong (besides performing a manual lookup of SessionContext)?
According to specification of Java EJB (this one is for EJB 3.2. but nothing changed about EJBContext from previous one, EJB 3.x), you can inject EJBContext into your components either using annotation #Resource or manually via lookup (section 11.15):
The container must make a component’s EJBContext interface available either through injection
using the Resource annotation or in JNDI under the name java:comp/EJBContext
Standard way of looking up for EJB resource is via EJBContext.lookup method but there is also JNDI way which is the only possibilities if you don't have already EJBContext:
Context initCtx = new InitialContext();
EJBContext ejbCtx = (EJBContext) initCtx.lookup("java:comp/EJBContext");
This is exactly what you did, so what is wrong? There are two things, which one I'm not sure about. First, with manually lookup it's sometime needed to assign resource to component with annotation at class level:
#Resource(name = "EJBContext", type = javax.ejb.EJBContext)
public class MyComponent {
...
}
but I'm not sure is it needed for EJBContext as well, I guess not. The second thing, more important and critical - according to specification once again:
EJBContext objects accessed through the naming environment are only valid within the bean
instance that performed the lookup.
this one is section 11.15.1, and the next one, section 11.15.2:
The Container Provider is responsible for providing an appropriate EJBContext object to the refer-
encing component. The object returned must be of the appropriate specific type for the bean requesting
injection or performing the lookup—that is, the Container Provider must return an instance of the SessionContext interface to referencing session beans and an instance of the MessageDrivenCon-
text interface to message-driven beans.
Those both mean that injection and lookup for EJBContext are only valid in Enterprise Java Beans, so those which are annotated with #MessageDriven, #Stateful, #Singleton or #Stateless (or described as EJBs in deployment descriptor file, also as EJB 2.x Specification). Maybe your component isn't valid EJB and it's why lookup isn't working? This is only suggestion of course.
There's one more possibilities to get EJBContext (more correctly SessionContext). Your component should implements SessionBean interface which has setSessionContext(SessionContext sessionContext) method. This method should be invoked by EJB container every time when component is used (injected somewhere, invoked by client or timeout, especially when it's created) and inside this method you should assign sessionContext parameter to bean's field.
I am writing an application in Java and I have some REST web services there. My application has following structure: http://cl.ly/L7Pv/o
REST web service classes are Stateless session beans. It works like charm. But Classes in red on the picture want to use that REST resources too.
As fas as I know I cannot use dependency injection and annotation #EJB there. I believe I have to use JNDI lookup. Documentation: http://docs.oracle.com/javaee/6/tutorial/doc/gipjf.html
But now I dont know how to write this JNDI lookup. I have tried these two:
context.lookup("java:global/diplomka/ListResource");
context.lookup("java:global/Diplomka_maven/ListResource");
What am I doing wrong? Is this a correct approach in the first place?
Thank you
If these classes (ListResource etc.) are Stateless session beans, you can put attribute name or mappedName in #Stateless annotation, e.g.:
#Stateless(mappedName="ejb/myRestService")
public class ListResource { ..
Once you have specified JNDI name of your stateless bean, it's easy to fetch the bean through JNDI lookup:
InitialContext ic = new InitialContext();
ListResource lr = (ListResource) ic.lookup("ejb/myRestService");
lr.doWhateverNeeded(..);
I'm trying to use JNDI to obtain a new Stateful Session Bean in a servlet (as a local variable). My doGet() method has the following:
Bean bean = (Bean) new InitialContext().lookup("beanName");
I've tried including java:comp/env but all of my attempts have led to naming exceptions.
I'm attempting to bind the bean in the #Stateful annotation, using various guesses like #Stateful(name="beanName") and #Stateful(mappedName="beanName")
What I needed was to use the #EJB annotation on the servlet at the class level, as follows:
#EJB(name="beanName", beanInterface = Bean.class)
Then lookup in the service method can happen using the name bound by the #EJB annotation:
Bean beanInstance = (Bean) new InitialContext().lookup("java:comp/env/beanName");
There is no need for anything in the Bean class itself, other than the plain #Stateful annotation.