Enterprise Java Technologies Tech Tips
Tips, Techniques, and Sample Code
Welcome to the Enterprise Java Technologies Tech Tips for
October 27, 2003. Here you'll get tips on using enterprise Java
technologies and APIs, such as those in Java 2 Platform,
Enterprise Edition (J2EE).
This issue covers:
* Introduction to JNDI
* Writing an Entity Resolver
These tips were developed using Java 2 SDK, Standard Edition,
v 1.4 and Java 2 SDK, Enterprise Edition, v 1.3.1 (Reference
Implementation). You can download the current Reference
Implementation or the next release of the Reference
Implementation, Java 2 SDK, Enterprise Edition, v 1.4 (currently
a Beta release), at http://java.sun.com/j2ee/download.html.
This issue of the Tech Tips is written by Mark Johnson, president
of elucify technical communications (http://www.elucify.com/),
and co-author of Designing Enterprise Applications with the J2EE
Platform, 2nd Edition (http://java.sun.com/blueprints/guidelines/
designing_enterprise_applications_2e/). Mark Johnson runs an open
forum for discussion of the tips at
http://groups.yahoo.com/group/techtipsarchive/.
You can view this issue of the Tech Tips on the Web at
http://java.sun.com/jdc/EJTechTips/2003/tt1027.html
See the Subscribe/Unsubscribe note at the end of this newsletter
to subscribe to Tech Tips that focus on technologies and products
in other Java platforms.
You can download the sample archive for these tips at
http://java.sun.com/jdc/EJTechTips/download/ttoct2003.ear. The
context root for the application is ttoct2003, and the index.html
welcome file indicates how to use the sample code. Any use of
this code and/or information below is subject to the license
terms at http://developer.java.sun.com/berkeley_license.html
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
INTRODUCTION TO JNDI
Many J2EE developers use the Java Naming and Directory Interface
(JNDI) on a regular basis to look up environment entries,
DataSource objects, JMS message destinations, and enterprise bean
home interfaces. But many people simply copy, paste, and modify
code that does these things without really understanding JNDI.
This tip provides an introduction to using JNDI as a way to
access the distributed resources in your enterprise.
Enterprise applications, by their very nature, pull together
distributed resources from multiple locations to support business
operations. Services may come and go as new systems are created,
existing systems are upgraded, and old systems are retired.
Decoupling application services from one another makes a system
easier to extend and maintain. But when services are decoupled,
they need to be able to find one another to do their jobs. That's
where naming services and directories become useful.
A naming service provides a way to look up objects or references
to objects by name. Examples of such objects include message
queues, database connection factories, environment parameters,
and distributed components such as enterprise beans. Application
developers give names to objects by binding them to names within
a naming service. Application code can then use the naming
service to look up objects by their bound names. This decoupling
means that network objects can be brought up and down for
maintenance, requests can be redirected, and services can be
reconfigured dynamically, all without any change to the system
components that use them.
You probably already have a good understanding of several
existing naming services:
o DNS (Domain Name Service) maps hostnames like java.sun.com to
IP addresses like %nslookup java.sun.com%.
o The COS (Common Object Services) naming service, used for
CORBA (Common Object Request Broker Architecture), maps CORBA
interface names to object interfaces.
You can even consider a computer's filesystem as a naming service
that maps a file's pathname to its contents.
A mapping from a name to an object is called a binding. Bindings
are created by the person who configures the naming service. Most
naming services also provide a way for programs to bind and
unbind names to objects at runtime.
A context is a set of bindings of names to objects. For example,
in a filesystem the pathname /home is often the context that
contains the system's user directories. Contexts may contain
other contexts. The user directories in the /home context are
themselves contexts that contain user files.
At the very least, a context has a naming convention and a lookup
function. For example, DNS has a naming convention of strings
separated by dots, with the least-significant string to the left,
and the domain on the right. The lookup function for DNS can be
accessed from the command line using the nslookup program. (Of
course, there is also a programmatic interface to the DNS naming
service.) Contexts often offer ways to bind and unbind objects,
and to list their contents.
Sometimes the objects in a naming service contain the data
another program needs. For example, the objects representing
environment entries in a J2EE application are often stored in the
naming service. Other times, the object in the naming service
represents a reference to the object. For instance, an object
that provides a reference to a server is usually stored by the
naming service as a reference to a server, not as an open server
connection. The reference object returned by the naming service
can be used to create a server connection when it is needed.
A naming service that provides data about bound objects is called
a directory. For example, filesystem directories typically offer
information about the size, type, access permissions, and
creation and modification dates of a file. Some directories allow
lookup both by name or by combination of attributes.
Although each naming service is well suited to its task, the way
each naming service works is different. Each naming service has
its own naming conventions, lookup, binding, and directory
protocols, and object service interfaces. JNDI provides a
consistent way to name and find network services.
Java Naming and Directory Interface
You might already know how to use JNDI to access network objects
such as JDBC database connections, JMS queues, or enterprise bean
home interfaces. Although JNDI does map names to objects, JNDI is
not a naming service. Rather, it is a set of interfaces that wrap
existing naming services, making them accessible in a standard
way.
Code in an application calls JNDI interface methods. The objects
that implement these methods map the JNDI interface calls to
calls on the underlying naming service. JNDI also defines a
unified naming convention. JNDI names are mapped by JNDI's
naming manager into names that conform to the underlying naming
services' naming conventions.
The package javax.naming contains the following naming and
directory-related interfaces:
o javax.naming.Context represents a naming context, which is used
to look up and manage bindings and subcontexts.
o javax.naming.Name provides an abstract representation of a name
in a naming service.
o javax.naming.Binding is a representation of a naming service
name and the object to which the name is bound.
o javax.naming.Reference is a representation of how to get a
copy of an object.
Looking Up a Context
The sample code that accompanies this tip shows how to list the
contents of a JNDI context. The sample servlet, Oct2003Servlet,
looks up and displays the contents of the JNDI namespace for
a name supplied by the user.
The easiest way to get a context is to create an instance of
class javax.naming.InitialContext. The sample servlet method
jndiList creates an initial context, and uses it to look up
a named object:
InitialContext ic = new InitialContext();
Object objFound = ic.lookup(name);
The name comes from the HTTP GET or POST variable name supplied
by the user HTML page. If the object returned is a Context,
jndiList calls method listContext. The listContext method lists
the contents of the context at the given name. If the object is
a DataSource, jndiList prints some information about the named
data source.
Method listContext prints a table of the contents of a given JNDI
context. To do so, it uses Context method listBindings, which
returns a NamingEnumeration.
NamingEnumeration ne = context.listBindings("");
NamingEnumeration implements java.util.Enumeration. Method
NamingEnumeration.next returns an object of type
javax.naming.Binding, which contains the name of the object, the
object's class name, and the stored object itself:
while (ne.hasMore()) {
Binding ncp = (Binding)ne.next();
String objName = ncp.getName();
String objClass = ncp.getClassName();
Object objObj = ncp.getObject();
...
}
If you simply want the names and classnames in a Context, you can
use method Context.list. That method also returns a
NamingEnumeration, but the collection it iterates is of type
NameClassPair instead of Binding. NameClassPair contains only the
name and object class name.
See the section "Running the Sample Code" for instructions on
how to deploy and run the sample application.
For more information about JNDI, see the JNDI Tutorial
(http://java.sun.com/products/jndi/tutorial/index.html).
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
WRITING AN ENTITY RESOLVER
If you write XML, you've certainly seen the symbols XML uses
to represent characters such as ampersand (&) and less-than
(<). These symbols are called entities. In XML, entities
always begin with an ampersand and end with a semicolon. Entities
are used to parameterize XML documents. This tip explains how to
parameterize your XML documents by defining entities in the
document's DTD. It also explains how to write an entity resolver
that controls what text replaces entity references in an XML
document.
Defining Entities
Any text that is repeated in multiple places in an XML document
might be better represented as an entity. Entities can easily be
defined in the document's DTD. An example of defining such an
entity appears in the sample code file misc/data.xml:
The entity can then be used in the XML itself, just as you
would use & or <. Here's an example from the file
data.xml:
&helloWorld;
&helloWorld;
...
When an XML parser reads this text, it replaces everything
between the ampersand and the semicolon with the contents of the
entity:
Hello, world!
Hello, world!
...
An entity whose value is defined directly in the DTD is called an
internal entity. It's a lot like a #define in a C program.
Internal entities are useful for placing repetitive text, such as
disclaimers or copyright notifications, in a single place in a
project. Updating the definition of the internal entity changes
the replacement text everywhere.
Including File Contents
A second kind of entity, called an external entity, gets its
replacement text from a file instead of from a string. An
external entity is more like a #include in C. The replacement text
for an external entity is the entire contents of a file.
One kind of external entity uses a public identifier to name the
entity, and a system identifier to tell the XML processing
program where to find the replacement text. The sample code for
this tip includes an XML file, data.xml, that defines four
external entities in this way:
Each entity has a unique name (its public id), that is the first
string after the PUBLIC keyword. The second string is the system
id -- it provides a system-specific identifier for where to find
the replacement text.
When an entity reference (such as &firstLine) is used in an XML
document, the XML processor must know how to find the replacement
text. This is called resolving the entity reference.
By default, most XML processors try to use the system ID, as
either a URL or a filesystem pathname, to retrieve the entity's
replacement text. Sometimes, though, you need to take control of
how the XML processor resolves the reference. For example, what
if the system id is a URL, but you're not connected to the
Internet? The SAX interface, on which SAX and DOM parsers are
based, allows you to control the replacement text for an external
entity by defining an entity resolver class.
Resolving Entity References
An entity resolver implements interface
org.sax.xml.EntityResolver. It has a single method, resolveEntity,
whose signature is as follows:
public InputSource resolveEntity(String publicID,
String systemID)
throws SAXException;
The method receives as arguments the public id and system id of
the entity, which are the first and second arguments after the
PUBLIC keyword in the declaration above. It returns an
object that implements interface org.sax.xml.InputSource. An
entity resolver uses the public and system ids to locate the
replacement text for the entity, and returns an InputSource object
that the parser accesses to get the replacement text.
The sample code for this tip also includes an entity resolver,
PropertyEntityResolver, that reads a Java property file using the
Properties class. The code that loads the file and retrieves the
replacement text appears below:
// Load properties from a URL.
protected void loadProps(String urlString)
throws MalformedURLException, IOException {
URL url = new URL(urlString);
URLConnection con = url.openConnection();
_props = new Properties();
_props.load(con.getInputStream());
}
// Given the name of a property,
// return the property's value
public InputSource resolveEntity(
String publicID, String systemID)
throws SAXException {
// Load properties from URL referenced
// by system id.
// Assume system id is the same for all symbols.
if (_props == null) {
try {
loadProps(systemID);
} catch (Exception e) {
throw new SAXException(e);
}
}
// Extract property name from public ID
int index = publicID.lastIndexOf("//");
String value = "???PropertyNotFound???";
if (index >= 0) {
value = _props.getProperty(
publicID.substring(index + 2));
}
StringReader sr = new StringReader(value);
return new InputSource(sr);
}
When the sample class EntityResolverDemo creates its DOM parser,
it sets the entity resolver for the class to an instance of
PropertyEntityResolver, like this:
// Create the parser, and set the error handler
// and entity resolver
DocumentBuilder db = dbf.newDocumentBuilder();
db.setErrorHandler(this);
PropertyEntityResolver propertyEntityResolver =
new PropertyEntityResolver();
db.setEntityResolver(propertyEntityResolver);
When the parser encounters an external entity reference, it calls
PropertyEntityResolver.resolveEntity, and receives as a response
an InputSource containing the replacement text. It then reads the
replacement text and substitutes it in the result XML where the
entity reference occurred.
See the section "Running the Sample Code" for instructions on
how to deploy and run the sample application. You'll find
instructions for running the ERDemo program on the index page.
Try running the sample program with the data.xml file specifying
the file english.props. To do that:
1. Extract the data files from the entity resolver jar file,
ERDemo.jar: jar xvf ERDemo.jar misc
2. Put ERDemo.jar in your CLASSPATH.
3. Run the example: java ERDemo misc/data.xml
When you run the ERDemo program, it should generate an XML
document with English replacement text like this:
Hello, world!
Hello, world!
Line 1: This line is written in English.
Line 2: How are you?
Line 3: What would you like to eat?
Last line: I must say goodbye.
Then edit data.xml and replace "english.props" with
"spanish.props" in all of the
The parsed text will change to the following:
Hello, world!
Hello, world!
Line 1: Esta linea esta escrito en Espagnol.
Line 2: Como estas?
Line 3: Que quieres cenar?
Last line: Hay que decir adios!
The sample code also includes property files in French and
German. Try running the sample code using those property files too.
For more information about JNDI, see the JNDI Tutorial
(http://java.sun.com/products/jndi/tutorial/index.html).
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RUNNING THE SAMPLE CODE
Download the sample archive for these tips (from
http://java.sun.com/jdc/EJTechTips/download/ttoct2003.ear).
The application's context root is ttoct2003. The downloaded
ear file also contains the complete source code for the sample.
To run the sample program, follow these steps:
1. Be sure that both the j2ee server and database server
are running. For the J2EE Reference Implementation, execute
both:
j2ee -verbose
and
cloudscape -start
2. Download the ear file as described above.
3. Deploy the ear file to your server. You can deploy the
application archive (ttoct2003.ear) in the J2EE Reference
Implementation using the deploytool program:
$J2EE_HOME/deploytool -deploy ttoct2003.ear localhost
Replace localhost with the name of the host on which the
server is installed. For a standard installation on a single
machine, the hostname typically (and literally) is localhost.
4. Access the application at http://localhost:8000/ttoct2003.
For a J2EE-compliant implementation other than the Reference
Implementation, use your J2EE product's deployment tools to
deploy the application on your platform.
See the index.html welcome file for instructions on running the
application.
. . . . . . . . . . . . . . . . . . . . . . .
IMPORTANT: Please read our Terms of Use, Privacy, and Licensing
policies:
http://www.sun.com/share/text/termsofuse.html
http://www.sun.com/privacy/
http://developer.java.sun.com/berkeley_license.html
* FEEDBACK
Comments? Send your feedback on the Enterprise Java Technologies
Tech Tips to:
http://developers.sun.com/contact/feedback.jsp?category=sdn
* SUBSCRIBE/UNSUBSCRIBE
Subscribe to other Java developer Tech Tips:
- Core Java Technologies Tech Tips. Get tips on using core
Java technologies and APIs, such as those in the Java 2
Platform, Standard Edition (J2SE).
- Wireless Developer Tech Tips. Get tips on using wireless
Java technologies and APIs, such as those in the Java 2
Platform, Micro Edition (J2ME).
To subscribe to these and other JDC publications:
- Go to the JDC Newsletters and Publications page,
(https://softwarereg.sun.com/registration/developer/en_US/subscriptions),
choose the newsletters you want to subscribe to and click
"Update".
- To unsubscribe, go to the subscriptions page,
(https://softwarereg.sun.com/registration/developer/en_US/subscriptions),
uncheck the appropriate checkbox, and click "Update".
- To use our one-click unsubscribe facility, see the link at
the end of this email:
- ARCHIVES
You'll find the Enterprise Java Technologies Tech Tips archives at:
http://developer.java.sun.com/developer/EJTechTips/index.html
- COPYRIGHT
Copyright 2003 Sun Microsystems, Inc. All rights reserved.
901 San Antonio Road, Palo Alto, California 94303 USA.
This document is protected by copyright. For more information, see:
http://java.sun.com/jdc/copyright.html
Enterprise Java Technologies Tech Tips
October 27, 2003
Trademark Information: http://www.sun.com/suntrademarks/
Java, J2SE, J2EE, J2ME, and all Java-based marks are trademarks
or registered trademarks of Sun Microsystems, Inc. in the
United States and other countries.