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 ."

Get WebLogic: The Definitive Guide now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.