Contact Us 1-800-596-4880

Caching in a Custom Policy for Mule 4

When a value needs to be retrieved from an external system, like a token, to be used in an app or a policy, the value can be stored temporarily in the Mule 4 Object Store. Information on the Object Store connector is available in Anypoint Exchange. See also the Object Store backend documentation.

You can enable access to Object Store from Anypoint Platform > Runtime Manager. However, the Runtime Manager object store is only for CloudHub and hybrid applications.

An application that stores the data in the object store can, or any of its workers, retrieve the data. The object store provides the cache for your custom policy, thus avoiding making external calls multiple times. In addition, the object store can be used by multiple policies applied in different APIs running in different instances in CloudHub.

Caching with the Object Store

Mule provides the Mule 4 Object Store connector that retrieves and stores values for up to 30 days.

The following example retrieves data from an object store and if no data is found for a specific key, a new value is retrieved from the external system and stored in the object store.

<try>
   <os:retrieve key="token" target="myVar" objectStore="objectStore"/>

   <error-handler>
       <on-error-continue type="OS:KEY_NOT_FOUND" logException="false">
           <http:request method="GET"
            url="http://localhost:8081/retrieve"
            target="myVar"/>

           <os:store key="token" objectStore="objectStore">
               <os:value>
                   #[vars.myVar]
               </os:value>
           </os:store>
       </on-error-continue>
   </error-handler>
</try>

In this example, the <os:retrieve key="token" target="myVar" objectStore="objectStore"/> statement executes the retrieve operation of the Object Store connector using the value token as the key to look for and assign the retrieved value to the myVar variable.

If no value is associated with a key, an error is thrown by the retrieve operation. This is why the operation is enclosed in a <try> block, so that should an error occur, it can be handled by retrieving a fresh value and storing it in the object store.

The HTTP request is implemented inside the <error-handler> block. The value is obtained through an HTTP call and assigned to the myVar variable. Then that value is stored in the object store using the store operation of the Object Store connector.

After this block executes, myVar contains a value that was retrieved from either the cache or the external system, and stored in the cache for later use. You can use that value for any purpose such as adding it as a new header to a request or response, or more.

Defining an Object Store

To use the Object Store connector operations, you need to first define the object store that those operations use.

The following configuration defines the myObjectStore and stores the values for an hour. The object store in this example is set to be persistent so that if the runtime is restarted, the Object Store won’t be cleared.

<os:object-store name="myObjectStore" persistent="true"
 entryTtl="1" entryTtlUnit="HOURS"/>

The time to live (TTL) value is set to 1 hour.

Caching in Runtime Manager

When the policy is deployed to an API running in Runtime Manager and that instance also has the Object Store v2 (OSv2) enabled, then using the above persistent object store configuration results in the policy caching the values into the Cloud Object Store.

However, there is a limitation, as the configured entry time to live is not honored when using it, as the Object Store connector is not able to create new stores, but rather it uses the default object store partition that has a 30 days TTL.

Enabling an Object Store for Multiple APIs

When using Object Store v2, you can share the cache between multiple API deployments. To achieve this, the object store configuration needs to have the same name attribute in every deployed policy and also the runtime needs to have a system property named objectstore.enable.global.sharing that is set with the value true.

After enabling this, data persisted by a policy running in that instance is also available to policies running in other instances under the same environment.

Caution: Enabling the global sharing OSv2 mode makes that object store used by the application or any other policy also shared across all your organization’s APIs.

Reviewing full example of a Policy Caching with an Object Store

This example shows a policy that adds a header to an incoming HTTP Request with a value retrieved from an external system and cached in the Mule Object Store.

<?xml version="1.0" encoding="UTF-8"?>
<mule xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.mulesoft.org/schema/mule/core"
xmlns:os="http://www.mulesoft.org/schema/mule/os"
xmlns:http="http://www.mulesoft.org/schema/mule/http"
xmlns:http-policy="http://www.mulesoft.org/schema/mule/http-policy"
xmlns:http-transform="http://www.mulesoft.org/schema/mule/http-policy-transform"
xsi:schemaLocation="http://www.mulesoft.org/schema/mule/core
http://www.mulesoft.org/schema/mule/core/current/mule.xsd
http://www.mulesoft.org/schema/mule/http
http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd
http://www.mulesoft.org/schema/mule/http-policy
http://www.mulesoft.org/schema/mule/http-policy/current/mule-http-policy.xsd
http://www.mulesoft.org/schema/mule/http-policy-transform
http://www.mulesoft.org/schema/mule/http-policy-transform/current/mule-http-policy-transform.xsd
http://www.mulesoft.org/schema/mule/os
http://www.mulesoft.org/schema/mule/os/current/mule-os.xsd">

 <!-- Object Store configuration -->
 <os:object-store name="myObjectStore"
 persistent="true"
 entryTtl="1"
 entryTtlUnit="HOURS"/>

 <http-policy:proxy name="caching">
     <http-policy:source>
         <try>
             <!-- Checking Cache for already stored value -->
             <os:retrieve key="aKey" target="myVar" objectStore="myObjectStore"/>

             <error-handler>
                 <on-error-continue type="OS:KEY_NOT_FOUND" logException="false">
                     <!-- If value not found, then retrieve it from  -->
                     <!-- the external system. -->
                     <http:request method="GET"
                     url="http://localhost:8081/retrieve"
                     target="myVar"/>

                     <!-- After retrieving a fresh value, store it in the cache -->
                     <os:store key="aKey" objectStore="myObjectStore">
                         <os:value>
                             #[vars.myVar]
                         </os:value>
                     </os:store>
                 </on-error-continue>
             </error-handler>
         </try>

         <!-- Use the retrieved value to add a new header -->
         <http-transform:add-headers outputType="request">
             <http-transform:headers>#[{'new-custom-header': vars.myVar}]</http-transform:headers>
         </http-transform:add-headers>

         <http-policy:execute-next/>

     </http-policy:source>
 </http-policy:proxy>

</mule>

The necessary dependencies for the example are:

<dependencies>
   <dependency>
       <groupId>org.mule.connectors</groupId>
       <artifactId>mule-http-connector</artifactId>
       <version>1.2.1</version>
       <classifier>mule-plugin</classifier>
       <scope>provided</scope>
   </dependency>

   <dependency>
       <groupId>org.mule.connectors</groupId>
       <artifactId>mule-objectstore-connector</artifactId>
       <version>1.1.1</version>
       <classifier>mule-plugin</classifier>
   </dependency>

   <dependency>
      <groupId>com.mulesoft.anypoint</groupId>
      <artifactId>mule-http-policy-transform-extension</artifactId>
      <version>1.1.0</version>
      <classifier>mule-plugin</classifier>
  </dependency>
</dependencies>