Tuesday, 7 October 2014

Web Service Caching Using Command Cache

Web Service Caching Using Command Cache 

Introduction
Caching can be done at Server Side or Client Side. This article focuses on server side caching of web services using command cache.
This article will explain the same using an example. This article assumes some basic level of web services knowledge
  1. 1)  We need to create a web service called FindDoctor.
  2. 2)  FindDoctor web service should have a following method
  3. 3)  FindDoctorDAO class is used to retrieve required rows from database.
  4. 4)  We need to cache the output of the FindDoctor web service at WAS (Websphere Application
    Server) using Zip Code and Doctor Name as cache IDs.
  5. 5)  We need to allow client to dynamically specify the TTL (Time to Live) value for each cache
    entry.
  6. 6)  Use Cache Monitor to view cache entries.
Implementation

1) Creating FindDoctor Web service
  1. Create a dynamic web project.
  2. Now Create a java Class say FindDoctor which has our web service methodpage1image13288 ArrayList<Doctor> fetchDoctorList(String zipCode, String name, long ttlSec){
//return list of Doctor whose first name is name and location zip code is zipCode
}
page1image16816 page1image16976
The following is the stub of the class
public class FindDoctor { //public constructor public FindDoctor(){
}

  //web service method
public ArrayList<Doctor> fetchDoctorList(String zipCode, String name, long ttlsec){
return null; }
}
Note: Doctor is a java bean which our DAO (FindDoctorDAO) is returning.

2) Creating a class that extends CacheableCommandImpl

  1.  CacheableCommandImpl class provides methods to cache data.
  2. The CacheableCommandImpl is an abstract class. In order to use caching make a concrete sub-class of it (FindDoctorCacheCmd) and override the isReadyToCallExecute() and performExecute() methods.
  3. The CacheableCommandImpl class provides life cycle for the cache-able object, it will call the isReadyToCallExecute() method of the extending class(FindDoctorCacheCmd) to check if the class is ready to execute. You can use this method to check if your data access part is ready. For eg, if you are accessing database you can check in this method whether your database connection is null and if you are talking to a web service then use this method to check if you can get a connection to the web service.
  4. Only when the isReadyToCallExecute() method returns true, the flow will move forward. If it returns false, exception will be thrown.
  5. The performExecute() method of the extending class is where we write our data access code(service logic). In our case we want to query the database for a list of doctors having the given name and residing in the given zip code.
public class FindDocCacheCmd extends CacheableCommandImpl{ //list of doctors from database
private ArrayList<Doctor> docList=null;
//zip code where the doctor lives
private String zipCode; //name of the doctor private String name;
public FindDocCacheCmd(){ }
public FindDocCacheCmd(String zipCode,String name){ this.zipCode=zipCode;
this.name=name;
}
@Override
public boolean isReadyToCallExecute() {
return (zipCode != null) && (name != null);
}
@Override
public void performExecute() throws Exception { System.out.println("Not in Cache"); FindDoctorDAO docDAO=new FindDoctorDAO(); try {
setDocList((ArrayList<Doctor>) docDAO.fetchDoctors(zipCode,name));
}catch (SQLException e) { e.printStackTrace();
} }
public void setDocList(ArrayList<Doctor> docList) { this.docList = docList;
}
public ArrayList<Doctor> getDocList() {
return docList; public String getZipCode(){
return zipCode; public String getName() {
return name;
}
public void setName(String name) {
} }
}
}
this.name = name;
Note: We have called the DAO (service logic) in the performExecute() method.


3) Creating cachespec.xml

<?xml version="1.0"?>
<!
DOCTYPE cache SYSTEM "cachespec.dtd"> <cache>
<cache-entry>
<
class>command</class> <sharing-policy>not-shared</sharing-policy> <name>com.atech.training.cachecmd.FindDocCacheCmd</name> <cache-id>
<component type="method" id="getZipCode"> <required>true</required>
</component>
<
component type="method" id="getName">
           <required>true</required>
        </component>
<timeout>150</timeout>
<inactivity>100</inactivity> </cache-id>
   </cache-entry>
</cache>
  1.  Place the cachespec.xml file within the WEB-INF folder.
  2.  You can also place a global cachespec.xml file in the application server properties
      directory.
  3.  The cachespec.dtd and a sample cachespec.xml file are available in the application
      server’s properties directory (<APP-SERVER HOME>\properties).
  4.  In the cachespec.xml first we are setting value of class to command, to let
      DynaCache know that we want to cache a custom java object.
  5.  The value of name attribute is fully qualified name of the Java object.
  6.  The cache-id element is used to define how to generate unique cache key for the
       cache-able object.
  7.   In our case we wanted to use the zipCode and name (Doctor Name) as the caching
       id. Since the DynaCache can use getZipCode() and getName() methods to get the caching id,        we configure it inside the component tag with type=”method” and id=”<METHOD NAME>
  8.  The value of timeout element equal to 150. What this means is that the cache entry is valid    only for 150 seconds. The value of inactivity is 100. This means that the cache entry will be removed if it is not accessed within 100s. The maximum period for which the cache entry      exists in the cache is determined by the timeout and inactivity. The following explains this
Case 1: Timeout > Inactivity Timeout=60s
Inactivity=40s
Cache entry accessed within 40s will make it stay till 60s. After 60s (timeout) it will be removed. If it is not accessed within 40s (inactivity), it will be removed from the cache after 40s, even before the timeout. This means that inactivity takes precedence over timeout in this case.
Case 2: Timeout < Inactivity Timeout=30s
Inactivity=50s
page5image9664
Cache entry will stay till 30s only, whether it is accessed or not. This means that timeout takes precedence over inactivity in this case.



4) Calling execute() method in web service.

 Update the fetchDoctorList() method of our FindDoctor web service as follows
//service method
public ArrayList<Doctor> fetchDoctorList(String zipCode, String name, long ttlsec){
}
FindDocCacheCmd findDoc=new FindDocCacheCmd (zipCode, name); try {
System.out.println("Calling Execute");
     findDoc.execute();
} catch (CommandException e) {
            e.printStackTrace();
}
return findDoc.getDocList();
  1.   The client of this web service on calling the fetchDoctorList() method will supply the zipCode, doctor name and time to live (timeout of the cache) value as input parameters. 
  2.   Out of these parameters, zipCode and name are used to create a new object of the FindDocCacheCmd class.
  3.   Call the execute() method of the created object.
  4.   Have a look at FindDocCacheCmd class code. You will notice that we are not
    overriding the execute() method.
  5.   The execute() method is provided by the super class and it is the place where the life
    cycle of cache is provided.
page5image26264 page5image26688 page5image26848 page5image27272 page5image27432
FindDocCacheCmd findDoc=new FindDocCacheCmd (zipCode, name);
page5image28200 page5image28624 page5image28784
  •   The execute() method checks if there is object in cache for the current zipCode and name pair (zipCode and name together are the cache-id that we configured in the cachespec.xml). If yes, it will return the FindDocCacheCmd object from the cache. Otherwise it will call performExecute() method of FindDocCacheCmd object and once the result is returned it will store the instance of FindDocCacheCmd in DynaCache for the zipCode and name pair.
  •   After execute() method has been called, the FindDocCacheCmd object gets ready and we can call its getter methods to read the doctor list. i.e. getDocList(); 

  • 5) Enabling dynamic caching

  • You will have to enable the dynamic caching on your server if it is not already enabled. You

    can use the WebSphere Application Server Administrative Console to do that. Follow these steps
  1.  Log into WebSphere Application Server Admininstrative Console 
  2.  Go to Servers -> WebSphere_Portal. If you are using a different server (say Websphere Application Server), select it. 
  3. On the server details page expand Container Services and click on Dynamic Cache Service. 
  4. On the Dynamic Cache Service page make sure that "Enable Service at server startup" check box is checked. 
  5.  To enable servlet caching, click Web Container Settings and check Enable Servlet Caching. 
  6.  To enable portlet caching, click Portlet Container Settings and check Enable Portlet Fragment Cache 
  7.   Save your changes and restart the server 

  • Note: The web service is now ready for caching. You can test it using cache monitor. Remember that the TTL for the cache entries is taken from the cashespec.xml file because we have not changed it programmatically in our implementation.
    If cache monitor is already installed (which will not be the default) follow these steps to view the cache entries:
Open http://<SERVER URL>/cachemonitor/ (eg. http://locahost:10000/cachemonitor).
page6image17760

Login
Click Cache Contents in the left menu

  •  Call the FindDoctor webservice from some client or use Generic Service Client
  •  Open Cache Monitor and Click Refresh Instances button under Cache Monitor 

Note: The response is cached. If you call the web service with same zipCode and name arguments again (before cache entry’s TTL), the response will be taken from the cache. The timeout (seconds) field value is 150 which we configured in cachespec.xml. If you want to install the cache monitor see the appendix at the end.

6) Allowing the Client to customize the TTL values for each cache entry

For this to happen we need to first update our FindDocCacheCmd class and
FindDoctor class as follows. Updating FindDocCacheCmd class
  •  Add a private class variable 
  •  Generate setter and getter for cacheTimeOut 
  •  Add the following method 
  •  Update the performExecute() method. 
page9image4824 page9image5248 page9image5408
private long cacheTimeOut;
page9image6360 page9image6520 page9image6680 page9image7104 page9image7264
public void setCacheTimeOut(long cacheTimeOut) { this.cacheTimeOut = cacheTimeOut;
}
public long getCacheTimeOut() {
}
return cacheTimeOut;
page9image12504 page9image12928 page9image13088 page9image13512 page9image14104
public void setExpTime(long ttlsec){ this.getEntryInfo().setTimeLimit((int) ttlsec); this.getEntryInfo().setInactivity((int) ttlsec);
}
page9image15888
public void performExecute() throws Exception { System.out.println("Not in Cache"); setExpTime(getCacheTimeOut()); FindDoctorDAO docDAO=new FindDoctorDAO(); try {
              setDocList((ArrayList<Doctor>)
docDAO.fetchDoctors(zipCode));
} catch (SQLException e) { e.printStackTrace();
} }
Updating FindDoctor class
Update fetchDoctorList() method so it look as follows
public ArrayList<Doctor> (String zipCode, String name, long ttlsec){
name);
}
FindDocCacheCmd findDoc=new FindDocCacheCmd(zipCode, try {
System.out.println("Calling Execute"); findDoc.setCacheTimeOut(ttlsec); findDoc.execute();
}catch (CommandException e) { e.printStackTrace();
}
return findDoc.getDocList();
fetchDoctorList
  •   Save and Generate the Web Service Again. 
  •   Open Cache Monitor
  •   Call the FindDoctor webservice from a client or use Generic Service Client. 
  •   Give ttlsec value this time, say 60. 
  •   In Cache Monitor click Refresh Instances button under Cache Monitor 

Note: The value of Timeout(seconds) field is 60, the one we passed as ttlsec parameter instead of 150 which we initially specified in cachespec.xml.
APPENDIX:INSTALL DYNAMIC CACHE MONITOR IN WEBSPHERE


To see the dynamic cache monitor, make sure that the websphere server is running and go to the url
http://<server url>/cachemonitor/

No comments:

Post a Comment

Custom single threaded java server

 package com.diffengine.csv; import java.io.*; import java.net.*; import java.util.Date; public class Server { public static void main(Str...