Showing posts with label dynamiccaching. Show all posts
Showing posts with label dynamiccaching. Show all posts

Controlling memory size of DynaCache

One of the common problems with using caching frameworks is how do you control the size of the memory taken by the cache. Most of the caches allow you to control number of entries that should be cached, but not the maximum memory size of the cache. So it might happen that your cache will grow too big and cause memory issue for your application/application server.

Starting with WebSphere Application Server 7.0, you can control what is the maximum memory that your cache instance can take. I wanted to create cache instance and set its size to 10MB and start putting objects in the cache and make sure that the cache does not grow above 10MB. YOu can download the sample application from here

First i went into the WAS Admin Console and i did create a new cache instance SampleMemInstance and bound it at services/cache/samplememcache JNDI location like this,



As you can see i did configure maximum number of entries to 20k and maximum mem size to 10MB

Then i did create a new Web SampleDynaCacheMemSize web application, which has a servlet that takes number of entries that should be put in cache as argument and starts adding those entries in cache like this


package com.webspherenotes.dynacache;

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 SampleDynaCacheMemSize
*/
public class SampleDynaCacheMemSize extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
int startIndex = Integer.parseInt(request.getParameter("startIndex"));
int number = Integer.parseInt(request.getParameter("number"));
for(int i = startIndex ; i < startIndex + number ; i++ ){
System.out.println("Putting object in cache for key " + i);
SampleMemObject sampleMemObject = new SampleMemObject(i);
try {
sampleMemObject.execute();
} catch (CommandException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

}


Then the last piece is to create a SampleMemObject CacheableCommand object like this


package com.webspherenotes.dynacache;

import com.ibm.websphere.cache.Sizeable;
import com.ibm.websphere.command.CacheableCommandImpl;


public class SampleMemObject extends CacheableCommandImpl implements Sizeable{
/**
*
*/
private static final long serialVersionUID = 1027837849378068229L;
private int cacheKey;

public SampleMemObject(int cacheKey){
this.cacheKey = cacheKey;
}

@Override
public long getObjectSize() {
return 1048576;
}

@Override
public boolean isReadyToCallExecute() {
return true;
}

@Override
public void performExecute() throws Exception {
System.out.println("Inside SampleMemObject.performExecute()");
}

public int getCacheKey() {
return cacheKey;
}

public void setCacheKey(int cacheKey) {
this.cacheKey = cacheKey;
}

}


If you want to control size of memory then you should implement Sizeable interface in your Command object and implement getObjectSize() method, this method returns a long value with size of object, in my case i am returning a value which is equal to 1 MB.

After deploying the code i did execute the servlet like this http://localhost:9080/SampleDynaCacheMemSize/SampleDynaCacheMemSize?startIndex=0&number=11, and asked it to put 11 entries in cache at that time i could see that following message is being return to the SystemOut.log because memory size is full, the message wording is wrong it should have said memory size is reached.


[2/14/11 6:11:49:718 PST] 00000018 Cache A DYNA1070I: Cache instance "services/cache/samplememcache" is full and has reached the maximum configured size of 200000 entries. Space on the JVM heap for new entries will now be made by evicting existing cache entries using the LRU algorithm. Please consider enabling the disk offload feature for the cache instance to prevent the discard of cache entries from memory.

How to use custom servlet cache instance for Custom Java Objects

In the Storing custom java objects in Dynacache using CachableCommand entry i talked about how to use the DynaCache for storing the custom java object, similarly you can use the DynaCache for caching output of a portlet or servlet

By default websphere application server creates one instance of cache(you can think of cache instance as bucket) baseCache and that cache instance is used for storing all the custom java objects, output of servlet, output of portlet. But what if you want to have more granular control over the cache instance. Ex. you want to cache say up to 2000 instances of custom java object or you want to make sure that only 10mb size is used for storing servlet output. In order to solve those advanced use cases you should create separate cache instance.

I wanted to try how to use a separate cache instance for my storing custom java object, so first i went to WAS Admin Console and i did define a new Servlet cache instance like this



When you create a new cache instance you can define things like the JNDI name where the instance would be bound, also Cache size, which means how many objects would be stored in the cache. In my case i am using default cache size of 2000 and then i am setting JNDI name for the cache instance to be services/cache/samplecache.

I can configure my Sample application for storing custom java object to use the services/cache/samplecache by making one change in the cachespec.xml file like this


<?xml version="1.0" ?>
<!DOCTYPE cache SYSTEM "cachespec.dtd">
<cache>
<cache-instance name="services/cache/samplecache">
<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-instance>
</cache>


You can configure the cache-entry to use different cache-instance by enclosing it in cache-instance element where value of name attribute should be equal to the JNDI name where the cache instance is bound, the same name that you used while configuring the cache instance

After making the change i did execute the servlet so that the CacheableContact objects is set in cache, now i can see that instance using cache monitor

Information about DynaCache

So far i have published few blog entries about DynaCache, so i thought i should create one page with links to all the Dynamic caching information


  1. Caching output of portlet

  2. Caching output of servlet

  3. Caching using distributed map

  4. Attaching invalidation listener to dyanacache

  5. Caching static resources of web application

  6. Storing custom java objects in Dynacache using CachableCommand

  7. How to use custom servlet cache instance for Custom Java Objects

  8. Controlling memory size of DynaCache




Storing custom java objects in Dynacache using CachableCommand

The WebSphere Application Server provides DynaCache component that can be used for caching. You can use DynaCache for storing output of Servlet, portlet, JSP,...

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

  1. Create a Java class that extends CacheableCommandImpl class, this class provides logic to cache data by providing life cycle for your class. The CacheableCommandImpl is abstract class so in order to make a concrete class you will have to override its isReadyToCallExecute() and performExecute() method

  2. The CacheableCommandImpl class provides life cycle for the cache-able object, it will call the isReadyToCallExecute() 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

  3. 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 the CacheableContact 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

External cache groups

The dynamic cache can control caches outside of the application server, such as the Edge server, an IBM HTTP Server, or an HTTP Server ESI Fragment Processor plugin

When external cache groups are defined, the dynamic cache matches externally cacheable cache entries with those groups, and pushes cache entries and invalidations out to those groups. This allows WebSphere Application Server to manage dynamic content beyond the application server. The content can then be served from the external cache, instead of the application server, improving savings in performance.

Edget side caching

The Web server plug-in contains a built-in ESI processor. The ESI processor can cache whole pages, as well as fragments, providing a higher cache hit ratio. The cache implemented by the ESI processor is an in-memory cache, not a disk cache, therefore, the cache entries are not saved when the Web server is restarted.

When a request is received by the Web server plug-in, it is sent to the ESI processor, unless the ESI processor is disabled. It is enabled by default. If a cache miss occurs, a Surrogate-Capabilities header is added to the request and the request is forwarded to the WebSphere Application Server. If servlet caching is enabled in the application server, and the response is edge cacheable, the application server returns a Surrogate-Control header in response to the WebSphere Application Server plug-in.

The value of the Surrogate-Control response header contains the list of rules that are used by the ESI processor to generate the cache ID. The response is then stored in the ESI cache, using the cache ID as the key. For each ESI include tag in the body of the response, a new request is processed so that each nested include results in either a cache hit or another request that forwards to the application server. When all nested includes have been processed, the page is assembled and returned to the client.
The ESI processor is configurable through the WebSphere Web server plug-in configuration file plugin-cfg.xml. The following is an example of the beginning of this file, which illustrates the ESI configuration options.

Speed WebSphere Apps with Edge Side Includes, has some information on WebSphere WebSphere APIs for Edge Side Includes (WESI) are a set of Java application programming interface (API) and JavaServer Page (JSP) custom tags for accelerating Web application delivery through distributed fragment caching and assembly with Edge Side Includes (ESI).

Distribute DynaCache Replication Sample Web Application

I wanted to see how the distributed Session works so i created this DistributedCacheReplication.war file. This Web Application has a simple DynaCacheReplicationSampleServlet servlet like this

public class DynaCacheReplicationSampleServlet extends HttpServlet {
private static final long serialVersionUID = 1L;

private DistributedMap distributeMap;
public void init() throws ServletException {
super.init();
System.out.println("Entering DynaCacheReplicationSampleServlet.init()");
try {

Context ctx = new InitialContext();
System.out.println("Before getting the distributedMap");
distributeMap =(DistributedMap) ctx.lookup("services/cache/distributedmap");
System.out.println("After getting the distributedMap " + distributeMap);
} catch (NamingException e) {
e.printStackTrace(System.out);
}
System.out.println("Exiting DynaCacheReplicationSampleServlet.init()");
}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("Entering DynaCacheReplicationSampleServlet.doGet() " );

String serverName = (String)distributeMap.get("serverName");
System.out.println("Server Name " + serverName);
if(serverName == null){
System.out.println("Server Name is empty. Adding data to distributedMap");
serverName = ServerName.getDisplayName() + new Date();
distributeMap.put("serverName", serverName);
}
response.setContentType("text/html");
response.getWriter().println("Server Name from Distribute Cache " + serverName +"
");
response.getWriter().println("Server Display Name " + ServerName.getDisplayName() +"
");
response.getWriter().println("Server Full Name " + ServerName.getFullName() +"
");
response.getWriter().println("Server Id " + ServerName.getServerId() +"
");

System.out.println("Exiting DynaCacheReplicationSampleServlet.doGet()");
}
}


I did deploy this application on my cluster and then i tried Hitting the servlet on first server followed by second server. On both of them i should have got same data but somehow my DynaCache replication is not working, i need debug it further

Configuring Cache replication

You can configure DynaCache so that, Data replication service will replicate data across all the servers in a replication domain, i.e. you can configure it so that if Server 1 creates a cache entry that gets copied to all other servers in say that cluster and other clusters wont have to create that entry explicitly. Invalidations of cache entries are sent across the cluster to keep the cached data consistent and valid.

Follow these steps to configure dynamic caching service replication for all the servers in particular cluster


  • Before you configure the DynaCache replication, make sure that you already have a replication domain. You can either create the replication domain as part of cluster creation process or manually

  • Go to WAS Admin Console and for each of the servers in the cluster go to Servers -> Application Servers -> server_name -> Container Services -> Dynamic Cache service



    On this page Check "Enable Cache Replication" checkbox and then set following values

    • Full group replication domain: Use different replication domains for each type of consumer. For example, dynamic cache should use a different replication domain than session manager. The only replication domains that you can select in this panel include replication domains that are configured to use full-group replication. In a full-group configuration, every cache entry is replicated to every other cache that is configured in the servers that are in the replication domain. If none of the replication domains in your configuration meet these requirements, the list is empty. In this case, create a replication domain or alter an existing replication domain so that you have a replication domain that can perform full-group replication.

    • Replication Type: Select appropriate replication type where

      • Not Shared: Cache entries for this object are not shared among different application servers. These entries can contain non-serializable data. For example, a cached servlet can place non-serializable objects into the request attributes, if the class type supports it.

      • PUSH: Cache entries for this object are automatically distributed to the dynamic caches in other application servers or cooperating Java Virtual Machines (JVMs). Each cache has a copy of the entry at the time it is created. These entries cannot store non-serializable data.

      • PULL:Cache entries for this object are shared between application servers on demand. If an application server gets a cache miss for this object, it queries the cooperating application servers to see if they have the object. If no application server has a cached copy of the object, the original application server runs the request and generates the object. These entries cannot store non-serializable data. This mode of sharing is not recommended.

      • PUSH_PULL: Cache entries for this object are shared between application servers on demand. When an application server generates a cache entry, it broadcasts the cache ID of the created entry to all cooperating application servers. Each server then knows whether an entry exists for any given cache ID. On a given request for that entry, the application server knows whether to generate the entry or pull it from somewhere else. These entries cannot store non-serializable data.



    • Push Frequency: you can define when and how often data is replicated across the dynamic cache replication domain



Configuring Object Cache

By default you can store your application specific objects in the default instance provided by the WAS. But if you want more control about say what level those objects should be store or how those objects should get flushed to disk then you can configure a custom Object instance from WAS Admin Console like this



As you can see i configured it so that if there are more than 2000 entries in the distributedMap those entries would get off loaded to the disk in c:/temp/objects directory. I wanted to try that so i created a DynaObjectServlet like this

public class DynamicObjectServlet extends HttpServlet {
private static final long serialVersionUID = 1L;

/**
* @see HttpServlet#HttpServlet()
*/
public DynamicObjectServlet() {
super();
// TODO Auto-generated constructor stub
}

private DistributedMap distributeMap;
public void init() throws ServletException {
super.init();
try {
Context ctx = new InitialContext();
System.out.println("Before getting the distributedMap");
distributeMap =(DistributedMap) ctx.lookup("services/cache/object_instance");
System.out.println("After getting the distributedMap " + distributeMap);
} catch (NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
System.out.println("Inside DynamicObjectServlet.doGet()");
for( int i = 0 ; i < 3000 ; i++){
System.out.println("Putting objects in distributedMap");
distributeMap.put("testObject"+i, "testValue");
}
System.out.println("DistributeMap " + distributeMap.get("testObject"));
response.getWriter().println("After storing object in distribute Map");
}

}


I have a big loop in the doGet() method that stores 3000 objects in the distributed map, where as the limit is 2000 so some of the objects will get pushed to disk. I tried hitting the DynaObjectServlet and then i checked the disk which is configured to act as offload location for the dynacache, i saw these files being created there



I tried opening the files but the content is stored in non text format there were funny characters in there.

Servlet Cache instance

By default when you use the servlet output caching it gets store in the default baseCache instance. But there could situations where you would want to use separate cache for your servlet for better control, may be because you want to cache content say across cluster or want to make sure that 2000 entries are stored in the cache or some other example. If thats the case then you can create a Servlet cache instance and configure your web application to use.

I made changes in DynaCacheSample to try this by first creating ServletInstanceOne cache instance using the WAS Admin Console at the cell level like this



Then i changed the cachespec.xml file in my web application to add cache-instance element with name equal to JNDI name of the cache instance that i just configured and making all the cache-entry elements as its child element. If you want you can create multiple cache instances and use one instance for each of the cache entry. My cachespec.xml looks like this


<?xml version="1.0" ?>
<!DOCTYPE cache SYSTEM "cachespec.dtd">
<cache>
<cache-instance name="services/cache/instance_one" >
<cache-entry>
<class>servlet</class>
<name>/dynacacheservlet</name>

<cache-id>
<component id="action" type="parameter">
<required>false</required>
</component>
<timeout>180</timeout>
</cache-id>
</cache-entry>
<cache-entry>
<class>servlet</class>
<name>/dynachache.jsp</name>
<cache-id>
<component id="action" type="parameter">
<required>false</required>
</component>
<timeout>180</timeout>
</cache-id>
</cache-entry>
<cache-entry>
<class>static</class>
<name>com.ibm.ws.webcontainer.servlet.SimpleFileServlet.class</name>
<cache-id>
<component id="" type="pathinfo">
<required>true</required>
</component>
<component id="If-Modified-Since" type="header">
<required>false</required>
</component>
<timeout>300</timeout>
</cache-id>
</cache-entry>
</cache-instance>
</cache>


I did deploy the new application and try hitting the URL few times. Now when i use the CacheMonitor application i can see the services/cache/instance_one cache instance in it with the servlet content being stored in it like this

Caching static resources in web application

Most of the Dynamic Web Applications that we create has static resources such as JavaScript file or image file. Now the best practice would be to keep these resources on the web server and let web server serve static requests but in order to keep the deployment simple we package these resources as part of the Web Application. The WebSphere Application Server has a com.ibm.ws.webcontainer.servlet.SimpleFileServlet Servlet that gets request if a request is made to static resource in the web application. You can enable caching for SimpleFileServlet so that the content of static resource is cached.

I wanted to try that so i made changes in my DynaCacheSample.war to add sample.js and enableservletcaching1.GIF files in it and then dynacache.jsp includes both these files. I changed the cachespecs.xml file of the DynacacheSample.web to add this cache-entry element


<cache-entry>
<class>static</class>
<name>com.ibm.ws.webcontainer.servlet.SimpleFileServlet.class</name>
<cache-id>
<component id="" type="pathinfo">
<required>true</required>
</component>
<component id="If-Modified-Since" type="header">
<required>false</required>
</component>
<timeout>300</timeout>
</cache-id>
</cache-entry>


You can download the DynaCacheSample.war from here

I did deploy the modified DyanCacheSample.war file on the sever and tried hitting it few times this is what i see in the CacheMonitor application



As you can see in addition to content of dynacacheservlet and dynacache.jsp, now the content of sample.js and enableservletcaching1.gif is also cached

Servlet Caching Sample

I wanted to try how to enable caching for Servlet and JSP so i tried creating a simple DynaCacheSample Web application. This application has a DynaCacheServlet, that forwards control to dynachache.jsp for actual markup generation. So i wanted to see how can i enable the Servlet Side cache.

I created this cachespec.xml file in the WEB-INF folder of my web application

<?xml version="1.0" ?>
<!DOCTYPE cache SYSTEM "cachespec.dtd">

<cache>
<cache-entry>
<class>servlet</class>
<name>/dynacacheservlet</name>
<cache-id>
<component id="action" type="parameter">
<required>false</required>
</component>
<timeout>180</timeout>
</cache-id>
</cache-entry>
<cache-entry>
<class>servlet</class>
<name>/dynachache.jsp</name>
<cache-id>
<component id="action" type="parameter">
<required>false</required>
</component>
<timeout>180</timeout>
</cache-id>
</cache-entry>
</cache>


As you can see that i have two cach-entry element one for the /dynacacheservlet URL, that specifies that cache content of the /dynacacheservlet URL and other for /dynacache.jsp that caches content of the dynacache.jsp.

Now other part of the question is what should be the cache key, i.e. if you have a news site that generates same content for same URL for all the users you can say that cache content based on URL but if you have a site which has view mode and update mode you can say that cache content only when value of action request parameter is view,.. THe component elements let you configure the key. You can also create your own class that will get control to generate cache key based on the request. Take a look at cachespec.xml for more information

You can download the DynaCacheSample.war from here

Once my DynaCacheSample.war was deployed i tried hitting DynaCacheServlet by going to http://localhost:9080/dynasample/dynacacheservlet and this is what i see in the CacheMonitor application



As you can my request to /dynacacheservlet is cached as well as the JSP page that DynaCaheServlet is including to generate response is also cached.

What is Servlet/ JSP Caching ?

You can improve performance of your web application by caching the output of either servlet or JSP (Since JSP gets compiled into servlet). The way it works is after a servlet is invoked and completes generating the output to cache, a cache entry is created containing the output and the side effects of the servlet. These side effects can include calls to other servlets or JavaServer Pages (JSP) files or metadata about the entry, including timeout and entry priority information. Configure servlet caching to save the output of servlets and JavaServer Pages (JSP) files to the dynamic cache

Unique entries are distinguished by an ID string that is generated from the HttpServletRequest object each time the servlet runs. You can then base servlet caching on:


  • Request parameters and attributes of the Universal Resource Identifier (URI) that was used to invoke the servlet

  • Session information

  • Other options, including cookies



Because JavaServer Pages files are compiled into servlets, the dynamic cache function treats JavaServer Pages files the same as servlets, except in specifically documented situations.

What is dynamic cache

Caching the output of servlets, commands, and JavaServer Pages (JSP) improves application performance. WebSphere Application Server consolidates several caching activities including servlets, Web services, and WebSphere commands into one service called the dynamic cache. These caching activities work together to improve application performance, and share many configuration parameters that are set in the dynamic cache service of an application server. You can use the dynamic cache to improve the performance of servlet and JSP files by serving requests from an in-memory cache. Cache entries contain servlet output, the results of a servlet after it runs, and metadata.

The dynamic cache service works within an application server Java virtual machine (JVM), intercepting calls to cacheable objects. For example, it intercepts calls through a servlet service method or a command execute method, and either stores the output of the object to the cache or serves the content of the object from the dynamic cache.

You can cache following things

  • Servlet output

  • JSP output

  • WebSphere command

  • Edge side include cache

  • Web Service Output

  • Application specific custom objects


How load balancing works in case of WebSphere XD/ virtual enterprise

The On Demad Router (ODR) is an intelligent routing engine that forms the core of WebSphere XD topologies. It does many of the features of Web Server Plugin and the edge-of-network routers, like the WebSphere Edge Server Network dispatcher

It does following things


  • Listens for requests that it proxies
  • Classifies the requests according to the configuration of the WebSphere application server for which the requests are destined

  • Prioritizes the requests and uses its knowledge of the status of the application server to determine which application server the request should be routed to

  • Queues and issues the requests to the application server that are servicing them according to the weigth assigned to the request

  • Communicates with other parts of the XD system to coordinate its activities with other components and driver features like dynamic placement






There are a number of advantages that this topology holds over the standard Network Deployment topology:


  • Here, the routing information used by the ODR is dynamic; as applications are deployed (or undeployed), or as application servers are added to (or removed from) the topology, the ODR routing tables are automatically updated. This eliminates the need for a plugin-xml.cfg file, since the communication between the deployment manager and the ODR is direct.

  • Since the ODR is an active application server instance in its own right, it can use additional information from the application servers to make dynamic determinations about request weighting.

Cache Invalidation Listener

The Dynamic Cache provides following types of listeners

Invalidation Listener



The Invalidation Listener receive InvalidationEvents (defined in the com.ibm.websphere.cache package) when entries from the cache are removed, due to an explicit user invalidation, timeout, least recently used (LRU) eviction, cache clear, or disk timeout. Applications can immediately recalculate the invalidated data and prime the cache before the next user request.

Ex. In most of the database applications, whenever you retrieve a record from database you can cache it or when user creates a new record instead of directly creating it in database you can create cached entry for it. You keep updating/modifying this record. And when application server is about wipe this record out from memory it will call your invalidation listener at that point you can write that record into database.

This is how you create an InvalidationListener

public class MyInvalidationListener implements InvalidationListener {

public void fireEvent(InvalidationEvent invalidationEvent) {
System.out.println("Entering MyInvalidationListener.fireEvent()");
System.out.println("Cache Name " +invalidationEvent.getCacheName());
System.out.println("Cache ID " +invalidationEvent.getId());
System.out.println("Cache Value " +invalidationEvent.getValue());
System.out.println("Exiting MyInvalidationListener.fireEvent()");
}

}

As you can see i am just printing the invalidated entry into system.out
Then you attach it to your Distributed map using this code

distributedMap= (DistributedMap)context.lookup("wpcertification/cache/customCache");
System.out.println("Distributed Map " + distributedMap);
distributedMap.enableListener(true);
distributedMap.addInvalidationListener(new MyInvalidationListener());


When you get distributedMap from JNDI first you set enableListener to true and then create object of your listener and attach it to the distributed map.

In order to test the invalidation listener, i went to cache monitor application and i did click on the invalidate button next to the entry that i want to invalidate.



WHen the entry was invalidated this is the output that i see in my SystemOut.log

invalidating id: timeofCache
Entering MyInvalidationListener.fireEvent()
Cache Name wpcertification/cache/customCache
Cache ID timeofCache
Cache Value 16:38:16
Exiting MyInvalidationListener.fireEvent()


As you can see i am getting sufficient data to post this cached entry into database.

You can download the modified version of CustomDynaCache portlet to try this listener

Change Entry Listener



If you want you can also attach an listener that gets called every time the cached entry is modified.


public class MyChangeListener implements ChangeListener{

public void cacheEntryChanged(ChangeEvent changeEvent) {
System.out.println("Entering MyChangeListener.cacheEntryChanged()");
System.out.println("Cache Name " +changeEvent.getCacheName());
System.out.println("Cache ID " +changeEvent.getId());
System.out.println("Cache Value " +changeEvent.getValue());
System.out.println("Exiting MyChangeListener.cacheEntryChanged()");

}

}


Then attach the Change event listener to the distributedMap like this

distributedMap= (DistributedMap)context.lookup("wpcertification/cache/customCache");
distributedMap.addChangeListener(new MyChangeListener());


Now when you update the cached entry you should see messages like this in SYstemOut.log

Entering MyChangeListener.cacheEntryChanged()
Cache Name wpcertification/cache/customCache
Cache ID timeofCache
Cache Value 17:16:20
Exiting MyChangeListener.cacheEntryChanged()

Special consideration for Dynamic Cache in clustered environment

If you are using custom object keys, you must place your classes in a shared library. You can define the shared library at cell, node, or server level. Then, in each server create a class loader and associate it with the shared library that you defined.

Place JAR files in a shared library when you deploy the application in a cluster with replication enabled. Simply turning on replication does not require a shared library; however, if you are using application-specific Java objects, such as cache key or cache value, those Java classes are required to be in the shared library.

Custom Cache Instance

If you want to use say 3-4 different caches in your application or you want to better track your cache instance then you can create your own instance of dynamic cache and configure and use it.

You can create custom cache instance either using administrative console or declaratively using your application.

Administrative Console



YOu can create and configure new cache instance using WAS Administrative console. Inside the Administrative Console go to Resources -> Cache Instance -> Object Cache Instance and create a new cache instance like this


Once the instance is created you can access it using

distributedMap= (DistributedMap)context.lookup("/services/cache/samplecache");


Using cacheinstance.properties



Other method of creating cache is that you can create cacheinstance.properties file like this in your WEB-INF/classes forlder. If your using RAD you can create in root of your source folder, so that it gets copied to WEB-INF/classes folder.


cache.instance.0=/wpcertification/cache/customCache
cache.instance.0.cacheSize=1000
cache.instance.0.enableDiskOffload=true
cache.instance.0.diskOffloadLocation=c:/temp/diskOffload
cache.instance.0.flushToDiskOnStop=true
cache.instance.0.useListenerContext=true
cache.instance.0.enableCacheReplication=false
cache.instance.0.disableDependencyId=false
cache.instance.0.htodCleanupFrequency=60


Inside your code you can access the distributed Map object using this code

distributedMap= (DistributedMap)context.lookup("/wpcertification/cache/customCache");


You can download the CustomDynaCache portlet and install it on your server. After installing it try accessing it couple of times and then check it in the CacheMonitor

Caching Custom Objects

The DistributedMap and DistributedObjectCache interfaces are simple interfaces for dynamic cache. Using these interfaces J2EE applications and system components can cache and share java objects by storing references to the object in the cache.

The default dynamic cache instance is created if the dynamic cache service is enabled in the administrative console. The default instance is bound in global JNDI tree at services/cache/distributedmap.

This is the sample code that demonstrate how to use default instance of dynamic cache

public class DynaCachePortlet extends javax.portlet.GenericPortlet {
private DistributedMap distributedMap;
public void init() throws PortletException{
System.out.println("Entering DynaCachePortlet.init()");
super.init();
try {
InitialContext context = new InitialContext();
distributedMap= (DistributedMap)context.lookup("services/cache/distributedmap");
System.out.println("Distributed Map " + distributedMap);

distributedMap.enableListener(true);
} catch (NamingException e) {
e.printStackTrace();
}
System.out.println("Exiting DynaCachePortlet.init()");
}
public void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException {
// Set the MIME type for the render response
response.setContentType(request.getResponseContentType());
System.out.println("Entering DynaCachePortlet.doView()");
String timeOfCache = (String)distributedMap.get("timeofCache");
System.out.println("Value of timeOfCache from Cache " + timeOfCache);
if(timeOfCache == null){
SimpleDateFormat sd = new SimpleDateFormat("HH:mm:ss");
timeOfCache = sd.format(new Date());
System.out.println("Setting value of TimeCache to " + timeOfCache);
distributedMap.put("timeofCache", timeOfCache);
}
response.getWriter().println("Hello from distributedCache " + timeOfCache);
System.out.println("Exiting DynaCachePortlet.doView()");
}
}


Actually using Dynamic Cache is pretty simple, all you have to do is lookup the distributedMap object from the cache and then you can store and retrieve objects from it as normal Hashmap.

In this sample when you go to the View mode of the portlet for first time it will store the current time in the cache and thereafter whenever you go back to the VIEW mode it will always return the same time from cache. I know this is very simple but our main goal is to learn how to use cache.

If you want you can download the DynaCache Sample portlet. Now install it on your portal and once installed try hitting it few times.

If you have not installed Cache Monitor already, install it and then go to it, you should be able to see the default instance in the instance list select it and click on go



It will display the cache statics for the dyna cache. In our case we are saving only one key in cache which is timeofCache so value of Used Entries is 1 and if you refreshed the page 3 times then the cached entry would be accessed 2 times so the value of Cache Hits would be 2


Click on Cache Contents links to go to the page that displays what all keys are stored in cache

Installind Extending Dynamic Cache Monitor

http://www.ibm.com/developerworks/websphere/downloads/cache_monitor.html#download