I'm trying to (unit) test my EJB class without having to startup my websphere environment. Now I'm using Open EJB, but there are some issues with resolving the JNDI Names for other EJBs that are used within my EJB... and there is no way for me to inject mocked classes from my test right now.
Getting the InitialContext
final Properties properties = new Properties();
properties.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.LocalInitialContextFactory");
properties.setProperty("log4j.category.OpenEJB.options ", "debug");
properties.setProperty("log4j.category.OpenEJB.startup ", "debug");
properties.setProperty("log4j.category.OpenEJB.startup.config ", "debug");
properties.setProperty("MyOwnDatasource.JdbcDriver ", "com.ibm.as400.access.AS400JDBCDriver");
properties.setProperty("MyOwnDataSource.JdbcUrl ", "jdbc:as400:MYHOSTNAME;database name=MYDATABASE;libraries=MYDEFAULTTABLE");
ic = new InitialContext(properties);
Inside my class under test there is a lookup for java:comp/env/ejb/PrefixEjbNameLocalHome and I can not set Open EJB to generate JNDI names in that format.
Additional Property for JNDI name format
I tried setting the formatting rule like this:
properties.setProperty("openejb.jndiname.format ", "comp/env/ejb/{interfaceClass}");
Properties aren't used?
Also the logging configuration isn't used. I'm only seeing INFO and WARN messages from Open EJB, although I set log4j.category.OpenEJB.* and the like to DEBUG or TRACE.
It's the "java:" part that is messing up your test case. Basically Context.INITIAL_CONTEXT_FACTORY and "java:" are mutually exclusive. The InitialContext class has a special understanding of "java:" or any "foo:" lookups and if they are at the beginning of the name it will not use INITIAL_CONTEXT_FACTORY you specified. A somewhat frustrating part of JNDI.
If you lookup the name exactly as printed in the log, it will work. So for example this log message:
INFO - Jndi(name=WidgetBeanRemote) --> Ejb(deployment-id=WidgetBean)
Then in code:
Properties p = new Properties();
p.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.LocalInitialContextFactory");
// set any other properties you want
Context context = new InitialContext(p);
Object o = context.lookup("WidgetBeanRemote");
Related
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).
In my web.xml od my webapp application I have following element:
<env-entry>
<env-entry-name>aMessage</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>Hello World</env-entry-value>
</env-entry>
EJB in this web application can read it:
final InitialContext context = new InitialContext();
final Context env = (Context) context.lookup("java:comp/env");
System.out.println("MSG: " + env.lookup("aMessage")); // prints Hello World
Now I am trying to change that value with asadmin:
martin#bono:~/glassfish4/glassfish/bin$ ./asadmin set-web-env-entry --name=aMessage --value=test webapp
Previous env-entry setting of aMessage for application/module webapp was overridden.
Command set-web-env-entry executed successfully.
martin#bono:~/glassfish4/glassfish/bin$ ./asadmin list-web-env-entry webapp
Reported 1 env-entry setting
aMessage (java.lang.String) = test ignoreDescriptorItem=true //
Command list-web-env-entry executed successfully.
Unfortunately my EJB still prints the old value "Hello World", even after re-enabling this webapp or restarting webserver.
I've also tried to set-web-env-entry for names not defined in web.xml and also played with --ignoredescriptoritem parameter, but nothing helped. Enumerating whole environment also shows no additional or changed web environment entries, but shows he old one plus many other objects not related to this problem:
final NamingEnumeration<Binding> enu = env.listBindings("");
while (enu.hasMore()) {
final Binding binding = enu.next();
System.out.println(binding);
}
What I am doing wrong?
It seems to be a bug - but I have another solution for your needs. You can use custom resources which are available in glassfish. You have to declare custom resource in domain.xml
<resources>
<custom-resource factory-class="org.glassfish.resources.custom.factory.PropertiesFactory" res-type="java.util.Properties" jndi-name="test/properties">
<property name="aMessage" value="Hello World"></property>
</custom-resource>
</resources>
then you can use it in the code
public class Environment
{
public String getProperty() {
InitialContext ctx = new InitialContext();
properties = (Properties) ctx.lookup("test/properties");
if(properties == null) {
return "default value - hello";
}
return properties.getProperty("aMessage");
}
}
The one disadvantage of this approach is that custom resources are global for whole domain. But this solution has advantage you can change resources by using asadmin and admin web console also.
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.
I created a small junit test that is launched from my application client.
My server is glassfish 3.1.1 .
When i run the test i get this error:
javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial
I have already added the propreties to the context but nothing changed.
This how i set up my context:
#Before
public void setUp() throws NamingException {
Properties props = new Properties();
System.out.println("launch");
props.setProperty("java.naming.factory.initial",
"com.sun.enterprise.naming.SerialInitContextFactory");
props.setProperty("java.naming.factory.url.pkgs",
"com.sun.enterprise.naming");
props.setProperty("java.naming.factory.state",
"com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl");
// optional. Defaults to localhost. Only needed if web server is running
// on a different host than the appserver
//props.setProperty("org.omg.CORBA.ORBInitialHost", "localhost");
// optional. Defaults to 3700. Only needed if target orb port is not 3700.
//props.setProperty("org.omg.CORBA.ORBInitialPort", "3700");
Context annuaire = new InitialContext(props);
GeoBeanRemote service = (GeoBeanRemote) annuaire.lookup(GeoBeanRemote.class.getName());
BeanProvider.setGeoBean(service);
}
app-serv.rt and gf-client are added to my run-test.
Thanks.
I've the same issue when jndi.properties was missing from my classpath.
Addendum:
I'm feeling a bit silly here, but I don't think you can set your initial context that way. I note though that JNDI properties don't even need to be set on Glassfish, so color me confused.
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).