Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 113 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
113
Dung lượng
1,62 MB
Nội dung
05 0672323842 CH03 3/20/02 9:31 AM Page 88 88 Day • Specify command-line parameters to be passed to an application • Specify parameters to be passed into an applet • Hard-code the parameters into the program The last option is the weakest approach, because it restricts the program to working with one type of JNDI service provider on one specific host The first two options are the most suited to production environments They both require that you distribute simple text configuration files with the program JNDI Properties Files An application resource file called jndi.properties defines the JNDI service The JNDI system automatically reads the application resource files from all components in the program’s CLASSPATH and from lib/jndi.properties in the Java runtime home directory (this is the jre sub-directory of the Java JDK home directory) The following example from Sun Microsystems’ J2EE RI shows a typical jndi.properties file: java.naming.factory.initial=com.sun.enterprise.naming.SerialInitContextFactory java.naming.provider.url=localhost:1099 java.naming.factory.url.pkgs=com.sun.enterprise.naming Each entry in the property file defines a name value pair The InitialContext object uses these properties to determine the JNDI service provider The J2EE server vendor usually supplies a sample jndi.properties file defining the properties that need to be configured with their server You can find the J2EE RI jndi.properties file in the lib/classes directory in the J2EE RI installation directory Normally, any given JNDI service will require the following named properties: java.naming.provider.url This defines the DNS host name of the machine running the JNDI service and the service port number This is the only property that the network administrator needs to customize The property value is a machine name with an optional colon and port number By default, JNDI uses port 1099, and most sites not change this value The default server is usually localhost Consider a host called nameserver in the Sams Publishing domain (samspublishing.com), the full URL including port number for a default JNDI server on this host would be as follows: nameserver.samspublishing.com:1099 java.naming.factory.initial 05 0672323842 CH03 3/20/02 9:31 AM Page 89 Naming and Directory Services 89 You set this property to the classname (including the package) of the Initial Context Factory for the JNDI Service Provider This value effectively determines which JNDI Service Provider you use To use the default Naming Service supplied with the J2EE RI, you would specify this property as follows: java.naming.factory.initial=com.sun.enterprise.naming.SerialInitContextFactory java.naming.factory.url.pkgs This property defines prefix package names the InitialContext class uses for finding other classes JNDI requires The J2EE RI uses the following value for this property: java.naming.factory.url.pkgs=com.sun.enterprise.naming More information on these and other JNDI properties can be found in the API documentation for the Context class and in the JNDI Tutorial from Sun Microsystems The simplest way to define the JNDI Service Provider is to configure every client’s Java home directory to include the necessary JNDI properties This approach suits an intranet where all machines are centrally managed Another approach is to include a suitable JNDI properties file with the client program and distribute everything as a JAR file (program class files and the jndi.properties file) This suits Web-based intranets or extranets, where applets are used or where you can distribute the client JAR file to users Application Properties Using the -D option, you can supply the JNDI properties on the java command line of an application This has the disadvantage of requiring long command lines that are hard to remember and easy to mistype A way around this problem is for you to provide script files to run the application on the target platforms; typically, you will supply batch files for Windows and shell scripts for Linux and Unix The following is an example of a command line that defines the JNDI factory classes and server: java ➥-D java.naming.factory.initial=com.sun.enterprise.naming.SerialInitContextFactory ➥–D java.naming.provider.url=localhost:1099 MyClass Providing a jndi.properties file in the application JAR file is a cleaner solution than providing command-line parameters However, using command-line parameters makes the JNDI properties more apparent when customizing the application for a local site It is easy to overlook a jndi.properties file in a JAR file 05 0672323842 CH03 3/20/02 9:31 AM Page 90 90 Day Applet Parameters An applet can accept the JNDI properties as parameters, for example Using parameters with the applet HTML file makes the JNDI properties more apparent when customizing the applet for a local site A jndi.properties file in the jar file is easily overlooked Hard-Coded Properties The least desirable way to specify the JNDI properties is via hard-coded values in the program Hard coding the properties means including the JNDI classnames and the server name in the source code This is undesirable because it means that should the network architecture change, you must edit, recompile, and redistribute the program Obviously, you want to avoid this maintenance overhead if you can The network architecture may change if the JNDI service moves to a different server or you install a new JNDI Service Provider The mechanism for defining the service in code is via a hash table of properties passed into the InitialContext constructor: Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, “com.sun.enterprise.naming.SerialInitContextFactory”); env.put(Context PROVIDER_URL, “localhost:1099”); Context ctx = new InitialContext(env); Notice how the code uses symbolic constants from the Context class rather than using strings representing the properties (such as “java.naming.factory.initial”) This approach makes the code more portable should the property names change in future versions of Java or JNDI Binding JNDI Objects After the initial JNDI context has been obtained, a program can look up existing objects and bind new objects to the context When working with EJBs, the main JNDI activity is to look up existing bound objects; the J2EE server does most of the binding of the objects automatically 05 0672323842 CH03 3/20/02 9:31 AM Page 91 Naming and Directory Services 91 Because this section discusses the binding of objects, you can skip it if your primary purpose for using JNDI is to obtain EJB and other references within a J2EE application Binding Objects Binding an object means adding a name to the JNDI service and associating that name with a Java object The name and object are bound to a context Listing 3.1 shows how a text message can be bound to the name sams/book LISTING 3.1 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: Full Text of JNDIBind.java import javax.naming.*; public class JNDIBind { private final static String JNDI = “sams/book”; public static void main(String[] args) { try { Context ic = new InitialContext(); ic.bind(JNDI,”Teach Yourself J2EE in 21 Days”); System.out.println(“Bound “+JNDI); } catch (NamingException ex) { System.err.println(ex); System.exit(1); } } } The object to be bound must implement the Serializable interface so that the name server can store a copy of the object The Context.bind() method will fail with a NameAlreadyBoundException (which extends NamingException) if an object is already bound to that name Another subclass of NamingException is thrown if there is some other form of error, such as an invalid name Remember that different Service Providers may have different naming conventions Binding Problems A Service Provider may not support binding of all types of objects If the service cannot bind a particular object, it will throw an exception Using the default naming service for J2EE RI that uses a transient CORBA naming service, the class of the object must be in the CLASSPATH used by the J2EE RI JNDI server For now, this means using standard J2SE and J2EE classes or configuring the J2EE RI services to include your class files The recommended approach is to edit the user 05 0672323842 CH03 3/20/02 9:31 AM Page 92 92 Day configuration file (userconfig.sh or userconfig.bat) in the bin directory of the J2EE RI home directory, and add the required class directories or JAR files to the J2EE_CLASSPATH variable defined in the configuration file An alternative is to use a Web Service to dynamically upload the required class files Dynamic uploading of class files is dicussed in the “Loading Classes from a Code Base” section, later in this chapter Some Naming Services (such as LDAP) may use security features to ensure that only authorized programs can bind new objects The bind() method can also fail if it violates any security features of the underlying naming service The “Security” section of today’s lesson covers this in more detail Name Persistence A bound object normally remains in the namespace until it is unbound If the bound name remains across server restarts, it is said to be persistent Commercial servers, such as NDS, Active Directory, and LDAP, are persistent name servers and store the bound names and ancilliary information on disk (typically in a database) The default naming service for Sun Microsystems’ J2EE RI is a transient service; it reloads bound objects from configuration files in the SDK home directory whenever it is restarted This naming service will not retain objects bound with the Context.bind() method across server restarts Rebinding Objects You can use the rebind() method to solve the problem of bind() failing if a name is already bound For example, ic.rebind(“sams/book”,”Teach Yourself J2EE in 21 Days”); The code unbinds any existing object bound to that name and binds the new object in its place Using rebind() is a good design technique when a programmer is sure the name will not be in use by another component The alternative is to explicitly unbind the old name first if it is in use as discussed in the next section on “Unbinding Objects.” Unbinding Objects You can remove an object from a namespace by using the Context.unbind() method A program uses this method when it is closing down and needs to remove its advertised service because a bound name is not automatically unbound when the program shuts down 05 0672323842 CH03 3/20/02 9:31 AM Page 93 Naming and Directory Services 93 Another common use for unbind() is to test if a name is already in use and unbind the old object before binding a new object The advantage of using unbind() in preference to rebind() is that you can verify that the object to be unbound is at least of the same type as the new object to be bound String JNDI = “sams/book”; try { Object o = ic.lookup(JNDI); if (o instanceof String) ic.unbind (JNDI); } catch (NameNotFoundException ex) {} ic.bind(JNDI,”Teach Yourself J2EE in 21 Days”); This example rebinds a string bound to the name sams/book, but will fail with a NameAlreadyBoundException if the name is bound to another class of object This is a better design approach than that of using the rebind() method Renaming Objects You can rename objects using Context.rename() by specifying the old name and then the new name as parameters The new name must specify a name in the same context as the old name An object must be bound to the old name, and the new name must not have a bound object; otherwise, a NamingException is thrown ic.rename(“sams/book”,”sams/teachyourself”); JNDI Name Lookup The most common use of JNDI is to look up objects that have been bound to a name To this, you require two items of information: • The JNDI name • The class of the bound object With this information in hand, object lookup is the simple matter of using the Context.lookup() method to find the object and then to cast that object to the required class Listing 3.2 shows a simple program to look up the name sams/book that was bound by the program in Listing 3.1 LISTING 3.2 Full Text of JNDILookup.java 1: import javax.naming.*; 2: public class JNDILookup 05 0672323842 CH03 3/20/02 9:31 AM Page 94 94 Day LISTING 3.2 3: { 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: } Continued private final static String JNDI = “sams/book”; public static void main(String[] args) { try { Context ic = new InitialContext(); String name = (String)ic.lookup(JNDI); System.out.println(JNDI+”=”+name); } catch (NamingException ex) { System.err.println(ex); System.exit(1); } catch (ClassCastException ex) { System.err.println(ex); System.exit(1); } } You can run the JNDIBind program in Listing 3.1 and then run this JNDILookup program to print out the value of the string bound against sams/book Note When casting an object that the lookup() method returns, that object’s class must be in the client program’s class path If this is not the case, the program throws an exception Changing Contexts The example name sams/book used in Listings 3.1 and 3.2 is an example of a Composite Name If you need to look up many names in the same context of a composite name (names of the form sams/ ), it is better to change to sub-context and look up the simple name within that context With this information in hand, the sub-context is a name entry just like any other name, and you look it up in just the same way The retrieved object is another Context object Listing 3.3 shows code that retrieves a name from a sub-context LISTING 3.3 Full Text of JNDILookupSAMS.java 1: import javax.naming.*; 2: public class JNDILookupSAMS 3: { 05 0672323842 CH03 3/20/02 9:31 AM Page 95 Naming and Directory Services LISTING 3.3 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: } 95 Continued public static void main(String[] args) { try { Context ic = new InitialContext(); Context ctx = (Context)ic.lookup(“sams”); String name = (String)ctx.lookup(“book”); System.out.println(name); } catch (NamingException ex) { System.err.println(ex); System.exit(1); } catch (ClassCastException ex) { System.err.println(ex); System.exit(1); } } Narrowing RMI-IIOP Objects There is only one additional twist to the lookup tale, and that is when dealing with RMI over IIOP objects The implementation of J2EE requires the use of RMI-IIOP to implement the remote interfaces to EJB components Consequently, when a lookup is for an EJB name (more on this on Day 4, “Introduction to EJBs”), you cannot cast the returned object to the required class; instead, you must narrow it RMI-IIOP uses a portable remote object to encapsulate information about the real remote object A portable remote object contains information about the real bound object in a portable format that can be interogated by the recipient to find the real remote object The process of obtaining the real object from the portable remote object is called narrowing You use the PortableRemoteObject.narrow() method in the javax.rmi package to narrow a protable remote object to obtain the actual remote object The narrow() method takes two parameters: • The object to narrow • A java.lang.Class object defining the real remote object’s class Listing 3.4 previews the discussion on Day about EJB objects, but also serves to illustrate the use of the narrow() method 05 0672323842 CH03 3/20/02 9:31 AM Page 96 96 Day LISTING 3.4 Narrowing an EJB Home Object 1: InitialContext ic = new InitialContext(); 2: Object lookup = ic.lookup(“java:comp/env/ejb/Agency”); 3: AgencyHome home = (AgencyHome) ➥PortableRemoteObject.narrow(lookup, AgencyHome.class); If your primary purpose for understanding JNDI is to enable the lookup and use of EJBs and other J2EE technologies (such as JDBC data sources and Message queues), you can skip the rest of this day’s material and return to it at a later date Contexts Contexts provide a hierarchical structure to JNDI names, and composite names group together related names The initial context provides a top-level view of the namespace and any sub-contexts reflect the hierarchical composite name structure Listing Contexts The namespace represents contexts as names, and you can look these up just like any other name You can obtain a listing of the names in a context by using Context.list() This method provides a list of name and class bindings as a javax.naming.NamingEnumeration, where each element in the enumeration is a javax.naming.NameClassPair object Listing 3.5 shows a simple program to list the names and classes for the example sams sub context LISTING 3.5 Full Text of JNDIListSAMS.java 1: import javax.naming.*; 2: public class JNDIListSAMS 3: { 4: public static void main(String[] args) 5: { 6: try { 7: Context ctx = new InitialContext(); 8: NamingEnumeration list = ctx.list(“sams”); 9: while (list.hasMore()) { 10: NameClassPair item = (NameClassPair)list.next(); 11: String cl = item.getClassName(); 12: String name = item.getName(); 13: System.out.println(cl+” - “+name); 14: } 15: } 16: catch (NamingException ex) { 17: System.out.println (ex); 18: System.exit(1); 05 0672323842 CH03 3/20/02 9:31 AM Page 97 Naming and Directory Services LISTING 3.5 19: 20: 21: } 97 Continued } } You must run the JNDIBind program from Listing 3.1 before running this program; otherwise, the “sams” sub context will not exist Running the program in Listing 3.5 will produce a single line of output: java.lang.String – book The parameter to the list() method defines the name of the context to list If this is the empty string, the method lists the current context If the initial context of the J2EE RI namespace is listed, you must have the J2EE RI classes in your search path; otherwise, you will get an org.omg.CORBA.BAD_PARAM exception caused by another CORBA exception: org.omg.CORBA.MARSHAL: Unable to read value from underlying bridge : ➥ Serializable readObject method failed internally ➥ minor code: 1398079699 completed: Maybe Don’t believe the completed: complete Maybe tagged on to the end of the error message It didn’t The easiest solution to this problem is to run the setenv script in the bin directory of J2EE RI This script creates a variable CPATH that you can use as the CLASSPATH for running J2EE RI client programs Under Windows use %J2EE_HOME%\bin\setenv java –classpath ;%CPATH% JNDIList Under Linux and Unix use $J2EE_HOME/bin/setenv java –classpath :$CPATH JNDIList The CD-ROM accompanying this book includes the JNDIList program, which is the same as the program in Listing 3.5 but without the parameter to the list() method The list() method returns the name and the bound object’s classname, but not the object itself It is a lightweight interface designed for browsing the namespace A second method, called Context.listBindings(), retrieves the object itself The method returns a NamingEnumeration, where each element is of type listBindings() 07 0672323842 CH05 3/20/02 186 9:36 AM Page 186 Day The (fragment of the) underlying deployment descriptor for the Agency bean that is represented in Figure 5.8 is as follows: AgencyBean AgencyBean agency.AgencyHome agency.Agency agency.AgencyBean Stateless … lines omitted … This should tie in with the code that was presented earlier today Thus far, you have only seen the EJB standard deployment descriptor, but there is also the vendor-specific deployment descriptor for J2EE The structure of this auxiliary deployment descriptor is defined by %J2EE_HOME%\lib\dtds\sun-j2ee-ri_1_3.dtd, but it is not so necessary to learn its structure in detail because it all vendor specific, and deploytool allows it to be configured through its GUI The auxiliary information in this descriptor maps the logical names to the physical runtime environment For the Session bean itself, a mapping is required from its logical ejb-name to its JNDI name The following fragment from the auxiliary deployment descriptor shows this: // lines omitted // code omitted AgencyBean ejb/Agency Figure 5.9 shows how deploytool portrays this information Figure 5.9 also shows the JNDI mappings for references; you will learn about these shortly The remaining optional items of the EJB deployment descriptor (env-entry, resourceand so on) also correspond to the different tabs of the deploytool window shown in Figure 5.8 These each indicate different types of dependencies that the bean may have with its runtime environment The following sections discuss each in turn ref, 07 0672323842 CH05 3/20/02 9:36 AM Page 187 Session EJBs 187 FIGURE 5.9 Behind the scenes, deploytool stores the JNDI mappings to an auxiliary vendorspecific deployment descriptor Environment Entries Environment entries allow general configuration information—as might be found in a ini file or in the Windows Registry—to be made available to the bean Such entries are represented by the env-entry element in the deployment description and—not surprisingly—are configured on the Env Entries tab within deploytool The Agency bean uses an environment entry to look up its name The relevant code is in the ejbCreate() method: InitialContext ic = new InitialContext(); // code omitted name = (String)ic.lookup(“java:comp/env/AgencyName”); The EJB specification requires that the EJB container makes the environment entries available under the well-defined context of java:comp/env Therefore, this is needed in the JNDI lookup However, this prefix is not required in the deployment descriptor itself The DTD defines the env-entry element as follows: 07 0672323842 CH05 3/20/02 9:36 AM Page 188 188 Day The type of the environment entry (String, Integer, and so on) is indicated through the env-entry-type element The actual value (env-entry-value) is optional, meaning that the bean provider/application assembler does not need to define it If the actual value isn’t specified by these roles, the deployer will need to define the value Figure 5.10 shows this information being configured within deploytool FIGURE 5.10 Environment entries can be managed graphically by deploytool In the underlying deployment descriptor for the Agency bean, this corresponds to AgencyName java.lang.String J2EE in 21 Days Job Agency To re-emphasize, note that the entry name is AgencyName, not java:comp/env/AgencyName EJB References Declaring EJB references for an EJB indicates that the bean being deployed depends on other EJBs Generally speaking, when there are inter-bean dependencies, both the dependent and dependee will have been written by the same bean provider However, there will be cases when this isn’t so The issue then arises that the dependent EJB may not know the deployed name of the EJB upon which it depends 07 0672323842 CH05 3/20/02 9:36 AM Page 189 Session EJBs 189 For example, a vendor might provide some sort of business-oriented bean (perhaps an Invoice EJB) that can optionally perform logging through a Logging EJB The same vendor might well provide an implementation of a Logging EJB, but would also allow for application assemblers to use some other EJB that implements the appropriate home and remote interfaces In this way, the application assembler could ensure that all logging from beans within its application was consistent Now the Invoice EJB will have a reference to a Logging EJB in its JNDI lookup This might be something like the following: InitialContext ic = new InitialContext(); Object lookup = ic.lookup(“ejb/Logger”); LoggerHome home = (AgencyHome)PortableRemoteObject.narrow(lookup, LoggerHome.class); Logger logger = home.create(); The “ejb/Logger” string is hard-coded into the Invoice EJB source code and cannot be changed by the application assembler or by the deployer This is what is sometimes referred to as the coded name or coded entry However, the deployment descriptor allows this logical name to be associated with an actual physical name through the ejb-ref elements The following is the ejb-ref element, as defined by the DTD: The ejb-ref-name element is the coded name—ejb/Logger in the previous example The names of the home and remote interfaces must be specified Finally, the ejb-link element specifies the actual ejb-name of the EJB that implements these interfaces This is a way for the application assembler to constrain the reference in the dependent EJB to a particular EJB within the enterprise application In the Day version of the case study, there are no EJB references between EJBs (you will see some such references between EJBs tomorrow), but there are EJB references from the clients and the EJBs These are shown in Figure 5.11 When EJB references are defined, they must also be mapped to the physical environment Figure 5.9 showed the mapping of EJBs and of references to JNDI names These are shown in the bottom half of the window of Figure 5.9 As an experiment, try temporarily creating a EJB (remote) reference between the beans themselves, and then return to the JNDI tab in deploytool (as shown in Figure 5.9) Here, the deployer should indicate the JNDI name for the EJB that implements the remote interfaces As required by the EJB specification, deploytool validates that the JNDI name that is mapped by the deployer to the EJB reference is compatible with any ejb-link that may have been specified by the application assembler When you have finished experimenting, you should delete these EJB references 07 0672323842 CH05 3/20/02 9:36 AM Page 190 190 Day FIGURE 5.11 There are EJB references from the clients to the EJBs Resource References Yesterday, you learned that the EJB container allows DataSources to be obtained via JNDI Within the deployment descriptor, a resource reference is used to define that dependency of the EJB The term resource reference is used instead of database reference because an EJB can depend on data resources other than databases It could also depend on an e-mail session (Day 11, “JavaMail”), on a URL, on a message topic or queue factory (Day 10, “Message-Driven Beans”), or on a general resource as defined by the Connector architecture (Day 19, “Integrating with External Resources”) The Agency bean has a dependency on a DataSource reference that it refers to as This can be seen in the ejbCreate() method of the AgencyBean code As with the environment entries, note that resource reference has been bound by the EJB container under the context java:comp/env: jdbc/Agency InitialContext ic = new InitialContext(); dataSource = (DataSource)ic.lookup(“java:comp/env/jdbc/Agency”); Resource references are defined in the DTD as follows: Going through these in turn • The res-ref-name element is the coded name of the referenced resource • The res-type element is the fully-qualified interface or class name of the resource 07 0672323842 CH05 3/20/02 9:36 AM Page 191 Session EJBs 191 • The res-auth element indicates whether the container will provide the authentication information (username and password) or the application itself In other words, it indicates which overloaded version of DataSource.getConnection() will be called—the one where username and password are supplied (Application authentication) or where they are not (Container authentication) • The res-sharing-scope element indicates whether this resource can be shared among beans (the default) or whether a separate resource will be set up for this bean’s exclusive use So, in the deployment descriptor for the Agency bean, you will see the following: jdbc/Agency javax.sql.DataSource Container Shareable This information is also shown in deploytool, as shown in Figure 5.12 FIGURE 5.12 Resource references can be managed graphically by deploytool Again, resource references must be mapped to physical resources You defined the physical resources for the case study on Day 2, when you edited the resource.properties file within %J2EE_HOME%\config The entries that are relevant to this discussion are as follows: jdbcDataSource.5.name=jdbc/Agency jdbcDataSource.5.url=jdbc:cloudscape:rmi:Agency 07 0672323842 CH05 192 3/20/02 9:36 AM Page 192 Day This instructs J2EE RI to create a DataSource using a URL of jdbc:cloudscape:rmi:Agency and bind it into JNDI with a name of jdbc/Agency From the deployment descriptor, you can see that the declared resource reference in the AgencyBean Session bean code is jdbc/Agency and is also mapped to a JNDI name of jdbc/Agency The fact that the logical and physical names are the same string strings can be a source of confusion; the point is that both are needed Moreover, the logical resource reference could have been anything at all, so long as it is the same string that appears in the code, in the standard ejb-jar.xml deployment descriptor and the vendorspecific auxiliary deployment descriptor The mapping between these two names is performed on the JNDI tab of deploytool, as shown in Figure 5.9 You should be able to see there that, for example, the resource reference in the Agency bean called jdbc/Agency maps onto the JNDI name of jdbc/Agency The auxiliary deployment descriptor has the following entries: // lines omitted // lines omitted AgencyBean // lines omitted jdbc/Agency jdbc/Agency // remaining lines omitted Resource Environment References Resource environment reference entries allow access to so-called “administered objects.” In J2EE RI, this means JMS queues or JMS topics In future versions of J2EE, other administered objects may well be specified Resource references and resource environment references sound very similar, and, indeed, they are related However, a resource reference is access to some sort of factory object, used to manufacture (variously) database connections, URLs, JMS sessions, and so on On the other hand, an administered object must be defined up-front by an administrator and is persistent The EJB specification doesn’t define RDBMS tables as administered objects, but it might well have If it had, a resource reference would be to a database connection, and a resource environment reference might be to a table on the database to which you have connected The DTD defines resource environment references as follows: 07 0672323842 CH05 3/20/02 9:36 AM Page 193 Session EJBs 193 The resource-env-ref-name is the name of the reference in the code, less the java:comp/env prefix, and the resource-env-ref-type is either javax.jms.Queue or javax.jms.Topic The Agency session bean does not have any dependencies on resource environment references, so there is no screen shot of the deploytool However, you will be using resource environment references on Day 10 when you work with the Java Messaging Service and Message-driven beans But (again) as an experiment, try adding an resource environment reference on the Resource Env Refs tab On the JNDI tab (as shown in Figure 5.9), you should be able to indicate the JNDI name for the administered object When you have finished experimenting, delete the resource environment reference Deploying the Enterprise Application The enterprise application can be deployed from deploytool by using the Tools, Deploy menu option This causes the bean’s code components to be compiled and the proxy and home interfaces to be generated and then packaged up into an ejb-jar using information from the underlying deployment descriptor and auxiliary deployment descriptor Finally, the package is deployed across the network to the J2EE RI Once deployed, you can run your application To this, use day05\agency\run\runAll Stateful Session Bean Lifecycle Now that you have learned how to specify, implement, and deploy stateless Session beans, it is time to look at stateful Session beans As you shall see, there are many similarities (especially with regard to the deployment), but the lifecycle is different and warrants close attention The client’s view of the lifecycle of a stateful Session bean is identical to that of a stateless Session bean and, in truth, more closely matches the actual lifecycle as managed by the container Figure 5.13 shows this lifecycle From the client’s perspective, the bean is simply instantiated when the client calls create() on the bean’s home interface, and it’s removed when the bean calls remove() on the bean itself The bean’s viewpoint of its lifecycle is as shown in Figure 5.14 The principle difference between stateful and stateless Session beans is the duration of the time that the bean is bound to the client With a stateless Session bean, the duration was only as long as the time needed to execute the business method With a stateful Session bean, however, the bean stays bound until the client releases it In this way, there is a quite close correspondence between the client’s and the bean’s perspectives 07 0672323842 CH05 3/20/02 9:36 AM Page 194 194 Day FIGURE 5.13 The client’s view of the lifecycle of stateful beans is identical to that of stateless Session beans deploy/setSessionContext Context Set create/ejbCreate Ready remove/ejbRemove business method FIGURE 5.14 A stateful Session bean’s view of its lifecycle includes passivation and timeouts pool too small/setSessionContext Pooled [timeout] [surplus] create/ejbCreate remove/ejbRemove Bound to client Passivated [too many active] /ejbPassivate Ready [business method or remove invoked]/ejbActivate business method When the client calls create() on the home interface, a Session bean instance is obtained Most EJB containers maintain a pool of unbound Session bean instances, so any unused instance will be chosen This is then bound to the client The client can call business methods on the bean, and because the bean will remain bound, these can legitimately save to instance variables the state pertaining to the client When the client is done with the bean, it calls remove() which releases the bean back to the pool of unbound instances 07 0672323842 CH05 3/20/02 9:36 AM Page 195 Session EJBs 195 The EJB specification uses the analogy of a shopping cart, and it is easy to see that this is a natural fit In such a case, the client would obtain a shopping cart bean using create(), call methods such as addItem(), removeItem(), and checkout(), and then release the bean using remove() If there are many concurrent clients, the amount of memory to manage all of the clients’ state can become significant Moreover, there is nothing to prevent a client from acquiring a bean, and then not using it—an abandoned shopping cart in the aisles, if you like The EJB specification addresses these issues by defining the notions of passivation and of timeouts Passivation allows the EJB container to release the memory used by a bean, first writing out its internal state to some secondary storage This is transparent to the bean’s client; when the bean is next used, the EJB container first activates the bean How the EJB container actually implements passivation is not specified, but the specification does require that the Session bean is serializable, so many implementations will take this route and serialize the bean to a file If a bean is not used for longer than its timeout, it can be timed out and its memory released Note Perhaps surprisingly, the EJB specification does not define how the timeout for a Session bean reference is specified Section 7.6.3 of the specification indicates clearly that its definition is specific to the EJB container Usually, the information will be captured in a vendor deployment tool and stored in an auxiliary deployment descriptor Equally, the EJB specification does not indicate how the EJB container should decide when to passivate beans (though it does suggest that a “least recently used” strategy can be employed, see section 7.6) Figure 5.14 showed the bean’s viewpoint of its lifecycle, but the actual lifecycle as managed by the EJB container is likely to be different again After all, the whole point of passivation is to reduce the number of bean instances; if the bean was merely “put to sleep,” that wouldn’t have been accomplished, so Figure 5.15 shows the actual lifecycle used by many EJB container implementations When the EJB container passivates a bean, its state is written out to secondary storage The bean instance is then destroyed If a client whose bean instance has been passivated invokes a method, the EJB container first re-instantiates the bean by deserializing it from secondary storage The business method is then invoked 07 0672323842 CH05 3/20/02 9:36 AM Page 196 196 Day FIGURE 5.15 [pool too small] /setSessionContext [surplus] The actual lifecycle of stateful Session beans as managed by the EJB container is somewhat more complex Pooled create/ejbCreate [business method invoked && ! timed out] [state written] Passivating do/'write out state to secondary storage' [timeout] Activating do/'restore state from secondary storage' remove/ejbRemove [too many active] /ejbPassivate Bound to client [state restored]/ejbActivate business method Specifying a Stateful Session Bean Specifying a stateful Session bean is similar to specifying a stateless Session bean The remote interface defines access to the bean by remote clients, and every method of the remote interface must throw an RemoteException The primary difference (from a specification viewpoint) is that there can be multiple create() methods in the home interface You will recall that a stateless Session bean allows only for a single create() method in the home interface, and this corresponds to the ejbCreate() method of the bean itself For a stateful Session bean, the create() method can be overloaded, so that the stateful bean can be given some initial state From the client’s viewpoint, this is somewhat analogous to invoking a constructor on the bean For example, the Advertise bean in the case study is stateful It represents an advertiser of job positions The home interface for this bean is as follows: package agency; import java.rmi.*; import javax.ejb.*; public interface AdvertiseHome extends EJBHome 07 0672323842 CH05 3/20/02 9:36 AM Page 197 Session EJBs 197 { Advertise create (String login) throws RemoteException, CreateException; } Obviously, the create() method has a corresponding ejbCreate() method in the AdvertiseBean class itself This ejbCreate() method must instantiate the bean with any appropriate state, as shown in listing 5.4 LISTING 5.4 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: AdvertiseBean.ejbCreate() Method package agency; // imports omitted public class AdvertiseBean implements SessionBean { private String login; private String name; private String email; private String[] address; private Collection jobs = new TreeSet(); public void ejbCreate (String login) throws CreateException { this.login = login; // database detail not shown name = …; email = …; address[0] = …; address[1] = …; jobs = new TreeSet(); while(…) { jobs.add( … ); } } } Alternatively, the EJB specification allows for methods named createXXX() to be defined in the home interface, with corresponding methods ejbCreateXXX() These methods can take parameters if required Whether you choose to use this facility or just use regular overloaded versions of create()/ejbCreate() is up to you Other than this one change of being able to pass in state in the create() method, there really is little difference in the specification of a stateful bean compared to that of a stateless Session bean The remote interface of the stateful Advertise Session bean is shown in Listing 5.5 07 0672323842 CH05 3/20/02 9:36 AM Page 198 198 Day LISTING 5.5 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: The Remote Interface for the Stateful Advertise Bean package agency; import java.rmi.*; import javax.ejb.*; public interface Advertise extends EJBObject { void updateDetails (String name, String email, String[] Address) ➥throws RemoteException; String getName() throws RemoteException; String getEmail() throws RemoteException; String[] getAddress() throws RemoteException; String[] getJobs() throws RemoteException; 15: 16: } void createJob (String ref) throws RemoteException, ➥DuplicateException, CreateException; void deleteJob (String ref) throws RemoteException, NotFoundException; The ejbCreate() method from the home interface supplies the information to the bean so that it can retrieve the data about the advertiser from the database The remote interface allows this information to be accessed and be updated Implementing a Stateful Session Bean When implementing a stateful Session bean, there are a number of issues that you must keep in mind They are discussed in this section Passivation Unlike stateless Session beans, stateful Session beans are at liberty to store clientspecific state information in instance variables However, because your bean may be passivated, any instance variables you define must be either primitives, references to serializable objects, or—failing that—be transient Of course, any transient variables will be reset to null if the bean is passivated and then re-activated, so your implementation will need to deal with this Classes that are not serializable often depend in some way on the environment, such as an open java.net.Socket, so that your bean can act as a network client to some service The general approach to dealing with this is to store other data that is serializable in instance variables during the ejbPassivate() method Then, the non-serializable reference can be re-instantiated in the ejbActivate() method using this other data 07 0672323842 CH05 3/20/02 9:36 AM Page 199 Session EJBs 199 For example, in the case of a Socket, your bean could hold a String and an int representing the hostname and the port number of the socket The instance variables and ejbActivate() method could would be something like the following: import java.net.*; // code omitted private transient Socket clientSocket; private String socketHost; private int socketPort; public void ejbActivate() { this.clientSocket = new Socket(socketHost, socketPort); } Although your Session bean must itself be serializable, it is not necessary to explicitly implement the java.io.Serializable interface This is because the javax.ejb.SessionBean interface extends from Serializable (by way of its superinterface, javax.ejb.EnterpriseBean) Tip Given that passivation causes quite a few implementation headaches, some commentators have asked why the EJB specification goes to such lengths to define a passivation mechanism After all, operating systems are very good at paging memory to disk, and this is all that the “secondary storage” is really accomplishing These are good questions, with no ready answers But if you would rather have the operating system the work and keep your beans simple, just configure your beans with a very high passivation threshold You may be wondering how to handle passivation in a stateful Session bean that has a reference to a home or remote interface of another EJB, a javax.sql.DataSource or some other resource connection factory After all, none of these references may be serializable Luckily, though, the EJB specification puts the onus for worrying about these references on the EJB container (section 7.4.1) In other words, your bean is at liberty to hold references to any of these types of objects, and you don’t need to care whether they are serializable Of course, most EJB container vendors are likely to comply with this part of the specification by making sure that these objects are serializable Timeouts Another difference between stateful and stateless Session beans is that stateful Session beans may timeout If a bean is timed out, the client’s reference is no longer valid, and any further invocations on that reference will result in a java.rmi.NoSuchObjectException for remote clients 07 0672323842 CH05 3/20/02 9:37 AM Page 200 200 Day You should note from Figure 5.14 that if a bean times out, its ejbRemove() method will not be called This means that you shouldn’t adopt a convention of acquiring external resources in ejbCreate() with a view to releasing them in ejbRemove() Even releasing the resources in ejbPassivate() is not enough, because a bean can be timed out even from its ready state Caution Don’t confuse passivation and timeout An EJB container might implement passivation using an LRU strategy and allow a bean timeout to be specified in seconds If the EJB container is not busy, a bean will not be passivated according to the LRU strategy, but it may hit its timeout nevertheless Chaining State It is generally a bad idea to have more than one stateful Session bean involved in any conversation, because no matter which way you cut it, there’s always the chance that one of them will time out, preventing the other from completing To see this, suppose the client calls stateful Session A, which, in turn, uses the services of stateful Session B There are two cases—the timeout of A is larger than that of B, or the timeout is less than that of B Taking each in turn • Suppose that the timeout of Session bean A is 30 minutes and that of Session bean B is 20 minutes The client makes a call on A at time t1=0, which then calls B If the client calls A at time t2 = t1 + 25 minutes, A’s call to B will fail because B will have timed out • Suppose now that the timeout of Session bean A is 20 minutes, and that of Session bean B is 30 minutes The client makes a call on A at time t1=0, which then calls B The client then calls A again at time t2 = t1 + 19 minutes, although for this call, A does not need to call B to service the request If the client calls A again at time t3 = t2 + 19 minutes = t1 + 38 minutes, A’s call to B will fail because B was last invoked more than 30 minutes ago and will have timed out A similar problem can occur with session beans and servlets; this is discussed on Day 12 Configuring and Deploying a Stateful Session Bean Configuring and deploying a stateful Session bean is just the same as deploying a stateless Session bean The only difference is that the session-type element in the deployment descriptor will be set to Stateful ... ctx.getAttributes(args[0]); 05 06 723 238 42 CH03 3 /20 / 02 9:31 AM Page 109 Naming and Directory Services LISTING 3.11 13: 14: 15: 16: 17: 18: 19: 20 : 21 : 22 : 23 : 24 : 25 : 26 : 27 : 28 : } 109 Continued NamingEnumeration... tree-walking program that is a useful diagnostic tool for examining JNDI namespaces LISTING 3.6 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20 : 21 : 22 : 23 : 24 : 25 : 26 : 27 : 28 : 29 :... System.out.println(JNDI+”=”+name); } catch (NamingException ex) { System.err.println(ex); 05 06 723 238 42 CH03 3 /20 / 02 9:31 AM Page 120 120 Day LISTING 3 .21 15: 16: 17: 18: 19: 20 : 21 : 22 : } Continued System.exit(1); }