Enterprise Java Technologies Tech Tips Tips, Techniques, and Sample Code Welcome to the Enterprise Java Technologies Tech Tips for December 9, 2003. Here you'll get tips on using enterprise Java technologies and APIs, such as those in Java 2 Platform, Enterprise Edition (J2EE). This issue covers: * Customizing Deployment With Environment Entries * Exploring Web-tier Attribute Scope These tips were developed using Java 2 SDK, Standard Edition, v 1.4 and Java 2 SDK, Enterprise Edition, v 1.3.1 (Reference Implementation). You can download the current Reference Implementation or the next release of the Reference Implementation, Java 2 SDK, Enterprise Edition, v 1.4 (currently a Beta release), at http://java.sun.com/j2ee/download.html. This issue of the Tech Tips is written by Mark Johnson, president of elucify technical communications (http://www.elucify.com/), and co-author of Designing Enterprise Applications with the J2EE Platform, 2nd Edition (http://java.sun.com/blueprints/guidelines/ designing_enterprise_applications_2e/). Mark Johnson runs an open forum for discussion of the tips at http://groups.yahoo.com/group/techtipsarchive/. You can view this issue of the Tech Tips on the Web at http://java.sun.com/developer/EJTechTips/2003/tt1209.html See the Subscribe/Unsubscribe note at the end of this newsletter to subscribe to Tech Tips that focus on technologies and products in other Java platforms. You can download the sample archive for these tips at http://java.sun.com/developer/EJTechTips/download/ttnov2003.ear. The context root for the application is ttnov2003, and the index.html welcome file indicates how to use the sample code. Any use of this code and/or information below is subject to the license terms at http://developer.java.sun.com/berkeley_license.html - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CUSTOMIZING DEPLOYMENT WITH ENVIRONMENT ENTRIES Sometimes, when writing a J2EE Web application component, you might want to provide some flexibility to the person deploying your application. For example, perhaps you'd like to offer several different versions of the application, each one tailored to the needs of a particular user. Possibly some part of the code requires a host name and port number that are available only at deploy time. Or maybe you just want to give the deployer some flexibility in how data is displayed. You can add this type of flexibility by using environment entries. Environment entries are parameters that you can define in a component's deployment descriptor. An application component looks up environment entries by name using JNDI, and uses the values to customize behavior or presentation. Environment entries are available for all application component types. Servlets, enterprise beans, JSP pages, and custom tags can all use environment entries. The entries must be defined in the appropriate deployment descriptor for the component type, such as web.xml for Web components, and ejb-jar.xml for enterprise beans. For example, suppose you are writing a servlet for an e-commerce application. The servlet sends email to customers to acknowledge receipt of an order. Your servlet needs the host name, port number, login name, and password of an authenticated SMTP server. As the component (servlet) developer, you don't know this information -- only the deployer knows. But the email feature requires this information. How do you provide the information to the servlet? One way to get the information to the servlet is to use environment entries in the servlet's deployment descriptor, web.xml. First, define environment entries for the host name, port number, login name, and password. Then write code that uses JNDI to get these values from the environment, and use the values in your code. At deploy time, the deployer uses deployment tools to fill in appropriate values for the environment entries. At runtime, the program retrieves the values the deployer set, and uses them to access the correct server. Defining an Environment Entry You define environment entries in XML in a component's deployment descriptor. If you use a deployment tool (such as the deploytool program that comes with the J2EE Reference Implementation), you can resolve deployment descriptor references in a GUI, instead. But let's assume you're editing the deployment descriptor by hand with a text editor. Environment entries have four parts: o Description. A string within a tag o Name. A string within an tag. o Value. A value within an tag. o Classname. The type of the entry within an tag. The description is an optional text description that appears in the deploy tool user interface. It tells the deployer what to do to resolve the environment entity reference. In other words, it's a human-readable description that tells the deployer how to fill in the other values. It can also indicate to the deployer whether the entry is optional. The env-entry-name is the name, relative to JNDI context name "jndi:comp/env", the component uses to look up the entry. All environment entries are registered in that JNDI context by their containers. The env-entry-value is the value the environment entry should take, formatted as a string. Except for java.lang.Character, which takes a single character, all of the types allowed for environment entries have constructors that take a String. The env-entry-value tag contains the String used for the value's constructor. The env-entry-type is the class name of the type of the environment entry value. This type must be one of the following: o java.lang.Boolean o java.lang.Byte o java.lang.Character o java.lang.Double o java.lang.Float o java.lang.Integer o java.lang.Long o java.lang.Short o java.lang.String The environment entries for the SMTP host example might look something like this: Enter the host name for sending email SMTP Host Name homer.springfield.ma.us java.lang.String SMTP port number for email SMTP Port 2101 java.lang.Integer User authentication for SMTP server SMTP User bart java.lang.String Password for SMTP user SMTP Password D'oh! java.lang.String Using the Environment Entry To use the environment entry in code, simply look up the entry using JNDI. Be sure to typecast the result of the Context.lookup method to the appropriate type. For the example above: try { InitialContext ic = new InitialContext(); Context ctx = ic.lookup("java:comp/env"); String hostname = (String)(ctx.lookup("SMTP Host")); Integer port = (Integer)(ctx.lookup("SMTP Port")); String user = (String)(ctx.lookup("SMTP User")); String password = (String)(ctx.lookup("SMTP Password")); sendEmail( emailText, port, hostname, user, password); } catch (NamingException nex) { ... } Environment Entries vs. Servlet Initialization Parameters In a Web application, servlet behavior can be customized using servlet initialization parameters instead of environment entries. A servlet developer defines a servlet initialization parameter using the init-param tag in web.xml, and accesses it in servlet code using method javax.servlet.GenericServlet.getInitParameter. The servlet initialization parameter's scope is limited to the servlet that defines it. So, how do you choose between an environment entry and a servlet parameter for a particular customization? The answer depends on the customization's natural scope. Just as global variables can pollute the namespace of programs, placing all customizations in environment entries clutters the JNDI namespace. It can also lead to unwanted dependencies between components. A servlet initialization parameter is best when a customization affects only one servlet. Consider using an environment entry when a customization spans multiple components. Sample Code The sample code for this tip comes in two parts. The first part is a servlet that prints all of the environment entries for an application. The "business end" of the servlet lists all of the bindings in the JNDI context java:comp/env, using the Context.listBindings method discussed in October 27, 2003 Tech Tip "Introduction to JNDI" (http://java.sun.com/developer/EJTechTips/2003/tt1027.html). Here is a code excerpt from the servlet: public void printEnvEntries(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException { res.setContentType("text/html"); PrintWriter out = res.getWriter(); try { InitialContext ic = new InitialContext(); NamingEnumeration ne = ic.listBindings("java:comp/env"); out.println( "Environment Entries"); out.println( "" + ""); while (ne.hasMore()) { Binding ncp = (Binding)ne.next(); String objName = ncp.getName(); Object objObj = ncp.getObject(); out.println(""); out.print( ""); } out.println("
EntryValue
" + objName + "" + objObj.toString() + "
"); } catch (Exception e) { throw new ServletException(e); } } This method iterates through each object in java:comp/env, printing the name and text representation of each entry in a table. Try deploying the application and looking at the entries defined in the deployment descriptor. See the section "Running the Sample Code" for deployment instructions. The second sample code for this tip is a custom tag, DateTag.java. The sample is a prime example of how to use an environment entry to make a component (in this case a custom tag) customizable. DateTag is a simple tag that a page developer can use to print the date and time at the server. When used alone, (like this: ""), it prints the time and date in a standard format. If you define a format with the tag's format attribute, it uses that format, instead. (The format syntax is defined by standard class SimpleDateFormat.) A deployer can use environment entries to define a list of time/date formats with symbolic names. If the value of the DateTag's format attribute starts with $, then the tag looks for the format in the value of the environment entry named in the attribute. For example, the following environment entry occurs in web.xml: LongTimeDateFormat 'Date:' EEEE, d MMMM yyyy', Time:' kk:mm:ss z java.lang.String The sample JSP page contains the following text: The server date in "Obtuse" format is .
At runtime, this translates to: The server date in "Obtuse" format is 20030511-23:05:04EST. This means that the deployer can define a list of commonly-used date formats in the application deployment descriptor. Application developers can then reference the date formats in JSP pages using the DateTag. If you change the definition of the format in the deployment descriptor, the appearance of all of the date/time values in the application changes. How does this work? Method DateTag.setFormat checks to see if the format starts with a $, and if so, it loads the corresponding environment variable: public void setFormat(String sFormat) { if (sFormat.length() > 0 && sFormat.charAt(0) == '$') { // Remove $ sFormat = sFormat.substring(1); // Look up environment entry String format = null; try { InitialContext ic = new InitialContext(); format = (String)ic.lookup( "java:comp/env/" + sFormat); } catch (NamingException nex) { format = "[" + nex.toString() + "]"; } // If we found something if (format != null) { sFormat = format; } else { sFormat = "[" + sFormat + ": environment entry not set]"; } } _sFormat = sFormat; } Deploy and experiment with the sample code. Again, see the section "Running the Sample Code" for deployment instructions. Try defining your own formats and using them in the application's JSP pages. For even more of a challenge, refactor the tag to support internationalization. The Data Access Objects in the Java Pet Store Sample Applications (http://java.sun.com/blueprints/code/index.html#java_pet_store_demo) from the Java BluePrints program provide an example of how to internationalize data resources. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - EXPLORING WEB-TIER ATTRIBUTE SCOPE Web-tier container context interfaces such as ServletContext, HttpSession, and PageContext all offer attributes. The attributes are named objects that can be placed in and accessed from the runtime environment while a service request is being processed. Attributes are used by programmers to associate data with various objects in the Web container. This tip introduces the concept of attribute scope and describes how it works in different contexts. What Is Attribute Scope? Attribute scope determines the lifetime and visibility of the attributes of the context objects in the Web tier. The four types of attribute scope are application scope, session scope, request scope, and page scope. The class PageContext provides a set of methods for getting data by name from each of these scopes. It also provides a method for finding the value of a variable in the most specific scope. JSP pages can access scoped attributes using methods in the PageContext interface. Before describing how to use these methods, let's review the semantics of the four types of scope. In each of the following four scopes, the methods to set and read attributes are called setAttribute and getAttribute. The interface used to access the attribute determines the scope of the attribute. Application Scope Application scope attributes are set and read using the ServletContext interface. The value of an application-scoped object lasts the lifetime of an application instance. When an application is undeployed, or when the container restarts, all application attribute values are lost. Attributes in application scope are visible to all Web-tier components in the application. A typical example of an object stored in application scope is a synchronized reference to a global id server which is shared between application components. Session Scope Session scope attributes are set and read using the HttpSession interface. Session-scoped attributes survive as long as the session. Because of this, they can be set by one request, and then used by multiple subsequent requests. Session attribute values are lost when a session times out, is discarded, when the session's application is undeployed, or when the Web container restarts. Attributes in session scope are visible only to the HTTP client that owns the session, but are visible across multiple requests. A typical example of an object stored in session state is an e-commerce application user's shopping card. Request Scope Request scope attributes are set and read using the HttpServletRequest interface. They are distinct from the POST or GET parameters of a request (which can be retrieved using HttpServletRequest.getParameter). Request-scoped attributes survive only as long as a single HTTP request is being serviced. If a request is processed by one Web-tier component, and then forwarded to another, the attribute is visible to the second and all subsequent components. Request-scoped attributes are discarded before the HTTP response is transmitted to the client. Attributes in request scope are visible only to the application component servicing a single request. An example of a request scope is an object that a servlet forwards to a JSP page for formatting. Page Scope Page scope attributes are set and read using the PageContext interface. They are visible only in the context of a single JSP page, and survive only until the entire output of the page is being processed. In particular, they are not visible across request forwards or inclusion of the contents of other Web resources. An example of a page scope attribute is a particular style for all of the elements on one JSP page. Getting An Attribute With a Specific Scope In a JSP page, the PageContext attribute has a pair of methods setAttribute/getAttribute for setting and reading attributes. It also has overloaded convenience methods for dealing with attributes in other scopes: public Object getAttribute(String name, int scope); public void setAttribute( String name, Object value, int scope); These two methods set and get objects in a specific scope. The value of scope in the parameter lists of these methods must be one of the following final int values defined in PageContext: o PageContext.APPLICATION_SCOPE o PageContext.SESSION_SCOPE o PageContext.REQUEST_SCOPE o PageContext.PAGE_SCOPE Another PageContext method searches all of the scopes for an attribute with a particular name: public Object findAttribute(String name, int scope) This method looks through each scope in order, from most to least restricted, searching for an attribute with a given name. It first looks in page scope, then in request scope, then session scope, and finally application scope. It returns the value of the first attribute it finds with that name. This mechanism can be very useful for setting defaults at higher levels, and overriding them at more specific levels. For example, a developer could define an application attribute, color.background, that defines the background color for all pages in the application. Each successive scope, down to the individual page level, could override the value of color.background for its specific context. If you want to know the least-specific scope for a particular attribute, use: public int getAttributesScope(String name) It returns one of the final ints listed above. The following method: Enumeration getAttributeNamesInScope(int scope) returns an enumeration of all of the names defined in that scope. When To Use Which Scope Application, session, request, and page scope each correspond to different design scenarios. Application scope is most appropriate for resources that are shared between application components. Data in application scope should virtually always be read-only. When application-scope state is writable, multiple components might try to write it simultaneously, causing race conditions. In distributed applications, each virtual machine maintains its own application scope memory space. These memory spaces are not required to be synchronized between virtual machines, so any data written to them by an application component will usually be visible only to components inside that virtual machine. Session scope is specific to a user session. Remember that data kept in session scope has an impact on scalability. That's because session scope state takes up resources in proportion to the number of user sessions (unlike state in other scopes). Data in session-scope state must be serializable if the application is to be distributed. If scalability is a concern, consider keeping session-scope state in a stateful session bean, instead of in the Web tier. Request scope is the appropriate scope for data that is specific to an individual request. Multiple components such as servlets, JSP pages, and custom tags all have access to request-scope variables. Request-scope state has less of an impact on session-scope state because it is discarded after the response completes. Page scope is appropriate for data that components within a single page use to communicate with each other. For example, an iteration tag commonly stores data in page scope for the tags in its body to use. Page scope cannot be used to communicate data between a page and any pages it includes. In general, try to use the narrowest scope possible -- this conserves resources. Also, try to avoid cluttering namespaces between components. To summarize: o If data is used only within one page, use page scope. o If data is used by more than one page, use request scope. o If data is used across multiple requests, use session scope. o If data is used across sessions, use application scope. o If data must be writable across sessions, consider using entity beans or messaging. Sample Code The sample code for this tip comprises a JSP page (ShowAllAttrs.jsp) and a custom tag (ShowAttrsTag.java). The ShowAttrsTag prints a table of the names and values of all of the attributes in the scope indicated by the tag's scope attribute. In the absence of the scope attribute, the tag prints a table of all attribute/value pairs, retrieved from findAttribute(). The tag color-codes each attribute to indicate the scope where that attribute was defined. The JSP page allows you to experiment with setting attributes in various scopes, playing with their visibility and lifetimes. At the top of the page is a form that allows you to set or remove attribute/value pairs in any scope. Filling out the form and hitting Submit runs a scriptlet in the JSP page that sets or removes the requested element. The remainder of the JSP page prints all attributes in each scope, and at the bottom, uses findAttribute to find all of the attributes. The JSP page uses the tag to print the tables, so the code in the JSP page is simple:

Application Scope Attributes

Session Scope Attributes

Request Scope Attributes

Page Scope Attributes


All Attributes

Try the following with the sample code: 1. Set an attribute in application scope. Open a completely new browser, and access the JSP page. Can you see the attribute? 2. Set an attribute in application scope, then set the same attribute in session scope to a different value. Do you see both values? What value does findAttribute return? Does the session attribute exist across mutiple requests? Open another browser and visit the JSP page. Is the session attribute set? 3. Set the same attribute in application and session scope with different values. Now set the same attribute in request scope. What value does findAttribute return? Make sure there's nothing in the Attribute Name box, then click the Submit button. What happens to the Request attribute? What happens to the value returned by findAttribute? Delete the session attribute and try this experiment again with a request attribute, and then page attribute. 4. Modify the program and the form so you can set multiple attributes at once. For even more of a challenge, define the scope for each attribute set or removed. A few minutes spent playing with these attributes and exploring their behavior in different scopes can help you understand how attribute scope works. For additional guidance about scope see the section "Web-Tier State" (http://java.sun.com/blueprints/guidelines/ designing_enterprise_applications_2e/web-tier/web-tier5.html#1083750) in the "Designing Enterprise Applications with the J2EE Platform, Second Edition. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - RUNNING THE SAMPLE CODE Download the sample archive for these tips (from http://java.sun.com/developer/EJTechTips/download/ttnov2003.ear). The application's context root is /ttnov2003. The downloaded EAR file also contains the complete source code for the sample. You can deploy the application archive (ttnov2003.ear) in the J2EE Reference Implementation using the deploytool program: $J2EE_HOME/deploytool -deploy ttnov2003.ear localhost Replace localhost with the name of the host on which the server is installed. For a standard installation on a single machine, the hostname typically (and literally) is localhost. You can access the application at http://localhost:8000/ttnov2003. For a J2EE-compliant implementation other than the Reference Implementation, use your J2EE product's deployment tools to deploy the application on your platform. See the index.html welcome file for instructions on running the application. . . . . . . . . . . . . . . . . . . . . . . . IMPORTANT: Please read our Terms of Use, Privacy, and Licensing policies: http://www.sun.com/share/text/termsofuse.html http://www.sun.com/privacy/ http://developers.sun.com/dispatcher.jsp?uid=6910008 * FEEDBACK Comments? Send your feedback on the Enterprise Java Technologies Tech Tips to: http://developers.sun.com/contact/feedback.jsp?category=sdn * SUBSCRIBE/UNSUBSCRIBE Subscribe to other Java developer Tech Tips: - Core Java Technologies Tech Tips. Get tips on using core Java technologies and APIs, such as those in the Java 2 Platform, Standard Edition (J2SE). - Wireless Developer Tech Tips. Get tips on using wireless Java technologies and APIs, such as those in the Java 2 Platform, Micro Edition (J2ME). To subscribe to these and other JDC publications: - Go to the JDC Newsletters and Publications page, (https://softwarereg.sun.com/registration/developer/en_US/subscriptions), choose the newsletters you want to subscribe to and click "Update". - To unsubscribe, go to the subscriptions page, (https://softwarereg.sun.com/registration/developer/en_US/subscriptions), uncheck the appropriate checkbox, and click "Update". - To use our one-click unsubscribe facility, see the link at the end of this email: - ARCHIVES You'll find the Enterprise Java Technologies Tech Tips archives at: http://java.sun.com/developer/EJTechTips/ - COPYRIGHT Copyright 2003 Sun Microsystems, Inc. All rights reserved. 901 San Antonio Road, Palo Alto, California 94303 USA. This document is protected by copyright. For more information, see: http://java.sun.com/developer/copyright.html Enterprise Java Technologies Tech Tips December 9, 2003 Trademark Information: http://www.sun.com/suntrademarks/ Java, J2SE, J2EE, J2ME, and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries.