Over a million developers have joined DZone.

How to Enforce Size Constraints (in Infinispan) with Java Instrumentation API

DZone's Guide to

How to Enforce Size Constraints (in Infinispan) with Java Instrumentation API

Most standard caching frameworks don't provide out-of-the-box size constraints. Luckily, the Java instrumentation API's got your back.

· Integration Zone ·
Free Resource

WSO2 is the only open source vendor to be named a leader in The Forrester Wave™: API Management Solutions, Q4 2018 Report. Download the report now or try out our product for free.

Infinispan does not provide size constraints out of the box, just like almost all of the other standard caching frameworks. You may use the Java Instrumentation API as follows to enforce cache size constraints for your product or project that uses Infinispan.

1. Introduce Instrumentation API in Your Code

Develop a class that has the following method in it. The VM will automatically insert an implementation of the Instrumentation API - once it encounters this method.

 public class ServerInstrumentation {  
      private Instrumentation instrumentation = null;   
      private static final Logger LOGGER = Logger.getLogger(ServerInstrumentation.class);  
      private static ServerInstrumentation serverInstrumentation;  

      public static void premain(String args, Instrumentation pInstrumentation) {  
           serverInstrumentation = new ServerInstrumentation();  
      public static Instrumentation getInstance() {  
           return serverInstrumentation.instrumentation;  

You may choose to add the following EJB (optionally) to make this object available throughout your JEE application. Re-implement the methods that you need to use from the Instrumentation API. For example, below we have implemented the sizeOf() , which actually internally invokes Instrumentation.getObjectSize().

 public interface ServerInstrumentationInterface {  
      public long sizeOf(Object toBeSized);  
 public class ServerInstrumentationEJB implements ServerInstrumentationInterface {  
      private static Instrumentation instrumentation;  
           ClassLoader classLoader=this.getClass().getClassLoader().getParent();  
           try {  
                Class clazz = classLoader.loadClass(ServerInstrumentation.class.getName());  
           } catch (Exception e) {  
      public long sizeOf(Object toBeSized) {  
           return instrumentation.getObjectSize(toBeSized);  

Package this code as agent.jar with the following contents in MANIFEST.MF.

Premain-Class: ServerInstrumentation  
 Agent-Class: ServerInstrumentation  

You should place this in the normal deployment location (standalone\deployments) of Wildfly and also mention it as the  -javaagent during server startup. The following contents should be in Wildfly the standalone.bat . 

 "%JAVA%" %JAVA_OPTS% "-javaagent:%JBOSS_HOME%\standalone\deployments\agent.jar" ^  
  "-Dorg.jboss.boot.log.file=%JBOSS_LOG_DIR%\server.log" ^  
  "-Dlogging.configuration=file:%JBOSS_CONFIG_DIR%/logging.properties" ^  
   -jar "%JBOSS_HOME%\jboss-modules.jar" ^  
   -mp "%JBOSS_MODULEPATH%" ^  
    org.jboss.as.standalone ^  
   "-Djboss.home.dir=%JBOSS_HOME%" ^  

At the end of this, the Instrumentation methods are available throughout your JEE application for all instrumentation purposes. The EJB will act as a facade over this functionality. Make sure that the following settings exist in your standalone.conf.bat:

set "JAVA_OPTS=%JAVA_OPTS% -Djava.net.preferIPv4Stack=true -Djava.util.logging.manager=org.jboss.logmanager.LogManager -Xbootclasspath/p:d:\wildfly\modules\system\layers\base\org\jboss\log4j\logmanager\main\log4j-jboss-logmanager-1.1.0.Final.jar;d:\wildfly\modules\system\layers\base\org\jboss\logmanager\main\jboss-logmanager-1.5.2.Final.jar" 
set "JAVA_OPTS=%JAVA_OPTS% -Djboss.modules.system.pkgs=org.jboss.byteman,org.jboss.logmanager"

2. Use This in Your Actual Application (Client)

The lookup code is provided below to perform methods using Instrumentation, such as Object Size. This may be used for multiple purposes in your JEE products or solutions, apart from caching constraints.

// let's do the lookup           
ServerInstrumentationInterface cacheInstrumentationService = (ServerInstrumentationInterface)   
               initialContext.lookup("java:global/agent/" + ServerInstrumentationEJB.class.getSimpleName());  
cacheInstrumentationService.sizeOf(new String("Java and Infinispan Caching Innovation")));  

The following method can be implemented in a different class (CacheInstrumentation.java) to perform the following function every time you add an element to the cache to check whether the Allocated Size is exceeded. This will have the above EJB Reference/Instance encapsulated in it.

public boolean isEntityCacheLimitExceeded(Cache cache) {  
           boolean exceeded=false;  
           long currSize = cacheInstrumentationService.sizeOf(cache);  
           long allocSize = 10*1000*1024;  // 10 MB = 10 * 1000 * 1024 bytes
           if(currSize >= allocSize) {  
           return exceeded;  

3. Use ConfigurationBuilder to Introduce Constraints

The following method will be used to setConfiguration(), which will help to notify Infinispan that the configuration has changed so that it can perform eviction dynamically.

public class CacheManagerUtilities {  
      public static Configuration setConfiguration(int maxNumberofEntries) {  
           return new ConfigurationBuilder().eviction().maxEntries(maxNumberofEntries)  

4. Request and Enforce Eviction Using EvictionManager 

Every time you put or add an element in your cacheyou can use sizeOf() from the EJB to check if the size constraints are exceeded. If they are, then dynamically change the configuration to make the max-entries limit to the currently existing max-entries. We will now rely on Infinispan's Eviction Algorithms to kick in and do its duty.

public void put(K key, V value) {            
           while (true) {  
                if (!cacheInstrumentation.isEntityCacheLimitExceeded(cache)) {  
                     cache.put(key, value);  
                } else {  
                     // set the configuration to the infinispan cache programmatically  
                     Configuration configuration = CacheManagerUtilities.setConfiguration(cache.size());  
                     cache.getCacheManager().defineConfiguration(cache.getName(), configuration);  

                     // start eviction manually  
                     EvictionManager evictionManager = cache.getAdvancedCache().getEvictionManager();  

                     // set the configuration to the infinispan cache programmatically - large number such as 9999  
                     configuration = CacheManagerUtilities.setConfiguration(9999);  
                     cache.getCacheManager().defineConfiguration(cache.getName(), configuration);  

The above usually performs three things:

  1. Expire all elements that have reached expiration time (data container).

  2. Expire all elements that have reached expiration time (cache store).

  3. Remove all elements as per the eviction policy (prunes to max-entries).

You may also test the above using Arquillian to try out whether the eviction works by creating data within test cases that adhere to LIRS, LRU, and size constraints.

Note that the above code will also make sure that only serializable objects are allowed to be cached. It's suitable for all Java/JEE products and solutions that need to enforce size as a constraint mechanism for caching. 

Happy caching!

Read the WSO2 Methodology for Agility to see how you can transform your integration projects from semi-agile to a scalable continuous agile approach.

jee ,infinispan ,wildfly ,instrumentation ,integration ,api ,tutorial ,size constraints

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}