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.
4.1.1 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.
|
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.
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.
4.1.2 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:
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.
4.1.3 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."
Introduction
Web Applications
Managing the Web Server
Using JNDI and RMI
JDBC
Transactions
J2EE Connectors
JMS
JavaMail
Using EJBs
Using CMP and EJB QL
Packaging and Deployment
Managing Domains
Clustering
Performance, Monitoring, and Tuning
SSL
Security
XML
Web Services
JMX
Logging and Internationalization
SNMP