Thursday 21 November 2013

Caching in Spring using Ehcache and monitoring it using the Ehcache Monitor

Cache as we all know is a component that transparently stores data so that the future requests for that data can be served faster.Previously calculated or duplicates of the original data are stored in the cache.

Ehcache is java's most widely used cache. It is an open source, standards-based cache for boosting performance, offloading your database, and simplifying scalability. It's the most widely-used Java-based cache because it's robust, proven, and full-featured. Ehcache scales from in-process, with one or more nodes, all the way to mixed in-process/out-of-process configurations with terabyte-sized caches.

We can cache the values returned by any function based on the arguments that are passed to that function.Cache values are stored in the form of key-value pairs.The function arguments act as the key. One can specify which argument/arguments act as the key.Some easy configurations are required to enable caching in your application.

The Ehcache Monitor is an add-on tool for Ehcache which provides enterprise-class monitoring and management capabilities for use in both development and production. It is intended to help understand and tune cache usage, detect errors, and provide an easy to use access point to integrate with production management systems. It also provides administrative functionality such as the ability to forcefully remove items from caches.

To install the Monitor, you need add the Monitor Probe JAR to your app and a few lines of config in ehcache.xml. The package contains a probe and a server. The probe installs with your existing Ehcache cache instance, and communicates to a central server. The server aggregates data from multiple probes. It can be accessed via a simple web UI, as well as a scriptable API.



Steps:

1.Download ehcache core jars from here or  if you are using Maven, add the following dependency into your pom.xml:

<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>2.6.6</version>
</dependency>

2. Download the Ehcache monitor kit from here . After you get the Ehcache monitor kit look for the ehcache-probe-ver.jar, copy this jar and paste it into your WEB-INF/lib folder. 

3. Ehcache.xml is the configuration file having all the necessary configuration. Create this xml file and paste it into your classpath (src/main/resources).Its contents are as follows:

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

<cacheManagerPeerListenerFactory
     class="org.terracotta.ehcachedx.monitor.probe.ProbePeerListenerFactory"
     properties="monitorAddress=10.35.34.193, monitorPort=9889,        memoryMeasurement=true" />

<cache name="employees" eternal="false" maxElementsInMemory="1000"
overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="0"
timeToLiveSeconds="500" memoryStoreEvictionPolicy="LRU" statistics="true">
</cache>

</ehcache>

The cacheManagerPeerListenerFactory has to be configured with details about where the Ehcache Monitor server is listening. Monitor Address is the ip and monitorPort is the port at which the monitor server is listening. The MemoryMeasurement Attribute will specify whether the Memory Measurement is to be done.

All the properties about every cache are also to be mentioned. 
  • timeToLive - The maximum number of seconds an element can exist in the cache regardless of use. The element expires at this limit and will no longer be returned from the cache. The default value is 0, which means no TTL eviction takes place (infinite lifetime).
  • timeToIdle - The maximum number of seconds an element can exist in the cache without being accessed. The element expires at this limit and will no longer be returned from the cache. The default value is 0, which means no TTI eviction takes place (infinite lifetime).
  • Local sizing attributes maxEntriesLocalHeap, maxBytesLocalHeap, maxEntriesLocalDisk, maxBytesLocalDisk.
  • Note that the eternal attribute, when set to "true", overrides timeToLive and timeToIdle so that no expiration can take place.

4. Define the Cache Manager bean in your spring-servlet.xml
and enable the spring cache annotations:


<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"

xmlns:context="http://www.springframework.org/schema/context"

xmlns:cache="http://www.springframework.org/schema/cache" xmlns:mvc="http://www.springframework.org/schema/mvc"

xmlns:tx="http://www.springframework.org/schema/tx"

xsi:schemaLocation="http://www.springframework.org/schema/beans

        http://www.springframework.org/schema/beans/spring-beans-3.1.xsd

        http://www.springframework.org/schema/context

        http://www.springframework.org/schema/context/spring-context-3.1.xsd

        http://www.springframework.org/schema/mvc

        http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd

        http://www.springframework.org/schema/cache 

        http://www.springframework.org/schema/cache/spring-cache-3.2.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd">

<context:component-scan base-package="com.spring.hibernate" />

<mvc:annotation-driven />
<bean
class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"  >
<property name="messageConverters">
<list>
<bean
class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"       />
</list>
</property>
</bean>

<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://10.35.34.193:3306/test11" />
<property name="username" value="root" />
<property name="password" value="admin" />
</bean>

<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation">
<value>classpath:hibernate.cfg.xml</value>
</property>
<property name="configurationClass">
<value>org.hibernate.cfg.AnnotationConfiguration
</value>
</property>
</bean>
<tx:annotation-driven />
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>

<cache:annotation-driven />
<bean id="cacheManager"
  class="org.springframework.cache.ehcache.EhCacheCacheManager"
p:cacheManager-ref="ehcache" />

<bean id="ehcache"
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"
p:configLocation="classpath:ehcache.xml" p:shared="true" />

</beans>

5. Use the Spring Caching Annotations in your code:

Ex-

@Cacheable("employees")

public Employee getEmployee(int empId) {

   Employee emp = employeeDao.getEmployee(empId);

   return emp;

}



@CacheEvict (value = "employees", key="#empId")
public void deleteEmployee(int empId) {
    employeeDao.deleteEmployee(empId);
}



@Cacheable is used with the function whose return values are to be cached. Cache values are stored in the form of key-value pairs. In this case, empId is the key. "employees" is the name of the cache where these values are stored.



@CacheEvict is used to evict the entries from your cache. An optional key parameter can be given to specify which of the arguments are used as keys.


Start the Ehcache Monitor:


Include SLF4J logging jars.Ehcache 1.7.1 and above require SLF4J.Earlier versions used commons logging. The probe, like all new Ehcache modules, uses SLF4J, which is becoming a new standard in open source projects.

If you are using Ehcache 1.5 to 1.7.0, you will need to add slf4j-api and one concrete logger. If you are using Ehcache 1.7.1 and above you should not need to do anything because you will already be using slf4j-api and one concrete logger.

To start the monitor, run the startup script provided in the bin directory: startup.sh on Unix and startup.bat on Microsoft Windows.

NOTE: If errors occur at startup, remove the line -j "$PRGDIR/etc/jetty.xml" \ (or -j %PRGDIR%\etc\jetty.xml ^) from the startup script.

The monitor port selected in this script should match the port specified in ehcache.xml. The monitor can be configured, including interface, port and simple security settings, in etc/ehcache-monitor.conf. Note that for the commercial version, the location of your license file must be specified in ehcache-monitor.conf.


Example:
license_file=/Users/karthik/Documents/workspace/lib/license/terracotta-license.key 
The monitor connection timeout can also be configured.The web-based GUI is available by pointing your browser at http://:/monitor. For a default installation on the local machine, this would be http://localhost:9889/monitor

NOTE:

Note that Caching will not work on private methods of a class. Make sure Cacheable method is public. Caching will not work if you try to call the Cacheable method from the same class in which it has been written.



You can download the Sample source code from here

under SpringHibernateProjectCaching



References: 






Friday 15 November 2013

Starting with a Spring Hibernate MySQL Maven Project

Assuming Maven plugin is installed in your Eclipse IDE and MySQL already setup, following are the steps to create the project :-


Step 1


Under File Menu New -> Project -> select Maven Project. 




Step 2

You will be prompted to select an archetype. Select maven-archetype-webapp and click Next





Step 3


Enter your Project Name in the Artifact Id textfield and Group Name in the Group Id field and click Finish. A project with the given name will appear in your Project Explorer

Step 4


To deploy this Web Project on Tomcat Web Server, Right Click on the Project select Properties.Under  Project Facets, change to faceted form check Dynamic Web Module and click OK. 




Step 5

In the Properties menu, Deployment Assembly make sure you add java build path entries for Maven Dependencies.






Step 6


Delete the webapp folder in src -> main. Create a spring-servlet.xml in the WEB-INF folder. This is the spring context file which has all the context configurations. Also make sure you have web.xml (Deployment Descriptor) in your WEB-INF folder. Your final directory structure should look like this -





Step 7


Following are spring-servlet.xml contents:-


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:cache="http://www.springframework.org/schema/cache" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.1.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">


<context:component-scan base-package="com.spring.hibernate" />

<mvc:annotation-driven />
<bean
class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<bean
class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" />
</list>
</property>
</bean>

<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://10.35.34.193:3306/test11" />
<property name="username" value="root" />
<property name="password" value="admin" />
</bean>

<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation">
<value>classpath:hibernate.cfg.xml</value>
</property>
<property name="configurationClass">
<value>org.hibernate.cfg.AnnotationConfiguration
</value>
</property>
</bean>
<tx:annotation-driven />
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
</beans>

6. Contents of pom.xml :-

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>IDM</groupId>
<artifactId>SpringHibernateProject</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>SpringHibernateProject Maven Webapp</name>
<url>http://maven.apache.org</url>
<repositories>
<repository>
<id>terracotta-releases</id>
<url>http://www.terracotta.org/download/reflector/releases</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.12</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>3.6.10.Final</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.26</version>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>20030825.184428</version>
</dependency>
<dependency>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
<version>20030825.183949</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.12.1.GA</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.0.13</version>
</dependency>

<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.0.13</version>
</dependency>
</dependencies>
<build>
<finalName>SpringHibernateProject</finalName>
</build>

</project>

7. Contents of web.xml ( in WEB-INF folder):-

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>SpringHibernateProject</display-name>
  <welcome-file-list>
    <welcome-file>/WEB-INF/jsp/index.jsp</welcome-file>
  </welcome-file-list>
  <servlet>
    <servlet-name>spring</servlet-name>
    <servlet-class>
            org.springframework.web.servlet.DispatcherServlet
        </servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>spring</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

</web-app>

8. The hibernate configurations are included in the hibernate.cfg.xml. This file must be present in the src/main/resources. Here are its contents :-

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
<session-factory>

<!-- <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost:3306/test22</property>
<property name="connection.username">root</property>
<property name="connection.password"></property>
-->
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">1</property>

<!-- SQL dialect -->
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>

<!-- Disable the second-level cache -->
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>

<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>

<property name="hbm2ddl.auto">create</property>

<mapping class="com.spring.hibernate.model.Employee"/>
<mapping class="com.spring.hibernate.model.Department"/>

</session-factory>

</hibernate-configuration>

9. You are all set to write your Spring Web Application with Hibernate MySQL as your back end database. Run your project by right clicking on your project and selecting
Run as -> Run on server.
    
Also ensure that under Windows -> Preferences -> Java , the Installed JRE's is set to point the JDK and not the JRE. This will ensure effective development.
         
The JAVA version that is used for the current blog is Java 1.7 . Source code is available for reference/ download here:


https://github.com/Nirman-Rathod/Spring  under the SpringHibernateMavenProject