HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML

While J2EE technology is designed to support portability, it's always possible to write nonportable code. In Part 2 of this series, we (the BluePrints development team) offer recommendations on how to avoid specific design, implementation, and deployment pitfalls that can compromise portability.

A comprehensive collection of the J2EE BluePrints application programming model can be found at http://java.sun.com/j2ee/blueprints.

This month, our recommendations are:

  • Deploy each Web application with a unique context root to avoid colliding with other applications.
  • Use handles or remote object references to store session state in HttpSession.
  • Be aware of changes in taglib DTD from J2EE 1.2 (JSP 1.1) to J2EE 1.3 (JSP 1.2).
  • Always import all classes used in a JSP.
  • Never throw resource-specific exceptions from BMP code.
  • Wrap authentication code in helper classes.
  • Always completely specify security roles.
  • Uses of EJBContext.getCallerPrincipal ().getName().
Unique Context Roots
Always specify a unique context root for every Web application you deploy to avoid naming collisions with applications already deployed.

The context root is the base path of a Web application, relative to the server's base URL. For example, if your server's base URL is http://j2eeserver.com:8000 and the context root is apps/myapp, then all components of the Web application will be accessed relative to http://j2eeserver.com:8000/apps/myapp. The <context-root> element of the Web application's deployment descriptor (web.xml) specifies the application context root.

Server behavior can be unpredictable if two applications are deployed to the same context root since it isn't clear which application should receive an incoming request. Servers and deployment tools will differ in how they handle context root collisions. A good deployment tool will detect context root collisions at deployment time and allow the deployer to choose a different context root. Selecting a unique context root for every application will prevent this problem from ever occurring. For example:

App1.ear: war_runtime.xml

<web>
<display-name>WebTier</display-name>
<context-root>coolpetsestore</context-root>
</web>

App2.ear: war_runtime.xml
<web>
<display-name>WebTier</display-name>
<context-root>exoticpetsestore</context-root>
</web>

The code snippets in the preceding example show how App1 and App2 specify unique <context-root> entries so that requests can be clearly channeled to the appropriate application Web page.

Store Enterprise Bean Handles or Remote References in HttpSession
It's perfectly legal to store either handles or remote references to enterprise beans in HttpSession.

Every enterprise bean remote reference extends EJBObject. A remote reference's method EJBObject.getHandle() returns a handle, which is a persistable reference to the object. A handle can be serialized and persisted, and then at some later time deserialized and converted back into a remote reference to the original bean (if the bean still exists).

Some application developers believe that a handle is the only way to store a reference to an enterprise bean in HttpSession, but this isn't the case. In fact, an enterprise bean reference (acquired from a home interface create or finder method) can be stored directly in HttpSession without first converting it to a handle. A compatible application server that supports distributable servlets is required by the specification to correctly manage enterprise bean references stored in HttpSession. Therefore, remote references (that extend EJBObject) can be stored directly in HttpSession with no loss of portability.

For more information refer to the J2EE Platform Specification 1.2, section 6.6.

New TagLibrary Format: J2EE 1.2
If you're migrating applications from J2EE 1.2 to J2EE 1.3, you may need to update any TagLibrary descriptor (TLD) files in your application to the new format. The TLD file DTD has changed from J2EE 1.2 (using JSP 1.1) to J2EE 1.3 (JSP 1.2 ).

Here's an excerpt from a JSP 1.1 taglib.tld file:

<taglib>
<tlibversion>1.0</tlibversion>
<jspversion>1.1</jspversion>
<shortname>j2ee</shortname>
Here is the same snippet for JSP 1.2:

<taglib> <tlib-version>1.0</tlib-version> <jsp-version>1.2</jsp-version> <short-name>j2ee</short-name>
Your application server will hopefully provide good migration tools that will update these files for you; otherwise, you may have to check and edit them manually.

The samples used in this example are just a few of the JSP DTD tag name changes. See the relevant sections of the JSP 1.1 (section 5.3.5) and JSP 1.2 (Appendix C) specifications for details.

Always Import Classes Used in JSP Pages
With the exception of classes that are guaranteed to be imported, always import any class you use in a JSP page. This may seem like an obvious point, but this item addresses a subtlety of Tomcat - and possibly other JSP containers. Tomcat (version 3.0) found in the J2EE 1.2 Reference Implementation imported more packages than those required by the specification (this has been fixed in the latest release). In particular, it was possible to use java.io.PrintWriter without importing it. Pages that did so would work in Tomcat but not in other JSP page environments. Importing all classes used by a JSP page will prevent this problem from occurring.

The only packages that can portably be used without importing are java.lang.*, javax.servlet.*, javax.http.*, and javax.servlet.jsp.*. All other packages should be imported in the JSP page.

JSP containers other than Tomcat may import classes beyond those required by the specification, but relying on the presence of those classes may make your JSP pages nonportable. See the section on the import keyword in the JSP 1.2 pfd 2 specification, section 2.10.1.1.

Never Throw Resource-Specific Exceptions from BMP Code
Entity beans that manage their own persistence should never throw exceptions (such as SQLException) from BMP code; instead, they should define and use application exceptions to report persistence errors.

Bean providers should be especially careful to avoid including back-end resource-specific details in their components' interfaces since doing so may limit where the components might be used. One easily overlooked form of resource dependence is the set of exceptions a method may throw. BMP methods don't necessarily use a SQL database to manage their persistence, so SQLException doesn't belong in the BMP method signatures.

Instead of throwing SQLException, define system- and application-level exceptions for the class and throw those exceptions in response to error conditions.

The BluePrints program recommends the Data Access Object (DAO) design pattern ( http://java.sun.com/j2ee/blueprints/design_catalog/dao) to encapsulate a back-end resource, such as a SQL database. A DAO provides access to a system resource without reference to how that resource is implemented. For example, a DAO might offer methods to load, update, and delete a persistent object, and methods to set and get that object's properties, while hiding the implementation details of how those operations occur.

The DAO can catch resource-specific exceptions and translate those exceptions to the custom system- or application-level exceptions described above.

See Listing 1 for an example from the Java Pet Store. In method deleteAccount(), a SQLException is translated to an AccountDAOSysException, which extends java. lang.RuntimeException to indicate a system-level exception.

It's worth noting that the Account bean indicates application-level errors with subclasses of AccountDAOAppException (such as AccountDAODBUpdateException). An application-level exception is typically thrown when a recoverable error has occurred, such as an incorrect user password. A system-level exception (like AccountDAOSysException above) is typically thrown when a nonrecoverable error has occurred, such as a database crash. Learn more about the differences between system- and application-level exceptions in Chapter 12 of the EJB specification.

Security: User Authentication
Keep all user login code in classes separate from your application classes so you can reimplement them if you port the application or change your user authentication mechanism. The J2EE platform's long-term expectations are that developers won't be writing authentication functionality directly into their applications; however, developers will do so as long as the container-provided mechanisms aren't adequate to suit the needs of an application. If this is done, it would be wise for the developer to isolate the code so that it can be easily removed as containers become more capable.

Isolating the code specific to an authentication mechanism makes porting easier since just that part of the code will need to be rewritten. See Scenario #3 on packaging from the JDJ "Build to Spec!" article (Vol 6. issue 7) for more information.

For managing user authentication, consider using Java Authentication and Authorization Service. JAAS allows services to authenticate and enforce access controls on users. To learn more about JAAS, see the resources section at the end of this article.

See also Table 6.2 of the J2EE 1.2 platform specification, where the J2EE security permission set for each type of component (application clients, applet clients, servlets/JSPs, EJBs, and so on) are defined.

Define Method Permissions When Security Roles Are Used in EJBs
If your application uses security roles, always specify method permissions with the <method-permission> element in the EJB deployment descriptor (ejb-jar.xml) for all of the EJB client-view methods.

There is a gray area in the J2EE 1.2 specification when it comes to defining the behavior of an unspecified method permission if security roles are used. The gray area has, legitimately, been interpreted inconsistently by container vendors. This means that EJB containers may not behave consistently for EJB methods with an unspecified method permission when security roles are used.

For example, some container vendors may interpret unspecified method permissions to mean none, essentially an uncallable method for no user access; others may interpret unspecified method permissions to mean everybody for wide-open user access. In any case, if you use security roles and want the security behavior to be portable across container vendors, specify your intentions clearly with well-defined method permissions.

The application developer may choose to create a role named everybody and then map this role to wide-open user access. This assumes the container has the ability to map the everybody role to a set of users.

The everybody role could then be used in the ejb-jar.xml files for those methods where wide-open user access is allowed. For an example, see Listing 2.

Refer to the J2EE 1.3 specification for the latest changes for using security roles and EJB method permissions.

Use of Security APIs:
EJBContext.getCallerPrincipal().getName()

The J2EE 1.2 specification leaves the result of EJBContext.getCallerPrincipal().getName() as unspecified in terms of the requirements of the returned value. An application developer shouldn't make any assumptions about the returned value when designing an application or component. Why?

The main reason is that there's no standard format for the return value of EJBContext.getCallerPrincipal().getName(). As of proposed final draft2, the EJB 2.0 specification mentions that calling methods "might, for example, use the name as a key to information in a database." In general this will work; however, there's a caveat that in complex security infrastructures this value might change. Here are some scenarios where this might be a problem:

  • When running a server from one vendor and then switching to a server from another vendor while keeping the same database (e.g., using BMP)
  • When running two servers from different vendors where a bean on each server needs access to that row
In short, the call itself is portable but the return value isn't. One portable example might be simply as a display value in the Web tier, for example:

"Hello <%= request.getUserPrincipal().getName() %>"

Another possibility is as a log message in the EJB tier for auditing, for example:

"8:15 AM: User " + ejbContext.getCallerPrincipal().getName() + " withdrew money."

Until the return values are standardized from this API, its use to produce values that must be compatible with values that may be produced by other container vendors isn't a recommended portability practice. Look for this item to be addressed in future versions of the J2EE platform specification.

We Want to Hear from You
The J2EE BluePrints team is interested in hearing about your experiences developing applications for the J2EE platform and your experiences with BluePrints. Send feedback and comments to j2eeblueprintsinterest@sun. com. Special thanks goes to the J2EE development community for their continued commitment to the J2EE platform.

Resources

  • Check out the J2EE BluePrints Web site at http://java.sun.com/j2ee/blueprints, where you can download the Java Pet Store sample application, the BluePrints book Designing Enterprise Applications with J2EE, and view the complete BluePrints design pattern catalog - all for free.
  • Download the J2EE Platform Specification, as well as other J2EE developer support documentation and software (including the free J2EE Reference Implementation), from http://java.sun.com/j2ee.
  • View the current list of J2EE licensees who have passed the Compatibility Test Suite at http://java.sun.com/j2ee/compatibility.
  • Learn more about JAAS, the Java Authentication and Authorization Service, at http://java.sun.com/products/jaas.

Author Bio
Elizabeth Blair is a staff engineer with Sun Microsystems, where she leads the Java 2 Platform, Enterprise Edition BluePrints development team. Liz contributed to the recent Java Pet Store sample application, with an emphasis on the tests for EIS and EJB architecture. In the past she has worked on the Compatibility Test suite (CTS) for the J2EE platform, the J2SE platform, and the WABI (Windows Applications Binary Interface) projects. liz.blair@sun.com

	



Listing 1

  public class AccountDAOImpl implements AccountDAO { 

public void remove(String id) throws AccountDAODBUpdateException, 
                                            AccountDAOSysException { 
           deleteAccount(id); 
   } 
   
private void deleteAccount (String userId) throws 
                                         AccountDAODBUpdateException, 
                                         AccountDAOSysException { 
        String queryStr = "DELETE FROM "; 
        PreparedStatement stmt = null; 
        Debug.println("queryString is: "+ queryStr); 

        try { 
            getDBConnection(); 
            //stmt = dbConnection.createStatement(); 
            //int resultCount = stmt.executeUpdate(queryStr); 
            stmt = createPreparedStatement(dbConnection, queryStr); 
            int resultCount = stmt.executeUpdate(); 

            if (resultCount != 1) 
                throw new AccountDAODBUpdateException 
                ("ERROR deleteing account from ACCOUNT_TABLE!! resultCount = "+ 
                 resultCount); 
        } catch(SQLException se) { 
            throw new AccountDAOSysException("SQLException while removing " + 
                            "account; id = " + userId + " :\n" + se); 
        } finally { 
            closeStatement(stmt); 
            closeConnection(); 
        } 
    } 
} 

    
Listing 2 

<security-role> 
      <role-name>manager</role-name> 
    </security-role> 

    <security-role> 
      <role-name>everybody</role-name> 
    </security-role> 

    <method-permission> 
       <role-name>manager</role-name> 
       <method> 
         <ejb-name>EmployeeService</ejb-name> 
         <method-intf>Remote</method-intf> 
         <method-name>setPerformanceRating</method-name> 
         <method-params> 
            <method-param>java.lang.String</method-param> 
         </method-params> 
      </method> 
    </method-permission> 

    <method-permission> 
       <role-name>everybody</role-name> 
       <method> 
         <ejb-name>EmployeeService</ejb-name> 
         <method-intf>Remote</method-intf> 
         <method-name>getPerformanceRatingForm</method-name> 
         <method-params/> 
      </method> 
    </method-permission> 

  
 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: info@sys-con.com

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.