feedburner
Enter your email address:

Delivered by FeedBurner

feedburner count
Showing posts with label JBoss. Show all posts
Showing posts with label JBoss. Show all posts

How to use JNDI with your J2SE application

Labels: , , , , ,

Have you ever wished you could have a JNDI (Java Naming and Directory Interface) server in your J2SE application? Do you need to unit test a Java class which gets its resources from JNDI? Well, you can. One of the nice things about JBoss is that it's Java open source project and that it's built from different components which can be used own their own. One of these components is the Java Naming Provider (JNP). Using the JBoss JNP we can add naming abilities to our J2SE application.

What we need is the following Java jars files:

   jnpserver.jar
jbossall-client.jar
log4j.jar
Of course we need to have a log4j configuration file (log4j.properties or log4j.xml) in our Java classpath.

In order to start the JNP we need to write the following code
  System.setProperty("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory");

NamingBeanImpl jnpServer = new NamingBeanImpl();
jnpServer.start();

This will start the JNP in a different thread.

We can put the following jndi.properties file in the classpath
   java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
and then we can remove the System.setProperty(...) from the code.

Now lets see how we can use it to test JDBC code which gets its connection to the database from a DataSource object which is bound to JNDI Using JUnit and DbUnit.
Here is the class which we want to test:
public class DatabaseClass {

public static final String DB_JNDI_NAME = ...

public Connection getConnection() throws SQLException, NamingException {
Context ctx = new InitialContext();
DataSource ds = (DataSource) ctx.lookup(DB_JNDI_NAME);
return ds.getConnection();
}

public void insertData() {

Connection con = null;
try {
con = getConnection();
...
...
}
...
}

You can see that the method gets the connection to the database from JNDI.
Here is the JUnit test class:
public class TestClass {

@Test
public void testData() throws Exception {
System.setProperty("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory");

DataSource ds = new EmbeddedDataSource();
((EmbeddedDataSource) ds).setUser("");
((EmbeddedDataSource) ds).setPassword("");
((EmbeddedDataSource) ds).setDatabaseName("testdb");
((EmbeddedDataSource) ds).setCreateDatabase("create");

NamingBeanImpl jnp = new NamingBeanImpl();
jnp.start();

Context initContext = new InitialContext();
initContext.createSubcontext(DatabaseClass.DB_JNDI_NAME);
initContext.rebind(DatabaseClass.DB_JNDI_NAME, ds);

DatabaseClass db = new DatabaseClass();
db.insertData();

// assert the database content using DbUnit
}
}

Then we can use DbUnit to assert the content in the database with the expected content. This way we can unit test the Class although it relies on the J2EE application server naming service.

Some problems you might encounter:
  Exception in thread "main" java.lang.NoClassDefFoundError: org/jboss/logging/Logger at org.jnp.server.NamingBeanImpl.(NamingBeanImpl.java:48)
You need to add jbossall-client.jar to your classpath.

I used to run the JNP by writing the following Java code:
  org.jnp.server.Main.main(new String[] {});
as opposed to
   NamingBeanImpl jnpServer = new NamingBeanImpl(); jnpServer.start();
The last time I tried, I got the following exception:
  java.lang.NullPointerException
at org.jnp.server.Main.getNamingInstance(Main.java:301)
at org.jnp.server.Main.initJnpInvoker(Main.java:354)
at org.jnp.server.Main.start(Main.java:316)
at org.jnp.server.Main.main(Main.java:104)
Looking at the source code I noticed the NamingBeanImpl class and Scott Stark comment
  * A naming pojo that wraps the Naming server implementation. This is
* a refactoring of the legacy org.jnp.server
Using the NamingBeanImpl class I managed to solve the problem.

How to share objects between different classloaders on Terracotta

Labels: , , ,

Object identity in Terracotta is based on a combination of classloader name and object reference. When using Terracotta to share an object between different applications where the object class is loaded by classloaders with different names, exception is thrown. In my case I shared an object between deployed ear application on JBoss (app-ee.ear) and a console application.

Exception in thread "Thread-10" com.tc.exception.TCClassNotFoundException: java.lang.ClassNotFoundException: No registered loader for description: JBoss.UnifiedClassLoader3:deploy/app-ee.ear, trying to load org.terracotta.datagrid.workmanager.routing.RoutableWorkItem
at com.tc.object.bytecode.ManagerUtil.lookupObject(ManagerUtil.java:335)
at java.util.concurrent.LinkedBlockingQueue$Node.getItem(LinkedBlockingQueue.java:65)
at java.util.concurrent.LinkedBlockingQueue.extract(LinkedBlockingQueue.java:139)
at java.util.concurrent.LinkedBlockingQueue.poll(LinkedBlockingQueue.java:386)
at org.terracotta.datagrid.workmanager.routing.RoutingAwareWorker.start(RoutingAwareWorker.java:46)
at com.shimi.example.StartRoutingWorker$1.run(StartRoutingWorker.java:20)
Caused by: java.lang.ClassNotFoundException: No registered loader for description: JBoss.UnifiedClassLoader3:deploy/app-ee.ear, trying to load org.terracotta.datagrid.workmanager.routing.RoutableWorkItem
at com.tc.object.loaders.StandardClassProvider.getClassFor(StandardClassProvider.java:43)
at com.tc.object.ClientObjectManagerImpl.lookup(ClientObjectManagerImpl.java:522)
at com.tc.object.ClientObjectManagerImpl.lookupObject(ClientObjectManagerImpl.java:423)
at com.tc.object.ClientObjectManagerImpl.lookupObject(ClientObjectManagerImpl.java:412)
at com.tc.object.bytecode.ManagerImpl.lookupObject(ManagerImpl.java:651)
at com.tc.object.bytecode.ManagerUtil.lookupObject(ManagerUtil.java:333)
... 5 more


In order to solve the problem, all the VMs which are synchronized by Terracotta needs to load the shared class with the same classloader name. Since the name of the classloader which loaded the class on JBoss was JBoss.UnifiedClassLoader3:deploy/app-ee.ear, I needed to load the class on the console application using a classloader with the same name. I used a wrapper main class around the application main class. The wrapper class rename the system classloader using Terracotta NamedClassLoader and start the application.



import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import com."com/caucho/loader/EnvironmentClassLoader"tc.object.bytecode.hook.impl.ClassProcessorHelper;
import com.tc.object.loaders.NamedClassLoader;

public class CustomLoader {

// usage: java CustomLoader com.example.MyRealMainClass [arg1] [arg2]

public static void main(String[] args) throws Exception {
URL[] systemURLs = ((URLClassLoader) ClassLoader.getSystemClassLoader()).getURLs();

ClassLoader loader = new URLClassLoader(systemURLs, ClassLoader
.getSystemClassLoader().getParent());

// here is the custom classloader name
((NamedClassLoader) loader).__tc_setClassLoaderName("JBoss.UnifiedClassLoader3:deploy/app-ee.ear");
ClassProcessorHelper.registerGlobalLoader((NamedClassLoader) loader);

Thread.currentThread().setContextClassLoader(loader);
Class mainClass = loader.loadClass(args[0]);

Method main = mainClass.getMethod("main", new Class[] { args.getClass() });

String[] nextArgs = new String[args.length - 1];
System.arraycopy(args, 0, nextArgs, 0, nextArgs.length);
main.invoke(null, new Object[] { nextArgs });
}
}