CONTENTS | PREV | NEXT | INDEX Designing Enterprise Applications
with the J2EETM Platform, Second Edition



7.3 Packaging J2EE Applications

A J2EE application is packaged as a portable deployment unit called an enterprise archive (EAR) file. An EAR file is standard JAR file with a .ear extension. An EAR file contains:

Creation of a J2EE application is a two-step process. First, application component providers create EJB, Web, and application client modules. Second, the application assembler packages these modules together to create a J2EE application module that is ready for deployment. This section discusses issues involved in both of these steps.

All J2EE modules are independently deployable units. This enables component providers to create independent units of functionality without having to implement full-scale applications.

Figure 7.2 illustrates the various types of J2EE modules (EJB, Web, application client, and application) and how they can be deployed. Although the figure shows only an independently deployed EJB module (at the bottom of the figure), all four types of J2EE modules can be deployed independently.

To assemble an application, an application assembler resolves dependencies between components by creating links in the corresponding modules' deployment descriptors. Each component may have dependencies on other components within the same archive, on components in different archives, or both. All such dependencies must be resolved before deployment. For example, in the sample application, the Web components in the WAR file need to refer to ShoppingClientController, Catalog, Account, Order, and ShoppingCart enterprise beans in the EJB JAR file. The application assembler ensures that the description of the enterprise beans in the WAR file matches their descriptions in the EJB JAR file.

The J2EE specifications place a number of requirements on components and deployment units, most of which are essential for proper component operation. Yet component containers are not required to enforce many of these rules at run-time. Application assemblers should run verifier tools (such as the one included with the J2EE SDK) on assembled EAR files to verify that their contents are internally consistent. A verifier performs a number of static checks to ensure that the deployment descriptor and the archive file contents are referentially valid and conform to the EJB, servlet, and J2EE specifications. Common errors that a verifier can identify include mandatory naming convention violations, missing exception declarations, missing deployment descriptor entries, unresolved external component and resource references, name collisions, structural information that conflicts with code, and inaccessible support classes and interfaces. In addition, a vendor-supplied verifier can check the consistency of product-specific deployment information. An intelligent verifier can guide a deployer through the process of resolving these inconsistencies, provide contextual help, and even present suggested solutions. While verification does not guarantee correct runtime behavior, it can catch a wide class of errors before deployment.

Figure 7.2 J2EE Packages

The following sections discuss the different types of J2EE modules and give some heuristic rules and practical tips on how best to package different component types.


7.3.1 EJB Modules

An EJB module is packaged and deployed as an EJB JAR file, a JAR file with a .jar extension. It is the smallest deployable and usable unit of enterprise beans. An EJB module contains:

An EAR file differs from a standard JAR file in one key aspect: It includes a deployment descriptor (called META-INF/ejb-jar.xml) that contains meta-information about one or more enterprise beans.

In addition to the EJB JAR file for server-side use, an EJB JAR file producer should create a client JAR file containing all class files that a client program needs to access the enterprise beans contained in the EJB JAR file. Server-side component implementation classes are not included in a client JAR file.

A class may be included in an EJB JAR file or client JAR file either by direct inclusion of a class file, or by external reference to some other JAR file in the same J2EE application.


7.3.2 EJB Module Packaging Guidelines

These guidelines cover how to group EJB components into useful modules and provide pointers on packaging EJB modules.

7.3.2.1 Packaging Components into EJB Modules

A typical enterprise application contains many enterprise beans. Some of these enterprise beans may be off-the-shelf components, while others may use third-party libraries. The application assembler, therefore, must choose from the following packaging options:

  1. Package each enterprise bean for an application in its own EJB module. In this approach, each enterprise bean has its own deployment descriptor and is packaged in one EJB module along with its dependent classes. One advantage of this approach is that it maximizes reusability of each enterprise bean by leaving the application assembler free to pick and choose among these EJB modules to compose additional J2EE applications. This option is recommended if your enterprise beans are each highly reusable. In such a case, the application assemblers will be able to reuse precisely those enterprise beans that they wish to reuse, and no more.
  2. Package all enterprise beans for an application in one EJB module. In this approach, all enterprise beans and their dependent classes are packaged together in one EJB module. This approach is the simplest to implement. The application assembler does not have to specify references to the enterprise beans present in this EJB module as unresolved. This makes the job of application assemblers easier. Application assemblers who only wish to use a subset of the enterprise beans in the EJB module will still be able to do so, but may end up with a bloated application. The deployer in this case may have to deploy superfluous enterprise beans.
  3. Package all related (closely-coupled) enterprise beans for an application in one EJB module. In this approach, all off-the-shelf components are used as is (that is, in their own EJB modules). All in-house enterprise beans are grouped based on their functional nature and put in one EJB module. For example, all enterprise beans related to account management can be put in one EJB module.

The third option is more modular and thus is recommended for most J2EE applications. It strikes the right balance between maximum reusability (option 1) and maximum simplicity (option 2). It promotes the black-box use of third-party components, which is especially important in the case where those components are digitally signed (although this is not a requirement of the J2EE platform). Another value of the third option arises when a J2EE server deploys each EJB module on a separate Java virtual machine for load balancing. In such cases, the third option is most efficient since it groups closely-coupled enterprise beans together, allowing many remote calls to be optimized to local calls. Another advantage of option 3 is that it promotes reusability at the functional level rather than at the enterprise bean level. For example, making a single Account enterprise bean reusable is more difficult than providing a reusable set of classes that provide account management functionality collectively. Logical grouping also makes sense from a tool point of view. A deployment or assembly tool may show the EJB module as a group under a single icon. The following discussions provide guidelines on various ways to group enterprise beans.

7.3.2.1.1 Grouping by Related Functionality

A group of enterprise beans that is packaged into the same EJB module may not easily be separated without knowing significant implementation details of each enterprise bean. To reuse one bean from an EJB module, you must generally deploy the entire module, including beans that you don't use. It thus makes good sense to package together a group of enterprise beans only if they will be commonly deployed and used together.

All utility classes used by a bean may be packaged into the EJB module of that bean. But redundant copies of utility classes increase the virtual machine size of most J2EE implementations and may cause potential conflicts during upgrades. Packaging related beans together reduces the number of copies of utility classes in memory. For these reasons, it is recommended that utility classes used by only one bean be packaged within the same EJB JAR file as that bean. Utility classes that are shared between modules should be packaged into utility JAR files and accessed referentially by their clients.

Grouping related beans in functional packages makes components easier to use with development tools. J2EE application assembly tools commonly display EJB modules in a palette of reusable components. Tools also typically visually group together enterprise beans from the same EJB module. For example, when server-side components related to accounting are grouped in a single code library or EJB module, they show up as accounting components in the development user interface.

7.3.2.1.2 Grouping Interrelated Beans

Enterprise beans can call one another at runtime, and one enterprise bean can delegate some of its functionality to another. Though some J2EE servers will support highly efficient cross-application dependencies, enterprise beans that depend on one another should be grouped together in the same JAR file for both organizational and performance reasons. In particular, all local beans that refer to one another should be packaged in the same JAR file.

Where beans call one another, an EJB module may be delivered preassembled, with all the enterprise bean cross-references resolved within the same unit. This makes the tasks of both the assembler and the deployer much easier. Locating an appropriate accounting bean for use by a teller bean across a number of servers may prove tedious, despite the best efforts and user interface wizardry of the authors of a J2EE deployment tool. Where one bean delegates to another, many servers will partition deployed EJB modules across different process and even machine boundaries. A bean that makes frequent calls to another bean in a separate address space can cause performance problems.

7.3.2.1.3 Grouping for Circular References

When two enterprise beans refer to each other, the result is a circular dependency. Neither bean can function without the other, and so neither is reusable without the other. In some cases redesign may eliminate these dependencies. When circular references are necessary, you should also package the components together in the same EJB module to ensure reusability.

7.3.2.1.4 Grouping with Common Security Profiles

While each EJB module allows a number of abstract security roles to be specified, enterprise beans are often written with a discrete set of users in mind. Enterprise beans that have the same security profile should be grouped together to keep security role names consistent.

7.3.2.2 Local Interfaces in the JNDI Namespace

Many EJB implementations expose enterprise bean home interfaces at defined, vendor-specific places in the Java Naming and Directory Interface (JNDI) namespace. A vendor-specific auxiliary deployment descriptor then usually binds the component's ejb-name (a component's application-global symbolic name) to its JNDI name (the name of the actual component).

But because there is no need for remote access to local interfaces, local home interfaces need not be exposed in the global JNDI namespace. While the component may look up local home interfaces using JNDI, and receive valid results, the container is not required to expose local beans anywhere in the JNDI namespace. Instead, the container implements all JNDI lookups of local interfaces, returning an appropriate object on request.

7.3.2.3 EJB Module Deployment Recommendations

This section provides a few minor defensive deployment recommendations for EJB modules.

Enterprise bean classes may have public methods that aren't declared in the bean's home and component interfaces. Deployment descriptors should not indicate transaction or security attributes for such methods. An EJB container cannot provide transactional behavior or enforce security constraints on such methods, because it can interpose only on public component or home interface method invocations.

Occasionally the primary key class for an entity bean using container-managed persistence will be either undefined or unknown to the component provider. In such cases, set the prim-key-class deployment descriptor element for the entity bean to java.lang.Object.

Some entity beans simply wrap a layer of functionality around existing enterprise data, managing and updating that data with container-managed persistence. Deployers should be certain that undeploying such beans does not cause the table representing the beans to be dropped, unless that behavior is what is desired. Likewise, the same component may be used in multiple places in an application; for example, ContactInfo for both Customer and Supplier components. When such beans use container-managed persistence, the deployer should consider whether all instances of the component should be stored in one table or multiple tables and configure the persistence behavior accordingly. A deployer can typically control these features by using vendor-specific deployment information (see Section 7.5.2.1 on page 244).


7.3.3 Web Modules

A Web module is packaged and deployed as a Web archive (WAR) file, a JAR file with a .war extension. It is the smallest deployable and usable unit of Web resources. A Web module contains:

Unlike other deployment unit types, a WAR file usually cannot be loaded by a classloader, because its internal directory structure differs from that of a loadable JAR file (see Section 7.3.4.2 on page 216). Like other module types, a WAR file may be deployed independently as a Web application or packaged in an EAR file and deployed as a J2EE application.


7.3.4 Packaging Components into Web Modules

The Web module is the smallest indivisible unit of Web resources that an application component provider supplies to the application assembler. This section contains guidelines for how to package Web-tier components into Web modules.

7.3.4.1 Request Path Elements

Understanding how Web application components map into a server address space requires an understanding of the structure of a request Uniform Resource Identifier (URI). The URI representing a request to a Web component is called a request path. After the protocol and hostname, a request URI has the following components:

For example, consider the following request URI:

http://localhost/webapps/sample_app/jsp/Login.jsp/foo?uid=123 

If the servlet mapping pattern that matched this request is jsp/*.jsp, then the context path is /webapps/sample_app, the servlet path is jsp/Login.jsp, and the path info is foo. Except for URL encoding details, a valid request URI is always a context path, followed by a servlet path, followed by path info.

The deployer maps the context root of a Web application into a Web server's namespace using vendor-specific tools. The servlet specification does not define a mechanism for this mapping.

7.3.4.2 Web Application Directory Structure

The Java Servlet specification defines a mandatory directory structure for a Web application deployment unit. This structure is defined in Section 9.4 of the Java Servlet specification, version 2.3. The Web application directory structure applies to the internal structure of a WAR file. The Java Servlet specification recommends, but does not require, that this same structure also be used as a runtime representation. Figure 7.3 shows this structure graphically.

Figure 7.3 Web Application Directory Structure

The root directory of the Web application is the context root, which is mapped to the context path at deployment time. The context root contains the application's JSP pages, content, graphics, applet classes, and other files that the application serves to clients. These files are shown on the right in Figure 7.3.

Also under the context root is the WEB-INF directory, which contains files that are not intended to be served to clients. The WEB-INF directory has a specific structure, and has the following contents:

Both the context root and WEB-INF may contain other files and directories in addition to those that are required. Files and directories in WEB-INF are accessible to Web components such as servlets and JSP pages, but can never be accessed directly by clients. Sensitive files, such as configuration files and security descriptors, should reside in the WEB-INF directory to protect them from unauthorized access by clients. Web-tier components may access these files using the methods ServletContext.getResource or ServletContext.getResourceAsStream.

Because servlet classes, servlet filter classes, tag libraries, and server-side utility classes are never served to clients, they should always reside either in JAR files in WEB-INF/lib or as class files in WEB-INF/classes. The BluePrints best practice for such classes is to package them into JAR files in WEB-INF/lib for ease of management.

7.3.4.3 Hyperlinks within a Web Module

Hyperlinks in a Web application should reference pages or components within the same module using relative, rather than absolute, paths. Using absolute URLs in paths assumes a fixed context path. If the context path changes for some reason, every absolute URL in the application will also need to be changed. For example, a component or static page that references another page in the same module using path ../help/purchasing.html will work correctly regardless of the value of the context path. By contrast, a link that used path /myapp/help/purchasing.html in a link would require changes if the context path were ever changed from /myapp to some other value.

Legacy content may include absolute URLs. URLs for legacy content often can be mapped into a Web application's namespace using a Web server's proprietary aliasing features.

7.3.4.4 Decoupling Application Components

Web components may directly invoke one another via HTTP, creating dependencies between components and applications. To avoid these dependencies, components that call one another should be packaged in the same Web module and deployed together.

Sometimes calling components between applications is unavoidable. Unfortunately, hard-coding a path to a component in another application makes the referencing application dependent on the context path of the referenced application. For example, if a servlet mapped to /apps/myStore/servlets/orderServlet invokes a servlet at /apps/warehouseApp/servlets/checkInventory, then application myStore requires that application warehouseApp be available at context path /apps/warehouseApp. If that context path changes, application myStore will require a code modification to operate properly.

Dependencies of a component on its environment should always be explicit. The BluePrints best practice for accessing components between applications is to externalize the dependency by using environment entries in the deployment descriptor instead of hard-coding paths to external components. In the example above, an application component provider could define an environment entry in the Web application's deployment descriptor, as shown in Code Example 7.3.

<env-entry>
	<description>
		Path to the warehouse checkInventory service
	</description>
	<env-entry-name>warehouseCheckInventory</env-entry-name>
	<env-entry-value>
		/apps/warehouseApp/servlets/checkInventory
	</env-entry-value>
	<env-entry-type>java.lang.String</env-entry-type>
</env-entry>
Code Example 7.3 Externalizing a Dependency between Applications

Any component of the myStore application could then access the inventory service indirectly by looking up its path in the environment entry.

InitialContext ctx = new InitialContext();
String invPath = (String)ctx.lookup("java:comp/env/warehouseCheckInventory");

// ... invoke servlet using invPath...
Code Example 7.4 Accessing Another Application Indirectly

When the myStore application is deployed, a deployer ensures that the env-entry-value for the environment entry corresponds to a valid path. The application myStore accesses application inventoryApp indirectly through deployment information, so the dependency can be managed without changes to application code.

7.3.4.5 Cross-Linked Static Content

Cross-linked Web pages must be packaged in a single Web module to avoid broken links. Moreover, cross-linked HTML Web pages are typically reusable as a bundle, so it makes sense to package them together. As recommended in Section 7.3.4.3, the best practice is to use relative paths where possible for static content.

One way to make absolute URLs independent of context path is to use JSP pages with a custom action for linking. At runtime, the custom action can generate an HTML a tag with an href attribute that includes the context path.

<%@ taglib uri="http://java.sun.com/j2ee/blueprints/sampletags" 
prefix="newtag" %>

This hyperlink is portable: <newtag:alink href="/a/b/c.html">Link 
Here</newtag:alink>
Code Example 7.5 Using A Custom Action for Context Path Independence

Code Example 7.5 is an example of a JSP page that uses a custom tag called alink, which outputs an HTML a tag with the context path prepended to its href attribute value.

public int doStartTag() {
	HttpServletRequest req =
		(HttpServletRequest)pageContext.getRequest();
	String contextPath = req.getContextPath();
	try {
		pageContext.getOut().println(
			"<a href=\"" + contextPath + href + "\">");
	} catch (Exception e) { ... }
		return EVAL_BODY_INCLUDE;
	}

	public int doEndTag() {
		JspWriter out = pageContext.getOut();
		try { pageContext.getOut().println("</a>"); 
		} catch (Exception e) { ... }
		return EVAL_PAGE;
	}
}
Code Example 7.6 Tag Handler for Context Path-Independent Hyperlinks

Method doStartTag in Code Example 7.6 builds a URL from the context path from the pageContext and the value of the alink tag's href attribute. It outputs an HTML a tag with href set to the constructed URL. Method doEndTag simply closes the a tag. Code Example 7.7 shows the result of serving the JSP page.

This hyperlink is portable: <a href="/the/context/path/a/b/c.html">
Link Here</a>
Code Example 7.7 Result of JSP Page

The string /the/context/path in Code Example 7.7 is the context path inserted by the custom action's tag handler class ALink. Small to medium amounts of Web content that do not change often can easily be converted to JSP pages that manage the context path portably.

7.3.4.6 Logical Grouping of Functionality

A Web module that has a clearly defined purpose is easier to reuse in different scenarios than one with less well-defined overall behavior. For example, a well- designed Web module concerned purely with inventory management can be reused in many e-commerce applications that need inventory management capability. Such a module would be ideal for adding a Web-based interface for inventory management to the sample application.

7.3.4.7 Utility Libraries

In general, it is good practice to group utility classes into libraries, include those libraries in an application EAR file, and access them by reference from Web-tier components. This technique avoids unnecessary code duplication in the deployed application. All such libraries in a Web component deployment unit must be in the directory WEB-INF/lib. The J2EE 1.3 Specification section J2EE.8.2 explains how to access classes from JAR files in a J2EE application.

7.3.4.8 Accessing EJB Components from Web Components

When a Web component uses enterprise beans, package the EJB component's client EJB jar in the Web archive's WEB-INF/lib directory. Putting the client EJB jar in this directory places the files in that JAR in the Web container's classpath. Avoid mixing Web components and EJB components in the same JAR file, because doing so makes reuse impossible. Instead, package each Web application that provides a specific service in an individual WAR file, then create J2EE application EAR files by combining Web modules (WAR files) with EJB client JAR files.

When packaging, keep in mind that Web components that use local interfaces must reside within the same J2EE application EAR file as the enterprise beans that implement those interfaces.


7.3.5 Application Client Modules

Application client modules are packaged in JAR files with a .jar extension. Application client modules contain:

An application client uses a client JAR file created by the EJB JAR file producer. The client JAR file consists of all the class files that a client program needs to access the enterprise beans in an EJB module.


7.3.6 Resource Adapter Modules

A Java Connector is packaged and deployed as a resource adapter archive (RAR) file, a JAR file with a .rar extension. It is the smallest deployable and usable unit of a Java Connector. A resource adapter module contains:

A resource adapter module requires a native support library compatible with each platform it supports to implement the platform-specific parts of the resource adapter. Note also that the support classes and interfaces for a resource adapter must be packaged as a JAR file within the RAR file. See the J2EE Connector Architecture Specification for details.



CONTENTS | PREV | NEXT | INDEX
Copyright © 2002 Sun Microsystems, Inc. All Rights Reserved.