Home | 2.7.2 Java objects and the command cache | 2.7.4 DynaCache full page caching
2.7.3 Command interface
Commands are Java objects that follow a usage pattern that consists of three operational methods. They are:
Set Initialize the input properties of the command object. Execute Execute the specific business logic for which the command was written. Get Retrieve the output properties that are set by the execution. Each command can be in one of three states based on which methods have been executed:
New The command has been created but the input properties have not been set. Initialized The input properties have been set. Executed The execute method has been called and the output properties have been set and can be retrieved. Executed command objects can be stored in the cache so that subsequent instances of that command object's execute method can be served by copying output properties of the cached command to the current command for retrieval by its get methods.
DynaCache supports caching of command objects for later reuse by servlets, JSPs, EJBs, or other business logic programming. To identify these cached command objects, a unique cache ID is generated based on the fields and methods that represent or are used to retrieve input properties of the command.
To cache a command in an application, a cache ID creation rule must be written in the cache policy file, and the command must be changed to extend the "CacheableCommandImpl" class instead of implementing the standard "TargetableCommand" interface.
CacheableCommandImpl is an abstract class that implements the methods necessary for the command to interact with the caching framework. Because the CacheableCommand interface extends the TargetableCommand interface, the command in the application must continue to implement the methods needed for the TargetableCommand interface.
The standard TargetableCommand interface provides only the client-side interface for generic commands and declares three basic methods:
IsReadyToCallExecute() This method is called on the client side before the command passes to the server for execution. Execute() This method passes the command to the target and returns any data. Reset() This method resets any output properties to the values they had before the execute method was called so that the object can be reused. When a command is called to execute, the request is intercepted by the cache, and a cache ID is generated based on the values of the input properties specified in its cache policy. If a cache entry exists for this cache ID, the output properties are copied from the cached object to this instance of the command, and the state of this instance is changed to "executed" without actually executing the business logic. If an entry with the generated ID is not found, the execute method is called, and the executed command object is stored in the cache. If an ID is not generated, this is considered not to be an instance of cacheableCommand caching.
Figure 2-16 Implementing a quote command used in a stock quote application
The figure below shows the logic of the sample code. The code has been taken from a StockQuote application. In order to execute this command, first invoke all of the setter methods. Once this is done, the isReadyToCallExecute() method returns true, then the command is executed and sets the output parameter which, in this case, is the QuoteDataBean variable called quoteData. The getPrice() method returns the QuoteData bean object.
package com.ibm.cache.sample.command; import javax.naming.InitialContext; import com.ibm.cache.sample.QuoteDataBean; import com.ibm.cache.sample.ejb.*; import com.ibm.websphere.command.CacheableCommandImpl; public class QuoteCommand extends CacheableCommandImpl { public StockSession stockHome = null; public QuoteDataBean quoteData = null; public String symbol = null; public QuoteCommand() { try { StockSessionHome StockSessionHome = null; if (StockSessionHome == null) { InitialContext ic = new InitialContext(); try { StockSessionHome = (StockSessionHome) (javax.rmi.PortableRemoteObject .narrow(ic.lookup("ejb/StockSession"), StockSessionHome.class)); } catch (Exception e) { StockSessionHome = (StockSessionHome) (javax.rmi.PortableRemoteObject .narrow(ic.lookup("ejb/StockSession"), StockSessionHome.class)); } } stockHome = StockSessionHome.create(); } catch (Exception e) { System.out.println("Error on StockSession Lookup"); e.printStackTrace(); } } public QuoteCommand(String symbol) { this(); this.symbol = symbol; } //////// // CacheableCommand interface methods //////// public boolean isReadyToCallExecute() { return stockHome != null && symbol != null; } public void performExecute() throws Exception { quoteData = stockHome.getPrice(symbol); } //////////// //end Cacheable command methods //////////// public void setSymbol(String symbol) { this.symbol = symbol; } public String getSymbol() { return symbol; } public QuoteDataBean getPrice() { return quoteData; } }The above is an example of a Java Command class in action. The example shows how to cache the response to a call made against a stock quote session EJB.Here is the cachespec.xml file entry to get the previous Java command to work with DynaCache.
<?xml version="1.0"?> <!DOCTYPE cache SYSTEM "cachespec.dtd"> <cache> <cache-entry> <class>command</class> <sharing-policy>not-shared</sharing-policy> <name>com.ibm.cache.sample.command.QuoteCommand.class</name> <cache-id> <component type="method" id="getSymbol"> <required>true</required> </component> <priority>1</priority> <timeout>180</timeout> </cache-id> </cache-entry> </cache>The Java snippet below shows how the previous example, QuoteCommand class, is called by client Java code.
Example 2-11 Calling QuoteCommand class
String id = "IBM"; QuoteDataBean quoteData = new QuoteDataBean(id); QuoteCommand cmd = new QuoteCommand(id); cmd.execute(); quoteData = cmd.getPrice();The cmd.execute() method call, as you can see from the code, is implemented in QuoteCommand's inherited class (CacheableCommandImpl), which is where the caching management takes place. If there is no cache entry, the CacheableCommandImpl parent class then calls the executeCommand() method of a CommandTarget proxy class, which in turn calls the performExecute() method of the target class (QuoteCommand). Once the performExecute() method has finished, the parent class caches the results. Clients can then retrieve information via getter methods on the Command object.
Under the covers, the CacheableCommandImpl.execute() implements the execute() method defined in the Command interface, thereby overriding the implementation provided in the TargetableCommandImpl class. During execution, the execute() method does the following:
- It throws an UnsetInputPropertiesException if this command's isReadyToCallExecute() method returns false.
- It retrieves the CommandTarget object for this command from the TargetPolicy.
- If the target is already cached, it returns the cached value.
- If it is not cached, it calls the proxy CommandTarget.executeCommand() method to execute the command, which in turn calls the TargetableCommand.performExecute() method, and then caches the entire object.
The command may be cached after execution, depending on the sharing policy. If the hasOutputProperties() method returns true and the returned command is not the same instance as this command, it calls the setOutputProperties method so that the results will be copied into this command. Finally, it sets the time of execution of the command.
It is important to remember that any objects referenced in a command must be serializable.