For testing purposes, I'm looking for a simple way to start a standalone JNDI server, and bind my javax.sql.DataSource to "java:/comp/env/jdbc/mydatasource"
programmatically.
The server should bind itself to some URL, for example: "java.naming.provider.url=jnp://localhost:1099" (doesn't have to be JNP), so that I can look up my datasource from another process. I don't care about which JNDI server implementation I'll have to use (but I don't want to start a full-blown JavaEE server).
This should be so easy, but to my surprise, I couldn't find any (working) tutorial.
The Java Naming and Directory InterfaceTM (JNDI) is an application programming interface (API) that provides naming and directory functionality to applications written using the JavaTM programming language. It is defined to be independent of any specific directory service implementation.
A JNDI DataSource object is a file that contains the configuration details necessary to connect to a database. The DataSource object must be registered on a JNDI server, where it is identified using a JNDI name. You can register your DataSource object directly on your application server via its JNDI service.
JNDI is an API used to access the directory and naming services (i.e. the means by which names are associated with objects). The association of a name with an object is called a binding. A basic example of a naming service is DNS which maps machine names to IP addresses.
It is an API to providing access to a directory service, that is, a service mapping name (strings) with objects, reference to remote objects or simple data. This is called binding. The set of bindings is called the context. Applications use the JNDI interface to access resources.
The JDK contains a JNDI provider for the RMI registry. That means you can use the RMI registry as a JNDI server. So, just start rmiregistry, set java.naming.factory.initial to com.sun.jndi.rmi.registry.RegistryContextFactory, and you're away.
The RMI registry has a flat namespace, so you won't be able to bind to java:/comp/env/jdbc/mydatasource, but you will be able to bind to something so it will accept java:/comp/env/jdbc/mydatasource, but will treat it as a single-component name (thanks, @EJP).
I've written a small application to demonstrate how to do this: https://bitbucket.org/twic/jndiserver/src
I still have no idea how the JNP server is supposed to work.
I worked on the John´s code and now is working good.
In this version I'm using libs of JBoss5.1.0.GA, see jar list below:
This is the new code:
import java.net.InetAddress;
import java.util.Hashtable;
import java.util.concurrent.Callable;
import javax.naming.Context;
import javax.naming.InitialContext;
import org.jnp.server.Main;
import org.jnp.server.NamingBeanImpl;
public class StandaloneJNDIServer implements Callable<Object> {
public Object call() throws Exception {
setup();
return null;
}
@SuppressWarnings("unchecked")
private void setup() throws Exception {
//configure the initial factory
//**in John´s code we did not have this**
System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
//start the naming info bean
final NamingBeanImpl _naming = new NamingBeanImpl();
_naming.start();
//start the jnp serve
final Main _server = new Main();
_server.setNamingInfo(_naming);
_server.setPort(5400);
_server.setBindAddress(InetAddress.getLocalHost().getHostName());
_server.start();
//configure the environment for initial context
final Hashtable _properties = new Hashtable();
_properties.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
_properties.put(Context.PROVIDER_URL, "jnp://10.10.10.200:5400");
//bind a name
final Context _context = new InitialContext(_properties);
_context.bind("jdbc", "myJDBC");
}
public static void main(String...args){
try{
new StandaloneJNDIServer().call();
}catch(Exception _e){
_e.printStackTrace();
}
}
}
To have good logging, use this log4j properties:
log4j.rootLogger=TRACE, A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
To consume the Standalone JNDI server, use this client class:
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.InitialContext;
/**
*
* @author fabiojm - Fábio José de Moraes
*
*/
public class Lookup {
public Lookup(){
}
@SuppressWarnings("unchecked")
public static void main(String[] args) {
final Hashtable _properties = new Hashtable();
_properties.put("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory");
_properties.put("java.naming.provider.url", "jnp://10.10.10.200:5400");
try{
final Context _context = new InitialContext(_properties);
System.out.println(_context);
System.out.println(_context.lookup("java:comp"));
System.out.println(_context.lookup("java:jdbc"));
}catch(Exception _e){
_e.printStackTrace();
}
}
}
Here's a code snippet adapted from JBoss remoting samples. The code that is in the samples (version 2.5.4.SP2 ) no longer works. While the fix is simple it took me more hours than I want to think about to figure it out. Sigh. Anyway, maybe someone can benefit.
package org.jboss.remoting.samples.detection.jndi.custom;
import java.net.InetAddress;
import java.util.concurrent.Callable;
import org.jnp.server.Main;
import org.jnp.server.NamingBeanImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class StandaloneJNDIServer implements Callable<Object> {
private static Logger logger = LoggerFactory.getLogger( StandaloneJNDIServer.class );
// Default locator values - command line args can override transport and port
private static String transport = "socket";
private static String host = "localhost";
private static int port = 5400;
private int detectorPort = 5400;
public StandaloneJNDIServer() {}
@Override
public Object call() throws Exception {
StandaloneJNDIServer.println("Starting JNDI server... to stop this server, kill it manually via Control-C");
//StandaloneJNDIServer server = new StandaloneJNDIServer();
try {
this.setupJNDIServer();
// wait forever, let the user kill us at any point (at which point, the client will detect we went down)
while(true) {
Thread.sleep(1000);
}
}
catch(Exception e) {
e.printStackTrace();
}
StandaloneJNDIServer.println("Stopping JBoss/Remoting server");
return null;
}
private void setupJNDIServer() throws Exception
{
// start JNDI server
String detectorHost = InetAddress.getLocalHost().getHostName();
Main JNDIServer = new Main();
// Next two lines add a naming implemention into
// the server object that handles requests. Without this you get a nice NPE.
NamingBeanImpl namingInfo = new NamingBeanImpl();
namingInfo.start();
JNDIServer.setNamingInfo( namingInfo );
JNDIServer.setPort( detectorPort );
JNDIServer.setBindAddress(detectorHost);
JNDIServer.start();
System.out.println("Started JNDI server on " + detectorHost + ":" + detectorPort );
}
/**
* Outputs a message to stdout.
*
* @param msg the message to output
*/
public static void println(String msg)
{
System.out.println(new java.util.Date() + ": [SERVER]: " + msg);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With