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) Creating FindDoctor Web service
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) We need to create a web service called FindDoctor.
-
2) FindDoctor web service should have a following method
-
3) FindDoctorDAO class is used to retrieve required rows from database.
-
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) We need to allow client to dynamically specify the TTL (Time to Live) value for each cache
entry.
-
6) Use Cache Monitor to view cache entries.
1) Creating FindDoctor Web service
- Create a dynamic web project.
- Now Create a java Class say FindDoctor which has our web service method
ArrayList<Doctor> fetchDoctorList(String zipCode, String name, long ttlSec){
//return list of Doctor whose first name is name and location zip code is zipCode
}
}
The following is the stub of the class
public class FindDoctor {
//public constructor
public FindDoctor(){
}
return null; }
}
}
//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
2) Creating a class that extends CacheableCommandImpl
- CacheableCommandImpl class provides methods to cache data.
- 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.
- 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.
- Only when the isReadyToCallExecute() method returns true, the flow will move forward. If it returns false, exception will be thrown.
- 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) {
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">
<inactivity>100</inactivity> </cache-id>
<!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>
- Place the cachespec.xml file within the WEB-INF folder.
- You can also place a global cachespec.xml file in the application server properties
directory.
- The cachespec.dtd and a sample cachespec.xml file are available in the application
server’s properties directory (<APP-SERVER HOME>\properties).
- 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.
- The value of name attribute is fully qualified name of the Java object.
- The cache-id element is used to define how to generate unique cache key for the
cache-able object.
- 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>”
- 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 |
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
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");
System.out.println("Calling Execute");
findDoc.execute();
} catch (CommandException e) {
e.printStackTrace();
}
return findDoc.getDocList();
- 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.
- Out of these parameters, zipCode and name are used to create a new object of the
FindDocCacheCmd class.
- Call the execute() method of the created object.
- Have a look at FindDocCacheCmd class code. You will notice that we are not
overriding the execute() method.
- The execute() method is provided by the super class and it is the place where the life
cycle of cache is provided.
FindDocCacheCmd findDoc=new FindDocCacheCmd (zipCode, name);
-
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
- Log into WebSphere Application Server Admininstrative Console
- Go to Servers -> WebSphere_Portal. If you are using a different server (say Websphere Application Server), select it.
- On the server details page expand Container Services and click on Dynamic Cache Service.
- On the Dynamic Cache Service page make sure that "Enable Service at server startup" check box is checked.
- To enable servlet caching, click Web Container Settings and check Enable Servlet Caching.
- To enable portlet caching, click Portlet Container Settings and check Enable Portlet Fragment Cache
- 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:
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
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.
private long cacheTimeOut;
public void setCacheTimeOut(long cacheTimeOut) {
this.cacheTimeOut = cacheTimeOut;
}
public long getCacheTimeOut() {
}
public long getCacheTimeOut() {
}
return cacheTimeOut;
public void setExpTime(long ttlsec){
this.getEntryInfo().setTimeLimit((int) ttlsec);
this.getEntryInfo().setInactivity((int) ttlsec);
}
}
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();
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/
http://<server url>/cachemonitor/
No comments:
Post a Comment