Object reference lookup from JNDI results in ClassCastException - java

I'm having problems calling EJB3 stateless bean outside the container.
Code for getting the object reference:
Context envCtx = (Context) context.lookup("ejb");
MyObject o = (MyObject) envCtx.lookup(MyObject);
The second row results in exception:
java.lang.ClassCastException: javax.naming.Reference
I use JBoss.org 5.1.0 GA.
Based on some other posts I suspect this might be due to wrong version of client libraries. However, I'm unsure which library jar(s) I should include in the jar? (I get the error using 5.0.4.GA jnpserver.)

For JBoss, your code should look something like that:
Properties properties = new Properties();
properties.put("java.naming.factory.initial","org.jnp.interfaces.NamingContextFactory");
properties.put("java.naming.factory.url.pkgs","=org.jboss.naming:org.jnp.interfaces");
properties.put("java.naming.provider.url","localhost:1099");
Context context = new InitialContext(properties);
(EchoBeanRemote) c.lookup("EchoBean/remote");
If you prefer, you can put the JNDI environement settings in a jndi.properties file (that needs to be on the classpath):
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
java.naming.provider.url=jnp://localhost:1099
And use the non-arg InitialContext constructor:
Context context = new InitialContext();
(EchoBeanRemote) c.lookup("EchoBean/remote");
This is obviously more portable.
And in both case, you'll need jbossall-client.jar on the classpath on the client side.
P.S.: You can check the Global JNDI Name your bean is registered at in the JNDI View of the web-based JMX console (if it still exists).

Related

Spring & JNDI: locate resource platform independent

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).

Get Datasources programmatically

I have a web-app in Java/Java EE deployed on any application server/web server. I would like to get all the datasources configured on this server in my application.
Does anyone have any ideas to achieve it? I can get those through WLST ant tasks. But I need to get them programatically.
If your datasources are configured with JNDI then you can list the context and can get all the names (more from here) with Context.list() method and from those name you can find all the datasources
the Context.list() returns an enumeration of NameClassPair. Each NameClassPair consists of the object's name and its class name. So just iterate it and check the class name for java.sql.DataSource and then get the object name to retrieve it.
With JBoss you can do the following (assuming JMX is available):
Context ctx = new InitialContext();
MBeanServerConnection mconn = (MBeanServerConnection)ctx.lookup("jmx/invoker/RMIAdaptor");
ObjectName name = new ObjectName("jboss.jca:service=DataSourceBinding,*");
Set s = mconn.queryMBeans(name, null);
Where s is an mbeans collection.

Cannot instantiate class: com.sun.enterprise.naming.SerialInitContextFactory upon doing lookup

I am trying to get this standalone (no server involved) JNDI InitialContext lookup to work. I am getting this exception:
Cannot instantiate class: com.sun.enterprise.naming.SerialInitContextFactory
try {
Hashtable <String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.enterprise.naming.SerialInitContextFactory");
env.put(Context.PROVIDER_URL,"localhost:1099");
env.put("java:comp/env/jms/RNCQueueConnectionFactory",
"javax.jms.QueueConnectionFactory");
env.put("Big", "Data");
Context jndiContext = new InitialContext(env);
jndiContext.lookup("java:comp/env/jms/RNCQueueConnectionFactory");
testval = (String) jndiContext.lookup("Big");
} catch (NamingException ne) {
System.out.println(ne.getMessage());
}
To me and from what I have read, I probably did not set up my context correctly. Has anyone successfully set up a standalone JNDI?
I had the same problem. I am working with a Maven project and I just added the following dependency:
<dependency>
<groupId>org.glassfish.main.common</groupId>
<artifactId>glassfish-naming</artifactId>
<version>4.1.1</version>
</dependency>
JNDI is an API to access naming and directory services. There's The JNDI Tutorial that should let you get up to speed with the API and how it works.
As an API, JNDI needs an implementation to work. In your particular case, you need to find the jar with the class com.sun.enterprise.naming.SerialInitContextFactory. Once you've figured out what jar it should be, you will need to add it to CLASSPATH and re-run the application.
It might not be all you need to access the JNDI service, but at least will get you past the initial error. Please consult the article and The JNDI Tutorial.

Tomcat +jndi.properties

i have a problem with JNDI in Tomcat:
without tomcat I have used JNDI as following:
Context context = new InitialContext();
JVoiceXml jvxml = (JVoiceXml) context.lookup("JVoiceXml");
And jndi.properties :
java.naming.factory.initial=com.sun.jndi.rmi.registry.RegistryContextFactory
java.naming.provider.url=rmi://localhost:1099
java.naming.rmi.security.manager=true
Now I want to use this code in my Web Application(deployed with Tomcat), I have changed the code to:
Context context = new InitialContext();
Context envCtx = (Context) context.lookup("java:comp/env");
JVoiceXml jvxml = (JVoiceXml) envCtx.lookup("JVoiceXml");
In web.xml:
<resource-env-ref>
<resource-env-ref-name>
JVoiceXml
</resource-env-ref-name>
<resource-env-ref-type>
org.jvoicexml.JVoiceXml
</resource-env-ref-type>
</resource-env-ref>
And in context.xml:
<Resource name="JVoiceXml" auth="Container"
type="org.jvoicexml.JVoiceXml"
factory="com.sun.jndi.rmi.registry.RegistryContextFactory"
url="rmi://localhost:1099"/>
What I have missed, because after lookup() my JVoiceXml is null
Your Tomcat code is not equivalent to what you wrote outside Tomcat. All you've done in the Tomcat case is name an RMI Registry context factory as JVoiceXML. That doesn't seem to make sense: the RMI Registry has nothing to do with XML. I suspect you don't need the java:comp/env stuff at all here. Presumably you have an RMI Registry running somewhere: just look it up with Naming.lookup(). If you want it to act as a Tomcat Resource, you need to define a factory that can create it. That needs to be an instance of javax.naming.spi.ObjectFactory (?). That may in turn do a Naming.lookup(), or a JNDI lookup, outside the java:comp/env namespace.

JNDI ClassCastException

I am attempting to use JNDI with a custom DataSource called CEDataSource. From my understanding for this to work I would have to create a custom factory as well.
So I created a custom factory that would return the CEDataSource object but now when I attempt to use this in Java with
Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
// Look up our data source
CEDataSource ds = (CEDataSource)envCtx.lookup("jdbc/cePu");
I get the exception ClassCastException
"CEDataSource cannot be mapped to CEDataSource". I added the CEDataSource and the CEDataSourceFactory to the TOMCAT/lib folder as well as referenced this same jar on my deployed application.
Any help would be greatly appreciated on why this possible error may occur. Thanks
"CEDataSource cannot be mapped to CEDataSource" seems to point to the fact that it's not the same "CEDataSource" in both places.
What could be different is the classloader and this usually happens if you have the same jars/.class(es) in multiple locations.
Do you have multiple copies of your jar?
Try to have a single copy, maybe in the shared tomcat lib so it's loaded by the same classloader no matter from where you access it from.
It is actually not too difficult to start Tomcat under an Eclipse debug session (just put all the Bootstrap.jar in a project and add the System properties in the JVM parameters). II've done that many times, if only to dissect the bowels of that feline. Once this is done you can break on the class cast exception of the JNDI connection factory and you will then be able to see if your factory is called or not.
From what I remember Tomcat uses the DBCP DataSource. Actually repackaged under com.apache.tomcat.dbcp.dbcp.DataSource (IIRC).
So I would not be surprised if this is what you end up with as a result of your look-up.
With hindsight, I now realize I also forgot to mention that if any underlying class (for instance a JDBC driver) needed to create the instance of your CEDataSource is missing you also get this ClassCastException. Fair enough, but you always focus on the class itself and not on the other jars...
CEDataSource ds = (CEDataSource)envCtx.lookup("jdbc/cePu");
The lookup you are doing on jdbc/cePu is not of class type CEDataSource , it belongs to some other class type, that is why you are getting class cast exception. if you could show me the configuration for jdbc/cePu that would be helpful.

Categories

Resources