But sometimes you might want to store Custom Java Objects for caching, if that's the case you should use
CacheableCommandImpl
. Ex. Lets say your application makes a SQL query to database and you want to cache output of that query, or your application makes a Web Service call and you want to cache output of that web service call(DynaCache has a feature that lets you cache service request response similar to output of servlet or portlet).I wanted to learn how to use DynaCache for storing custom java objects so i built SampleCacheCommand application. This application has a
SampleCacheCommandServlet
servlet that takes a ContactId
has parameter and fires a SQL query to get Contact record for the supplied ContactId
and caches that contact record. So next time when you make a request to SampleCacheCommandServlet
servlet for same ContactId
, that record would be served from cache instead of going to database.First create
CacheableContact.java
like this, this object will be used for accessing the contact table data as well as for caching.
package com.webspherenotes.cache;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import com.ibm.websphere.command.CacheableCommandImpl;
public class CacheableContact extends CacheableCommandImpl {
private static final long serialVersionUID = -8098013422300089468L;
private int contactId;
private String firstName;
private String lastName;
public CacheableContact(int contactId){
this.contactId = contactId;
}
@Override
public boolean isReadyToCallExecute() {
return true;
}
@Override
public void performExecute() throws Exception {
Connection conn = null;
try {
System.out.println("Entering CacheableContact.performExecute()");
Class.forName("org.apache.derby.jdbc.ClientDriver");
conn =DriverManager.getConnection
("jdbc:derby://localhost:1527/sample;create=true", "admin", "admin");
PreparedStatement stmt = conn.prepareStatement
("SELECT * FROM ADMIN.CONTACT WHERE CONTACTID=?");
stmt.setInt(1, this.contactId);
ResultSet rs= stmt.executeQuery();
rs.next();
this.firstName =rs.getString("FIRSTNAME");
this.lastName = rs.getString("LASTNAME");
} catch (Exception e) {
e.printStackTrace(System.out);
}finally{
if(conn != null)
conn.close();
}
System.out.println("Exiting CacheableContact.performExecute()");
}
public int getContactId() {
return contactId;
}
public void setContactId(int contactId) {
this.contactId = contactId;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
@Override
public String toString() {
return "CacheableContact [contactId=" + contactId + ", firstName="
+ firstName + ", lastName=" + lastName + "]";
}
}
When your creating a cache-able object you will have to follow these rules
- Create a Java class that extends
CacheableCommandImpl
class, this class provides logic to cache data by providing life cycle for your class. TheCacheableCommandImpl
is abstract class so in order to make a concrete class you will have to override itsisReadyToCallExecute()
andperformExecute()
method - The
CacheableCommandImpl
class provides life cycle for the cache-able object, it will call theisReadyToCallExecute()
method of your class to check if your class is ready to execute. You can use this method to check if your data access part is ready. Ex. if your accessing database you can check in this method if database connection is not null, if your talking to web service then use this method to check if you can get connection to web service. If this method returns true then only the flow will move forward, if it returns false exception will be thrown - The
performExecute()
method of your class is where you write your data access code, In my case i want to make query database for supplied contactId and once the record is returned i used the values to populate instance variables of theCacheableContact
class.
I guess one disadvantage of the
CacheableCommandImpl
class is that it is little difficult to understand how it works but once i understood how it works in think its a very good pattern to structure your code as well. Lets take a look at the SampleCacheCommandServlet
code to see how the CacheableContact
object is used that will make it easier to understand
package com.webspherenotes.cache;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.ibm.websphere.command.CommandException;
/**
* Servlet implementation class SampleCacheCommandServlet
*/
public class SampleCacheCommandServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
int contactId = Integer.parseInt(request.getParameter("contactId"));
CacheableContact cacheableContact = new CacheableContact(contactId);
try {
cacheableContact.execute();
String firstName = cacheableContact.getFirstName();
String lastName = cacheableContact.getLastName();
response.getWriter().println("
"+firstName+" " + lastName);
} catch (CommandException e) {
e.printStackTrace();
}
}
}
In the servlet first i am reading the contactId parameter supplied by user and using the contactId to create a new object of the
CacheableContact
, then i am calling its execute()
method, if you look at CacheableContact
code you will notice that we are not overriding its execute()
method. The execute()
method is provided by the super class and its the place where the life cycle of cache is provided. The execute()
method checks if there is object in cache for the contactId(ContactId is primary key that we configure in the cachespec.xml) if yes it will return the CacheableContact
object in the cache for that contactId if its not there in the cache it will call performExecute()
method of CacheableContact
object and once the result is returned it will store the instance of CacheableContact
in DynaCache for the contactId. In the
SampleCacheCommandServlet
code once execute()
method is called we can assume that the CacheableContact
object is ready and use its getter methods to read firstName and lastName.The last piece of puzzle is
cachespec.xml
, This xml file is used to configure the caching of CacheableContact
object. You can create a cachespec.xml in the WEB-INF folder of your web application.
<?xml version="1.0" ?>
<!DOCTYPE cache SYSTEM "cachespec.dtd">
<cache>
<cache-entry>
<class> command</class>
<sharing-policy>not-shared</sharing-policy>
<name> com.webspherenotes.cache.CacheableContact.class</name>
<cache-id>
<component type="method" id="getContactId">
<required>true</required >
</component>
<priority>1</priority>
<timeout>180</timeout >
</cache-id>
</cache-entry>
</cache>
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 with .class
prefix. The
cache-id
element is used to define how to generate unique cache key for the cache-able object. In my case i want to use the contactId as caching key and since the DynaCache can use getContactId()
key to get the caching key i configure the key like this
<component type="method" id="getContactId">
<required>true</required >
</component>
The value of
timeout
element equal to 180 means this cache entry is valid only for 180 seconds.In the default installation of WAS or WebSPhere portal the dynamic caching service is disabled and as a result the CacheableContact object wont be cached. So use Enabling Dynamic Caching steps to enable the dynamic caching.
After deploying the application on server try invoking a servlet for different contactIds and then you can use the DynaCacheMonitor web app to see if the record is cached or not. By default the objects will go to
baseCache
instance You can use the CacheMontior tool to even look at content of the cache entry. This part is inconsistent some time it shows only the address object and some times it shows the output of toString() method
I have one query.The Cahce gets refeshed with in certain period of time, but while refershing if the Database is down the object will get null. then the null object is replaced in the chache. can any way we can stop the null object replacement by using Dyanchaching
ReplyDeletelike if we get the null object from the database we will not replace it. we will keep the older one or we will make again the database call till we get the data.
ReplyDeleteAs mentioned the refresh time for cache, but if the values are updated before the default refresh time, it will return the stale data in that case. Please clarify
ReplyDeleteHi,
ReplyDeleteCan anyone help,Above application is running successfully but the object value is not showing in the CacheMonitor.Why it is not showing object value in the cache.
Hi,
ReplyDeleteCan you please also give me an example how to clear the cache by program.
This comment has been removed by the author.
ReplyDeleteStill data is coming from database only not from cache.Can u please suggest?
ReplyDeleteThanks for info
ReplyDeleteWeb Design Company in Bangalore
Website development in Bangalore