Using a cache to improve performance for custom policy retrieval from WebSphere Service Registry and Repository in WebSphere ESB

When developing a custom mediation to retrieve custom metadata, such as a policy from IBM® WebSphere® Registry and Repository, the number of requests can become very large and could dramatically increase the resources needed. One way to reduce the load and increase response time is to use a cache. This article shows how you can extend a mediation primitive deployed in WebSphere Enterprise Service Bus (ESB) to add a cache support This content is part of the IBM WebSphere Developer Technical Journal.

Share:

Arnauld Desprets, IT Architect, IBM

Arnauld DespretsArnauld Desprets is an IT architect with IBM Software Services for WebSphere in the United Kingdom. Arnauld has over 10 years of consulting with companies all over Europe. He has over eight years of experience with WebSphere Application Server, and now specializes in SOA and Web services, especially in WS-Security. His latest interest is on the role of governance and the registry, including the WebSphere Services Registry and Repository in an SOA. You can reach Arnauld at arnauld_desprets@fr.ibm.com.



24 June 2009

Also available in Chinese Japanese

Introduction

When integrating IBM WebSphere Enterprise Service Bus (ESB) and IBM WebSphere Service Registry and Repository (hereafter referred to as Service Registry) with a custom mediation, the issue of performance comes up very quickly. In fact, in almost every situation, there is a need to add caching capabilities in the mediation to not only shorten the response time, but also to reduce the resource consumption of the Service Registry servers themselves.

This article is a follow-up to Andre Tost’s article, Establish a policy-driven SOA using WebSphere Service Registry and Repository and WebSphere ESB, which explains how to implement a mediation consuming custom WS-Policy policies that are defined in Service Registry and enforced in WebSphere ESB. The information in this article not only concentrates on the implementation details of the cache, but on other important considerations as well, such as how to monitor and tune the cache.

André’s article used an example "audit" policy, the scenario for which went like this:

The mediation sitting in front of the TemperatureConverter service checks if there is an Audit policy attached (defined in Service Registry) to the service. If there is one, then the Audit primitive is invoked as part of the request message handling. Figure 1 shows the flow diagram for the CelciusToFahrenheit operation, where you can see the custom primitive.

Figure 1. Flow diagram
Figure 1. Flow diagram

Later in this article, you will see how you can change the custom Java™ PolicyRetrieval primitive and add a cache.


Architecture considerations

Before jumping to the implementation, let’s start by looking at some of the implications you face when using a cache:

  • Simple scenarios

    The simplest scenario is the one in which you have only one WebSphere ESB server instance and, hence, only one deployed mediation. There is no need to worry about data replication. You accept that the data might not be valid after some time in the cache. You just put and get the data from the cache. Data invalidation is governed by the timeout of the cache itself. As you will see later, adding a cache in this scenario is very easy.

  • More complex scenarios

    In this scenario, it is not acceptable that the cache contains invalid data at anytime, or at least you want to minimize the period during which the data will be invalid to the greatest extent possible. To do so, you consider notifying the cache when specific changes occur to the policies attached to the service operation. In other words, changing a policy will trigger an invalidation of the data in the cache. On the registry side, the implementation could be done using a Service Registry "notifier" plug-in. The implementation from a cache perspective is very simple since the cache already exposed a method to invalidate an entry. The only difficulty is really to access the cache from Service Registry, since it can run in different cells. There are several ways to do that, and a pretty simple one is to create a Web service or a Web application that could be deployed on the server where the cache resides. Service Registry will then invoke the cache instance through that interface.


Cache considerations

There are several key considerations to keep in mind when using a cache, some of which have been described in other articles. Let’s start with some important considerations from the developerWorks article, The requester side caching pattern specification, Part 1: Overview of the requester side caching pattern. The text below in italics is taken from that article:

  • Data keys

    A basic requirement for caching is that the cached data can be identified explicitly and uniquely. The identifier for a cacheable data item is called its key. Fundamentally, the cache supports operations to insert a data item with a specified key and then retrieve it later with the same key. So it must be true that if two data items represent different information they must have different keys. It is also necessary that each data item must have only one key.

    For the purpose of this article, take a simple approach by assuming uniqueness based on the name and namespace of a service operation. Also assume here that you have only one logical instance of Service Registry that contains the policy and policy attachment definition for the operation.

  • Data volatility

    If the data associated with a key changes after a previous version has been placed in the cache, then retrieving data with this key from the cache will retrieve obsolete data. This problem of data volatility is usually the biggest limitation to using a cache. There are two basic strategies for handling it, time out of items in the cache and an explicit invalidation of items in the cache:

    • Time out: Data is frequently subject to change, but it is ok to retrieve out-of-date data for a short period.

      When this is the case, the caches supported in this pattern allow a time-out value to be set.

    • Explicit invalidation: It is possible that all sources of change to data items held in the cache must cause changed data items to be removed from the cache immediately. You want to explicitly remove invalid data from the cache.

      However, it is the responsibility of the pattern user to ensure that the removals occur. In this case, you want Service Registry to inform the cache that data has changed. This is pretty easy to do with a Service Registry plug-in, but the difficulty with this, as you will see later, is that since there are several caches (one for each instance of WebSphere ESB), you might have to try and notify all those instances. In the case where you use the Data Replication Service (DRS) the entry should be in all the cache instances, but without DRS, the entry might not be in all instances; therefore, it is more likely that an exception will occur when the entry is invalidated.

  • Working set size

    A cache is said to have a working set size. This is the number of items that need to be kept in the cache so that most requests for an item will be satisfied from the cache. Generally, …a cache will operate with a bounded number of items that it can contain, referred to as its capacity. If more items than this are added then some items will be evicted. The eviction policy…of the cache implementations…is least-recently-used or LRU. This policy evicts the items that have gone the longest without being referenced. Under this policy if the cache's capacity is large enough then its working set will be maintained in the cache and it will be effective at accelerating requests to the target component. However if it is too large then space will be wasted. If it is too small the working set will not fit and the cache will be much less effective.

    Hence, the importance of monitoring the behavior of the cache is to maximize its efficiency, which is why this article will discuss how to monitor the cache.

  • Preload and cache refresh

    Under very heavy load, additional considerations might apply to help prevent other events from occurring when using a cache and Service Registry. One particular event happens in fairly rare circumstances, namely when an entry either is not already in the cache or because it has been invalidated for some reason (for example, if the cache is full and if the cache size is very small). The situation is that a large number of identical requests are sent to the Service Registry server to retrieve the same value. This has two effects:

    • The Service Registry server receives a large number of identical requests.
    • The response time to get the result is high because of the high load.

    You can reduce this effect by using a mutex, which is a technique that enables you to get exclusive access to shared resources. In this case, you want the mutex to be blocking the retrieval of information from one and only one cache entry, if possible, and to block other requests for the same ESB resource. It is possible to find Java implementations for mutexes on the Web (see Resources).

    Another consideration has to do with the use of a preload mechanism, which is kind of a variation of the previous consideration. In this case, you want to load data into the cache at initial application startup or at any other predefined event that could trigger high load. This reduces the response time for the first requests. Again, there are several ways you can implement this, such as using a custom application that would send test data to the service while going through the ESB or the ESB instances.

With all these considerations in mind, let’s now look at a pretty well known WebSphere Application Server implementation.


Using DistributedMap

To implement the cache you are going to use the DistributedMap interface, J2EE® applications, and system components to cache and share Java objects by storing a reference to the object in the cache. Figure 2 provides some details about the distributed map. You can certainly recognise the various capabilities of advanced caches, such as the capability to perform replication between several instances of the cache, the capability to offload the contents to a disk, the replacement policy implementation, and so on.

Figure 2. DistributedMap overview
Figure 2. DistributedMap overview

In this example, you will configure the cache instance using the WebSphere Application Server administrative console. (Alternatives to this method can be found in the WebSphere Application Server Information Center.)

Code changes

As you will see, only a few lines of code are needed to implement the cache. The most important class here is PolicyQueryTest. You can find the invocation of the cache in the hasPolicyDefined(String, String, String) method. Listing 1 below shows an extract of this method.

Listing 1
1 List<String> policies = null;
2 long beforeCache = System.currentTimeMillis();
3 CacheKey cKey = new CacheKey(policyElementName, namespace, operation);
4 logger.logp(Level.FINEST, CLASS_NAME, METHOD_NAME, "Checking if there is not already 
an entry in the cache with key {" + cKey + "}");
5 policies = (List) CacheManager.getInstance().get(cKey.toString());
6 if (policies == null) {
7   try {
8     policies = runQuery(namespace, operation);
9     long afterMiss = System.currentTimeMillis();
10    logger.logp(Level.FINEST, CLASS_NAME, METHOD_NAME, "CACHE Miss: key {" + cKey + "} 
retrieved in " + (afterMiss - beforeCache) + " ms");
11    // If policies are null should we add it
12    if (policies == null) {
13      logger.logp(Level.FINEST, CLASS_NAME, METHOD_NAME, "No policies applies to this 
operation");
14    } else {
15      logger.logp(Level.FINEST, CLASS_NAME, METHOD_NAME, "Adding the polices in the 
cache");
16      CacheManager.getInstance().put(cKey.toString(), policies);
17    }
18   } catch (Exception x) {
19    logger.logp(Level.SEVERE, CLASS_NAME, METHOD_NAME, "Exception during policy retrieval
: " + x.toString());
20    throw new RuntimeException(x);
21  }
22 } else {
23   long afterHit = System.currentTimeMillis();
24   logger.logp(Level.FINEST, CLASS_NAME, METHOD_NAME, "CACHE HIT: key {" + cKey + "} 
retrieved from cache in " + (afterHit - beforeCache) + " ms");
25 }
  • On line 3 in Listing 1, you create the key associated with the policy name, the operation name, and namespace. The operation name and namespace are part of the interface of the mediation primitive. The CacheKey class is a simple concatenation. If you have different versions of the same operation you could obtain the same key, unless either the name of the operation or the namespace convey version information about the operation or the service. In general, this is a good pratice when performing service management. You also want the CacheKey to be human readable in case you want to remove the entry using the cache monitoring application. If the entries in the cache can only be removed programmatically (aside from removing the cache itself), then you could use, for example, hash values, to delete entries.
  • On line 5, you try to get the value from the cache using the key you just generated. If there are no entries, you should query the registry using the namespace and operation (line 8) and save the result in the cache (line 16).

Listing 2 shows the main code for the CacheKey. As you can see, this is only a simple concatenation of the name and namespace, but it could be made more complex to ensure uniqueness in each cases. The interesting line here is line 11, which contains the key value.

Listing 2
1 public class CacheKey {
2   private String policyElementName;
3   private String namespace;
4   private String operation;
5   public CacheKey(String policyElementName, String namespace, String operation) {
6     this.policyElementName = policyElementName;
7     this.namespace = namespace;
8     this.operation = operation;
9   }
10  public String toString() {
11    return policyElementName + "." + namespace + "." + "." + operation;
12  }
13 }

Organisation of the code provided with this article

To help you perform a test in your environment, sample code is provided with this article. Below is some information that explains how this code is organised. Table 1 lists the modules you can use to run the complete test. Unlike the earlier article, this example uses a local implementation for the service provider.

Table 1
ProjectDescription
TemperatureConverterProviderEAREAR including the implementation of the TemperatureConverter service
TemperatureConverterTemperatureConverter service implementation
TemperatureConverterClientEAREAR including the implementation of the TemperatureConverter service consumer
TemperatureConverterClientTemperatureConverter service consumer implementation, this code is generated from the WSDL exposed by the mediation module
PolicyMediationMediation module
PolicyQueryLibrary containing the Java implementation of the mediation module and hence the cache implementation

Configuring the cache in WebSphere ESB

First, start by configuring the cache in the WebSphere Application Server administrative console. Navigate to Resources => Cache instances => Object cache instances. Click the New button, and the values for the fields listed below, and then click OK (Figure 3):

  • Name: WSRRPolicyCache
  • JNDI Name: services/cache/PoliciesQueries (this is the reference you had in the code earlier)
Figure 3. Cache instance configuration
Figure 3. Cache instance configuration

Test the application

At this point, we will assume that you have installed the two enterprise archives file of the TemperatureConverter consumer and provider, and also published the Policy mediation module in WebSphere ESB.

The test data in Service Registry is made up of:

  • The WSDL of the service provider for the TemperatureConverter service: TemperatureConversions.wsdl (Figure 4).
    Figure 4. WSDL document in WSRR
    Figure 4. WSDL document in WSRR
  • The policy file including the audit policy: audit.xml.
  • The policy attachment service: auditatachment.xml (Figure 5).
    Figure 5. Policy files
    Figure 5. Policy files

Now, you are ready to run the test:

  1. In order to see the caching trace, ensure the trace has been set to *=info: com.ibm.sample=all in the WebSphere admin console.
  2. In a browser, enter the URL of the Web service client tester application, which in this case is: https://localhost:9443/TemperatureConverterClient/jsps/TestClient.jsp.
  3. Click on the celciusToFahrenheit(java.math.BigDecimal) link and enter any value.
  4. Click on the Invoke button. The first hit is supposed to query the policy from Service Registry and put it into the cache. You should get a result similar to that shown in Figure 6.
    Figure 6. Web services invocation result
    Figure 6. Web services invocation result
  5. Now, enter any new value and click Invoke again. The second hit is supposed to find the policy in the cache.
  6. In your trace output, look for the lines shown in Listing 3.
    Listing 3
    TT T1 SystemOut O   Operation name is : CelciusToFahrenheit
    TT T1 SystemOut O   Operation namespace is : http://webservices.daehosting.co
    m/temperature
    TT T1 sample    > com.ibm.sample.PolicyQueryTest hasPolicyDefined ENTRY
    TT T1 sample    3 com.ibm.sample.PolicyQueryTest hasPolicyDefined policyEleme
    ntName: tns:Audit
    TT T1 sample    3 com.ibm.sample.PolicyQueryTest hasPolicyDefined Checking if
    there is not already an entry in the cache with key {tns:Audit.http://webserv
    ices.daehosting.com/temperature..CelciusToFahrenheit}
    TT T1 sample    3 com.ibm.sample.PolicyQueryTest runQuery Policy document: [B
    @43fc43fc
    TT T1 sample    3 com.ibm.sample.PolicyQueryTest hasPolicyDefined CACHE Miss:
    key {tns:Audit.http://webservices.daehosting.com/temperature..CelciusToFahren
    heit} retrieved in 344 ms
    TT T1 sample    3 com.ibm.sample.PolicyQueryTest hasPolicyDefined Adding the 
    polices in the cache
    TT T1 sample    3 com.ibm.sample.PolicyQueryTest hasPolicyDefined Policy name
    : tns:Audit
    TT T2 SystemOut O   Operation name is : CelciusToFahrenheit
    TT T2 SystemOut O   Operation namespace is : http://webservices.daehosting.co
    m/temperature
    TT T2 sample    > com.ibm.sample.PolicyQueryTest hasPolicyDefined ENTRY
    TT T2 sample    3 com.ibm.sample.PolicyQueryTest hasPolicyDefined policyEleme
    ntName: tns:Audit
    TT T2 sample    3 com.ibm.sample.PolicyQueryTest hasPolicyDefined Checking if 
    there is not already an entry in the cache with key {tns:Audit.http://webserv
    ices.daehosting.com/temperature..CelciusToFahrenheit}
    TT T2 sample    3 com.ibm.sample.PolicyQueryTest hasPolicyDefined CACHE HIT: 
    key {tns:Audit.http://webservices.daehosting.com/temperature..CelciusToFahren
    heit} retrieved from cache in 0 ms

    You can see in those traces that the first time there is no entry in the cache and the request took 344 ms (see the message CACHE Miss, shown in bold type in Listing 3), whereas the second time the policy exists in the cache, there is no need to query Service Registry and the response is immediate at 0 ms (see the message CACHE HIT, also shown in bold). (These figures do not reflect an actual production environment.)

Here is a helpful hint: You can use the REST interface to validate that the policy is correctly applied:

https://localhost:9444/WSRR/6.1/Metadata/XML/GraphQuery?query=/WSRR/WSDLPortType[@name=%27TemperatureConversionsSoapType%27%20and %20@namespace=%27http://webservices.daehosting.com/temperature%27]/ operations[@name=%27CelciusToFahrenheit%27].


Exploring the cache scenario to use notification

You have seen how easy it was to add a cache in the mediation primitive. Next, you are going to study a more complex scenario where you not only want to use the cache as before, but you also want to be able to explicitly notify the cache of a policy change that it needs to make. In this scenario, as explained earlier, you will use the notification capability of Service Registry to programmatically invalidate the cache entries related to a policy.

First, here is a quick overview of how policies are modeled in Service Registry. This will help you understand how to find the operations that are affected by a change on a policy. Ultimately, you need to calculate the key (or keys) that needs to be updated in the caches.

Figure 7. Policy model in Service Registry
Figure 7. Policy model in Service Registry

There are other scenarios beyond explicit policy changes that also apply here; in other words, entities that affect the result of the mediation. One example could be when an attachment is changed as a result of a policy being attached to a new entity in another operation. The details of how to update a policy or other entity are beyond the scope of this article, so we won’t go into that here. What you want to do here is capture the event that results in a changed policy.

What you are interested in here is the logical object. Look for all the operations that are attached to this policy, which will give you the objects in the cache that need to be updated. Remember that the cache key is a concatenation of the policyElementName, the name, and namespace of the operation. With this key, you are ready to update all the mediations that are hosting cached policy information.

This article does not provide a full implementation of this scenario, but does provide you with some important implementation elements. Consider the case where the configuration of the different mediations can change frequently (for example, the addition of a new instance of WebSphere ESB) and need to be easily accessible and updatable. To support this, you can use a Service Registry standard "user configuration" to specify the server properties. A user configuration is then managed directly inside Service Registry through the configuration perspective. Listing 4 shows a sample of a user configuration file.

Listing 4
<ServerConfigurations>
	<ServerConfiguration hostname="127.0.0.1" port="2819" />
	<ServerConfiguration hostname="127.0.0.1" port="2820" />
</ServerConfigurations>

To load a user configuration file, use the Service Registry administrative console. Go to the configuration perspective, and then navigate to Active Configuration Profile => Plug-ins => User Configurations. Select Load User Configuration and enter the appropriate file name.

Listing 5 shows a code segment that consumes this configuration file. This code is in the com.ibm.sample.wsrr.notifier.userconfig package in the CacheNotifier Java project.

Listing 5
AdminService adminService = AdminServiceFactory.getAdminService();
byte[] bytes = null;
// Locate the ServiceRegistryRepository MBean
ObjectName queryName;
try {
	queryName = new ObjectName("WebSphere:type=ServiceRegistryRepository,*");
	Set s = adminService.queryNames(queryName, null);
		// Find the first MBean reference
	Iterator iter = s.iterator();
	ObjectName mBean = null;
	if (iter.hasNext()) {
		mBean = (ObjectName) iter.next();
		// got one
		// Format the call parameters
		String[] signature = new String[] { String.class.getName(), 
			String.class.getName() };
		Object[] params = new Object[] { configName, configType.toString() };
		bytes = (byte[]) adminService.invoke(mBean, "retrieveConfiguration", 
			params, signature);
	}
} catch (MalformedObjectNameException e) {
...
}

This code is simply called from a Service Registry notifier. A very simple sample is provided in the com.ibm.sample.wsrr.notifier package in the CacheNotifier Java project. To configure this notifier, see the Configuring custom plug-ins section in the Service Registry Information Center.


Using the extended cache monitor

Now, return to the first running scenario and take a look at the tuning of the cache. Here, we'll just explain how to use the cache monitor to provide useful information on the cache behavior. You can then use the statistics provided to fine tune the cache by changing its configuration, size, time out, if you need to use disk offload, and so on. You can also validate the number of hits and misses, and check the efficiency of the cache.

To install the cache monitor application, you first need to install the default cache monitor application provided with WebSphere Application Server, then update this application with the one provided in the Extended Cache Monitor technology preview. When you have completed the installation, sign in as the security role to perform user/group mapping:

  1. In a browser, enter the cache monitor Web application URL, in this case: http://127.0.0.1:9080/cachemonitor/logon.jsp.
  2. Enter a valid user ID/password combination.
  3. From the option menu (Figure 8), select the cache instance that you want to monitor. In this case: services/cache/PoliciesQueries. Click OK.
    Figure 8. Cache statistics - Selecting the right instance
    Figure 8. Cache statistics - Selecting the right instance
  4. Click the Cache Statistics link on the left navigation panel. In this test, you can see that the cache contains one entry, it was hit 17 times, and three of those times it did not contain the value that was searched (Figure 9).
    Figure 9. Cache statistics - Results
    Figure 9. Cache statistics - Results
  5. By clicking the Cache Contents link, you get the content of that cache instance (Figure 10).
    Figure 10: Cache content
    Figure 10: Cache content

If you wish, you can clear the cache or invalidate specific entries from the cache monitor application.


Conclusion

This article showed using the DistributedMap feature in WebSphere Enterprise Service Bus can help quickly add a cache to a mediation flow component. It first reviewed various scenarios where a cache is added and then used a downloadable example to show how to add a cache. The article concluded with a quick overview of the extended cache monitor application.


Acknowledgements

The author thanks André Tost from the IBM Software Services for WebSphere team for his help with this article.


Download

DescriptionNameSize
Code samplePMWC_example_materials.zip334 KB

Resources

Learn

Get products and technologies

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into WebSphere on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere
ArticleID=399259
ArticleTitle=Using a cache to improve performance for custom policy retrieval from WebSphere Service Registry and Repository in WebSphere ESB
publish-date=06242009