In This Chapter
The Java Naming and Directory Interface (JNDI) provides the interface to a naming and directory service for Java applications including J2EE technology. It is extremely important to understand that JNDI does not provide the specification for the actual naming service; rather, it provides an independent, neutral and unified interface to the provider of the actual naming service. Its purpose, as used by J2EE, is to enable Java applications to locate remote objects on the network. The semantics of a naming service are very much like those of a hash table, where a unique name is paired with an object. The binding between the name and object is stored persistently by the service provider being accessed through JNDI. Examples of JNDI service providers include the Lightweight Directory Access Protocol (LDAP) server, Domain Naming Service (DNS), Network Information Service (NIS) and the RMI Registry.
The naming service provider allows the object to be retrieved by simply looking up its JNDI name, which is set by the object’s deployer in the deployment descriptor. The naming service is an integral part of all distributed processing middleware implementing RMI, CORBA or J2EE technologies. With any of these technologies, the typical scenario for a client to use a remote object is as follows:
The remote interface must be registered with the naming service.
The client uses JNDI to look up the registered name in the naming service and retrieve the remote object that is bound to that name.
The return value is an object that implements the remote interface.
The client uses the remote interface to communicate with the remote object.
As illustrated in Figure 14.1, JNDI provides two interfaces between a Java application and a naming or directory service, as follows:
Figure 14.1. The JNDI architectural framework provides a common API to a variety of naming and directory services.
The Application Programming Interface (API) can be used by a developer to access a naming or directory service from within a client application.
The Service Provider Interface (SPI) provides the capability for a J2EE Server to be independent of any specific industry naming or directory service provider. The SPI is plugged into the JNDI framework and provides a mapping from the JNDI API to the naming or directory service provider. Access to the service provider from the JNDI API is provided via a naming manager, which is typically provided by the J2EE server implementing the JNDI service, as in the case of WebLogic Server.
Further descriptions of the JNDI APIs and SPIs are provided in their respective sections later in this chapter.
Hence, the JNDI architecture is independent of any specific service provider and therefore supports access to a variety of existing and emerging naming and directory services.
The Java 2 SDK version 1.3 and later releases include the JNDI classes and the three Service Provider Interfaces—LDAP, COS, and RMI—shown in Figure 14.1. JNDI is also available as a Java Standard Extension for use with JDK 1.1 and Java 2 SDK 1.2. WebLogic Server includes these three standard SPIs, as well as the WebLogic SPI for WebLogic Server’s embedded naming service.
The four Service Provider Interfaces included in WebLogic Server are:
LDAP SPI—. The Service Provider Interface for an LDAP server
COS SPI—. The Service Provider Interface for the Common Object Services used by CORBA
RMI SPI—. The Service Provider Interface used by RMI to contact the rmiregistry
WebLogic SPI—. The implemented Service Provider Interface to make its services available through JNDI
Additional Service Provider Interfaces for JNDI are also available, including Domain Name Service (DNS) and Network Information Service (NIS).
A complete list of JNDI complaint service providers can be located at http://java.sun.com/products/jndi/serviceproviders.html.
To use JNDI, you need to have a basic understanding of what naming and directory services are. A naming service is a repository to store the bindings between a name and an object.
A directory service extends this capability by allowing the bindings to also contain a list of attributes. JNDI provides the ability to lookup the name of a bound object or search for bound objects by matching attributes that are stored with the object.
WebLogic Server is strictly used as a naming service to lookup names that are bound to an object in the JNDI tree; no directory service attributes are used.
A computer file system can be thought of as a naming service. You provide the filename, and it returns the data stored with that file. This is directly analogous to what is offered by a naming service. For J2EE applications, the unique JNDI name is bound to the interface for the remote object. The administration of the naming service is performed through the JNDI API or tools provided by the J2EE server implementing the naming service. For example, WebLogic Server provides access to its naming service through the Administrator Console.
The following sections describe the naming and directory services supported by WebLogic Server through the provided SPIs.
LDAP stands for the Lightweight Directory Access Protocol which provides both a naming and a directory service. LDAP evolved from the X.500 Directory Access Protocol, where it originally only provided the TCP/IP front end for DAP. The X.500 DAP is based on the OSI model for networking. LDAP provided the interface to TCP/IP clients. Gradually, LDAP and DAP merged into the standalone entity now known simply as LDAP which is considered a “lightweight” implementation of the Directory Access Protocol.
LDAP now runs as a standalone service. It must be configured with a back-end database that is used for persistent storage of the bindings and attributes. The schema of the database is organized as a hierarchical tree structure, also commonly referred to as the Directory Information Tree (DIT). Each element in the tree has a unique name, called its distinguished name. The distinguished name is constructed by concatenating the name of each element up to the root of the tree. An example of the LDAP tree structure is shown in Figure 14.2.
From the elements in Figure 14.2, you can see that the LDAP distinguished name for the RemoteObject
is "cn=RemoteObject,dc=weblogic,dc=com"
. Each element of the distinguished name is a node in the tree. This example contains three elements separated by commas which follows the syntax of an LDAP distinguished name. Each element must match an attribute type that is defined in a schema that the LDAP server was configured to install.
The syntax for an individual element in the distinguished name is:
<attribute>=<name>
The <attribute>
must match an attribute from the schema. The <name>
is any name of your choosing to uniquely identify the entry.
The specification for the LDAP schema used by Java can be found at http://www.ietf.org in RFC 2713 Schema for Representing Java Objects in an LDAP Directory. V. Ryan, S. Seligman, R. Lee. October 1999.
The configuration of the LDAP server specifies a base distinguished name in the hierarchical tree for each back-end database that it is configured to use. All elements below the base are physically stored in the corresponding back-end database. The LDAP server configuration also defines a root distinguished name which is used to control access to the server. The root distinguished name is password protected.
An example of how access is controlled by the root distinguished name is provided in the “LDAP Security Considerations” section later in this chapter.
Because LDAP is a directory service, each element in the tree may have a list of attributes. Again, the attributes must match the schema that is defined for the LDAP server. In the case of J2EE applications, the JNDI name is bound to a serialized Java object that represents the remote interface; no directory attributes are used.
WebLogic Server is compatible with any LDAP server that is LDAP v2 compliant. For example, the following LDAP servers have been tested with WebLogic:
The RMI registry is the naming service used by Java Remote Method Invocation. It maintains the bindings between a name and the object that implements the remote interface. The RMI registry is accessed from RMI clients using the JNDI API or the java.rmi.Naming
class.
Refer to the section “Using the RMI SPI” later in this chapter for further information on accessing the RMI registry through JNDI.
The entries in the RMI registry are created by the RMI remote object which binds the name to the remote object that it is implementing. The RMI client looks up the name in the RMI registry to retrieve the remote interface. The Java 2 Standard Edition includes the executable “rmiregistry” that implements the registry for RMI.
▸ For further information on RMI, see Chapter 12, “Distributed Processing Using RMI,” p. 385.
The Common Object Services (COS) Name Server is the naming service for CORBA. It maintains the bindings between a name and a CORBA server. The COS Name Server is accessed using the JNDI API or the classes in the org.omg
. CosNaming.NamingContext
package. Listing 14.1 shows an example of locating a CORBA server named “Hello” using the classes in the org.omg.CosNaming
and org.omg.CosNaming.NamingContextPackage
packages.
Example 14.1. The Hello CORBA Server Is Located Using the COS Naming Context Classes
import org.omg.CosNaming.*; import org.omg.CosNaming.NamingContextPackage.*; // get a reference to the Hello server from CORBA public void findHelloServer() { org.omg.CORBA.Object objRef = orb.resolve_initial_references("NameService"); NamingContext ncRef = NamingContextHelper.narrow(objRef); // resolve the Object Reference in Naming NameComponent nc = new NameComponent("Hello", ""); NameComponent path[] = {nc}; Hello helloRef = helloHelper.narrow(ncRef.resolve(path)); }
The COS naming service can also be accessed using JNDI. The advantage of using JNDI is to better achieve portability. Rather than being locked into CORBA, writing the code using JNDI provides the flexibility to attach to a different naming service without significant modification to the code.
WebLogic Server’s implementation of a JNDI naming service serves as an in-memory repository and lookup service for all hosted J2EE objects and services, such as:
EJB Home stubs
JDBC DataSources and Connection Pools
JMS Destinations and Connection Factories, Queues and Topics
RMI Stubs
For example, in a J2EE application, a JavaServer Page (JSP) or servlet is typically the client of the Enterprise Java Bean (EJB); thus, it performs the lookup operation using JNDI to retrieve the client-side interface to the EJB. An entity EJB also uses JNDI to lookup the DataSource and UserTransaction objects to communicate with a database.
However, since the JNDI objects are stored in-memory, the JNDI service should not be used as a database. The objective of JNDI is to store Java objects to support repetitive lookups only. Storing large objects, such as a file, using JNDI would not only require a large amount of memory, but would also be a burden on the network.
Refer to the section “Examples of Using JNDI in J2EE Applications” later in this chapter to see specific examples of accessing the WebLogic Naming Service using JNDI.
In order to bind and lookup objects using JNDI in WebLogic Server, you must understand the internal structure of WebLogic Server’s naming service, also referred to as the JNDI tree.
The structure of the WebLogic JNDI tree is illustrated in Figure 14.3, where:
A Context (C) represents a node in the tree and is bound to other contexts or objects. A context bound to another context is known as a subcontext. For example, given a DNS domain Bea.COM, the DNS domain Bea is a subcontext of COM.
Objects (O) are bound to a context and represent the leaves in a JNDI tree. Examples of objects include EJB Home interface objects, DataSource objects and factory objects for creating connections to JMS topics. A binding associates an object with a name with its relative context. Hence, each Context can be asked to find an object within its branch of the tree by giving the object’s name relative to its context.
The Root Context (RC) is the uppermost context of the JNDI tree.
The Initial Context (IC) serves as the starting point for all future JNDI tree searches (object lookups), similar to when you select your current directory on a file system.
The WebLogic Server binds J2EE services into the JNDI tree as part of the installation. In addition, the WebLogic container binds the local and remote home interfaces of EJBs into the JNDI tree when enterprise applications are deployed. The installed J2EE services can be viewed on the WebLogic Administrator Console using the following steps:
Connect to the WebLogic Administrator Console with a web browser. If the web browser is being run on the same system as the WebLogic Application Server, the URL is typically http://localhost:7001/console
.
The frame on the left side is the navigation tree to select the available services. In the navigation tree, open the Servers folder in the domain that is installed. This folder contains the list of configured servers. Right-click on a server name and select the “View JNDI tree” menu item.
The JNDI tree for the selected server opens in a new browser window. An example of the JNDI tree viewer is shown in Figure 14.4, which shows the default JNDI bind names and objects under the weblogic context.
You can also use the WebLogic.Admin utility to view the JNDI bindings in your WebLogic Server using the following syntax:
java weblogic.Admin [-url URL] -username username -password password LIST context
where:
URL specifies the listen address of the WebLogic Server that is the target of the command. The format for the URL is hostname:port; for example the default is localhost:7001.
username specifies the username to access the target WebLogic Server for the command.
password specifies the password associated with the username to access the target WebLogic Server for the command.
LIST specifies the command to list the JNDI tree node bindings.
context specifies the JNDI context for lookup, for example, weblogic, weblogic.ejb, or javax.
For example, in the following weblogic.Admin statement, the user wls, who has a password of einstein, requests a list of the node bindings in the context named weblogic:
java weblogic.Admin -url localhost:7001 -username wls -password einstein LIST weblogic
The output for this statement is shown in Figure 14.5.
A unique capability of the WebLogic naming service is that it can provide support for a WebLogic clustered environment. In a clustered environment, multiple WebLogic Servers work together to provide a seamless, fault-tolerant application server to the J2EE clients. Each WebLogic Server in the cluster shares information about its services with the other WebLogic Servers. From this collection of information, a clusterwide JNDI tree of services is created. A JNDI lookup operation with the WebLogic naming service can locate all the services available to the cluster, not just an individual application server. WebLogic clusters are supported by a replicated clusterwide JNDI tree that provides access to both replicated and pinned RMI and EJB objects.
The provision of the WebLogic naming service to support clustered capabilities enables it to offer services to J2EE applications that other naming services cannot support. Specifically, JNDI names are bound to objects in the WebLogic naming service that are used with the Enterprise Java Beans, RMI, JDBC, and JMS technologies. Therefore, it is important to understand the implications of binding an object to the JNDI tree in a clustered environment to use the WebLogic naming service effectively.
A WebLogic Server cluster provides multiple, cooperating WebLogic Servers that share common services. This configuration enables load-balancing and failover of the execution of J2EE services. Load-balancing spreads the execution over the available hardware resources. Failover provides robustness and fault tolerance by automatically identifying when a server is not operational, and choosing a different application server for the next instance of the remote object or the next invocation of an existing remote object. The capability to offer load-balancing and failover is possible because the objects that are bound in JNDI are automatically replicated throughout the cluster to create a common JNDI tree. The definition of replicated versus pinned objects is whether or not the object can participate in load-balancing and failover. Replicated objects are allowed to participate in load-balancing an failover because they can exist on any application server in the WebLogic cluster. Pinned objects have an application specific need to exist on only one server, and therefore cannot be replicated to another server in the cluster. Replicated objects are preferred in order to take advantage of the robustness and scalability of the WebLogic cluster.
There are, however, application-specific reasons for not allowing the replication of various J2EE services. The first issue deals with EJB session beans. If a bean is stateful, it may store client state information on the application server. To maintain state, the EJB session bean must always execute on the same application server. This concept is referred to as a pinned object meaning the object is pinned to a specific application server. The alternative is referred to as a replicated object which exists on multiple servers in the cluster. Though the terminology is not as explicit as stateful or stateless, an RMI remote object may also store state information and therefore must also be bound as a pinned object.
The JNDI architecture supports pluggable modules referred to as Service Provider Interfaces (SPIs), as illustrated earlier in Figure 14.1. They are responsible for implementing the client-side protocol for a specific naming service. JNDI provides a common API to the client independent of the selected SPI.
The Java application chooses which JNDI SPI it wants to use through the javax.naming.Context
interface. Each SPI implements an InitialContextFactory
that creates an instance of an object that implements the Context
interface. The context factory is responsible for creating an instance of a JNDI Context
for its particular protocol. A hash table is passed to the InitialContextFactory
that contains properties used to customize the SPI. This hash table is referred to as the JNDI context environment. The javax.naming.Context
interface defines constants that are used as the key value of the hash table, as listed in Table 14.1.
Table 14.1. The Context Environment Defines Property Names for Configuring the Hashtable
The WebLogic JNDI allows the SECURITY_CREDENTIAL
to be passed as an object that implements the weblogic.security.acl.UserInfo
interface which is the weblogic.security.acl.DefaultUserInfoImpl
class. However, this has been deprecated by the WebLogic 7.0 rules-based LDAP Authentication Provider in the Pluggable Security Infrastructure. The recommended security is provided by the Java Authentication and Authorizaton Service (JAAS). Refer to the “LDAP Security Considerations” later in this chapter for further information.
Examples of using the LDAP, RMI, COS, and WebLogic SPIs are discussed in the following sections.
The InitialContextFactory
class for the LDAP SPI is com.sun.jndi.ldap.LdapCtxFactory
. The PROVIDER_URL
is constructed with a protocol
, hostname
, and port
. The protocol
is ldap
, and the hostname
is the name of the host where the LDAP server is running. If the LDAP server is running on the same system as the Java application, you can use localhost
as the hostname. The default port for LDAP is 389, but the LDAP server may be running on another port. A sample PROVIDER_URL
for LDAP is
ldap://localhost:389
The example in Listing 14.2 creates a javax.naming.Context
for the LDAP SPI. The context environment specifies com.sun.jndi.ldap.LdapCtxFactory
as the InitialContextFactory
. The PROVIDER_URL
defines the hostname and port of the LDAP server. The SECURITY_AUTHENTICATION
, SECURITY_PRINCIPAL
, and SECURITY_CREDENTIALS
are used to authorize access to the root distinguished node of the LDAP database.
Example 14.2. The JNDI SPI Is Configured Through the LdapCtxFactory for LDAP
/** Create JNDI Context for LDAP @param rootDN LDAP root distinguished name @param rootPW LDAP root password @returns JNDI Context @exception NamingException */ private Context createLdapContext( String rootDN, String rootPW ) throws NamingException { Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" ); env.put( Context.PROVIDER_URL, "ldap://localhost:389" ); env.put( Context.SECURITY_AUTHENTICATION, "simple" ); env.put( Context.SECURITY_PRINCIPAL, rootDN ); env.put( Context.SECURITY_CREDENTIALS, rootPW ); Context ctx = new InitialContext( env ); return ctx; }
LDAP security properties are determined by the configuration of the LDAP server. Typically, no security credentials are required for lookup or search operations. To protect the contents of the LDAP server, security is enforced for bind, rebind, and unbind operations. If the LDAP server is configured with simple clear-text passwords, set the SECURITY_PRINCIPAL
to the root-distinguished name, and set the SECURITY_CREDENTIALS
to the root password. Listing 14.2 provided an example using the root distinguished name with a root password.
In addition to simple text passwords, the WebLogic server provides the capability to configure an LDAP Authentication Provider containing users and groups. This is configured through the Security Realms on the WebLogic Administrator Console. From the configuration of a security realm, the LDAP Authentication Provider is selected and configured. The WebLogic LDAP Authentication provides a fully compatible Java Authentication and Authorization Service (JAAS). A working JAAS sample is provided with the WebLogic Server software. The sample is located in the SAMPLES_HOMEserversrcexamplessecurityjaas directory provided with WebLogic Server.
▸ For further information on security see Chapter 26, “Managing the WebLogic Security Service,” p. 889.
The InitialContextFactory
class for the RMI SPI is com.sun.jndi.rmi.registry.RegistryContextFactory
. The PROVIDER_URL
is constructed with a protocol
, hostname
, and port
. The protocol
is rmi
, and the hostname
is the name of the host where the rmiregistry
is running. If the rmiregistry
is running on the same system as the Java application, you can use localhost
as the hostname. The default port for RMI is 1099, but the rmiregistry
may be running on another port. A sample PROVIDER_URL
for RMI is
rmi://localhost:1099
The example in Listing 14.3 creates a javax.naming.Context
for the RMI SPI. The context environment specifies com.sun.jndi.rmi_registry.RegistryContextFactory
as the InitialContextFactory
. The PROVIDER_URL
defines the hostname and port of the RMI server. The context environment for RMI requires the RMISecurityManager
to be specified.
Refer to the next section, “RMI Security Considerations” for further information on RMI security.
Example 14.3. JNDI Context for RMI
/** Create JNDI Context for RMI @returns JNDI Context @exception NamingException */ private Context createRmiContext() throws NamingException { Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory" ); env.put( Context.PROVIDER_URL, "rmi://localhost:1099" ); env.put( "java.naming.rmi.security.manager", "RMISecurityManager" ); Context ctx = new InitialContext( env ); return ctx; }
RMI requires that you install a security manager to load objects from a remote server. The RMI SPI uses the java.naming.rmi.security.manager
property to indicate that it should attempt to install the RMISecurityManager
. This property can be set to any value to request that the security manager be installed.
The InitialContextFactory
class for the COS SPI is com.sun.jndi.cosnaming.CNCtxFactory
. The PROVIDER_URL
for the Common Object Services is slightly more complex than the other Service Provider Interfaces. The URL is constructed with a protocol
, hostname
, port
, and the name of the CosNaming service. For COS, the URL is used to configure the root-naming context and/or the Object Request Broker (ORB). The protocol
can be either iiop
or iiopname
. The iiop
protocol specifies a single hostname, and the iiopname
protocol specifies an addr_list
, which is a list of hostnames. The default port
for iiop
is 900, and the default port for iiopname
is 9999. Examples of the two forms for the PROVIDER_URL
are:
iiop://localhost:900/<cosnaming_name> iiopname://<addr_list>:9999/<cosnaming_name>
The example in Listing 14.4 creates a javax.naming.Context
for the COS SPI. The context environment specifies com.sun.jndi.cosnaming.CNContextFactory
as the InitialContextFactory
. The PROVIDER_URL defines the hostname and port of the CosNaming
service. The COS SPI does not specify SECURITY_CREDENTIALS
as it uses the Java Security Manager.
Refer to the next section “COS Security Considerations” for further information on COS security.
Example 14.4. JNDI Context for COS
/** Create JNDI Context for COS @returns JNDI Context @exception NamingException */ private Context createCosContext() throws NamingException { Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.cosnaming.CNCtxFactory" ); env.put( Context.PROVIDER_URL, "iiop://localhost:900" ); Context ctx = new InitialContext( env ); return ctx; }
COS security is monitored by the installed security manager. This chapter specifically focuses on JNDI; details of the Java Security Manager are not covered in this chapter. However, as a brief overview, when a security manager has been installed, you must grant to the application using JNDI and the COS naming service provider the following permissions:
permission java.net.SocketPermission "host[:port]", "connect";
To grant permissions with the Java Security Manager, you add the permission entry to the file being administered by the java.security.policy
property.
The InitialContextFactory
class for the WebLogic SPI is weblogic.jndi.WLInitialContextFactory
. The URL is constructed with a protocol
, hostname
, and port
. The protocol
is t3
, and the hostname
is the name of the host where WebLogic Server is running. If WebLogic Server is running on the same system as the Java application, you can use localhost
as the hostname. The default port for the WebLogic name service is 7001, but it may be running on another port. A sample PROVIDER_URL
for the WebLogic name service is
t3://localhost:7001
WebLogic security properties are determined by the WebLogic Server security realm. The SECURITY_PRINCIPAL
property is used to specify a user for authentication purposes. The default user is "guest"
. The SECURITY_CREDENTIALS
property is overloaded; the value can be either the password for the user or an object that implements the weblogic.security.acl.UserInfo
interface. If a UserInfo
object is passed, the PROVIDER_URL
property is ignored.
The WebLogic SPI provides both the traditional context environment configuration using a Hashtable
, as well as the WebLogic Environment
class. The Environment class offers the convenience of a Java Bean API using set methods to configure the properties. This mechanism provides compile-time type checking of the parameters that cannot be done by the Hashtable
. The default constructor for the Environment
class initializes a set of default values. Table 14.2 shows the attributes of the Environment
class with the corresponding default value. You do not need to set a value if the default matches your needs.
Table 14.2. The Environment
Class Attributes That Control the Configuration of the Context Environment
Environment Attribute | Default Value |
---|---|
|
|
|
|
|
|
|
|
The example in Listing 14.5 shows the traditional Hashtable
implementation. Listing 14.6 shows an example using the WebLogic Environment
class to configure the context. Both examples perform identical initialization of the JNDI Context.
Example 14.5. JNDI Context for WebLogic: Traditional Hashtable
Implementation
/** Create JNDI Context for WebLogic @param user User in WebLogic Server security realm @param password Password for user @returns JNDI Context @exception NamingException */ private Context createWebLogicContext( String user, String password ) { Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory" ); env.put( Context.PROVIDER_URL, "t3://localhost:7001" ); env.put( Context.SECURITY_AUTHENTICATION, "simple" ); env.put( Context.SECURITY_PRINCIPAL, user ); env.put( Context.SECURITY_CREDENTIALS, password ); Context ctx = new InitialContext( env ); return ctx; }
Example 14.6. JNDI Context for WebLogic: Using the Environment
Object
/** Create JNDI Context for WebLogic @param user User in WebLogic Server security realm @param password Password for user @returns JNDI Context @exception NamingException */ private Context createWebLogicContext( String user, String password ) { try { // Create the WebLogic Environment // use default value: WLInitialContextFactory, "t3://localhost:7001, Environment env = new Environment(); env.setSecurityPrincipal( user ); env.setSecurityCredentials( password ); // in this case, we get the InitialContext from the Environment object Context ctx = env.getInitialContext(); // use ctx to lookup JNDI services } catch( NamingException x ) { System.err.println( x.toString() ); } }
The JNDI API is provided by the javax.naming.Context
interface for naming services and javax.naming.directory.DirContext
for directory services. J2EE applications rely exclusively on naming services; therefore, this introduction to the JNDI API focuses on the javax.naming.Context
class. However, an overview of using JNDI with directory services is also provided to give a complete introduction to the JNDI API. The Java application creates and uses a javax.naming.Context
object to lookup a name in the naming service. The return value is the object that was bound to that name.
After the context is created, the lookup()
method is used to locate an object by name, errors are handled with exceptions. The NameNotFoundException
is thrown if the name is not found in the naming service. The NamingException
is thrown if there was a communication failure with the naming service. Listing 14.7 retrieves an EJB named MySessionBean
from the Context created for WebLogic Server.
Example 14.7. Retrieving MySessionBean
from the Context Created for WebLogic Server
try { // create the Context first... // use ctx to lookup an EJB named MySessionBean MySessionBean msb = (MySessionBean)ctx.lookup( "ejb.mySessionBean" ); // use MySessionBean here } catch( NameNotFoundException x ) { System.err.println( "ejb.mySessionBean was not found" ); } catch( NamingException x ) { System.err.println( x.toString() ); }
It is important to close the Context
after the client is finished using it. By doing so, you release the resources that were created for the Context
. A finally
block is recommended to ensure the Context is closed whether or not an exception is thrown. The structure of using the finally
block to close the context is shown in Listing 14.8.
Example 14.8. Using the finally
Block to Close the Context
Context ctx = null; try { // create the Context ctx = new InitialContext(); // use the Context } catch( NamingException x ) { System.err.println( x.toString() ); } catch( Exception x ) { System.err.println( "unexpected exception: " + x ); } finally { try { if( ctx != null ) ctx.close(); } catch( Exception x ) { System.err.println( "failed to close Context: " + x ); } }
In this section, you learn how to bind an object with a JNDI naming service. The binding is created using the bind()
or rebind()
methods of the Context object.
An Enterprise Java Bean does not contain code to explicitly bind the EJB with the naming service. The EJB Container in WebLogic Server is responsible for updating the JNDI tree when the bean is deployed. The JNDI name for the binding is defined in the deployment descriptor for the EJB.
Follow these steps to bind an object with a JNDI naming service:
Choose a unique name for the object.
Create the object.
Create a Context for the selected service provider.
To support clustering for WebLogic Server, decide if the object is pinned or replicated. The environment property for controlling replication in WebLogic Server is WLContext.REPLICATE_BINDINGS
. The value for this property is set to either “true” or “false” as a java.lang.String.
Handle exceptions.
The parameters to the Context
method bind()
are a name as a String
and an object. You must use the rebind()
method to force the new object to be bound to a name that already exists. You use the unbind()
method to remove the binding from the service provider. The parameter to unbind()
is the bound name. All JNDI Context
methods throw NamingException
to indicate that a failure occurred while trying to communicate with the naming service. Listing 14.9 shows an example of binding a pinned object with the WebLogic naming service.
Example 14.9. Binding a Pinned Object with WebLogic Server
private void pinObject( String name, Object obj ) { try { Hashtable env = new Hashtable(); // pin the object env.put( WLContext.REPLICATE_BINDINGS, "false" ); Context ctx = new InitialContext( env ); // bind the object ctx.bind( name, obj ); } catch( NamingException x ) System.err.println( "Failed to bind " + name + ": " + x ); } }
The EJB container in the application server is responsible for binding the EJB object with the naming service when the object is deployed. Therefore, the most common function of JNDI for J2EE clients is to look up the interface for a remote object. In the following examples, you can assume that the client is running on the same system as WebLogic Server. From this assumption, you construct the JNDI Context by simply using the default constructor for InitialContext
. The default values for the environment apply under these conditions.
The heart of a J2EE application is the EJB. The clients of the EJB use JNDI to look up the home interface. The home interface is then used to create an instance of the remote interface for the EJB. With EJB, the name to look up is the same as the name of the home interface class. Listing 14.10 shows an example of an EJB client using JNDI to look up the home interface.
Example 14.10. An EJB Client Using JNDI to Look Up the Home Interface
Context ctx = null; try { ctx = new InitialContext(); MyEjbHome home = null; Object homeObj = ctx.lookup("MyEjbHome"); home = (MyEjbHome)javax.rmi.PortableRemoteObject.narrow(homeObj, MyEjbHome.class); } catch (NamingException e) { e.printStackTrace(); } catch( NamingException x ) { System.err.println( x.toString() ); }
The configuration of JMS includes connection factories and distributed destinations, each of which have a JNDI name. The connection factories enable JMS clients to create JMS connections. The distributed destinations are the JMS Queues and Topics.
▸ For further information on JMS see Chapter 15, “The Java Messaging Service (JMS),” p. 471.
Chapter 15 describes the details of JMS Queue Producer/Consumer and JMS Topic Producer/Consumer. Here, you see how to use JNDI to retrieve these services from the WebLogic naming service. The ConnectionFactory, Queue, and Topic are all retrieved by performing a JNDI lookup. The name of the default JMS connection factory is the full class name weblogic.jms.ConnectionFactory
. The JNDI name for the Queue and Topic is manually administered through the WebLogic Server Console as part of the configuration for JMS. The client of the JMS Queue uses JNDI to look up the name, as shown in Listing 14.11.
Example 14.11. A JMS Queue Client Using JNDI to Look Up the Name
try { Context ctx = new InitialContext(); QueueConnectionFactory qcf = (QueueConnectionFactory)ctx.lookup( "weblogic.jms.ConnectionFactory" ); Queue queue = (Queue)ctx.lookup( "MyJmsQueue" ); QueueConnection qc = qcf.createQueueConnection(); // use the QueueConnection and message queue } catch( NamingException x ) { System.err.println( x.toString() ); }
The JMS Topic Producer and Consumer use JNDI in a similar manner as the preceding Queue example, as shown in Listing 14.12.
Example 14.12. JMS Topic Producer and Consumer Using JNDI to Look Up the Name
try { Context ctx = new InitialContext(); TopicConnectionFactory tcf = (TopicConnectionFactory)ctx.lookup( "weblogic.jms.ConnectionFactory" ); Topic topic = (Topic)ctx.lookup( "MyJmsTopic" ); TopicConnection tc = qcf.createTopicConnection(); // use the QueueConnection and message queue } catch( NamingException x ) { System.err.println( x.toString() ); }
Chapter 16, “Managing Java Transactions Using JTA,” describes the advantages of using JDBC DataSources over the DriverManager
. With WebLogic Server, the DataSource
is bound to the naming service using the WebLogic Administrator Console. The client of the DataSource
uses JNDI to look up the name, as shown in Listing 14.13.
Example 14.13. A DataSource
Client Using JNDI to Look Up the Name
try { Context ctx = new InitialContext(); DataSource ds = (DataSource)ctx.lookup( "MyJDBCDataSource" ); Connection con = ds.getConnection( DATABASE_USER, DATABASE_PASSWORD ); } catch( NamingException x ) { System.err.println( x.toString() ); }
The UserTransaction
object is covered in Chapter 16. In this case, there is no manual administration of the JNDI name; it is simply the full class name, javax.transaction.UserTransaction
. The client that performs distributed transactions uses JNDI to look up the UserTransaction
object, as shown in Listing 14.14.
Example 14.14. A Client Performing Distributed Transaction Using JNDI to Look Up the UserTransaction
Object
try { Context ctx = new InitialContext(); UserTransaction tx = (UserTransaction)ctx.lookup( "javax.transaction.UserTransaction" ); tx.begin(); // start transaction // locate DataSource, establish connection, execute operations, close connections tx.commit(); // commit transactions } catch( Exception x ) { System.err.println( "error has occurred" ); try { tx.rollback(); } catch( javax.transaction.SystemException se ) { } }
As shown in this chapter, the distributed architecture of J2EE requires a naming service for clients to be able to look up the remote object. In fact, all middleware implementations, including RMI, CORBA, and EJB, rely on a naming service or registry. The WebLogic Server Cluster has even stronger needs for a naming service to support replication and failover in a clustered environment. JNDI provides a simple API that enables the client to retrieve the remote object by simply looking up a name.