2.3. Application Assemblies
Once we have our components assembled into the appropriate modules, each with a deployment descriptor, we can assemble an application by bundling the component modules along with an application deployment descriptor into an enterprise application archive (ear) file.
Like component modules, an application archive is just a jar file with files stored in specific locations, as dictated by the J2EE specifications. The deployment descriptor is stored as META-INF/application.xml in the jar file, and component modules are placed in the jar and referenced in the application descriptor using their location relative to the top of the application archive. The full syntax of the application deployment descriptors can be found in Appendix A.
Application deployment descriptors are simpler in structure, because they primarily serve to assemble component modules that have their own deployment information. The application deployment descriptor contains the following categories of information:
2.3.1. Application Assembly Example
Returning to our example application, we now need to create an application archive containing our application's two component module archives. One module is an EJB module that contains the profile EJB component, and the other is a web module that contains the UI elements, including the JSP used for viewing profiles.
Figure 2-6 shows our application deployment descriptor. The first section of the file contains some descriptive information about the application as a whole. Following this, we declare the two module archives that contain our components. These jar files must be found in the application archive when the application is deployed at the relative locations used here in the <module> elements. Finally, our example descriptor declares some security roles to potentially be used by the application at runtime. As with any roles declared at the component level, the application server should verify that these roles have been configured within its runtime environment when the application is deployed.
Figure 2-6. Application deployment descriptor
2.4. Deploying J2EE Applications
Once all of the J2EE modules have been packaged into their respective jar files, each with a deployment descriptor, and an application archive has been created with its own deployment descriptor, you still need to actually deploy the application to a J2EE application server. The deployment of an application (or even a single module) typically involves the steps described in the remainder of this section. In each case, we'll demonstrate the task using a JBoss application server and discuss how these tasks might be done in different ways in different application servers.
2.4.1. Adjust Deployment Descriptors
When you develop and package a set of components, you might imagine that the module should remain unchanged when it's deployed as part of an application. But there can be local environmental issues that force an application deployer to adjust the deployment descriptors . Some configuration parameters placed in environment entries may need to be adjusted, for example. A shopping cart web component might be delivered as part of an application with a JNDI environment entry that specifies the maximum number of line items the cart allows, with a default setting. This default value may need to be changed at deployment time to meet the needs of a particular user population (e.g., the application is being deployed for departmental administrators who require a much larger number of line items on average when ordering).
In addition to these configuration values, you may want to resolve component references directly in the module deployment descriptors. In our profile application, for example, we have a web component (our JSP for viewing profiles) that references the profile EJB component in its deployment descriptor because it makes use of it at runtime. As we mentioned earlier in this chapter, it's possible to directly resolve these references without setting up a link to a named asset in the server's JNDI service. In this case, we would alter the web module deployment descriptor to specify the component that the EJB reference should resolve to:
. . . <!-- Declare EJB components that our web components use --> <ejb-local-ref> <ejb-ref-name>ejb/Profile</ejb-ref-name> <ejb-ref-type>Entity</ejb-ref-type> <local-home> com.oreilly.jent.ejb.beanManaged.ProfileLocalHome </local-home> <local>com.oreilly.jent.ejb.beanManaged.ProfileLocal</local> <!-- The EJB module will be assembled as part of our app, so we can make a direct link to the Profile EJB using its ejb-name --> <ejb-link>BMP Profile Bean</ejb-link> </ejb-local-ref> </web-app>
Notice that the name used in the <ejb-link> element must match the value given to the EJB component in the <ejb-name> element in its deployment descriptor. You can verify our component name by checking Figure 2-4.
Either of these changes can be made directly to the deployment modules packaged with the modules, or edited deployment descriptors can be created and referred to using <alt-dd> elements[*] in the application assembly deployment descriptor. If, for example, we wanted to take this approach with our edited EJB deployment descriptor, we could create the file as described before, include it in our application archive under a suitable filename (like alt-Profile-ejb-jar.xml), and reference it as an alternative descriptor for the EJB module in our application deployment descriptor:
<application> . . . <!-- Declare the EJB module as part of the app --> <module> <ejb>ProfileEJB.jar</ejb> <alt-dd>alt-Profile-ejb-jar.xml</alt-dd> </module> . . . </application>
2.4.2. Perform Server-Specific Configuration
As we've seen in the previous sections, each J2EE module's deployment descriptor allows you to specify the runtime configuration of the components in the module. You can do things like declare resources (JDBC DataSources, JMS Destinations, general environment variables, and so on) that the components reference in their source code. You can also declare other components that are referenced by the components in the module, like EJB components used by web components or other EJB components. But there are almost always other configuration details that need to be specified within the application server itself. If, for example, you look back at the example deployment descriptors in the previous sections, you'll notice that while resource and component references allow you to specify what JNDI name the components use to look up these things, there is no place in the various deployment descriptors to define how a component itself should be published in the server's JNDI space or to define the parameters or the JNDI name for the various types of resources used by J2EE components. In addition, there are several places in the deployment descriptors where security-related parameters (such as application roles) are referenced, but there is no place in the deployment descriptors to specify actually how these roles are satisfied by the application server (Are they mapped to users in an LDAP directory? Are they mapped to groups defined in a relational database somewhere?).
The physical configuration of all of these application runtime assets, and the resolution of these assets against the things named in the component deployment descriptors, is left up to the application server and is not specified in any J2EE specification. This gap in the specifications is intentional: application servers implement these services in many different ways depending on their internal architecture and the business model of their vendors. So there needs to be some room in this regard to support different server implementation models.
Looking at our example application that deals with user profiles, we have the following assets and components declared in the web and EJB deployment descriptors:
As we've hinted at earlier in the chapter, typically you configure the required assets in the application server and link the references in the deployment descriptors to the assets configured in the application server. As we've also mentioned several times already, the mechanics of this task are server-specific. Once you've decided which application server you plan to use to deploy your application, your next step would be to read up on deployment details in the server's documentation. But to demonstrate the concepts, let's walk through these details for the JBoss application server.
Let's start with the DataSource reference made in the EJB module. First, we'll need to configure an actual DataSource in the application server that connects to the database required by the application. In JBoss, this is done using an XML configuration file that is placed in the server's deployment directory. The structure of this XML file is specific to JBoss. The following is the beginning of a JBoss configuration file that configures a DataSource in the server:
<?xml version="1.0" encoding="UTF-8"?> <datasources> <local-tx-datasource> <!-- The JNDI name of the DataSource. JBoss prefixes this name --> <!-- with "java:/" when the DataSource reference is created in --> <!-- the JNDI service. --> <jndi-name>ProfileDS</jndi-name> <!-- The JDBC URL used to create connections in this DataSource --> <connection-url>jdbc:hsqldb:hsql://localhost:1701</connection-url> <!-- The JDBC driver class required for the connections. --> <!-- Specifying this here allows the server to load the --> <!-- driver classes and verify that they are available. --> <driver-class>org.hsqldb.jdbcDriver</driver-class> <!-- The username and password to be used to connect --> <user-name>sa</user-name> <password></password> <!-- Various other parameters, such as connection pool --> <!-- parameters, would normally be specified next. --> . . .
This DataSource configuration uses the Hypersonic database bundled with the JBoss server, specifies a database account named sa to connect to the database, and names the DataSource java:/ProfileDS when it is published in the server's JNDI service. These details would be placed in a file in the server's deployment directory whose name ends with -ds.xml, which is the convention used by JBoss's internal deployment engine for distinguishing DataSource configurations from other service configuration files.
Now we need to connect the DataSource reference made in the EJB module descriptor to the DataSource that we just configured on the server. In the JBoss server environment, we do that using an additional EJB module configuration file specific to JBoss named jboss.xml that is placed within the EJB module archive in the same location as the standard J2EE ejb-jar.xml file. This configuration file can be used for several different configuration tasks. In our case, we need to do only two things: give our profile EJB a JNDI name in the server's JNDI service, and specify that the DataSource referenced in the ejb-jar.xml file should point to the DataSource we just configured. Giving the profile EJB a JNDI name is necessary so that other components can reference it at runtime. In our case, we resolved the web component reference directly without using the JNDI service, but it's good practice to specify JNDI names for EJB components regardless. You never know when it might come in handy, in your application or in someone else's.
The jboss.xml file for our EJB module is shown in Example 2-1. It follows the format of the ejb-jar.xml file somewhat, but the information provided here is complementary. First, we have an <entity> element that references the profile EJB component by specifying its ejb-name, BMP Profile Bean. Then we specify the JNDI name to be used for the component using a <jndi-name> element. Finally, we link the resource reference in the deployment descriptor, jdbc/ProfileDB, to the JNDI name of the DataSource we just configured, java:/ProfileDS.
Example 2-1. jboss.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE jboss PUBLIC "-//JBoss//DTD JBOSS 4.0//EN" "http://www.jboss.org/j2ee/dtd/jboss_4_0.dtd"> <jboss> <enterprise-beans> <entity> <!-- Specify the JNDI name of the EJB home interface --> <ejb-name>BMP Profile Bean</ejb-name> <jndi-name>ejb/bmpProfile</jndi-name> <!-- Resolve the DataSource reference in the EJB deployment descriptor --> <resource-ref> <res-ref-name>jdbc/ProfileDB</res-ref-name> <jndi-name>java:/ProfileDS</jndi-name> </resource-ref> </entity> </enterprise-beans> </jboss>
At this point, we've resolved the EJB component reference and the DataSource reference in the deployment descriptors. We'll leave the details concerning the resolution of the security roles to Chapter 10. But there are still a few server-specific configuration details to handle.
Our web components, for example, need to be given a URL context, the base to be used to access all of the mapped web components. We specified mappings for the web components in our web.xml file, but the context of these mappings needs to be specified somewhere as well. Assuming that we may deploy multiple applications in our server, and possibly include the same web components in multiple applications, we need a way to specify different URL bases on an application-by-application or module-by-module basis. In the case of JBoss, we use a JBoss configuration file for web modules, similar to the one we just saw for EJB modules. This configuration file, jboss-web.xml, is also placed in the web module archive in the same location as the standard J2EE standard web.xml file. The jboss-web.xml file for our example is shown in Example 2-2. Its only entry is the <context-root> element that specifies the URL base for our web components. In our case, assuming that the JBoss server is running on port 80 on a server named jent-apps.org, users would access our profile view JSP using the URL http://jent-apps.org/jent-deploy/viewprofile. The first portion comes from the hostname of the physical server, the jent-deploy segment is specified in the jboss-web.xml file, and the viewprofile segment comes from the <servlet-mapping> element in the web.xml file in Figure 2-5.
Example 2-2. jboss-web.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE jboss-web PUBLIC "-//JBoss//DTD Web Application 2H//EN" "http://www.jboss.org/j2ee/dtd/jboss-web_4_0.dtd"> <jboss-web> <context-root>/jent-deploy</context-root> </jboss-web>
2.4.3. Generate Container-Specific Classes
The various APIs used to implement components in the J2EE framework often leave some implementation details up to the application server. JSP files need to be converted into runnable Java classes, concrete implementations of EJB client and home interfaces need to be generated, and so forth.
The process for generating these container-specific classes is not defined in the J2EE specification or in the associated component API specifications (EJB, servlet, JSP, etc.). Each J2EE server provides its own tools or processes for generating these classes. JBoss, for example, automatically generates EJB interface implementations when you deploy an EJB module to the server, and the generation of Java classes from JSP pages is done at runtime. BEA's WebLogic application server, on the other hand, provides an optional command-line tool called weblogic.appc for generating container-specific classes for EJB components. This tool takes an ejb-jar file as an argument, parses its contents, and generates an augmented ejb-jar file that contains all of the WebLogic-specific classes for the EJB components in the module. Consult your J2EE vendor's documentation for details on its approach for generating these classes.