My JSF Books/Videos My JSF Tutorials OmniFaces/JSF PPTs
JSF 2.3 Tutorial
JSF Caching Tutorial
JSF Navigation Tutorial
JSF Scopes Tutorial
JSF Page Author Beginner's Guide
OmniFaces 2.3 Tutorial Examples
OmniFaces 2.2 Tutorial Examples
JSF Events Tutorial
OmniFaces Callbacks Usages
JSF State Tutorial
JSF and Design Patterns
JSF 2.3 New Features (2.3-m04)
Introduction to OmniFaces
25+ Reasons to use OmniFaces in JSF
OmniFaces Validators
OmniFaces Converters
JSF Design Patterns
Mastering OmniFaces
Reusable and less-verbose JSF code

My JSF Resources ...

Java EE Guardian
Member of JCG Program
Member MVB DZone
Blog curated on ZEEF
OmniFaces is an utility library for JSF, including PrimeFaces, RichFaces, ICEfaces ...

[OmniFaces Utilities] - Find the right JSF OmniFaces 2 utilities methods/functions

Search on blog

Petition by Java EE Guardians

Twitter

vineri, 22 aprilie 2016

Caching via PrimeFaces and EHCache (register MBeans in JConsole via OmniFaces @Eager)

PrimeFaces supports cache at rendering time. Basically, at initial request, PrimeFaces will cache the HTML markup that corresponds to the content delimited by <p:cache/> tag. This means that the initial request doesn't take advantage of caching, and even more it will take longer than usually since at this moment PrimeFaces caches the corresponding markup. But, at postbacks the cache will serve the cached HTML instead of rendering it again via specific renderers. This will reduce the time for loading page at postbacks.

PrimeFaces supports two different providers of cache implementation; EHCache and Hazelcast. In this post, we will take a look on the EHCache provider.

Let's suppose that we have a static table that list the tennis players from ATP. Something like in figure below (this table is produced via a simple usage of the <p:dataTable/> tag):

Rendering a table (<p:dataTable/>) is a time-consuming task, especially if the table contains many rows. So, instead of re-rendering this static table, we better cache it. In order to accomplish that via PrimeFaces and EHCache, we need to follow these steps:

1. Configure the pom.xml to contain the needed dependencies as below:

<dependencies>
 <dependency> 
  <groupId>org.primefaces</groupId> 
  <artifactId>primefaces</artifactId> 
  <version>5.3</version> 
 </dependency>
 <dependency>
  <groupId>org.ehcache</groupId>
  <artifactId>ehcache</artifactId>
  <version>3.0.0</version>
 </dependency>  
 <dependency>
  <groupId>net.sf.ehcache</groupId>
  <artifactId>ehcache</artifactId>
  <version>2.10.1</version>
 </dependency>
 ...
</dependencies>

2.Configure the cache provider in web.xml via the primefaces.CACHE_PROVIDER context parameter:

<context-param>
  <param-name>primefaces.CACHE_PROVIDER</param-name>
  <param-value>org.primefaces.cache.EHCacheProvider</param-value>
</context-param>

3. Configure EHCache. There are multiple ways to accomplish this, and one of these consist in providing the ehcache.xml file in /src/main/resources folder of your application. The content of this file calibrates cache as you want (more details are available here):

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true"
  monitoring="autodetect" dynamicConfig="true">

  <!-- By default, Ehcache stored the cached files in temp folder. -->
  <!-- <diskStore path="java.io.tmpdir" /> -->
      
  <!-- Ask Ehcache to store cache in this path -->
  <diskStore path="D:\\cache" />
       
  <!-- Sample cache named myCache
       This cache contains a maximum in memory of 10000 elements, and will expire
       an element if it is idle for more than 5 minutes and lives for more than
       10 minutes. If there are more than 10000 elements it will overflow to the
       disk cache -->   
  <cache name="myCache"
         statistics="true"
         maxEntriesLocalHeap="10000"
         maxEntriesLocalDisk="1000"
         eternal="false"
         diskSpoolBufferSizeMB="20"
         timeToIdleSeconds="300" timeToLiveSeconds="600"
         memoryStoreEvictionPolicy="LFU"
         transactionalMode="off">
          <persistence strategy="localTempSwap" />
 </cache>
</ehcache>

The path D:\\cache must be manually created, or simply modify this entry to reflect a convenient path for you.

4. Use the <p:cache/> tag to point out the content that should be cached. As you can see in the official documentation this tag support a bunch of optional attributes. We are especially interested in the region attribute which allows us to point to the cache region that we want to use, which is myCache in our case. Of course, this means that we can use <p:cache/> with different regions. Since, by default, the region defaults to view id (if you want to use it like this simply add a <defaultCache/> region), we need to explicitly set it as below:

<p:cache region="myCache">
 <p:dataTable var="t" value="#{playersBean.data}">
  <p:column headerText="Player">
   <h:panelGroup id="playerId">#{t.player}</h:panelGroup>
  </p:column>

  <p:column headerText="Age">
   <h:panelGroup id="ageId">#{t.age}</h:panelGroup>
  </p:column>

  <p:column headerText="Birthplace">
   <h:panelGroup id="birthplaceId">#{t.birthplace}</h:panelGroup>
  </p:column>

  <p:column headerText="Residence">
   <h:panelGroup id="residenceId">#{t.residence}</h:panelGroup>
  </p:column>

  <p:column headerText="Height">
   <h:panelGroup id="heightId">#{t.height} cm</h:panelGroup>
  </p:column>

  <p:column headerText="Weight">
   <h:panelGroup id="weightId">#{t.weight} kg</h:panelGroup>
  </p:column>
 </p:dataTable>
</p:cache>

Done! If you run the application at this point then everything should work as expected. You will notice that initial request take some time to load, while postbacks will work very fast. This is a sign that, at postbacks, the table markup comes from cache.

But, how can we be sure that this is working? Well, EHCache provides management and monitoring using JMX. A simple approach consist in registering the cache statistics in the JDK platform MBeanServer, which works with the JConsole management agent. The needed code is listed below:

CacheManager manager = CacheManager.create();
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
ManagementService.registerMBeans(manager, mBeanServer, true, true, true, true);

We can easily slip this code in an application scoped bean that is eagerly loaded via OmniFaces @Eager.

@Eager
@ApplicationScoped
public class CacheStatisticsBean {

 private static final Logger LOG = Logger.getLogger(CacheStatisticsBean.class.getName());
 private static final String CACHE_MANAGER = "net.sf.ehcache:type=CacheManager,name=__DEFAULT__";
 private static final String CACHE = "net.sf.ehcache:type=Cache,CacheManager=__DEFAULT__,name=myCache";
 private static final String CACHE_STATISTICS = "net.sf.ehcache:type=CacheStatistics,CacheManager=__DEFAULT__,name=myCache";
 private static final String CACHE_CONFIGURATION = "net.sf.ehcache:type=CacheConfiguration,CacheManager=__DEFAULT__,name=myCache";
 private static final ArrayList<ObjectName> objectNames = new ArrayList<ObjectName>() {
  {
  try {
      add(new ObjectName(CACHE_MANAGER));
      add(new ObjectName(CACHE));
      add(new ObjectName(CACHE_STATISTICS));
      add(new ObjectName(CACHE_CONFIGURATION));
  } catch (MalformedObjectNameException ex) {
                Logger.getLogger(CacheStatisticsBean.class.getName()).log(Level.SEVERE, null, ex);
  }
 }
 };

 @PostConstruct
 public void init() {
  try {
      LOG.info("------------ Configure JConsole MBeans ------------");
      MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
      LOG.info("----------------- Unregister MBeans ---------------");
      for (ObjectName name : objectNames) {
           if (mBeanServer.isRegistered(name)) {
               mBeanServer.unregisterMBean(name);
           }
      }

      LOG.info("------------------ Register MBeans ----------------");
      CacheManager manager = CacheManager.create();
      ManagementService.registerMBeans(manager, mBeanServer, true, true, true, true);

      LOG.info("------------ ------------------------  ------------");
  } catch (NullPointerException | InstanceNotFoundException | MBeanRegistrationException ex) {
    Logger.getLogger(CacheStatisticsBean.class.getName()).log(Level.SEVERE, null, ex);
  }
 }
}

Now we can perform a quick test to see if cache is working. For this, we have run the application on Payara 4. The steps of the test are:

1. Delete the content of D:\\cache folder and ensure that Payara is not running.
2. Start the application server. For Payara 4 simply start it via asadmin start-domain form /bin folder.
3. Start JConsole. Simply navigate to your Java home and double-click on jconsole.exe in the /bin folder.
4. Connect to Payara domain as in figure below:
5. After the connection is successfully accomplished navigate to the MBeans tab. Notice there the entry named net.sf.ehcache. This entry was added via the CacheStatisticsBean from above and it is what we are looking for.

At this moment, there is nothing in the cache. For checking this, simply expose some attributes under CacheStatistics as DiskStoreObjectCount, MemoryStoreObjectCount, CacheHits, etc:
 6. Now, deploy and run the application. After the application starts, let's point out that at this moment we have 1 object stored in memory and on disk (check also the D:\\cache content) and nothing was read from cache yet:
7. Now, refresh the browser few times or open the application in multiple tabs or even other browsers. After that refresh the indicators from figure above and you should notice something like below (pay attention to a few more also):
Well, now it is obvious that our cache is working fine.

Programmatically, you can clean cache content like this:
RequestContext.getCurrentInstance().getApplicationContext().getCacheProvider().clear();

The complete application is named PFAndECache.

Niciun comentariu :

Trimiteți un comentariu

JSF BOOKS COLLECTION

Postări populare

Follow by Email

Visitors Starting 4 September 2015

Locations of Site Visitors