JNDI (Java Naming and Directory Interface)

+ andere TechDocs
+ EJB
+ Sun JNDI
+


Java EE (Java Platform, Enterprise Edition) ist eine durch Schnittstellen definierte Architektur für Unternehmensanwendungen, bestehend aus verschiedenen Komponenten.

JNDI (Java Naming and Directory Interface) ist ein Teil der Java-EE-Spezifikation und bietet einen Namens- und Verzeichnisdienst, über den Objekte und Dienste gefunden und verfügbar gemacht werden.



Inhalt

  1. Programmierbeispiele zur Verwendung des JNDI
  2. Programmierbeispiel zum Auslesen der Namen im JNDI-Context
    1. Vorbereitungen
    2. JNDI-Auslese-Programm
    3. JNDI-Auslese-Servlet
  3. Service Locator
  4. Links auf weiterführende Informationen


Programmierbeispiele zur Verwendung des JNDI

... finden Sie zum Beispiel unter:

jee-ejb2.htm#Beispiel-StatefulSessionBean

jee-ejb2.htm#Beispiel-CMP-EntityBean

jee-ejb2.htm#Beispiel-MDB

jee-jms.htm#Queue-Beispiel

jee-jms.htm#Topic-Beispiel

jee-jms.htm#QueueRequestor-Beispiel

jee-transaktionen.htm#CMT

jee-transaktionen.htm#Client-gesteuerte-Transaktion-mit-JTA

jee-oracleweblogic.htm#Webanwendung

jee-ibmwebsphere.htm#Webanwendung



Programmierbeispiel zum Auslesen der Namen im JNDI-Context

Besonders zu Beginn kann es sehr hilfreich sein, die genauen Namen aller JNDI-Einträge anzeigen zu lassen. Zum Beispiel der Name für das 'UserTransaction'-Objekt heißt bei den verschiedenen Java EE Application Servern unterschiedlich.


Vorbereitungen

  1. Sie benötigen ein installiertes aktuelles Java SE JDK.
  2. Sie benötigen einen installierten Java EE Application Server, zum Beispiel Sun GlassFish, JBoss, Oracle WebLogic oder IBM WebSphere.

  3. Überprüfen Sie die Installation. Starten und stoppen Sie den Server je nach Java EE Application Server, Plattform und Installationsart zum Beispiel mit Kommandos ähnlich zu:

    GlassFish: GlassFish\bin\asadmin start-domain domain1
    GlassFish\bin\asadmin stop-domain domain1
    JBoss: JBoss\bin\run.bat
    JBoss\bin\shutdown.bat -S
    Oracle WebLogic: WebLogic\user_projects\domains\MeineDomain\bin\startWebLogic.cmd
    WebLogic\user_projects\domains\MeineDomain\bin\stopWebLogic.cmd
    IBM WebSphere: cmd.exe /c "\IbmWebSphere\AppServer\profiles\AppSrv01\bin\startServer.bat" server1 -profileName AppSrv01
    cmd.exe /c "\IbmWebSphere\AppServer\profiles\AppSrv01\bin\stopServer.bat" server1 -profileName AppSrv01
    Geronimo: Geronimo\bin\startup.bat
    Geronimo\bin\shutdown.bat
  4. Überprüfen Sie auch die Administrator-Management-Konsole. Je nach Java EE Application Server, Plattform und Installationsart zum Beispiel über folgende URLs:

    GlassFish V2: http://localhost:4848 (usr=admin, pwd=adminadmin)
    JBoss: http://localhost:8080/web-console
    Oracle WebLogic: http://localhost:7001/console
    IBM WebSphere: http://localhost:9060/ibm/console oder
    https://localhost:9043/admin
    Geronimo: http://localhost:8080/console (usr=system, pwd=manager)
  5. Legen Sie eine Projektverzeichnisstruktur an, zum Beispiel so:

    [\MeinWorkspace]
      '- [MeinJndiProjekt]
           |- [bin]
           |- [lib]
           |    '- ... eventuell die zum Java EE Application Server passende .jar-Lib (z.B. jbossall-client.jar)
           '- [src]
                '- [jnditest]
    
  6. Bei einigen Java EE Application Servern (z.B. GlassFish und JBoss) können Sie die benötigten AppServer-spezifischen .jar-Libs kopieren (z.B. in das Unterverzeichnis <MeinJndiProjekt>/lib). Bei anderen Java EE Application Servern (z.B. WebLogic 10.3) müssen Sie eine bestimmte .jar-Lib aus dem AppServer-Verzeichnis dem Classpath hinzugefügen, weil die Libs weitere Dateien in AppServer-Verzeichnissen benötigen. Alternativ können Sie bei einigen AppServern auch eine komplette .jar-Lib erstellen, z.B. für WebLogic eine wlfullclient.jar oder für WebSphere eine AppClient-Lib.

    für App-Server .jar-Lib AppServer-lib-Verzeichnis
    GlassFish V2: appserv-deployment-client.jar, appserv-ext.jar, appserv-rt.jar, javaee.jar
    (siehe auch META-INF/MANIFEST.MF in appserv-rt.jar)
    GlassFish\lib
    JBoss 4.0: jbossall-client.jar JBoss\client
    Oracle WebLogic 10.3: weblogic.jar (oder wlclient.jar) WebLogic\wlserver_10.3\server\lib
    (Alternativ können Sie eine wlfullclient.jar erstellen)
    IBM WebSphere 6.1: com.ibm.ws.runtime_6.1.0.jar,
    com.ibm.wsspi.extension_6.1.0.jar,
    ibmorb.jar, orbProperties.jar,
    org.eclipse.core.runtime_3.1.2.jar,
    osgi.jar, rsahelpers.jar,
    ws_runtime.jar
    IbmWebSphere\AppServer\bin\ProfileManagement,
    IbmWebSphere\AppServer\java\jre\lib,
    IbmWebSphere\AppServer\lib,
    IbmWebSphere\AppServer\plugins,
    IbmWebSphere\AppServer\systemApps
    (Alternativ können Sie eine AppClient-Lib erstellen)
    Geronimo: openejb-client-3.0-...jar, geronimo-ejb_3.0_spec-...jar Geronimo\repository\org\apache\openejb\openejb-client, Geronimo\repository\org\apache\geronimo\specs\geronimo-ejb_3.0_spec
  7. Für den JNDI-Zugriff sind Voreinstellungen notwendig. Eine Möglichkeit ist, die Parameter im Java-Code in einer 'HashTable' dem 'InitialContext()'-Konstruktor direkt zu übergeben. Siehe hierzu das Programmierbeispiel: jee-transaktionen.htm#Client-JTA-DataSource.

    Der üblichere weil flexiblere Weg ist eine 'jndi.properties'-Datei im Classpath anzulegen.

    Legen Sie im Projektverzeichnis '<MeinJndiProjekt>' eine zum verwendeten Java EE Application Server passende 'jndi.properties'-Datei an (Passen Sie den 'java.naming.provider.url'-Eintrag an die Adresse Ihres Java EE Application Servers an). Beispiele:

    Für GlassFish V2:

    org.omg.CORBA.ORBInitialHost=localhost
    org.omg.CORBA.ORBInitialPort=3700
    java.naming.factory.initial=com.sun.enterprise.naming.SerialInitContextFactory
    java.naming.factory.url.pkgs=com.sun.enterprise.naming
    java.naming.factory.state=com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl
    

    (Siehe auch jndi.properties in appserv-rt.jar.)

    Für JBoss:

    java.naming.provider.url=jnp://localhost:1099
    java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
    java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
    jnp.socket.Factory=org.jnp.interfaces.TimedSocketFactory
    

    Für Oracle WebLogic:

    java.naming.provider.url=t3://localhost:7001
    java.naming.factory.initial=weblogic.jndi.WLInitialContextFactory
    

    Für IBM WebSphere über IBM-WebSphere-Libs:

    java.naming.provider.url=corbaloc:iiop:localhost:2809
    java.naming.factory.initial=com.ibm.websphere.naming.WsnInitialContextFactory
    

    Für IBM WebSphere über Sun-j2ee.jar-Lib:

    java.naming.provider.url=iiop://localhost:2809
    java.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory
    

    (Siehe auch: http://publib.boulder.ibm.com/infocenter/.../tnam_develop_naming.html.)

    Für Geronimo:

    java.naming.provider.url=127.0.0.1:4201
    java.naming.factory.initial=org.openejb.client.RemoteInitialContextFactory
    

    (Eventuell in der 'j2ee-server-plan.xml' unter 'allowHosts' erlaubten IP-Adressbereich eintragen.)

    Für alle: Mit Anmelde-Account:

    java.naming.provider.url=...
    java.naming.factory.initial=...
    java.naming.security.principal=meinAppSrvBenutzername
    java.naming.security.credentials=meinAppSrvKennwort
    

JNDI-Auslese-Programm

  1. Die folgende Testroutine zeigt eine Liste der Namen im aktuellen JNDI-Context.

    Speichern Sie im Verzeichnis '<MeinJndiProjekt>\src\jnditest' die folgende Datei 'MeinJndiContext.java':

    package jnditest;
    
    import javax.naming.*;
    
    public class MeinJndiContext
    {
       public static void main( String[] args ) throws NamingException
       {
          Context ctx = new InitialContext();
          System.out.println( "\nJNDI-Context-Listing:\n" );
          showJndiContext( ctx, "", "" );
          ctx.close();
       }
    
       public static void showJndiContext( Context ctx, String name, String space )
       {
          if( null == name  ) name  = "";
          if( null == space ) space = "";
          try {
             NamingEnumeration<NameClassPair> en = ctx.list( name );
             while( en != null && en.hasMoreElements() ) {
                String delim = ( name.length() > 0 ) ? "/" : "";
                NameClassPair ncp = en.next();
                System.out.println( space + name + delim + ncp );
                if( space.length() < 40 )
                   showJndiContext( ctx, ncp.getName(), "    " + space );
             }
          } catch( javax.naming.NamingException ex ) {
             // Normalerweise zu ignorieren
          }
       }
    }
    
  2. Im Projektverzeichnis '<MeinJndiProjekt>' muss sich die passende 'jndi.properties'-Datei befinden.

    Bei laufendem Java EE Application Server öffnen Sie ein Kommandozeilenfenster und geben die folgenden Kommandos ein, nachdem Sie diese an Ihren Java EE Application Server angepasst haben, z.B. mit den oben genannten zum AppServer passenden AppServer-Client-Libs:

    cd \MeinWorkspace\MeinJndiProjekt 
    set CLASSPATH=.;bin;lib/* [falls AppServer-Lib kopiert wurde]
    set CLASSPATH=.;bin;lib/*;C:/WebLogic/wlserver_10.3/server/lib/weblogic.jar [falls WebLogic]
    javac -d bin src/jnditest/MeinJndiContext.java 
    java jnditest.MeinJndiContext 
  3. Falls Sie keine Ausgabe erhalten, können Sie Folgendes probieren:

    Um die erkannten Context-Properties zu überprüfen, fügen Sie hinter der Zeile 'InitialContext ctx = new InitialContext();' folgende zusätzliche Zeile ein:
    System.out.println( ctx.getEnvironment() ); 

    Um eventuell ungewollte Exceptions zu sehen, fügen Sie hinter der Zeile '} catch( javax.naming.NamingException ex ) {' folgende zusätzliche Zeile ein:
    System.out.println( ex ); 

    Falls Sie eine Fehlermeldung ähnlich zu 'java.lang.NoClassDefFoundError' oder 'javax.naming.NoInitialContextException: Cannot instantiate class' erhalten, fehlt eine benötigte .jar-Library (z.B. 'jbossall-client.jar' bzw. 'weblogic.jar', ...) (entweder im 'lib'-Verzeichnis oder in der 'CLASSPATH'-Angabe).

    Falls Sie eine Fehlermeldung ähnlich zu 'javax.naming.NoInitialContextException: Need to specify class name ...: java.naming.factory.initial' erhalten, befindet sich das Verzeichnis von 'jndi.properties' nicht im Classpath.

    Falls JNDI nicht funktioniert: Fahren Sie den Java EE Application Server herunter und überprüfen Sie, ob der JNDI-Port (z.B. für JBoss: 1099) durch ein anderes Programm belegt ist. Rufen Sie hierfür in einem Kommandozeilenfenster zum Beispiel netstat -a, netstat -a -b -n oder telnet 127.0.0.1 1099 auf.
    Je nach Java EE Application Server kann der JNDI-Port eventuell notfalls verstellt werden (z.B. bei JBoss über die JBoss-NamingService-MBean entweder über die JMX Console oder die entsprechende jboss-xxx.xml-Datei).


JNDI-Auslese-Servlet

  1. Manche Java EE Application Server listen nicht alle Einträge, wenn sie von außerhalb abgefragt werden. Dann kann es sinnvoll sein, die Abfrage als Servlet zu implementieren, wie das folgende Beispiel zeigt:

    package ...;
    
    import java.io.*;
    import javax.naming.*;
    import javax.servlet.ServletException;
    import javax.servlet.http.*;
    
    public class MeinJndiContextServlet extends HttpServlet
    {
       private static final long serialVersionUID = 1L;
    
       @Override
       protected void doGet( HttpServletRequest req, HttpServletResponse resp ) throws ServletException, IOException
       {
          PrintWriter writer = resp.getWriter();
          try {
             writer.println( "<html><head><title>JNDI-Context-Listing</title></head>" );
             writer.println( "<body><h3>JNDI-Context-Listing</h3>" );
             Context ctx = new InitialContext();
             printJndiContextAsHtmlList( writer, ctx, "" );
             ctx.close();
             writer.println( "</body>" );
             writer.println( "</html>" );
             writer.flush();
          } catch( Exception ex ) {
             throw new ServletException( ex );
          } finally {
             writer.close();
          }
       }
    
       public void printJndiContextAsHtmlList( PrintWriter writer, Context ctx, String name )
       {
          writer.println( "<ul>" );
          try {
             NamingEnumeration<Binding> en = ctx.listBindings( "" );
             while( en != null && en.hasMoreElements() ) {
                Binding binding = en.next();
                String name2 = name + (( name.length() > 0 ) ? "/" : "") + binding.getName();
                writer.println( "<li><u>" + name2 + "</u>: " + binding.getClassName() + "</li>" );
                if( binding.getObject() instanceof Context ) {
                   printJndiContextAsHtmlList( writer, (Context) binding.getObject(), name2 );
                }
             }
          } catch( NamingException ex ) {
             // Normalerweise zu ignorieren
          }
          writer.println( "</ul>" );
       }
    }
    
  2. Das Servlet muss in die web.xml eingetragen werden, zum Beispiel durch folgenden Block (bitte "..." durch den Package-Pfad ersetzen):

      <servlet>
        <servlet-name>MeinJndiContext</servlet-name>
        <servlet-class>...MeinJndiContextServlet</servlet-class>
      </servlet>
      <servlet-mapping>
        <servlet-name>MeinJndiContext</servlet-name>
        <url-pattern>/MeinJndiContext</url-pattern>
      </servlet-mapping>
    
  3. Das Servlet können Sie beliebigen Webanwendungen hinzufügen, zum Beispiel der Webanwendung mit Servlet, JSP, JavaBean, ....



Service Locator (für EJB 2)

Bei Remote-Zugriffen müssen Referenzen auf entfernete Objekte erzeugt werden.
Eine Sequenz zur Erlangung einer EJB2-Referenz lautet zum Beispiel:
Context ctx = new InitialContext();
Object ref = ctx.lookup("java:comp/env/ejb/MeinEjbJndiName");
IMeinEjbNameHome home = (IMeinEjbNameHome)PortableRemoteObject.narrow(ref,IMeinEjbNameHome.class);
IMeinEjbName meinEjbName = home.create();

Das Initialisieren des 'InitialContext', der 'lookup()' im 'InitialContext' und der 'PortableRemoteObject.narrow()'-Aufruf können mehrere Sekunden dauern. Damit diese Vorgänge nur einmal stattfinden, sollten die 'EJBHome'-Objekte gecacht werden.

Dies wird als 'Service Locator Pattern' bezeichnet (manchmal auch als EJBHome Factory), siehe 'sw-patterns.htm#ServiceLocator' und 'http://java.sun.com/blueprints/corej2eepatterns/Patterns/ServiceLocator.html'.

Eine Realisierung könnte zum Beispiel folgendermaßen aussehen:

import java.io.Serializable;
import java.rmi.RemoteException;
import java.util.*;
import javax.ejb.*;
import javax.naming.*;
import javax.rmi.PortableRemoteObject;

public class ServiceLocator implements Serializable
{
  private static ServiceLocator instance       = null;
  private Context               context        = null;
  private Map                   homeCache      = null;
  private Map                   localHomeCache = null;

  private ServiceLocator() throws NamingException
  {
    context        = new InitialContext();
    homeCache      = Collections.synchronizedMap( new HashMap() );
    localHomeCache = Collections.synchronizedMap( new HashMap() );
  }

  public static synchronized ServiceLocator getInstance() throws NamingException
  {
    if( instance == null ) instance = new ServiceLocator();
    return instance;
  }

  public EJBHome getHome( Class homeClass ) throws NamingException, RemoteException
  {
    EJBHome home = (EJBHome) homeCache.get( homeClass.getName() );
    if( home != null )
      return home;
    String name = "java:comp/env/ejb/" + homeClass.getName().replace( '.', '/' );
    Object ref = context.lookup( name );
    EJBMetaData meta = ((EJBHome) ref).getEJBMetaData();
    home = (EJBHome) PortableRemoteObject.narrow( ref, homeClass );
    if( !meta.isSession() || meta.isStatelessSession() )
      homeCache.put( homeClass.getName(), home );
    return home;
  }

  public EJBHome getHome( String name ) throws NamingException, RemoteException
  {
    EJBHome home = (EJBHome) homeCache.get( name );
    if( home != null )
      return home;
    Object ref = context.lookup( name );
    EJBMetaData meta = ((EJBHome) ref).getEJBMetaData();
    home = (EJBHome) PortableRemoteObject.narrow( ref, meta.getHomeInterfaceClass() );
    if( !meta.isSession() || meta.isStatelessSession() )
      homeCache.put( name, home );
    return home;
  }

  public EJBLocalHome getLocalHome( String name ) throws NamingException
  {
    EJBLocalHome home = (EJBLocalHome) localHomeCache.get( name );
    if( home != null )
      return home;
    home = (EJBLocalHome) context.lookup( name );
    localHomeCache.put( name, home );
    return home;
  }

  public static void main( String[] args ) throws Exception
  {
    ServiceLocator locator = ServiceLocator.getInstance();
    EJBHome home = locator.getHome( ( 0<args.length ) ? args[0] : "UserManager" );
    System.out.println( "EJBHome Class Name: " + home.getClass().getName() );
  }
}

Von den beiden Alternativen 'getHome( Class homeClass )' und 'getHome( String name )' sollte nur eine verwendet werden.

Der Präfix-String "java:comp/env/ejb/" muss eventuell angepasst werden.



Links auf weiterführende Informationen





Weitere Themen: andere TechDocs | JSP | EJB | SQL
© 1998-2007 Torsten Horn, Aachen