As you know, the J2EE specification comprises a number of functional sub-specifications. However, it is not always obvious how these should be put together to form a complete J2EE application. The J2EE specification provides guidelines for the structuring and the creation of J2EE applications and one of the major ones is related to packaging. Individual specifications provide guidelines for the packaging of individual components such as EJBs, JSP pages, and servlets. The J2EE specification then dictates how these heterogeneous components are themselves to be packaged together.
This chapter provides a thorough analysis of the J2EE packaging mechanism. We will not cover how to build EJB, web application, or resource adapter archive files. These aspects of J2EE are discussed in earlier chapters of their own. Instead, we will focus on the relationships that these components have within an EAR file, and the process involved in building EAR files.
Some of the questions we will ask are:
In answering these questions we will learn:
A J2EE application is composed of:
When one or more heterogeneous J2EE components need to use one another, a J2EE application must be created. There are many considerations that must be taken into account when building a J2EE application, including:
The J2EE specification differentiates between resources that run within a container and resources that can be packaged into a J2EE Enterprise Application ARchive (EAR) file.
| An EAR file is used to package one or more J2EE modules into a single module so that they can have aligned classloading and deployment into a server. |
J2EE clarifies the difference between run time containers and deployment modules. Run time containers are request-level interceptors that provide infrastructure services around components of the system. A deployment module is a packaging structure for components that will ultimately execute in a run time container. Recall how J2EE containers are structured:
A program running in an application client container is very similar to a Java program with a main() method. However, instead of the application being controlled by a JVM, a wrapper controls the program. This wrapper is the application client container. Application client containers are a new concept in the J2EE specification and should be provided by your application server provider.
An application client container can optimize access to a web container and EJB container through direct authentication, performing load balancing, allowing fail-over routines, providing access to server-side environment variables, and properly propagating transaction contexts.
Programs that run within an application client container have access to JAXP, JDBC, JMS, and JAAS resources on a remote application server.
Applets running within an applet container are expected to make requests for resources directly to an application server (as opposed to making the request to the container and letting the container ask the application server). The EJB specification doesn't make any regulations as to how an applet should communicate with an EJB container, but the J2EE specification does. The J2EE specification requires that applets that want to directly use an EJB must use the HTTP(S) protocol and tunnel RMI invocations. Many application server vendors support a form of HTTP tunneling to allow for this.
The components that can be packaged into a J2EE EAR file do not directly correlate to those components that contain containers. There are no basic requirements for what must minimally be included into an EAR file. An EAR file is composed of any number of the following components:
Each of these components are developed and packaged individually apart from the J2EE EAR file and own deployment descriptor. A J2EE EAR file is a combination of one or more of these components into a unified package with a custom deployment descriptor.
During the building, deployment, and use of an EJB, web application, or other component, different people will play different roles. The J2EE specification defines broad platform roles that developers play during the creation of an enterprise application. Even though there are many roles that individuals assume during the development and deployment process, these roles are nothing more than just logical constructs that allow an application to be better planned and executed. It is likely (and expected) that a single individual or organization will perform multiple roles. The common roles involved in building, deploying, or using an EAR file include:
In this chapter, during the discussion of the creation of EAR files and resolution of conflicts, we will be acting in the roles of application assembler and deployer.
EAR files meet the basic requirements for packaging an application as most web-based J2EE applications are composed solely of web and EJB applications. However, EAR files lack the capability of packaging complicated J2EE applications. For example, the following components cannot be declared in an EAR file, but are often used in a J2EE application:
At present, these components have to be manually configured and deployed via an administration interface provided by the implementation vendor and are the responsibility of the system administrator. Over time, the usage of these items will increase and consequently it will become important for EAR files to support the packaging of these components so that application portability is possible.
At runtime, when a class is referenced, it needs to be loaded by the Java Virtual Machine. The JVM uses a standard class loading structure for loading classes into memory. A class loader is a Java class that is responsible for loading Java classes from a source. Java classes can be loaded from disk, socket, or some other media; they can reside anywhere. Class loaders are hierarchical in the sense that they can be chained together in a parent-child relationship. Classes loaded by a child class loader have visibility (can use) classes loaded by any of the parent class loaders. Classes loaded by a parent class loader do not have visibility to classes loaded by any of its children's class loaders. Class loaders and EAR files are important since application server vendors can deploy application modules using common or different class loaders.
If, within an application, a web application needs to access an EJB, the web application will need to be able to load those classes it requires. Because of this implied dependency between different modules, application server vendors must consider different approaches for structuring EAR classloaders so that these dependencies are resolved.
A standalone application is deployed in its own class loader. This means that if you deploy a web application archive and an EJB application archive separately, the respective classes for each application will be loaded in different class loaders that are siblings of one another. The classes in the web application class loader will not be visible to the classes loaded by other class loaders. This creates a problem for web applications that want to use EJBs that have been deployed separately.
Before the advent of EAR files, many developers would deploy an EJB and then repackage the same EJB JAR file as part of the WEB-INF\lib directory of the web application. The same class files would exist in two different places so that the overall application could work correctly, a situation to be avoided. EAR applications were introduced to solve this problem. EAR files are not only a convenient packaging format; they also provide a special class loading scheme that allows applications within the EAR file to access the classes of other applications.
The J2EE 1.3 specification makes no specific requirements as to how an EAR class loader should work. This allows application server vendors flexibility in deciding how classes should be loaded. Before implementing an EAR class loader, a vendor needs to decide:
Prior to the EJB 2.0 Public Final Draft 2, vendors had a lot of flexibility in choosing how to set up a class loading scheme. JSP pages and servlets that needed to make use of an EJB only needed to be able to load the home interface, remote interface, common classes, and stub classes of the EJB. The common classes such as exceptions and parameters should be placed into a dependency JAR file and loaded as a dependency package. This configuration requires vendors to determine a way for a web application that depends upon an EJB to load the home interface, remote interface, and stubs.
A vendor could implement this simple class loading scheme:
Client application class loading is not included in this model since a client application will execute within another virtual machine and will be isolated from all other components.
In this model, each EAR application would be loaded by a custom EAR class loader instance. EJB applications and web applications would each be loaded by custom class loaders that are children of the EAR class loader. Any class file that is to be shared by more than one application in the EAR will be loaded by the EAR class loader. (This includes any common dependency libraries and resource adapter archives.) Any files loaded at the EAR class loader level are automatically visible to all classes loaded by child class loaders.
All EJB applications are loaded by a single EJB class loader that is a child of the EAR class loader. Even if you have different EJB JAR files, they will all be loaded by the same class loader. This is done to facilitate EJB-to-EJB calls in different applications that are hosted on the same JVM.
Each web application is loaded in a different class loader to maintain class isolation. For example, if every web application contains a file called index.jsp, that servlet created from the JSP page could have the same class name as the equivalent servlets in other web applications. Each web application needs to be able to load its own version of that servlet, so each web application must be isolated in its own class loader.
In order for web applications to make use of the EJBs deployed in the same EAR file, the web applications need to have visibility to the external interfaces and stub implementation classes of those EJBs. Since the EJB and web application class loaders are siblings, the web applications do not have direct visibility to the right class files. The web application class loader and the EJB application class loader do have the same parent class loader, however. To allow the web application to be able to use the class files of the EJB, the EJB class loader can take all of the public interfaces of each of the EJBs and their stub implementation files and 'export' them to the EAR class loader in which they will be visible to all applications in the EAR. The public interfaces and stub implementation files are the classes needed by a client to make use of an EJB. The web applications will then be able to load the classes required to use any EJB.
Dependency utility libraries can be loaded in different places depending on where the library is specified. If a single web application lists a dependency library in its WEB-INF\lib directory, then that library is unique to that web application. There is no need for other applications to access the contents of that library and so the EAR class loader should not load that library. In this situation, the web application class loader will load the utility JAR file. Other web applications should include the same dependency in their own WEB-INF\lib to maintain this isolation.
Dependency utility libraries that must be shared between EJB and web applications need to be loaded at the EAR class-loader level. It turns out that an EAR class loader will load any library specified as a dependency of an EJB in order to give the right visibility to those dependency classes. This allows an EJB developer to package any common exception classes, custom input parameter classes that are visible to a web application, and the EJB into a dependency library. This library is commonly called common.jar but does not have to be named that. In addition to the public interfaces and stub implementation classes, the common utility library will also be loaded at the EAR level, which allows a web application visibility to all of the classes used by the EJB.
Resource-adapter archives that are packaged within the EAR file along with EJBs and web applications are automatically loaded at the EAR class-loader level.
The EJB 2.0 Public Final Draft 2 introduced the concept of local interfaces and placed an interesting twist on the EAR class loading problem. Local interfaces allow co-located clients and EJBs to be accessed using pass-by-reference semantics instead of pass-by-value semantics.
Having visibility to the public interfaces and stub implementation classes of an EJB is not sufficient for a client of an EJB to perform pass-by-reference invocations. The client needs to have a direct reference to the implementation classes of the EJB's container. With local interfaces, clients of EJBs need access to much more than before. This restriction means that the class loading scheme used pre-EJB 2.0 will not work. To solve this problem the class loaders of any applications that act as clients to an EJB must be loaded as children of the EJB class loader:
In this model, web application class loaders are children of the EJB class loader. This allows all web applications to have visibility to the files they need to allow them to behave as clients of the EJBs. Each web application is still loaded in a custom class loader to achieve isolation, though. The overall structure of this implementation is simpler to understand, as it does not require the EJB class loader to export any files to the EAR class loader.
An ambiguity in the J2EE specification has been exposed by certain implementations. It arises because the J2EE specification is ambiguous as to how dependency libraries of a web application should be loaded. It is very clear that a utility library specified by WEB-INF\lib should remain isolated and be loaded by the class loader of the web application only. However, if a utility library is specified as a dependency library of the web application, it is not stated whether the library should be loaded by the web application's class loader or exported to the EAR class loader. This can have a behavioral impact. If it is known that a dependency utility library will only be loaded once for all web applications, the web applications can take advantage of knowing that a singleton class will only create a single object that can be shared among all web applications. However, if each web application's class loader isolated the utility library, a singleton class, which is a class that is intended to only create a single instance in the virtual machine, would create a single object in each web application.
Currently, Silverstream's application server and the J2EE Reference Implementation load utility libraries specified as a dependency library of a web application at the EAR class-loader level. WebLogic Server 6.0 loaded these libraries as part of the web application class loader. However, WebLogic Server 6.1 modified this approach to support the loading of web application dependency libraries at the EAR level. This makes sense since web application isolation can always be achieved by placing utility libraries in the WEB-INF\lib directory. This provides the best of both worlds: a dependency library loaded at the EAR class-loader level or a dependency library loaded at the web application class-loader level.
Now that we have a basic understanding of how the J2EE architecture is implemented, specifically the different roles and the behavior of class loaders, we are ready to configure and deploy enterprise applications. To do this we need to understand the process of EAR file creation, and the contents of the deployment descriptors that describe their contents.
The overall process that is used to build an enterprise application is:
The J2EE enterprise application development and deployment process might work like this:
Components are built and packaged into a J2EE module with a deployment descriptor; a deployment tool can be used to create these J2EE modules. The deployment tool can also be used to deploy and un- deploy standalone J2EE modules; to take one or more J2EE modules and package them into a J2EE application with another deployment descriptor; to add or remove items from the J2EE application; or to deploy the entire application to an application server.
The structure of a J2EE enterprise application package is straightforward; it is composed of one or more J2EE modules and a deployment descriptor named application.xml in a directory named META-INF\. The files are packaged using the JAR file format and stored in a file with an .ear extension. Optionally, you can include dependency libraries within the EAR file. The general structure of an EAR file is:
EJB .jar Files Web Application .war Files Resource Adapter .rar Files Application Client .jar Files Dependency Library .jar Files META-INF\ application.xml
An example EAR file that has an EJB module and a web application module with no dependency libraries might look like:
FirstEJB.jar
FirstWeb.war
META-INF\
application.xml
J2EE modules that are stored in the EAR file do not necessarily have to be in the root directory of the structure. For example, the contents of an EAR file that has an EJB module and a resource adapter archive stored in subdirectories might look like:
ejbs\ SecondEJB.jar resources\ LegacyAdapter.rar META-INF\ application.xml
Finally, an EAR file that has many components and dependency libraries may look like:
ejbs\ ThirdEJB.jar FourthEJB.jar resources\ LegacyAdapter.rar web\ WebApp1.war WebApp2.war lib\ xmlx.jar common.jar META-INF\ application.xml
The EAR file is created using a deployment tool provided by a tool provider or alternatively by using the jar tool provided with the JDK. The creation steps are:
An example execution of the jar utility for the complex example discussed earlier might be:
jar cvf Application.ear ejbs resources web lib META-INF
Once the EAR file has been created, you are free to deploy the J2EE application into the application server.
Ideally, a graphical deployment tool would be used to build the application.xml files. However, there may be many situations where you need to construct or maintain application.xml manually, and so it is important to understand the tags that are used.
The application.xml deployment descriptor is straightforward. A valid descriptor doesn't require many tags. The possible tags contained within the DTD of the deployment descriptor are:
All valid J2EE application deployment descriptors must contain the following DOCTYPE declaration:
<!DOCTYPE application PUBLIC "-//Sun Microsystems, Inc.//DTD J2EE Application 1.3//EN" "http://java.sun.com/dtd/application_1_3.dtd">
Configuring a simple application.xml deployment descriptor takes only a few steps:
<context-root>web1</context-root>
all HTTP requests for JSP pages and servlets will always be preceded with:
http://host:port/web1/<AsSpecifiedInServletSpec>
Each web application packaged within the EAR file must have a unique <context-root> value. Two web applications packaged in the same EAR file cannot have identical <context-root> values. If there is only one web application in the EAR file, the value of <context-root> may be an empty string.
The most common use of EAR files will be the scenario where an enterprise application has a single EJB module and a single web application module that makes use of the EJB components deployed in the EJB module. In this example, the EJBs and web applications do not depend upon any dependency libraries. This section describes the steps involved in building this example.
This example has a servlet invoke an invoke() method on the remote interface of a stateless session EJB. The servlet and EJB print out statements to the console to indicate that they are successfully executing. If the console receives any exceptions, it is likely to be an indication that the packaging of the components was not done correctly. All of the EJB source files for this example are in the wrox package. The servlet is in the unnamed package. The Java files used to implement this example include:
For complete information on how to author servlets and EJBs, please reference the appropriate chapters in this book. This section will only list the relevant code snippets to allow the reader to follow the example. Authoring EJB and web application deployment descriptors, creating JAR files, and creating web application WAR files are not demonstrated here and discussed in Chapters 7 to 21.
The sourcecode for the EnterpriseBean.java bean implementation class is:
package wrox;
import javax.ejb.*;
public class EnterpriseBean implements SessionBean {
private InitialContext ctx;
public void ejbCreate() {}
public void ejbRemove() {}
public void ejbActivate() {}
public void ejbPassivate() {}
public void setSessionContext(SessionContext c) {}
public void invoke() {
System.out.println("Executing in EJB.");
}
}
The sourcecode for the EnterpriseServlet.java servlet class is:
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import javax.naming.*;
public class EnterpriseServlet extends HttpServlet {
public void service(HttpServletRequest req, HttpServletResponse res)
throws IOException{
res.setContentType("text/html");
PrintWriter out = res.getWriter();
try {
System.out.println("Servlet Executing in Server");
InitialContext ctx = new InitialContext();
wrox.EnterpriseHome eHome = (wrox.EnterpriseHome)
ctx.lookup("EnterpriseEJB");
wrox.Enterprise e = eHome.create();
e.invoke();
} catch(Exception e) {
out.println("Exception: " + e);
System.out.println("Exception: " + e);
}
out.println("<html><head><title>Title</title></head>");
out.println("<body>");
out.println("<h1>See console to ensure EJB was invoked.</h1>");
out.println("</body></html>");
}
}
After the development of the EJB code and the relevant deployment descriptors (not listed here), the EJB should be packaged into a file named EnterpriseBean.jar. The EJB is configured to bind itself to EnterpriseEJB in the JNDI namespace.
After the development of the servlet code and the relevant deployment descriptors (also not listed here), the servlet should be packaged into a file named WebApp.war. The servlet class is registered to execute with the /enterpriseservlet/ mapping.
After completing the component build process, the enterprise application deployment descriptor must be developed. We need to register the EJB and web application as modules of the enterprise application. We also want the web application's components to execute under the /web/ context root. The application.xml file for this example becomes:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE application PUBLIC '-//Sun Microsystems, Inc.//DTD J2EE Application 1.3//EN' 'http://java.sun.com/dtd/application_1_3.dtd'> <application> <display-name>Enterprise Application</display-name> <module> <ejb>EnterpriseBean.jar </module> <module> <web> <web-uri>WebApp.war</web-uri> <context-root>web</context-root> </web> </module> </application>
After creating the application.xml deployment descriptor, the enterprise application build directory should resemble:
EnterpriseBean.jar WebApp.war META-INF\ application.xml
To create an EAR file named Enterprise.ear using the jar utility, the following command should be entered on the console:
jar cvf Enterprise.ear EnterpriseBean.jar WebApp.war META-INF
What's interesting about the deploytool that is provided with the J2EE Reference Implementation is that developers are never expected to author ejb-jar.xml, web.xml, or application.xml deployment descriptors. These files are always automatically generated for you. In the case of this enterprise application, the application.xml that is placed into the EAR file is generated automatically for the developer.
After the EAR file has been built, it needs to be deployed. Keep in mind that deployment is vendor specific and each vendor provides custom tools to allow this. The J2EE deploytool utility has an option to deploy an enterprise application to the Reference Implementation.
After the enterprise application has been successfully deployed, executing the client involves invoking the servlet that was deployed in the web application. Since the context root of the enterprise application is /web, the servlet is invoked as part of that. For example, to invoke the servlet, you would enter the following in a browser's window:
http://: /web/enterpriseservlet/
You should see the following appear in the browser after the servlet executes:
Two optional deployment descriptor tags can be used in certain scenarios. They are <alt-dd> and <security-role>.
<alt-dd> is a sub-tag of <module>. The value of this tag is a URI that would point to another deployment descriptor file for the module referenced from the root of the EAR file. The file does not have to be named the same as it is named inside the J2EE module. For example, all EJB module deployment descriptors must be named ejb-jar.xml. The value of this tag can be a file named other than ejb-jar.xml if it is referencing an alternative deployment descriptor for an EJB module.
The deployment descriptor file would override the one contained within the J2EE module. This is a type of post-assembly deployment descriptor. This tag can be used to reference an external version of the deployment descriptor that should be used if a deployer wants to use a deployment descriptor that is different from the one contained within an EJB, a web application, a resource adapter, or an application client module. If this value is not specified, then the deployment tool must use the values specified within the JAR, WAR, or RAR files provided in the EAR file. For example, to specify a web application with an external, alternative deployment descriptor that is located at the root of the EAR file, you would write:
<module>
<web>
<web-uri>web.war</web-uri>
<context-root>web</context-root>
</web>
<alt-dd>external-web.xml</alt-dd>
</module>
<security-role> allows the deployer to specify application-level security roles that should be used for all J2EE modules contained within the EAR file. If an EAR file contains multiple EJB modules and/or multiple web application modules, each of those modules may have its own security roles defined within. One of the deployer's responsibilities is to ensure that the names of all security roles contained within all J2EE modules are unique and have meaning for the application as a whole. Security roles can be 'pulled up' from the J2EE module level to the enterprise application level and included in this tag. If there is a duplicate security role value in one of the J2EE modules, that value can be removed if the value is provided at the enterprise application level.
This tag requires a <role-name> sub tag to actually provide the symbolic name of the security role. An example of configuring a <security-role> tag is:
<security-role> <description> This is administrator's security role </description> <role-name>Administrator</role-name> </security-role>
The J2EE specification doesn't make any specifications for how J2EE modules contained within an EAR file should be deployed. In particular, the order in which modules must be deployed is not explicitly outlined in the specification. This can be an issue if a component in one module needs to make use of another component in another module that has yet to be deployed.
Most application servers will deploy EAR files using the same approach:
The most frequent question raised about J2EE packaging is about utility and support classes. When packaging a web application or an EJB application, where should these libraries be placed? If you place these classes into the standard classpath of your application server, they will likely lose any unloading ability that web and EJB applications have that are driven by the class loaders used to load them at deployment. If your web/EJB applications need to change the version of the libraries that they use, then the dependent library will need to be re-deployed when the web/EJB application is re-deployed. In this scenario, storing utility classes on the standard classpath is not a feasible option since the entire application server would have to be restarted for each deployment of a web/EJB application, which is clearly not ideal.
Given the standard definition of J2EE, where are dependency libraries supposed to be placed so that they can be re-deployed with an application at run time? There are two creative, yet ultimately undesirable, solutions:
Although the second scenario achieves redeploy ability of dependency libraries, it is incredibly inefficient. The purpose of having multiple JAR files for packaging is to promote modularity of applications and placing the same class in multiple JAR files destroys this. In addition, having multiple copies of the same classes unnecessarily bloats your applications. Finally, there is an added step in the build process, as every JAR file will have to be rebuilt if you want to change even a single library.
One of the possible solutions to this problem is to eliminate the need for multiple JARs in J2EE applications by converging all EJBs and their utility classes into a single, unified package. The EJB 2.0 specification is driving some projects to do this. This version of the specification mandates that entity EJBs participating in a relationship do so using local interfaces and so requires both of the EJBs in the relationship to be packaged into the same JAR file. Earlier drafts of EJB 2.0 allowed EJBs in different JAR files to participate in relationships, promoting greater modularity of the system, but ultimately limited the persistence optimizations available for CMP entity beans in a relationship.
Public Final Draft 2 eliminated remote relationships; so many vendors are thinking about providing tools that perform EJB JAR convergence. These tools will take as input two valid EJB JAR files and merge their classes and deployment descriptors into a single, unified package. You could potentially use one of these convergence tools to re-package your existing JAR applications to reduce redundancy of dependency libraries among EJB JAR files. At the time of writing, these convergence utilities were still being developed. Check with your application server provider to see if they have a convergence utility available for you to use.
Keep in mind that even if all of the EJBs are converged into a single JAR application, you will have eliminated copies of your dependency library among the EJBs, but a copy will still need to exist in a WEB-INF\lib library if a web application depends upon it. Additionally, the need for modularity of EJB applications still exists since many companies desire to re-deploy EJBs on an individual basis. Since every EJB in a JAR will be re-deployed when that JAR file is re-deployed, an unnecessary amount of deployment processing could occur if your only desire is to re-deploy a single EJB.
With the release of JDK 1.3, Sun Microsystems redefined the "extension mechanism" which is the functionality necessary to support optional packages. The extension mechanism is designed to support two things:
Additionally, the J2EE 1.3 specification mandates that application servers must support the extension mechanism as defined for JAR files. This requires any deployment tool that references a JAR file be capable of loading any optional libraries defined through the extension mechanism. It also implies that if an application server or deployment tool supports run time un-deployment and re-deployment of EJB applications that use libraries via the extension mechanism, then that tool or application server must also support un-deployment and re-deployment of any dependent libraries.
Support for the extension mechanism does not exist for EAR or resource adapter applications as defined in the J2EE specification, since these applications are not directly loaded by an instance of ClassLoader. Web applications have the freedom of using the extension mechanism or the WEB- INF\lib directory when specifying a dependency library. As we discussed earlier, how a dependency library is loaded can vary depending upon whether the library is specified using the extension mechanism or the WEB-INF\lib directory.
Enterprise applications need to re-package any libraries that are needed by the web application or EJB application as part of the EAR file. Once packaged, the extension mechanism provides a standard way for web application WAR files and EJB application JAR files to specify which dependency libraries that exist in the enterprise application EAR file are needed.
How does the extension mechanism work with EJB applications? A JAR file can reference a dependent JAR file by adding a Class-Path: attribute to the manifest file that is contained in every JAR file. The jar utility automatically creates a manifest file to place in a JAR file and names it manifest.mf by default. This file can be edited to include a Class-Path: attribute entry in addition to the other entries that already exist in the file. In fact, many EJB packaging tools that are being released by vendors are taking dependency packages into account as part of the packaging process and will automatically create an appropriate manifest.mf file that contains a correct Class-Path: attribute entry.
For example, if you create an EJB JAR file and modify the manifest.mf to include a Class-Path: attribute, the container generation utility provided by your application server vendor must preserve this entry when it generates a new EJB application file. With WebLogic Server 6.1, if you provide an EJB JAR utility that already contains a Class-Path: entry in the manifest.mf file, the weblogic.ejbc utility will preserve this entry when it generates a new EJB application with the container files. At the time of printing of this book, a tool that creates and inserts the Class-Path: entry into a manifest.mf file does not yet exist. Unfortunately, this task has to still be done by hand by editing the manifest.mf file of a JAR file.
The Class-Path: manifest attribute lists the relative URLs to search for utility libraries. The relative URL is always from the component that contains the Class-Path: entry (not the root of the EAR file). Multiple URLs can be specified in a single Class-Path: entry and a single manifest file can contain multiple Class-Path: entries. The general format for a Class-Path: entry is:
Class-Path: list-of-jar-files-separated-by-spaces
For example, a JAR file might have:
Class-Path: log4j.jar xmlx.jar foo/bar/util.jar
If you use the extension mechanism in a J2SE application, the Class-Path: manifest entry can reference directories too. However, for J2EE applications that are wholly contained within JAR files, the Class-Path: manifest entry can only reference other JAR files. Additionally, the Class-Path: entry must reside on a separate line apart from other attribute entries in the same manifest file.
The extension mechanism is a nice capability especially since it is designed to handle circular redundancies by creating a unified classpath containing all dependencies in first-parsed-based ordering. For example, if the first EJB application parsed is EJB1.jar and it references:
Class-Path: jaxp.jar EJB2.jar ..\xmlx.jar
a class loader will then parse EJB2.jar that references:
Class-Path: jaxp.jar EJB1.jar
The resulting "application" classpath that a class loader would ultimately use would be:
Class-Path: jaxp.jar EJB2.jar ..\xmlx.jar EJB1.jar
This example is designed to demonstrate every possible loading configuration for an enterprise application. It demonstrates multiple EJB modules, multiple web applications, and dependency libraries that are unique and shared between these applications. Deploying and executing this example allows us to see how an application server loads different classes from different applications.
By printing out the ClassLoader hierarchy for different classes as they are executed we can see if all classes loaded in a single class loader, or different class loaders, and if so, what the hierarchy of the class loaders is.
Our example consists of two EJB modules, two web application modules, and seven dependency libraries that are used in different scenarios. The structure of the EAR file is:
Depend1-container.jar Depend2-container.jar WebApp1.war WebApp2.war Util1.jar Util2.jar Util3.jar Util4.jar Util5.jar Util6.jar Util7.jar META-INF\ application.xml
There is a single EJB in each of the Depend JAR files. There is also a servlet, TestServlet, which exists in each web application. Each of the dependency libraries contains a single class with a single method that prints out the ClassLoader hierarchy. This example causes different scenarios to occur to see how classes are loaded in the context of an EAR file. In particular, the following scenarios are tested:
To execute this example, deploy the Depend.ear file (available from http://www.wrox.com) and then run the TestServlet that is deployed in each of the web applications. The TestServlet will invoke the appropriate EJB methods that, in turn, invoke the methods on the classes in the dependency library. For example, to execute both servlets, you would use the following URIs in a browser:
http://<server_name>:<port_number>/web1/testservlet http://<server_name>:<port_number>/web2/testservlet
You should see something like this appear in the server window:
The application.xml deployment descriptor is:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE application PUBLIC "-//Sun Microsystems, Inc.//DTD J2EE Application
1.3//EN" "http://java.sun.com/dtd/application_1_3.dtd">
<application>
<display-name>Pretty Name</display-name>
<module>
<ejb>Depend1-container.jar</ejb>
</module>
<module>
<ejb>Depend2-container.jar</ejb>
</module>
<module>
<web>
<web-uri>WebApp1.war</web-uri>
<context-root>web1</context-root>
</web>
</module>
<module>
<web>
<web-uri>WebApp2.war</web-uri>
<context-root>web2</context-root>
</web>
</module>
</application>
The manifest classpath for the first EJB module is:
Class-Path: Util1.jar Util3.jar Util6.jar Util7.jar
The manifest classpath entries for the other EJB module and the web application modules vary and have different combinations of the seven dependency libraries contained in the EAR file. The servlets contained within each web application are fully documented outlining the thread of execution and which test case is being demonstrated for each dependency library.
The manifest classpath will definitely spur better modularity of J2EE packages in the future. Using this model, developers can employ a simple scheme for determining which EJBs should be packaged into a single JAR file and those that should be packaged in separate JAR files:
The only limitation that developers are now faced with is locating an application server that fully supports this packaging capability. Developers will need to look for application servers that run in a 1.3 JDK and fully support the J2EE 1.3 specification. This has the consequence that any J2EE 1.3-certified application server must only run in a 1.3 JRE environment.
This chapter covered J2EE applications in depth, including:
We've now seen the entire range of J2EE technologies, from the basic building blocks of RMI, JDBC, JNDI, and JAXP, through the servlet and JSP web content APIs and Enterprise JavaBeans, to complete J2EE applications. We hope this book has inspired you to get stuck into creating your own J2EE applications – enjoy!