Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Easy way to start a standalone JNDI server (and register some resources)

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.

like image 231
Chris Lercher Avatar asked May 02 '11 20:05

Chris Lercher


People also ask

What is a JNDI server?

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.

How JNDI DataSource works?

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.

What is JNDI example?

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.

What is a JNDI string?

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.


3 Answers

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.

like image 200
Tom Anderson Avatar answered Sep 18 '22 21:09

Tom Anderson


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:

  • jboss-5.1.0.GA\client\jbossall-client.jar
  • jboss-5.1.0.GA\server\minimal\lib\jnpserver.jar
  • jboss-5.1.0.GA\server\minimal\lib\log4j.jar
  • jboss-remote-naming-1.0.1.Final.jar (downloaded from http://search.maven.com)

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();
    }
}

}
like image 26
Fábio Avatar answered Sep 19 '22 21:09

Fábio


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);
    } 
}
like image 30
John Goyer Avatar answered Sep 18 '22 21:09

John Goyer



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!