Oracle® Application Server Containers for J2EE Services Guide
10g Release 2 (10.1.2) for Windows or UNIX B14012-02 |
|
Previous |
Next |
This chapter describes the Java Naming and Directory Interface (JNDI) service that is implemented by Oracle Application Server Containers for J2EE (OC4J) applications. It covers the following topics:
JNDI, part of the J2EE specification, provides naming and directory functionality for Java applications. Because JNDI is defined independently of any specific naming or directory service implementation, it enables Java applications to access different naming and directory services using a single API. Different naming and directory service provider interfaces (SPIs) can be plugged in behind this common API to handle different naming services.
Before reading this chapter, you should be familiar with the basics of JNDI and the JNDI API. For basic information about JNDI, including tutorials and the API documentation, visit the Sun Microsystems Web site at:
http://java.sun.com/products/jndi/index.html
A JAR file implementing JNDI, jndi.jar
, is available with OC4J. Your application can take advantage of the JNDI API without having to provide any other libraries or JAR files. A J2EE-compatible application uses JNDI to obtain naming contexts that enable the application to locate and retrieve objects such as data sources, Java Message Service (JMS) services, local and remote Enterprise Java Beans (EJBs), and many other J2EE objects and services.
Note: For information about controlling access to JNDI namespaces, see the Oracle Application Server Security Guide. |
The concept of the initial context is central to JNDI. Here are the two most frequently used JNDI operations in J2EE applications:
Creating a new InitialContext
object (in the javax.naming
package)
Using the InitialContext
, to look up a J2EE or other resource
When OC4J starts up, it constructs a JNDI initial context for each application by reading resource references in the configuration XML file of each application.
The following example shows two lines of Java code to use on the server side in a typical Web or EJB application:
Context ctx = new InitialContext(); myEJBHome myhome = (HelloHome) ctx.lookup("java:comp/env/ejb/myEJB");
The first statement creates a new initial context object, using the default environment. The second statement looks up an EJB home interface reference in the JNDI tree of the application. In this case, myEJB
might be the name of a session bean that is declared in the web.xml
(or orion-web.xml
) configuration file, in an <ejb-ref>
tag. For example:
<ejb-ref> <ejb-ref-name>ejb/myEJB</ejb-ref-name> <ejb-ref-type>Session</ejb-ref-type> <home>myEjb.HelloHome</home> <remote>myEjb.HelloRemote</remote> </ejb-ref>
This chapter focuses on setting up the initial contexts for using JNDI and describing how OC4J performs JNDI lookups. For more information about the other JNDI classes and methods, see the Javadoc at:
http://java.sun.com/products/jndi/1.2/javadoc/index.html
When OC4J starts up, it constructs a JNDI context for each application that is deployed in the server. There is always at least one application for an OC4J server, the global application, which is the default parent for each application in a server instance. User applications inherit properties from the global application and can override property values defined in the global application, define new values for properties, and define new properties as required.
For more information about configuring the OC4J server and its contained applications, see the Oracle Application Server Containers for J2EE User's Guide.
Note: During EJB deployment in OC4J, you load the bean class to find out its methods so that you can generate EJB wrappers. Because the code in the static block is executed as the class is being loaded, the JNDI environment context is not yet set up. Even during runtime, the bean is in the "does not exist" stage. In this stage of the life cycle, the JNDI environment context is undefined, and the bean provider cannot rely on it to be available.To work around this problem, set up and cache the context either during the construction of the bean, in the |
The environment that OC4J uses to construct a JNDI initial context can be found in three places:
System property values, as set either by the OC4J server or possibly by the application container.
A jndi.properties
file contained in the application EAR file (as part of application-client.jar
).
An environment specified explicitly in a java.util.Hashtable
instance passed to the JNDI initial context constructor. ("Accessing Objects from an Application Client" shows a code example of this constructor.)
The JNDI InitialContext
has two constructors:
InitialContext() InitialContext(Hashtable env)
The first constructor creates a Context
object, using the default context environment. If you use this constructor in an OC4J server-side application, then OC4J creates the initial context when the server is started, using the default environment for that application. This constructor is typically used in code that runs on the server side, such as in a JSP, EJB, or servlet.
The second constructor takes an environment parameter. You normally use the second form of the InitialContext
constructor in client applications, where it is necessary to specify the JNDI environment. The env
parameter in this constructor is a java.util.Hashtable
that contains properties required by JNDI. Table 2-1 lists these properties, which are defined in the javax.naming.Context
interface.
Table 2-1 InitialContext Properties
Property | Meaning |
---|---|
INITIAL_CONTEXT_FACTORY
|
Value for the java.naming.factory.initial property. This property specifies which initial context factory to use when creating a new initial context object.
|
PROVIDER_URL
|
Value for the java.naming.provider.url property. This property specifies the URL that the application client code uses to look up objects on the server. Also used by RMIInitialContextFactory and ApplicationClientInitialContextFactory to search for objects in different applications. See Table 2-2, "JNDI-Related Environment Properties" for details.
|
SECURITY_PRINCIPAL
|
Value for the java.naming.security.principal property. This property specifies the user name. Required in application client code to authenticate the client. Not required for server-side code, because the authentication has already been performed.
|
SECURITY_CREDENTIAL
|
Value for the java.naming.security.credential property. This property specifies the password. Required in application client code to authenticate the client. Not required for server-side code, because the authentication has already been performed.
|
See "Accessing Objects from an Application Client" for a code example that sets these properties and gets a new JNDI initial context.
Section 9.1 of the J2EE 1.3 specification defines application clients as follows:
"... first tier client programs that execute in their own Java virtual machines. Application clients follow the model for Java technology-based applications: they are invoked at their main method and run until the virtual machine is terminated. However, like other J2EE application components, application clients depend on a container to provide system services. The application client container may be very light-weight compared to other J2EE containers, providing only the security and deployment services described [in this specification]."
The following sections describe the ways in which JNDI initial contexts can be used:
When an application client must look up a resource that is available in a J2EE server application, the client uses ApplicationClientInitialContextFactory
in the com.evermind.server
package to construct the initial context.
Note: If your application is a J2EE client (that is, it has anapplication-client.xml file), then you must always use ApplicationClientInitialContextFactory regardless of the protocol (ORMI or IIOP) that the client application is using. The protocol itself is specified by the JNDI property java.naming.provider.url . See Table 2-2, "JNDI-Related Environment Properties" for details.
|
Consider an application client that consists of Java code running outside the OC4J server, and is also part of a bundled J2EE application. For example, the client code is running on a workstation and might connect to a server object, such as an EJB, to perform some application task. In this case, the environment that is accessible to JNDI must specify the value of the property java.naming.factory.initial
as ApplicationClientInitialContextFactory
. This can be specified in client code, or it can be specified in the jndi.properties
file that is part of the application-client.jar
file included in the EAR file.
To have access to remote objects that are part of the application, ApplicationClientInitialContextFactory
reads the META-INF/application-client.xml
and META-INF/orion-application-client.xml
files in the application-client.jar
file.
When clients use the ApplicationClientInitialContextFactory
to construct JNDI initial contexts, they can look up local objects (objects contained in the immediate application or in its parent application) using the java:comp/env
mechanism and RMIInitialContextFactory
. They can then use ORMI or IIOP to invoke methods on these objects. Note that objects and resources must be defined in deployment descriptors in order to be bound to the JNDI context of an application.
If the ORMI protocol is being used, ApplicationClientInitialContextFactory
reads the properties listed in Table 2-2 from the environment.
Table 2-2 JNDI-Related Environment Properties
Property | Meaning |
---|---|
dedicated.rmicontext
|
This property replaces the deprecated dedicated.connection setting. When two or more clients in the same process retrieve an InitialContext , OC4J returns a cached context. Thus, each client receives the same InitialContext , which is assigned to the process. Server lookup, which results in server load balancing, happens only if the client retrieves its own InitialContext . If you set dedicated.rmicontext=true , then each client receives its own InitialContext instead of a shared context. When each client has its own InitialContext , then the clients can be load balanced.
The |
java.naming.provider.url
|
This property specifies the URL to use when looking for local or remote objects. The format is either [http: | https:]ormi:// hostname / appname or corbaname : hostname : port . For details on the corbaname URL, see "The corbaname URL" .
You can supply multiple hosts (for failover) in a comma-separated list. |
java.naming.factory.url.pkgs
|
Some versions of the JDK on some platforms automatically set the system property java.naming.factory.url.pkgs to include com.sun.java.* . Check this property and remove com.sun.java.* if it is present.
|
http.tunnel.path
|
This property specifies an alternative RMIHttpTunnelServlet path. The default path is /servlet/rmi , as bound to the target site Web application. For more information, see "Configuring ORMI Tunneling through HTTP".
|
Context.SECURITY_PRINCIPAL
|
This property specifies the user name and is required in client-side code to authenticate the client. It is not required for server-side code because authentication has already been performed. This property name is also defined as java.naming.security.principal .
|
Context.SECURITY_CREDENTIAL
|
This property specifies the password and is required in client-side code to authenticate the client. It is not required for server-side code because authentication has already been performed. This property name is also defined as java.naming.security.credentials .
|
This section contains an example of how to configure an application client to access an EJB running inside an OC4J instance in the same location.
First, the EJB is deployed into OC4J. Here are excerpts of the deployment descriptors of the EJB.The EJB is deployed with the name EmployeeBean
. The name is defined this way in ejb-jar.xml
:
<ejb-jar> <display-name>bmpapp</display-name> <description> An EJB app containing only one Bean Managed Persistence Entity Bean </description> <enterprise-beans> <entity> <description>no description</description> <display-name>EmployeeBean</display-name> <ejb-name>EmployeeBean</ejb-name> <home>bmpapp.EmployeeHome</home> <remote>bmpapp.Employee</remote> <ejb-class>bmpapp.EmployeeBean</ejb-class> <persistence-type>Bean</persistence-type> ... </entity> </enterprise-beans> .. </ejb-jar>
The EJB EmployeeBean is bound to the JNDI location java:comp/env/bmpapp/EmployeeBean
in orion-ejb-jar.xml
:
orion-ejb-jar.xml file:
<orion-ejb-jar> <enterprise-beans> <entity-deployment name="EmployeeBean" location="bmpapp/EmployeeBean" table="EMP"> ... </entity-deployment> ... </enterprise-beans> ... </orion-ejb-jar>
The application client program uses the EmployeeBean
EJB, and refers to it as EmployeeBean
. An excerpt from the application client program follows:
public static void main (String args[]) { ... Context context = new InitialContext(); /** * Look up the EmployeeHome object. The reference is retrieved from the * application-local context (java:comp/env). The variable is * specified in the assembly descriptor (META-INF/application-client.xml). */ Object homeObject = context.lookup("java:comp/env/EmployeeBean"); // Narrow the reference to an EmployeeHome. EmployeeHome home = (EmployeeHome) PortableRemoteObject.narrow(homeObject, EmployeeHome.class); // Create a new record and narrow the reference. Employee rec = (Employee) PortableRemoteObject.narrow(home.create(empNo, empName, salary), Employee.class); // call method on the EJB rec.doSomething(); ... }
Note that we are not passing a hash table when creating a context in the line:
Context context = new InitialContext();
This is because the context is created with values read from the jndi.properties
file, which in this example contains:
java.naming.factory.initial=com.evermind.server.ApplicationClientInitialContextFactory java.naming.provider.url=ormi://localhost/bmpapp java.naming.security.principal=SCOTT java.naming.security.credentials=TIGER
Alternatively, you can pass a hash table to the constructor of InitialContext
instead of supplying a jndi.properties
file. The code looks like this:
Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.evermind.server.ApplicationClientInitialContextFactory"); env.put("java.naming.factory.initial", "com.evermind.server.ApplicationClientInitialContextFactory"); env.put("java.naming.provider.url","ormi://localhost/bmpapp"); env.put("java.naming.security.principal","SCOTT"); env.put("java.naming.security.credentials","TIGER"); Context initial = new InitialContext(env);
Because the application client code refers to the EmployeeBean
EJB, you must declare this in the <ejb-ref>
element in the application-client.xml
file:
<application-client> <display-name>EmployeeBean</display-name> <ejb-ref> <ejb-ref-name>EmployeeBean</ejb-ref-name> <ejb-ref-type>Entity</ejb-ref-type> <home>bmpapp.EmployeeHome</home> <remote>bmpapp.Employee</remote> </ejb-ref> </application-client>
Recall that the EmployeeBean
EJB is bound to the JNDI location java:comp/env/bmpapp/EmployeeBean
as configured in the orion-ejb-jar.xml
file. You must map the EJB name used in the application client program to the JNDI location where the EJB is actually bound to. You must do this in the orion-application-client.xml
file:
orion-application-client.xml file: <orion-application-client> <ejb-ref-mapping name="EmployeeBean" location="bmpapp/EmployeeBean" /> </orion-application-client>
You can use initial context factories in OC4J to access the following objects from J2EE application components:
You can use J2EE application components to access objects in the same application from servlets, JSP pages, and EJBs.
When code is running in a server, it is, by definition, part of an application. Because the code is part of an application, OC4J can establish defaults for properties that JNDI uses. Application code does not need to provide any property values when constructing a JNDI InitialContext
object.
When this context factory is being used, the ApplicationContext
is specific to the current application, so all the references specified in files such as web.xml
, orion-web.xml
, or ejb-jar.xml
for that application are available. This means that a lookup using java:comp/env works for any resource that the application has specified. Lookups using this factory are performed locally in the same Java virtual machine (JVM).
If your application must look up a remote reference, such as a resource in another J2EE application in the same JVM or a resource external to any J2EE application, then you must use RMIInitialContextFactory
or IIOPInitialContextFactory
. See "Objects Not in the Same Application".
As a concrete example, consider a servlet that must retrieve a data source to perform a JDBC operation on a database.
Specify data source location in data-sources.xml
as follows:
<data-source class="oracle.jdbc.pool.OracleConnectionCacheImpl" location="jdbc/pool/OracleCache" username="hr" password="hr" url="jdbc:oracle:thin:@//<hostname>:<TTC port>/<DB ID>" />
For more information on data source locations, see Chapter 4, "Data Sources".
The servlet web.xml
file defines the following resource:
<resource-ref> <description> A data source for the database in which the EmployeeService enterprise bean will record a log of all transactions. </description> <res-ref-name>jdbc/EmployeeAppDB</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> <res-sharing-scope>Shareable</res-sharing-scope> </resource-ref>
The corresponding orion-web.xml
mapping is:
<resource-ref-mapping name="jdbc/EmployeeAppDB" location="jdbc/pool/OracleCache" />
The name
value is the same as that specified in the <res-ref-name>
element in web.xml
. The location
value is the location
or ejb-location
in the <data-source>
element of data-sources.xml
.
In this case, the following code in the servlet returns the correct reference to the data source object:
... try { InitialContext ic = new InitialContext(); ds = (DataSource) ic.lookup("java:comp/env/jdbc/EmployeeAppDB"); ... } catch (NamingException ne) { throw new ServletException(ne); } ...
No initial context factory specification is necessary, because OC4J sets ApplicationInitialContextFactory
as the default value of the system property java.naming.factory.initial
when the application starts.
There is no need to supply a provider URL in this case, because no URL is required to look up an object contained within the same application or under java:comp/
.
Note: Some versions of the JDK on some platforms automatically set the system propertyjava.naming.factory.url.pkgs to include com.sun.java.* . Check this property and remove com.sun.java.* if it is present.
|
An application can use the java:comp/env mechanism to look up resources that are specified not only in its own name space, but also in the name spaces of any declared parent applications, or in the global application (which is the default parent if no specific parent application was declared).
Use one of the following context factories to access objects not in the same application:
For most application purposes, you can use either the default server-side ApplicationInitialContextFactory
or the ApplicationClientInitialContextFactory
. In some cases, however, you must use an additional context factory:
If your client application does not have an application-client.xml
file, then you must use the RMIInitialContextFactory
property and not the ApplicationClientInitialContextFactory
property.
If your client application accesses the JNDI name space remotely—not in the context of a specific application—then you must use RMIInitialContextFactory
.
The RMIInitialContextFactory
uses the following environment properties, which ApplicationClientInitialContextFactory
also uses. Table 2-2 lists these properties.
java.naming.provider.url
http.tunnel.path
Context.SECURITY_PRINCIPAL
Context.SECURITY_CREDENTIALS
Here is an example of a servlet that accesses an EJB running on another OC4J instance on a different machine. The EJB in this example is the EmployeeBean
that is used in the "Accessing Objects from an Application Client".
Here is an excerpt of the servlet code:
Hashtable env = new Hashtable(); env.put("java.naming.factory.initial", "com.evermind.server.rmi.RMIInitialContextFactory"); env.put("java.naming.provider.url","ormi://remotehost/bmpapp"); env.put("java.naming.security.principal","SCOTT"); env.put("java.naming.security.credentials","TIGER"); Context context = new InitialContext(env); Object homeObject = context.lookup("java:comp/env/EmployeeBean");
As in the case of the application client, you must declare <ejb-ref>
elements in the web.xml
file for this servlet:
<ejb-ref> <ejb-ref-name>EmployeeBean</ejb-ref-name> <ejb-ref-type>Entity</ejb-ref-type> <home>bmpapp.EmployeeHome</home> <remote>bmpapp.Employee</remote> </ejb-ref>
In addition, orion-web.xml
, must include a mapping from the logical name EmployeeBean
to the actual JNDI name where the EJB is bound, as shown in the following example:
<ejb-ref-mapping name="EmployeeBean" location="bmpapp/EmployeeBean" />
JNDI state replication ensures that changes made to the context on one OC4J instance of an OC4J cluster is replicated to the name space of every other OC4J instance.
When JNDI state replication is enabled, you can bind a serializable value into an application context (using a remote client, EJB, or servlet) on one server and read it on another server. You can also create and destroy subcontexts in this way.
This section explains:
JNDI state replication is enabled when EJB clustering is enabled.
To take advantage of JNDI state replication, you must enable EJB clustering, even if you do not specifically require EJB clustering (for example, when using JNDI to find startup classes or data sources).
For information on enabling EJB clustering, see the EJB clustering chapter in the Oracle Application Server Containers for J2EE Enterprise JavaBeans Developer's Guide.
For information on OC4J clustering in general, see the clustering chapter in the Oracle Application Server Containers for J2EE User's Guide.
Consider the following limitations when relying on JNDI state replication:
Although OC4J processes can be organized into groups (known as islands) to improve state-replication performance, EJB applications replicate state between all OC4J processes in the OC4J instance and do not use the island subgrouping. This is described in the clustering chapter in the Oracle Application Server Containers for J2EE User's Guide.
Consequently, JNDI clustering is not limited to an island subnet. If there are multiple islands on a single subnet, then all islands on that subnet share the global JNDI context.
Rebinding (renaming) and unbinding are not propagated: they apply locally but are not shared across the cluster.
Bindings to values that are not serializable are also not propagated across the cluster.