Writing commands for command-based invalidation

In order to allow a command call be intercepted by the dynamic cache, the command must be written to the WebSphere Command Framework with its implementation class extending from CacheableCommandImpl (in the com.ibm.websphere.command package). To simplify command writing for command-based invalidation, WebSphere Commerce has updated the abstract classes, ControllerCommandImpl and TaskCommandImpl to extend from CacheableCommandImpl so that any commands extend from these abstract classes would also extend from CacheableCommandImpl and therefore be eligible for command-based invalidation.

When writing such commands, it is also important to know what the invalidation IDs are, and understand the invalidation rules that intercepts calls to the commands. Since invalidation IDs are generated based on methods and fields present in the command as input parameters, all the methods that are required to construct the invalidation IDs, should be provided in the command interface and be implemented.

An example of using command invalidation in WebSphere Commerce

The following example shows how WebSphere Commerce uses command invalidation. When the command DeleteMemberGroupMemberCmdImpl which deletes a particular member belonging to a particular member group is executed successfully, the dynamic cache will invalidate the cache-entry defined in the invalidation rule. In this example, it is defined as "DC_userId:userId" where userId is the value being returned from the getMemberId method. (See Cache servlet filtering for the details of caching the member group information in WebSphere Commerce.) For example, DC_userId:-1000, DC_userId:-1001, and so on. This command has a get method, getMemberId(), that retrieves the user ID that is being deleted and this method is used in computing which cache entries with a dependency ID based on a user ID gets deleted. The same logic applies for the command AddMemberGroupMemberCmdImpl which also has a get method, getMemberId():

<cache-entry>
      <class>command</class>
      <name>com.ibm.commerce.membergroup.commands.AddMemberGroupMemberCmdImpl</name>
      <name>com.ibm.commerce.membergroup.commands.DeleteMemberGroupMemberCmdImpl</name>
  
      <invalidation>DC_userId
          <component type="method"id="getMemberId">
               <required>true</required>
          </component>
      </invalidation>
</cache-entry>

Note:

All the above invalidation rules are shipped with WebSphere Commerce in the sample cache policy. You can find more sample invalidation rules in the WebSphere Commerce installation directory under the /samples/dynacache/invalidation subdirectory. Please see the README file entitled "Sample invalidation cache policies for Dynacache" for more information on how to incorporate the invalidation rules into the cachespec.xml file.

Cache invalidation example

The following example shows how to set up caching policies in the cachespec.xml file to cache the ProductDisplay JSP page for the Consumer Direct business model in WebSphere Commerce and how to invalidate the cache entry by defining the invalidation rules in the same XML file. The example defines multiple dependency IDs along with the cache ID generation rule for the JSP file. Each dependency ID is used to invalidate the cache entry when the cache entry is updated under different circumstances. This example only shows a subset of policies required to invalidate the CachedProductDisplay JSP. For a complete example and detailed information, see the README file in the WC_installdir/samples/dynacache/invalidation directory.

<cache>

   <cache-entry>
       <class>servlet</class>
       <name>/ConsumerDirect/ShoppingArea/CatalogSection/CatalogEntrySubsection/CachedProductDisplay.jsp</name>
       <property name="save-attributes">false</property>

       <!-- Cache ProductDisplay.jsp --> 
       <cache-id>
           <component  id="storeId" type="parameter">
                  <required>true</required>
           </component>
           <component  id="catalogId" type="parameter">
                  <required>true</required>
           </component>
           <component  id="productId" type="parameter">
                <required>true</required>
           </component>
           <component  id="DC_lang" type="attribute">
                  <required>true</required>
           </component>
           <component  id="DC_curr" type="attribute">
                  <required>true</required>
           </component>
           <component  id="DC_porg" type="attribute">
                  <required>true</required>
           </component>
           <component  id="DC_cont" type="attribute">
                  <required>true</required>
           </component>
           <component  id="DC_mg" type="attribute">
                  <required>true</required>
           </component>
       </cache-id>
               
             
       <!-- Used for invalidating the product display cache entry -->
       <!-- that belongs to a specific store                      --> 
       <dependency-id>storeId
           <component id="storeId" type="parameter">
                  <required>true</required>
           </component>
       </dependency-id>
 
       <!-- Used for invalidating the cache entry of a specific product --> 
       <dependency-id>productId
            <component id="productId" type="parameter">
                 <required>true</required>
            </component>
        </dependency-id>

       <!-- Used for invalidating the product display cache entry -->
       <!-- that belongs to a specific catalog in the store       --> 
       <dependency-id>storeId:catalogId
            <component id="storeId" type="parameter">
                 <required>true</required>
            </component>
            <component id="catalogId" type="parameter">
                 <required>true</required>
            </component>
        </dependency-id>
     
       <!-- Used for invalidating the product display cache entry -->
       <!-- that is under a specific contract                     --> 
       <dependency-id>contractId
            <component id="DC_cont0" type="attribute">
                 <required>true</required>
            </component>
       </dependency-id>
   
   </cache-entry>
     
   <cache-entry>
        <class>command</class>
        <sharing-policy>not-shared</sharing-policy>
        <name>com.ibm.commerce.catalogmanagement.commands.AddCatalogDescCmdImpl</name>
        <name>com.ibm.commerce.catalogmanagement.commands.UpdateCatalogDescCmdImpl</name>
  
        <!-- ********************************************************* -->
        <!-- Invalidate all the product page cache entries that        -->
        <!-- might be affected when the catalog description is changed -->
        <!-- ********************************************************* --> 
        <invalidation>storeId:catalogId
        <component  id="getStoreId" type="method">
           <required>true</required>
        </component>
        <component  id="getCatalogId" type="method">
           <required>true</required>
        </component>
        </invalidation>
  
   </cache-entry> 
 
   <cache-entry>
        <class>command</class>
        <sharing-policy>not-shared</sharing-policy>
        <name>com.ibm.commerce.catalogmanagement.commands.ListpriceAddCmdImpl</name>
        <name>com.ibm.commerce.catalogmanagement.commands.ListpriceDeleteCmdImpl</name>
        <name>com.ibm.commerce.catalogmanagement.commands.ListpriceUpdateCmdImpl</name>
        <name>com.ibm.commerce.catalogmanagement.commands.OfferAddCmdImpl</name>
        <name>com.ibm.commerce.catalogmanagement.commands.OfferDeleteCmdImpl</name>
        <name>com.ibm.commerce.catalogmanagement.commands.OfferUpdateCmdImpl</name>
        <name>com.ibm.commerce.catalogmanagement.commands.ProductAttributeUpdateCmdImpl</name>
        <name>com.ibm.commerce.catalogmanagement.commands.AttributeValueUpdateCmdImpl</name>
        <name>com.ibm.commerce.catalogmanagement.commands.AddListpriceCmdImpl</name>
        <name>com.ibm.commerce.catalogmanagement.commands.DeleteListpriceCmdImpl</name>
        <name>com.ibm.commerce.catalogmanagement.commands.UpdateListpriceCmdImpl</name>
        <name>com.ibm.commerce.catalogmanagement.commands.AddOfferCmdImpl</name>
        <name>com.ibm.commerce.catalogmanagement.commands.DeleteOfferCmdImpl</name>
        <name>com.ibm.commerce.catalogmanagement.commands.UpdateOfferCmdImpl</name>
        <name>com.ibm.commerce.catalogmanagement.commands.UpdateAttributeCmdImpl</name>
        <name>com.ibm.commerce.catalogmanagement.commands.UpdateAttributeValueCmdImpl</name>

        <!-- ********************************************************* -->
        <!-- Invalidate the specific product page cache entry when the -->
        <!-- product is updated                                        -->
        <!-- ********************************************************* --> 
        <invalidation>productId
           <component id="getCatentryId" type="method">
                 <required>true</required>
           </component>
        </invalidation>
           
   </cache-entry>         
  
   <cache-entry>
      <class>command</class>
      <sharing-policy>not-shared</sharing-policy>
      <name>com.ibm.commerce.contract.commands.ContractSuspendCmdImpl</name>
      <name>com.ibm.commerce.contract.commands.ContractTCDeployCmdImpl</name>

      <!-- ********************************************************* -->
      <!-- Invalidate all the product page cache entries under a     -->
      <!-- specific contract                                         -->
      <!-- ********************************************************* -->
      <invalidation>contractId
         <component  id="getContractId" type="method">
            <required>true</required>
         </component>
      </invalidation>
   </cache-entry>
  
   <cache-entry>
      <class>command</class>
      <name>com.ibm.commerce.tools.devtools.store.commands.StoreProfileUpdateCmdImpl</name>
      <name>com.ibm.commerce.tools.devtools.flexflow.ui.commands.impl.FlexflowUpdateCmdImpl</name>
      <name>com.ibm.commerce.store.commands.StoreOpenCmdImpl</name>
      <name>com.ibm.commerce.store.commands.StoreCloseCmdImpl</name>
 
      <!-- ********************************************************* --> 
      <!-- Invalidate all the product page cache entries that        -->
      <!-- belong to the store when the store is updated             -->
      <!-- ********************************************************* --> 
      <invalidation>storeId
         <component  id="getStoreId" type="method">
              <required>true</required>
         </component>
      </invalidation>
    
   </cache-entry>
     
         
   <cache-entry>
        <class>command</class>
        <sharing-policy>not-shared</sharing-policy>
        <name>com.ibm.commerce.catalogimport.commands.CatalogImportJobAddCmd</name>

        <!-- ********************************************************* --> 
        <!-- Invalidate all the product page cache entries that        -->
        <!-- belong to the store when the store catalog is updated     -->
        <!-- ********************************************************* --> 
        <invalidation>storeId
           <component  id="getStoreId" type="method">
               <required>true</required>
           </component>
        </invalidation>
   </cache-entry>

</cache>

Cache Invalidation API (DynaCacheInvalidation)

The WebSphere Commerce scheduler periodically calls the DynaCacheInvalidation command to process the records in the CACHEIVL table and then calls the dynamic cache invalidation API to invalidate the cache entries. The startTime is retrieved from the checkpoint file. The checkpoint file is written into the instance_name/cache directory. This directory gets created the first time that DynaCacheInvalidation is executed. The timestamp of the last record is written into the checkpoint file during the wind up stage of the command.

CACHEIVL table

The rules for the DynaCacheInvalidation command when processing the CACHEIVL table are as follows:

Clear parameter on DynaCacheInvalidation

An alternate mechanism to clearing the cache using the "clearall" string value in the CACHEIVL table as described above is to use the "clear" parameter in the DynaCacheInvalidation API:

  1. Place the execution view JSP located at WC56_installdir/samples/dynacache/CacheInvalidation.jsp into the store directory (for example, WC56_installdir/installedApps/cell_name/WC_instance_name.ear/WAR.war/AdvancedB2BDirect)
  2. Log in as a user with administrative rights to tools or stores pages.
  3. From the same browser in step 2 above, issue the following command: http://node_name/webapp/wcs/stores/servlet/DynaCacheInvalidation?clear=true

The Cache Invalidation Report Page gets displayed with all cache entries cleared.

Invalidation in external caches

Result caches can be pushed to external caches such as IBM HTTP Server or Edge Server. Invalidation will occur in external caches when:

Web server plug-in cache

For WebSphere Application Server 5.0, the Web server plug-in contains a built-in Edge Side Include (ESI) processor. The ESI processor has the ability to 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 you restart the Web server.

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 WebSphere Application Server. If the dynamic servlet cache is enabled in the application server and the response indicates that the cache entry can be cached on the Web or Edge Server, the application server returns a Surrogate-Control header in the response to the WebSphere plug-in.

The ESI processor is configurable through the WebSphere Web server plug-in configuration file, plugin-cfg.xml. For example:

<Property Name="esiEnable" Value="true"/>
<Property Name="esiMaxCacheSize" Value="1024"/>
<Property Name="esiInvalidationMonitor" Value="true" />

where:

For more information on the ESI processor, see the topic "Configuring Edge Side Include caching" in the WebSphere Application Server Information Center.

Simple file servlet

By default, WebSphere Commerce does not cache static data such as images and HTML. This is set by setting the system property com.ibm.servlet.file.esi.timeOut to a value of 0 (zero). If you want to enable images and HTML caching, you can either:

A reason why invalidation is required for some images file is that in some business models, the logo and banner can be easily modified.

For example:

<cache-entry>
    <class>servlet</class>
      <name>com.ibm.ws.webcontainer.servlet.SimpleFileServlet.class</name>
      <property name="EdgeCacheable">true</property>
      <cache-id>
         <component id="" type="pathinfo">
            <required>true</required>
        </component>
      </cache-id>
      <dependency-id>
         <component id="" type="pathinfo">
             <required>true</required>
         </component>
      </dependency-id>
      <timeout>1800</timeout>
</cache-entry>

Here is an example of the cache entry for invalidating the images.

<cache-entry>
   <class>command</class>
   <name>com.ibm.commerce.tools.devtools.store.commands.StoreLogoUpdateCmdImpl</name> 
   <invalidation>ConsumerDirect/images/logo.gif</invalidation>
   </cache-entry>

<cache-entry>
   <class>command</class>
   <name>com.ibm.commerce.tools.devtools.store.commands.StoreBannerUpdateCmdImpl</name> 
   <invalidation>ConsumerDirect/images/banner.gif</invalidation>
  </cache-entry>

Feedback