Elizabeth Blair

Subscribe to Elizabeth Blair: eMailAlertsEmail Alerts
Get Elizabeth Blair: homepageHomepage mobileMobile rssRSS facebookFacebook twitterTwitter linkedinLinkedIn


Related Topics: Java EE Journal, Java Developer Magazine

J2EE Journal: Article

Build To Spec!

Build To Spec!

Many J2EE 1.2-based applications and components are emerging in the marketplace as the J2EE platform matures. Application portability is one of the most important benefits offered by the J2EE platform. Through the J2EE Java Pet Store sample application, the J2EE Blueprints team has developed a set of best practices for ensuring application portability across J2EE-compatible application servers.

This article is the first in a series of J2EE application and component portability recommendations for the J2EE development community. This series will focus J2EE application portability in light of maturing J2EE technologies, especially J2EE 1.3 and EJB 2.0.

This first article presents several portability guidelines for the J2EE platform, version 1.2, but includes references to the upcoming version 1.3 where appropriate. This article assumes that the reader is familiar with basic J2EE 1.2 platform terminology.

What Is Portability?
Portability is an important goal for application developers who want to maintain freedom of choice in technology. It's important to understand how J2EE application portability fits into the Java portability picture. Java technology provides several levels of portability, each supported by a particular specification:

Source-code: Allows a single source base to provide identical results, regardless of CPU, operating system, runtime system, or compiler. The Java language is highly source-code portable because its definition (the Java Language Specification) clearly spells out such details as byte order, memory management behavior, and the size and format of its primitive types.

  • CPU: Allows a compiled program to execute identically on computers with different CPUs. The Java Virtual Machine provides CPU portability by specifying a virtual instruction set to which Java (or other language) source code compiles, isolating the compiled code's behavior from the underlying CPU.
  • OS: Allows a developer to write a program that accesses system-level resources in a uniform way, regardless of the underlying operating system. The Java Platform provides OS portability by specifying a "virtual operating system" that gives developers a unified model of system services.
  • J2EE application: Allows J2EE application developers to write client/server components and applications that deploy and execute identically regardless of the underlying server implementation or vendor. The J2EE Platform Specification defines how the various J2EE platform technologies (servlet containers, EJB containers, transactions, security, and so on) must behave, so J2EE application developers can depend on consistent behavior across vendor implementations. This series focuses on J2EE application portability tips and guidelines. The J2EE Platform Specification defines a contract between J2EE product providers, who create J2EE servers, APIs, and so on, and J2EE component and application developers, who use those J2EE products to create solutions. Both sides of the contract must understand their responsibilities in order to realize the portability benefits of the J2EE platform. This article provides some detailed tips for ensuring that your J2EE components and applications will be portable between different J2EE vendors' products.

    J2EE Product Compatibility
    J2EE product providers produce Web and application servers, servlet and EJB containers, databases, platform APIs, and development tools. The J2EE Platform Specification defines the behavior of each type of J2EE product, so J2EE component and application developers can depend on consistent results. Several tools are available to help J2EE product providers create products that meet the platform specification requirements.

    The J2EE Compatibility Test Suite (CTS) helps J2EE product providers check that their products meet the requirements of the J2EE platform specification. The J2EE Compatibility Test Suite is a standard set of over 5,000 test cases derived from the J2EE specification requirements. Products that pass the CTS are considered J2EE-compatible. The tests check specific requirements for each of the J2EE technologies. For example, the EJB container tests check that the EJB instance life cycle behaves as defined in the EJB specification.

    The J2EE Reference Implementation (RI) provides J2EE server developers with an example implementation of a J2EE application server. The J2EE RI demonstrates that the specifications can indeed be implemented. The RI also gives application server vendors a working server they can use to check the correctness of their implementation. The J2EE RI is required to pass 100% of the CTS, since its purpose is to demonstrate the complete J2EE specification.

    Application Developer Tools
    Good tools play an important role in helping the J2EE application developer write portable applications. Providing powerful, easy-to-use tools is one of the ways that product providers can serve their customers, and thereby gain a competitive advantage in the marketplace.

    Several kinds of application development tools provide support for application portability. Code generation tools generate code for various parts of an application, based on higher-level specifications provided by the developer. Verification tools ensure the correctness of deployment descriptors. Sample code and references help developers understand how to use application components. Let's look at an example of each of these types of tools.

    Code generation tools automatically generate source code based on what the developer has specified or has already coded. Such tools improve portability by creating code that adheres to J2EE specifications. For example, a tool called EJBGen (v1.19 - an EJB 2.0 code generator) automatically generates the EJB Home and Remote interfaces for an existing EJB class. In addition to eliminating mindless coding, EJBGen improves portability and code quality by ensuring that the methods of the EJB class and its home and remote interface throw the proper set of exceptions as required by the EJB 2.0 specification. See the list of resources at the end of the article for more on EJBGen.

    Verification tools ensure that the contents of a deployment descriptor (e.g., web.xml or ejb-jar.xml) are both well formed (meaning that the descriptor meets the basic requirements of XML) and valid (meaning that the descriptor matches the structure defined by its DTD). A deployment tool may choose not to deploy an application if it detects a problem in a deployment descriptor. The Sun RI provides a tool known as the "verifier" that verifies the contents of a war, jar, or ear file. The verifier runs a series of checks (based on requirements of the J2EE specifications) against the J2EE component or application provided. The verifier allows the developer or deployer to fix errors at, or even before, deploy time.

    A good code example is worth a thousand words. Samples and references provide good examples to demonstrate how individual J2EE components, applications, and servers all come together. Many code snippets and tutorials for particular technologies are only a Web search away, but developers need to understand how to use these technologies together: they need to be able to see "the big picture".

    The Sun J2EE Blueprints program provides developers with a free sample application, the Java Pet Store. The Java Pet Store is an end-to-end, B2C, e-commerce application that gives the J2EE application developer an example from which to learn. The J2EE Blueprints program also offers design discussion explaining best practices for J2EE application design, and provides a set of design patterns for building robust, scalable, portable J2EE applications. The Java Pet Store sample application can be downloaded for free from http://java.sun.com/j2ee/blueprints.

    J2EE Application Portability Tips
    The rest of this article presents several specific technology usage tips for maximizing J2EE application portability. Each tip may also have other benefits, but the focus is on portability between J2EE product providers' products. This section assumes a working knowledge of J2EE 1.2 technology.

    Know When (and When Not) to Use Serializable Fields
    As an Enterprise JavaBean provider, you are responsible for ensuring that all of a bean's instance fields are serializable, so that the state of the bean can be preserved when the bean is passivated.

    While fields of primitive types like String and int are serializable, reference fields (fields whose value is a reference to a class instance) are serializable only if the reference's class implements java.io.Serializable and all of that class's nontransient fields are serializable.

    For example, consider an entity bean called AccountEJB, which has a field identifying the account owner, and another field recording the account contact information (see Listing 1).

    Since the ContactInformation class is serializable, and all of its fields are serializable, then AccountEJB's info field is also serializable, and the requirements are met.

    You must mark all fields of nonserializable types as transient. Fields marked as transient are ignored by the JVM's serialization machinery.

    For example, a database connection, represented by java.sql.Connection, is not serializable, so it is marked transient:

    public class AccountEJB implements EntityBean {

    private String userId;
    private ContactInformation info:
    // ...
    private transient Connection dbConnection = null;

    // ...
    }

    For more information on serialization and passivation, refer to Section 6.4.1 of the EJB 1.1 Specification (available for download from the resource list).

    Close Database Connections Cleanly
    When retrieving items from a database, open a connection through a Connection instance, and then execute an SQL statement through a Statement instance. When you're done, remember to close your Statement before closing your Connection (see Listing 2).

    What happens if you try to do this the other way around? Some JDBC 2.0 driver implementations will throw an exception if you try to close a Connection with open Statement instances. This is a portability issue because different JDBC implementations handle this detail differently.

    Be careful when writing deployment descriptors
    If your J2EE-compatible application server provides tools to construct deployment descriptors for you, you should use those tools. Hand-written deployment descriptors are prone to errors such as misordering of elements, duplicate element entries, and simple typos.

    If you still want (or need) to write your own descriptors, many application servers provide tools that validate a deployment descriptor against a DTD, ensuring that the descriptor's contents are both well formed and valid.

    The verifier tool shipped with the J2EE Reference Implementation will catch only some errors in a descriptor, but not all of them. You can supplement the verifier with a small program that uses a validating parser to check a descriptor's validity against its DTD (see Listing 3).

    Use Valid Encodings for Deployment Descriptors
    Ensure that the static deployment descriptors for your Web and EJB components include a valid encoding declaration. For example, if you're using encoding ISO-8859-1, the deployment descriptor should begin with the following line:

    <?xml version="1.0" encoding="ISO-8859-1"?>

    In an encoding declaration, the value ISO_8859_1 is not an acceptable substitute for ISO-8859-1.

    Refer to Section 4.3.3, "Character Encoding in Entities," of the W3C XML 1.0 Recommendation for more details on XML character encodings.

    Using Methods Correctly Is Vital
    When an entity bean is activated through a callback to ejbActivate(), it is returned to the ready state so that business methods can be called on it. The activation method, however, is not responsible for loading the bean's fields from a resource; that is the job of ejbLoad().

    Similarly, when an entity bean is passivated through a callback to ejbPassivate(), it's returned to the pooled state. The passivation method, however, is not responsible for storing the bean's fields into a resource; that is the job of ejbStore().

    Even if you tried accessing a resource from ejbActivate() or ejbPassivate(), a container implementation may reject the access, on the grounds that the EJB 1.1 Specification does not allow such access from either of these methods. Using these methods correctly is vital for application portability.

    For more information on these methods, refer to Sections 9.1.5 and 9.1.6 of the EJB 1.1 Specification.

    Throwing Exceptions from Entity Beans
    The EJBGen tool helps developers write EJBs, and in particular ensures that EJB method signatures express the appropriate thrown exceptions. If you do not use such a tool, then you should double check the EJB method signatures, especially the thrown exceptions, in your component's EJB class, Home and Remote interfaces.

    Here is a summary of the EJB 1.1 Specification exception requirements for EJB Home, Remote, and EJB class method signatures:

    Remote Interface:

  • Throws clause must include RemoteException.
  • All exceptions defined in the throws clause of the matching method of the EJB class must be defined in the throws clause of the method of the remote interface.

    Home Interface - Session:

  • All exceptions defined in the throws clause of the ejbCreate method must be defined in the throws clause of the matching create method in the Home interface. The throws clause must include CreateException.

    Home Interface - Entity:

  • All exceptions defined in the throws clause of the ejbCreate/ejbPostCreate methods must be defined in the throws clause of the matching create method (the set of exceptions must be a superset of the union of exceptions defined for the ejbCreate/ejbPostCreate methods).
  • All exceptions defined in the throws clause of an ejbFind method of the EJB class must be included in the throws clause of the matching find method of the Home interface. The throws clause of the finder method must include FinderException.

    The J2EE RI 1.2.1 verifier tool checks for method signature exceptions.

    Packaging
    How to package an application seems to be an area of confusion among developers in general.

    Packaging is an important portability consideration. Packaging requirements and options are described in the Servlet 2.2, EJB 1.1, and J2EE 1.2 specifications. The Servlet and EJB specifications do a good job of describing what each application component package (war, jar) should do and the J2EE specification describes how to pull it all together into an application package (ear).

    However, some concrete examples describing how you can package your application make the specifications more comprehensible. If you don't understand the specifications regarding application packaging, your application might not be portable.

    The following scenarios present increasingly complex deployment situations, describe options for packaging them, and provide some considerations and notes on deployment for each scenario. Keep in mind that war file and ear file formats are different.

    Scenario #1:
    Single ear, war references EJB

    This first, simple scenario shows how to reference an Enterprise JavaBean in a single ear file from a single war file.


    appA1.ear contains:
    x.war     References client view of ejbA1.
    ejbA1.jar

    Packaging Options:

    • Include ejbA1_clientview.jar in the WEB-INF/lib directory of x.war
    • Expand contents of ejbA1_clientview.jar into the WEB-INF/classes directory of x.war
    • Include ejbA1_clientview.jar in your own specific war directory (for example, "clientviews"). Then the war can refer to the clientviews directory jar file via its Class-Path entry in the manifest.mf.
    War File Notes:
    • The Web server should automatically recognize jar files in the war file's WEB-INF/lib directory. A bug in the J2EE RI 1.2.1 causes jar files located in the WEB-INF/lib directory not to be recognized.
    • If a war file needs to reference an EJB, the war should do so via the client view of that EJB. A client view of an EJB contains all of the classes that a client program needs to access a referenced EJB.
    • A packaging don't: don't try to access an EJB client jar file from the war's manifest.mf Class-Path entry when that EJB client jar does not exist in the war file. Remember, a war file has a different file format than a jar file. You can't access EJB client view jar files that are not local to the x.war file via the manifest.mf Class-Path entry.
    • When a Web application's web.xml file references ejbA1 via an <ejb-ref> element, the Web container is required to be able to find the ejbA1_clientview.jar file automatically, assuming ejbA1.jar's deployment descriptor includes an appropriate <ejb-client-jar> element. See EJB 2.0 specification, section 23.4.
    Scenario #2:
    Single ear, EJB references another EJB

    This scenario shows how to reference one EJB from another within a single ear file.


    appA1.ear:
    x.war
    ejbA1.jar     References client view of ejbA2
    ejbA2.jar

    Packaging Options:

  • Include ejbA2_clientview.jar in ejbA1.jar's Class-Path manifest.mf entry
  • Include ejbA2_clientview.jar expanded in ejbA1.jar file.

    EJB Jar file notes:

  • If an EJB has a client view available, it should specify the name of the client view jar file in the entry of its ejb-jar.xml file.
  • A packaging don't: EJB jar files cannot reference a war file using a manifest.mf Class-Path entry. Remember, a war file has a different file format than a jar file.

    Scenario #3:
    Single ear, EJB references util classes

    This scenario shows how to reference utility classes from within a single ear file.


    appA1.ear:
    x.war
    ejbA1.jar    References classes in yUtil.jar
    yUtil.jar

    Packaging options:

  • Include yUtil.jar in ejbA1.jar's Class-Path manifest.mf entry
  • Expand yUtil.jar's contents into ejbA1.jar file.

    Utility and implementation-specific notes:

  • A packaging don't: If you have common utility classes, don't duplicate the code in your application components. Instead, create a simple util.jar file and include the jar file as needed (see above options for wars and EJB jars).
  • If you've got implementation-specific code (i.e., de- ploy/undeploy), but want to provide a way to minimize the impact to different application servers, following these steps will help to minimize the effort to deploy the application on different servers:
    1. Provide an interface for a common look and feel.
    2. Provide your own server specific implementation classes.
    3. Document how to use and rebuild this piece. Important!
    4. Put it in its own jar file, for example, xAdapter.jar
    5. Include xAdapter.jar file as needed (see above options for wars and EJB jars).
      For example:

      public interface Foo {
      public doBar();

      }

      public class FooImpl implements Foo {
      public doBar() {
      // Your server-specific code here
      }
      }

    Scenario #4:
    Inter-ear references

    In this case, there are several ear files, and an EJB references another EJB in a different ear file (see Listing 4).

    Packaging options:

  • Include ejbA2_clientview.jar in x.war WEB-INF/inf directory
  • Include ejbA2_clientview.jar expanded in x.war WEB-INF/classes directory
  • Include ejbA2_clientview.jar in your own specific war directory ("clientviews", for example). Then the war can refer to the clientviews directory jar file via its Class-Path entry in the manifest.mf.

    Scenario #5:
    multi-ear, utility classes
    referenced from ear and war

    In this scenario, several ears exist, and reference utility classes from both ear and war files.


    appA1.ear:
    x.war    References classes in util.jar

    appA2.ear:
    ejbZ.jar   References classes in util.jar

    util.jar
    The util.jar file must be present in both application ear files. The appA1.ear application includes util.jar in its WEB-INF/lib directory. The appA2.ear application includes util.jar and is referenced by ejbXZ.jar.

    Conclusion
    This article has presented some tips that should help you create more portable J2EE applications. The J2EE Blueprints team is interested in hearing about your experiences with the J2EE platform. Please send feedback and comments to j2eeblueprints-feedback@sun.com. Special thanks to the J2EE development community for their continued commitment to the J2EE platform and for developing J2EE applications and components.

    Resources

    1. J2EE Blueprints: http://java.sun.com/j2ee/blueprints
    2. Download the J2EE Platform Specification, as well as other developer support documentation and software: http://java.sun.com/j2ee/download.html.
    3. EJBGen: http://beust.com/cedric/ejbgen."
  • More Stories By Elizabeth Blair

    J2EE BluePrints Project Lead

    Comments (0)

    Share your thoughts on this story.

    Add your comment
    You must be signed in to add a comment. Sign-in | Register

    In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.