Throughout the rest of this book, servlets have been used as a standalone technology built upon the standard Java base. Servlets have another life, however, where they act as an integral piece of what's known as Java 2, Enterprise Edition, or J2EE for short. J2EE 1.2 collects together several server-side APIs including Servlet API 2.2, JSP 1.1, EJB, JavaMail, the Java Messaging Service ( JMS), Java Transactions (JTA), CORBA, JDBC, the Java API for XML Parsing ( JAXP), and the Java Naming and Directory Interface ( JNDI). J2EE makes the whole greater than the sum of its parts by defining how these technologies can interoperate and make use of one another, and providing certification that certain application servers are J2EE compliant, meaning they provide all the required services as well as the extra connection glue.
J2EE Division of Labor
J2EE breaks enterprise application development into six distinct roles. Of course, an individual may participate in more than one role and multiple individuals may work together in a given role.
- J2EE product provider
- The operating system vendor, database system vendor, application server vendor, and/or web server vendor. The product provider provides an implementation of the J2EE APIs and tools for application deployment and management.
- Application component provider
- The author of the application's servlets, EJB, and other code as well as general content such as HTML. (In other words, you.)
- Application assembler
- Takes the application's components and (using tools from the product provider) places them in a form appropriate for deployment. As part of this the assembler describes the external dependencies of the application that may change from deployment to deployment, like database or user login information.
- Takes the output of the assembler and (using tools from the product provider) installs, configures, and executes the application. The configuration task requires satisfying the external dependencies outlined by the assembler.
- System administrator
- Configures and administers the network infrastructure to keep the application alive.
- Tool provider
- Creates tools to support J2EE development, beyond those provided by the product provider.
The division of labor between component provider, assembler, and deployer has an impact on how we (as servlet programmers in the content provider role) behave. Specifically, we should design our code to make external dependencies clear for the assembler, and furthermore we should use mechanisms that allow the deployer to satisfy these dependencies without modifying the files received from the assembler. That means no deployer edits to the web.xml file! Why not? Because J2EE applications are assembled into Enterprise Archive (.ear) files of which a contained web application's web.xml file is but one uneditable part.
This sounds more difficult than it actually is. J2EE provides a standard mechanism to achieve this abstraction using JNDI and a few special tags in the web.xml deployment descriptor. JNDI is an object lookup mechanism, a way to bind objects under certain paths and locate them later using that path. You can think of it like an RMI registry, except it's more general with support for accessing a range of services including LDAP and NIS (and even, in fact, the RMI registry!). An assembler declares external dependencies within the web.xml using special tags, a deployer satisfies these dependencies using server-specific tools, and at runtime our Java code uses the JNDI API to access the external resources -- kindly placed there by the J2EE-compliant server. All goals are satisfied: our Java code remains portable between J2EE-compliant servers, and the deployer can satisfy the code's external dependencies without modifying the files received from the assembler. There's even enough flexibility left over for server vendors to compete on implementations of the standard.
Context init parameters serve a useful purpose with servlets, but there's a problem with context init parameters in the J2EE model: any change to a parameter value requires a modification to the web.xml file. For parameter values that may need to change during deployment, it's better to use environment entries instead, as indicated by the
<env-entry> tag. The
<env-entry> tag may contain a
<env-entry-type>. The following
<env-entry> specifies whether the application should enable sending of PIN codes by mail:
<description>Send pincode by mail</description>
<env-entry-type>java.lang.Boolean</env-entry-type> <!-- FQCN -->
<description> explains to the deployer the purpose of this entry. It's optional but a good idea to provide. The
<env-entry-name> is used by Java code as part of the JNDI lookup. The
<env-entry-value> defines the default value to be presented to the deployer. It's optional, but not specifying a value requires the deployer to provide one. The
<env-entry-type> represents the fully qualified class name (FQCN) of the entry. The type may be a
Float (all with their full
java.lang qualification). The type helps the deployer know what's expected. If you're familiar with the EJB deployment descriptor, these tags may look familiar; they have the same names and semantics in EJB as well.
Java code can retrieve the
<env-entry> values using JNDI:
Context initCtx = new InitialContext( );
Boolean mailPincode = (Boolean) initCtx.lookup("java:comp/env/mailPincode");
All entries are placed by the server into the
java:comp/env context. If you're new to JNDI, you can think of this as a URL base or filesystem directory. The
java:comp/env context is read-only and unique per web application, so if two different web applications define the same environment entry, the entries do not collide. The context abbreviations, by the way, stand for component environment.