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.
... finden Sie zum Beispiel unter:
jee-ejb2.htm#Beispiel-StatefulSessionBean
jee-ejb2.htm#Beispiel-CMP-EntityBean
jee-jms.htm#QueueRequestor-Beispiel
jee-transaktionen.htm#Client-gesteuerte-Transaktion-mit-JTA
jee-oracleweblogic.htm#Webanwendung
jee-ibmwebsphere.htm#Webanwendung
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
Sie benötigen einen installierten Java EE Application Server, zum Beispiel Sun GlassFish, JBoss, Oracle WebLogic oder IBM WebSphere.
Ü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 |
Ü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) |
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]
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 |
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
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 } } }
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 |
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
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>" ); } }
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>
Das Servlet können Sie beliebigen Webanwendungen hinzufügen, zum Beispiel der Webanwendung mit Servlet, JSP, JavaBean, ....
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.