O'Reilly logo

WebLogic: The Definitive Guide by Avinash Chugh, Jon Mountjoy

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Chapter 4. Using JNDI and RMI

The Java Naming and Directory Interface (JNDI) provides a standard way of accessing naming and directory services. The API describes how objects can be bound in a distributed directory service, and how clients of the directory can locate and retrieve these objects. It plays a critical role in any J2EE environment, providing the central naming and directory service API used by containers, J2EE resources, deployed applications, and external clients.

JNDI has an open architecture that permits the integration of a number of different implementations of directory services. These service provider implementations map vendor-neutral JNDI calls to the operations supported by the underlying directory service. For instance, your Java SDK distribution comes equipped with service providers for LDAP, CORBA Naming Services, and Java's Remote Method Invocation (RMI) Registry. Other service providers are also available for Sun's Network Information Service (NIS), Novell's Network Directory Service (NDS), DNS, and Windows Registry. Everything from Java objects to email addresses can be stored in the various types of directories, making them useful for a variety of things. In fact, WebLogic uses an embedded LDAP repository to store security information about WebLogic users, groups, security policies, and much more. The JNDI provides a standard interface to all of these types of directory services, and in WebLogic you will find a robust, distributed JNDI implementation that provides the naming and directory support for all J2EE applications.

WebLogic's JNDI implementation is used primarily as the standard J2EE mechanism that allows clients to publish, locate, and retrieve objects in a distributed fashion. For instance, when a JDBC data source or EJB is deployed, it is made available in a global JNDI tree, bound to its configured JNDI name. A Java client then can use the JNDI name to look up and gain access to the resource. Other J2EE resources such as JMS connection factories, JTA transactions, and RMI objects also may be bound in the JNDI tree.

In this chapter, we will look at how WebLogic's JNDI implementation can be accessed and used, as well as how you can bind your own objects into the JNDI tree. You also will learn how WebLogic's JNDI implementation can take advantage of clusters, providing transparent global replication of bound services or objects across the members of the cluster. This cluster-wide JNDI tree may appear as a single, global hierarchy, but it actually is replicated across each member of the cluster. The JNDI implementation supports both replicated objects (or services), where the objects are replicated across all members of the cluster, and pinned objects, where the object (or service) is bound to a single server but still is accessible to the cluster.

The RMI framework is the standard technology underlying distributed object programming in Java. It enables a Java client to transparently access remote RMI objects by creating stubs that proxy requests through to the remote object and marshal the return values. WebLogic provides a high-performance implementation of RMI that complements the JNDI and clustering frameworks; in this chapter, you will learn how to create your own RMI objects that can take advantage of these features. For instance, you can easily create cluster-aware stubs that proxy requests through to a server hosting the runtime object. These cluster-aware stubs maintain a list of all servers in the cluster that host the object, allowing the stub to transparently provide load-balancing and failover facilities to the client. Moreover, the RMI registry that allows clients to locate the RMI objects with which they want to interact can be accessed using WebLogic's JNDI implementation. In fact, this technology is used in the implementation of many other J2EE resources within WebLogic, such as EJB home objects that provide failover and load-balancing facilities.

The final topic covered in this chapter is the use of CORBA and its IIOP transport protocol as an alternative to RMI. WebLogic can generate Interface Definition Language (IDL) descriptions of RMI objects, which then can be used to create clients on any language platform. These clients can interact with WebLogic using the standard CORBA/IIOP framework, providing a basis for rich interoperability.

Using WebLogic's JNDI

Each WebLogic server maintains a local JNDI tree. Typically, you will bind various J2EE resources to the JNDI tree, such as JDBC data sources, EJB home objects, JMS connection factories, and more. You can use the Administration Console to view the contents and structure of the JNDI tree on a server. Select a server from the left frame of the Administration Console, right-click the server node, and choose the "View JNDI tree" option from the pop-up menu. This launches a new window that displays the contents of the JNDI tree for the selected server. You can now navigate the JNDI tree and view the various objects bound to it.

In the following sections, you will learn how to programmatically establish a connection to WebLogic's JNDI server, from both an external client and an internal J2EE component, and how to use the connection to locate and bind objects. As the JNDI tree also permits authorization policies, we also will look at a few security issues surrounding the use of a JNDI tree.

Creating a Context

In order to access WebLogic's JNDI tree, you need to establish a standard JNDI InitialContext representing the context root of the server's directory service. You can do this by passing a Hashtable of property/value pairs to the javax.naming.InitialContext( ) constructor. The Hashtable represents environment properties that are used to establish the context; in an external client, you typically will need to supply at least a URL property that points to a server you wish to access, and a context factory class that represents the service provider to use in the construction of the context.

Example 4-1 shows how a Java client can obtain an initial JNDI context to a WebLogic Server.

Example 4-1. Obtaining the initial JNDI context

import javax.naming.*;

private static InitialContext ctx = null;
...
public static InitialContext getInitialContext( ) throws NamingException {
  if (ctx == null) {
    Hashtable env = new Hashtable( );
    env.put(Context.INITIAL_CONTEXT_FACTORY, 
            "weblogic.jndi.WLInitialContextFactory");
    env.put(Context.PROVIDER_URL, 
            "t3://myserver:8001");
    ctx = new InitialContext(env);
  }
  return ctx;
}

The PROVIDER_URL property specifies the URL of the server whose JNDI tree we want to access. The INITIAL_CONTEXT_FACTORY property contains the fully qualified class name of WebLogic's context factory, specifying the service provider to use. If you need to access WebLogic's JNDI tree, you should always establish the context using the class name weblogic.jndi.WLInitialContextFactory.

Tip

It is customary to cache the InitialContext object once it's created, and to use the cached object for subsequent requests for the initial JNDI context.

Table 4-1 provides the list of environment properties that may be used to customize the initial JNDI context. In the next section, we will examine other properties relevant to the use of JNDI in a clustered environment.

Table 4-1. Environment properties for the initial JNDI context

Context property

Description

Default

INITIAL_CONTEXT_FACTORY

Use this property to specify the name of the initial context factory. WLInitialContextFactory must be used to access WebLogic's JNDI service.

none

PROVIDER_URL

Use this property to set the URL of the WebLogic Server or cluster that you want to contact.

t3://localhost:7001

SECURITY_PRINCIPAL

If the client needs to authenticate itself to the server, use this property to specify the identity of a WebLogic user.

guest

SECURITY_CREDENTIALS

Use this property to specify the password for the WebLogic user.

guest

WebLogic providers a helper class, weblogic.jndi.Environment, which eases the creation of the initial context. The class provides type-safe methods for setting and accessing common JNDI properties, and assumes sensible defaults for many of the JNDI properties needed to access the JNDI tree on WebLogic Server. The following code sample shows how to obtain the initial JNDI context using the Environment object:

   Environment env = new Environment( );
   env.setProviderUrl("t3://servername:7001");
   env.setSecurityPrincipal("jon");
   env.setSecurityCredentials("jonpassword");
   Context ctx = env.getInitialContext( );
   ...

A client should use the close( ) method to release any resources allocated to the JNDI Context. We recommend that you always invoke the close( ) method when you no longer require the JNDI context:

  Context ctx = null;
  try {
    // Create the context using the code in Example 4.1
    ctx = getInitialContext( );
    //now use the JNDI Context to look up objects
  }
  catch (NamingException ene) {
    //handle JNDI Exceptions
  }
  finally {
    try {
      if (ctx != null) ctx.close( );
    }
    catch (Exception ignored) {}
  }

Server-side objects, such as servlets and EJBs, do not need to provide any environment properties when creating an initial JNDI context. When a server-side object invokes the InitialContext( ) constructor, the JNDI context will always connect to the local server that hosts the server-side object. This means that if you need only to access objects bound to the local JNDI tree, a server-side object can simply create the initial JNDI context, without explicitly specifying any environment properties. For example, if a servlet needs to look up the home object for an EJB that is deployed on the same server, a call to the default InitialContext constructor will suffice:

               Context ctx = new InitialContext( );
Object home = ic.lookup("java:comp/env/ejb/MyEJBHome");
MyEJBHome ejbHome = (MyEJBHome) 
PortableRemoteObject.narrow(home, org.foo.bar.MyEJBHome.class);
// now use the EJB home object to obtain an EJB instance

Of course, you could pass the security credentials of a valid WebLogic user if you need to establish and use the context under a particular security scope.

Security in a Context

As you'll see later in Chapter 17, WebLogic allows an administrator to set up authorization policies that can restrict access to any arbitrary branch of the JNDI tree. You can view and change these policies by using the Administration Console's JNDI viewer. Right-click a node to view the policy for that node. As the policies are inherited, by applying a policy to the root node you will effectively be applying it to all nodes of the tree. By applying such a policy, you are creating an authorization requirement on the actual lookup in the JNDI tree. You also can create authorization policies that apply to the objects bound in the JNDI tree itself. So, for instance, you can create a policy that restricts the lookup of a connection pool, and another that restricts the use of the connection pool.

The default policy on the root of the JNDI tree grants access to the anonymous user (in fact, to the group Everyone). Thus, if you do not specify the credentials of a WebLogic user when establishing an initial context, the anonymous user is automatically used to verify access control to the JNDI tree. So, by default, you do have the necessary authorization to access the JNDI tree. If the thread already has been associated with a WebLogic user, that security principal is used for all authorization checks when you subsequently access the JNDI tree. Hence, if a security policy has been defined on a specific branch of the JNDI tree, you need to ensure that a valid principal is associated with the thread, and that it has sufficient privileges to access the branch. There are two ways in which you can establish an identity under which the JNDI tree ought to be accessed:

  • Supply values for the SECURITY_PRINCIPAL and SECURITY_CREDENTIALS environment properties when creating an initial context. When you create a JNDI context in this way, the security scope is valid for the lifetime of the context. It is terminated when you invoke the close( ) method on the Context object. The security context actually is associated with the thread running the code. This has important implications. Establishing a new context will replace any previous security context associated with the thread. Thus, you should not try to establish a nested context using differing security principals. In that case, all code will run using the most recently created context.

  • Use the weblogic.security.Security.runAs( ) method to apply a security context to your code. In this case, you should use the standard JAAS login procedure to create and populate a security subject. The runAs( ) method accepts two arguments: the code that needs to be executed, and the subject under which it ought to be executed. The thread's security context is then propagated to any subsequent JNDI contexts created from within your code. Any such JNDI lookup will be authorized using the credentials of the current subject associated with the thread.

JAAS-based authentication is covered in Chapter 17, which also discusses how to use SSL certificates and mutual authentication to provide additional security when creating a JNDI context.

Using a Context

Once you have obtained the initial JNDI context, you can look up any named object bound in the context. Example 4-2 shows how you can look up a transactional data source and UserTransaction from the JNDI tree, and use them to create JDBC connections that participate in a JTA transaction.

Example 4-2. Using the initial JNDI context to look up a named object

  javax.naming.Context ctx = null;
  javax.transaction.UserTransaction tx = null;
  javax.sql.DataSource ds = null;
  java.sql.Connection con = null;

   //set up the JNDI environment

  try {
    ctx = new InitialContext(env);
    //use the JNDI tree to look up a configured TxDataSource
    ds = (DataSource) ctx.lookup("myTxDs");

    //use the JNDI tree to initiate a new JTA transaction
    tx = (UserTransaction) ctx.lookup("java:comp/UserTransaction");

    //initiate the transaction 
    tx.begin( );

    con = ds.getConnection( );
    //perform your JDBC updates here...

    //commit the transaction
    tx.commit( );
  }
  catch (Exception e) {
    // exceptions code goes here
  }
  finally {
    try {
      if (ctx != null) ctx.close( );
      //release other JDBC resources here...
    }
    catch (Exception ignored) {}
  }

You also may use the Context object to bind custom objects to WebLogic's JNDI context. The following code shows how you can bind an instance of a custom Java class, com.oreilly.custom.Foo, to the JNDI tree under the name myObj:

  try {
    // env represents an instance of weblogic.jndi.Environment
    ctx = env.getInitialContext( );
    ctx.bind("myObj", new com.oreilly.custom.Foo( ));
  }
  catch (NamingException e) {
    //handle exception in case an object is already bound under the same name
  }

A call to the bind( ) method for custom objects will not succeed if another object has already been bound to the JNDI tree under the same name. Instead, you should use the rebind( ) method to overwrite any previous binding and bind the new custom object under the same name:

  try {
    ctx = env.getInitialContext( );
    ctx.rebind("myObj", new com.oreilly.custom.Foo( ));
  }
  catch (NamingException e) {
  }

Remember that a custom object can be bound to the JNDI tree only if its class implements the java.io.Serializable interface. Typically, you should implement a WebLogic startup class that binds any needed custom objects at server startup. This approach is described later in this chapter in Section 4.3.2 ."

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required