![]() Writing the example applicationApplications interact with the work-area facility by using the UserWorkArea interface. This interface, shown in Figure 5, defines all the methods used to create, manipulate, and terminate work areas. Figure 5. Code example: The UserWorkArea interface package com.ibm.websphere.workarea; public interface UserWorkArea { void begin(String name); void complete() throws NoWorkArea, NotOriginator; String getName(); String[] retrieveAllKeys(); void set(String key, java.io.Serializable value) throws NoWorkArea, NotOriginator, PropertyReadOnly; void set(String key, java.io.Serializable value, PropertyModeType mode) throws NoWorkArea, NotOriginator, PropertyReadOnly; java.io.Serializable get(String key); PropertyModeType getMode(String key); void remove(String key) throws NoWorkArea, NotOriginator, PropertyFixed; }
The work-area facility defines the following exceptions for use with the UserWorkArea interface:
Creating a work areaThe client side of the application described in The example application creates a work area and inserts the site-identifier and priority properties into the work area. This requires four steps on the part of the client:
Binding to the work-area facilityThe work-area facility provides a JNDI binding to an implementation of the UserWorkArea interface under the name java:comp/websphere/UserWorkArea. Applications that need to access the service can perform a lookup on that JNDI name, as shown in Figure 6. Figure 6. Code example: Binding to the work-area facility import com.ibm.websphere.workarea.*; import javax.naming.*; public class SimpleSampleServlet { ... InitialContext jndi = null; UserWorkArea userWorkArea = null; try { jndi = new InitialContext(); userWorkArea = (UserWorkArea)jndi.lookup( "java:comp/websphere/UserWorkArea"); } catch (NamingException e) { ... } } Beginning a work areaAfter a client has a reference to the UserWorkArea interface, it can use the begin method to create a new work area and associate it with the calling thread. The begin method takes a string as an argument; the string is used to name the work area. The argument must not be null, which causes the java.lang.NullPointer exception to be raised. In Figure 7, the application begins a new work area with the name SimpleSampleServlet. Figure 7. Code example: Creating a new work area public class SimpleSampleServlet { ... try { ... userWorkArea = (UserWorkArea)jndi.lookup( "java:comp/websphere/UserWorkArea"); } ... userWorkArea.begin("SimpleSampleServlet"); ... } Each work area must also be terminated within the process that created it; each call to the begin method must have a corresponding call to the complete method. See Completing a work area for more information. The begin method is also used to create nested work areas; if a work area is associated with a thread when the begin method is called, the method creates a new work area nested within the existing work area. The work-area facility makes no use of the names associated with work areas; programmers can name work areas in any way they choose. Names are not required to be unique, but the usefulness of the names for debugging is enhanced if the names are distinct and meaningful within the application. Applications can use the getName method to return the name associated with a work area by the begin method. Setting properties in a work areaAn application with a current work area can insert properties into the work area and retrieve the properties from the work area. The UserWorkArea interface provides two set methods for setting properties and a get method for retrieving properties. The two-argument set method inserts the property with the property mode of normal. The three-argument set method takes a property mode as the third argument. See Setting property modes for more information on specifying property modes. Both set methods take the key and the value as arguments. The key is a String; the value is an object of the type java.io.Serializable. None of the arguments can be null, which causes the java.lang.NullPointer exception to be raised. The property classes used in the example applicationThe example application uses objects of two classes, the SimpleSampleCompany class and the SimpleSampleProperty class, as values for properties. The SimpleSampleCompany class is used for the site identifier, and the SimpleSamplePriority class is used for the priority. These classes are shown in Figure 4. Figure 8. Code example: Setting properties in a work area public class SimpleSampleServlet { ... userWorkArea.begin("SimpleSampleServlet"); try { // Set the site-identifier (default is Main). userWorkArea.set("company", SimpleSampleCompany.Main, PropertyModeType.read_only); // Set the priority. userWorkArea.set("priority", SimpleSamplePriority.Silver); } catch (PropertyReadOnly e) { // The company was previously set with the read-only or // fixed read-only mode. ... } catch (NotOriginator e) { // The work area originated in another process, // so it can't be modified here. ... } catch (NoWorkArea e) { // There is no work area begun on this thread. ... } // Do application work. ... } The get method takes the key as an argument and returns a Java Serializable object as the value associated with the key. For example, to retrieve the value of the company key from the work area, Figure 8 uses the get method on the work area to retrieve the value. Setting property modesThe two-argument set method on the UserWorkArea interface takes a key and a value as arguments and inserts the property with the default property mode of normal. To set a property with a different mode, applications must use the three-argument set method, which takes a property mode as the third argument. The values used to request the property modes follow:
(See Figure 1 for more information.) Completing a work areaAfter an application has finished using the work area, it can terminate the work area by calling the complete method on the UserWorkArea interface. This terminates the association with the calling thread and destroys the work area. If the complete method is called on a nested work area, the nested work area is terminated and the parent work area becomes the current work area. If there is no work area associated with the calling thread, the NoWorkArea exception is thrown. Every work area must be terminated, and work areas can be terminated only by the originating process. For example, if a server attempts to call the complete method on a work area that originated in a client, the work-area NotOriginator exception is thrown. Figure 9 shows the termination of the work area created in the client application. Figure 9. Code example: Terminating the work area public class SimpleSampleServlet { ... userWorkArea.begin("SimpleSampleServlet"); userWorkArea.set("company", SimpleSampleCompany.Main, PropertyModeType.read_only); userWorkArea.set("priority", SimpleSamplePriority.Silver); ... // Do application work. ... // Terminate the work area. try { userWorkArea.complete(); } catch (NoWorkArea e) { // There is no work area associated with this thread. ... } catch (NotOriginator e) { // The work area was imported into this process. ... } ... } Using a work areaThe server side of the application described in The example application accepts remote invocations from clients. With each remote call, the server also gets a work area from client if the client has created one. The work area is propagated transparently. None of the remote methods includes the work area on its argument list. In the example application, the server objects utilize the work-area interface for demonstration purposes only. For example, the SimpleSampleBean intentionally attempts to write directly to an imported work area, which triggers the NotOriginator exception. Likewise, the bean intentionally attempts to mask the read-only SimpleSampleCompany, which triggers the PropertyReadOnly exception. The SimpleSampleBean also nests a WorkArea and successfully overrides the priority property before invoking the SimpleSampleBackendBean. A real business application would extract the work area properties and use them to guide the local work. The SimpleSampleBean mimics this by writing a message that function is denied when a request emanates from a sales environment. The server must bind to the work-area facility before it can manipulate information in work areas. Binding to the work-area facilityThe work-area facility provides a JNDI binding to an implementation of the UserWorkArea interface under the name java:comp/websphere/UserWorkArea. Applications that need to access the service can perform a lookup on that JNDI name, as shown in Binding to the work-area facility. Extracting the name of the active work areaApplications use the getName method on the UserWorkArea interface to retrieve the name of the current work area. This is the recommended method for determining whether the thread is associated with a work area; if the thread is not associated with a work area, the getName method will return null. Figure 10 uses the getName method on the work area to retrieve the name of the active work area; in this example, the name of the work area corresponds to the name of the class in which the work area was begun. Figure 10. Code example: Retrieving the name from a work area public class SimpleSampleBeanImpl implements SessionBean { ... public String [] test() { // Get the work-area reference from JNDI. ... // Retrieve the name of the work area. In this example, // the name is used to identify the class in which the // work area was begun. String invoker = userWorkArea.getName(); ... } } Modifying information in a work areaWork areas are inherently associated with the process that creates them. In the sample application, the client begins a work area and sets into it the site-identifier and a priority properties into it. This work area is propagated to the server when the client makes a remote invocation. In Figure 11, the server-side sample bean attempts to write directly to the imported work area; this action is not permitted, and the NotOriginator exception is thrown. The sample bean must begin its own work area in order to override any imported properties. Figure 11. Code example: Attempting to modify an imported work area public class SimpleSampleBeanImpl implements SessionBean { public String [] test() { ... String invoker = userWorkArea.getName(); try { userWorkArea.set("key", "value"); } catch (NotOriginator e) { } ... } } Nesting work areasApplications nest work areas in order to temporarily override properties imported from a client process. The nesting mechanism is automatic; invoking begin on the UserWorkArea interface from within the scope of an existing work area creates a nested work area that inherits the properties from the enclosing work area. Properties set into the nested work area are strictly associated with the process in which the work area was begun; the nested work area must be completed within the process that created them. If a work area is not completed by the creating process, the work-area facility terminates the work area when the process exits. After a nested work area is completed, the original view of the enclosing work area is restored. However, the view of the complete set of work areas associated with a thread cannot be decomposed by downstream processes. Figure 12 demonstrates beginning a nested work area, using the name of the creating class to identify the nested work area. Figure 12. Creating a nested work area public class SimpleSampleBeanImpl implements SessionBean { public String [] test() { ... String invoker = userWorkArea.getName(); try { userWorkArea.set("key", "value"); } catch (NotOriginator e) { } // Begin a nested work area. By using the name of the creating // class as the name of the work area, we can avoid having // to explicitly set the name of the creating class in // the work area. userWorkArea.begin("SimpleSampleBean"); ... } } Applications set properties into a work area using property modes in ensure that a particular property is fixed (not removable) or read-only (not overrideable) within the scope of the given work area. In the sample application, the client sets the site-identifier property as read-only; that guarantees that the request will always be associated with the client's company identity. A server cannot override that value in a nested work area. In Figure 13, the SimpleSampleBean attempts to change the value of the site-identifier property in the nested work area it created. Figure 13. Code example: Attempting to modify a non-overridable property public class SimpleSampleBeanImpl implements SessionBean { public String [] test() { ... String invoker = userWorkArea.getName(); try { userWorkArea.set("key", "value"); } catch (NotOriginator e) { } // Begin a nested work area. userWorkArea.begin("SimpleSampleBean"); try { userWorkArea.set("company", SimpleSampleCompany.London_Development); } catch (NotOriginator e) { } ... } } Extracting properties from a work areaProperties can be retrieved from a work area by using the get method. The method is intentionally light-weight; there are no declared exceptions to handle. If there is no active work area or if there is no such property set in the current work area, the get method returns null. Figure 14 shows the retrieval of the site-identifier and priority properties by the SimpleSampleBean. Recall that one property was set into an outer work area by the client, and the other property was set into the nested work area by the server-side bean; the nesting is transparent to the retrieval of the properties.
Figure 14. Code example: Retrieving properties from a work area public class SimpleSampleBeanImpl implements SessionBean { public String [] test() { ... // Begin a nested work area. userWorkArea.begin("SimpleSampleBean"); try { userWorkArea.set("company", SimpleSampleCompany.London_Development); } catch (NotOriginator e) { } SimpleSampleCompany company = (SimpleSampleCompany) userWorkArea.get("company"); SimpleSamplePriority priority = (SimpleSamplePriority) userWorkArea.get("priority"); ... } } Completing a work areaAll work areas must be completed within the process in which they were created; every ivocation of the begin method must be matched by an invocation of the complete method. Work areas created in a server process are never propagated back to an invoking client process. Figure 15 shows the sample application completing the nested work area it created earlier in the remote invocation. The UserWorkArea reference points to the outer work area after the complete method concludes. Note that the work area service claims full local-remote transparency. Even if two beans happen to be deployed into the same server and therefore the same JVM and process, a work area begun on an invocation from another will be completed and the bean in which the request origininated will always be in the same state after any remote call as it was before. Figure 15. Code example: Retrieving properties from a work area public class SimpleSampleBeanImpl implements SessionBean { public String [] test() { ... // Begin a nested work area. userWorkArea.begin("SimpleSampleBean"); try { userWorkArea.set("company", SimpleSampleCompany.London_Development); } catch (NotOriginator e) { } SimpleSampleCompany company = (SimpleSampleCompany) userWorkArea.get("company"); SimpleSamplePriority priority = (SimpleSamplePriority) userWorkArea.get("priority"); // Complete all nested work areas before returning. try { userWorkArea.complete(); } catch (NoWorkArea e) { } catch (NotOriginator e) { } } } Other methods in the UserWorkArea interfaceThe simple example illustrated in Creating a work area and Using a work area does not make use of all the methods in the UserWorkArea interface. This section describes the additional methods: Obtaining a list of all keysThe UserWorkArea interface provides the retrieveAllKeys method for retrieving a list of all the keys visible from a work area. This method takes no arguments and returns an array of strings. This method returns null if there is no work area associated with the thread. If there is an associated work area containing no properties, the method returns an array of size 0. Querying the mode of a propertyThe UserWorkArea interface provides the getMode method for determining the mode of a specific property. This method takes the property's key as an argument and returns the mode as a PropertyModeType object. (See Setting property modes for more information on names of mode types.) If the specified key does not exist in the work area, the method returns PropertyModeType.normal, indicating that the property can be set and removed without error. Deleting a propertyThe UserWorkArea interface provides the remove method for deleting a property from the current scope of a work area. If the property was initially set in the current scope, then removing it deletes the property. If the property was initially set in an enclosing work area, then removing it deletes the property until the current scope is completed. When the current work area is completed, the deleted property is restored. The remove method takes the property's key as an argument. Only properties with the modes normal and read-only can be removed. Attempting to remove a fixed property causes the PropertyFixed exception to be thrown. Attempting to remove properties in work areas that originated in other processes causes the NotOriginator exception to be thrown. |