cache Posts

[Java] Cara Cache Data Hasil Query dengan SpringMVC

Pada tulisan kali ini, saya coba membuat suatu metode caching untuk menyimpan data-data master kedalam memory, sehingga tidak perlu query ke database lagi. Hal ini cocok digunakan untuk menyimpan data-data master yang sangat jarang berubah, seperti kode provinsi ataupun kode cabang.

Okay, di tutorial ini saya menggunakan Spring MVC 4, Hibernate dan ehCache sebagai cache provider. Berikut adalah pom.xml yang digunakan,

<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.edw</groupId>
    <artifactId>mavenproject5</artifactId>
    <version>1</version>
    <packaging>war</packaging>

    <name>mavenproject5</name>

    <properties>
        <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-web-api</artifactId>
            <version>6.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
        
        
        <!-- Spring 4 dependencies -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>4.1.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>4.1.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>4.1.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>4.1.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>4.1.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>4.1.2.RELEASE</version>
         </dependency>
         
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
        
        
        <dependency>
            <groupId>commons-dbcp</groupId>
            <artifactId>commons-dbcp</artifactId>
            <version>1.2.2</version>
        </dependency>
        
        <!--ehcache-->
        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache</artifactId>
            <version>2.7.0</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.5</version>
        </dependency>
        
        <!--hibernate-->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>4.3.8.Final</version>
        </dependency>
        
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                    <compilerArguments>
                        <endorseddirs>${endorsed.dir}</endorseddirs>
                    </compilerArguments>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.6</version>
                <executions>
                    <execution>
                        <phase>validate</phase>
                        <goals>
                            <goal>copy</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${endorsed.dir}</outputDirectory>
                            <silent>true</silent>
                            <artifactItems>
                                <artifactItem>
                                    <groupId>javax</groupId>
                                    <artifactId>javaee-endorsed-api</artifactId>
                                    <version>7.0</version>
                                    <type>jar</type>
                                </artifactItem>
                            </artifactItems>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

Kemudian konfigurasi standard yang digunakan di SpringMVC, applicationContext.xml

<?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:tx="http://www.springframework.org/schema/tx"
       xmlns:cache="http://www.springframework.org/schema/cache"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
        http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">

    <context:annotation-config/>
    
    <tx:annotation-driven/>
    
    <cache:annotation-driven />
    
    <context:component-scan base-package="com.edw.mavenproject5.service"/>
    
     <!-- datasource  -->
    <bean id="ds" class="org.apache.tomcat.jdbc.pool.DataSource" destroy-method="close"
          p:driverClassName="com.mysql.jdbc.Driver" p:url="jdbc:mysql://localhost/test"
          p:username="root" p:password=""
          p:initialSize="10"
          p:maxActive="50"
          p:maxIdle="10"
          p:minIdle="3"
          p:maxWait="30000"
          p:removeAbandoned="true"
          p:removeAbandonedTimeout="30"
          p:validationQuery="SELECT 1" />
    
    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"
        p:dataSource-ref="ds" p:configLocations="classpath:hibernate.cfg.xml" depends-on="ds">
        <property name="hibernateProperties">
            <value>
                hibernate.dialect=org.hibernate.dialect.MySQLDialect
                hibernate.query.substitutions=true
                hibernate.show_sql=true
                hibernate.enable_lazy_load_no_trans=true
            </value>
        </property>
    </bean> 
    
    <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>
    
    <bean id="cacheManager" 
          class="org.springframework.cache.ehcache.EhCacheCacheManager" 
          p:cache-manager-ref="ehcache"/>
    <bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"
          p:config-location="classpath:ehcache.xml"/>
    
</beans>

dan dispatcher-servlet.xml,

<?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:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">


    <context:component-scan base-package="com.edw.mavenproject5.controller" />
	
    <mvc:annotation-driven />
    <mvc:default-servlet-handler />

    <bean id="viewResolver"
          class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          p:prefix="/WEB-INF/jsp/"
          p:suffix=".jsp" />
</beans>

yang kemudian dipanggil dari web.xml,

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>2</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

Nah, ada satu konfigurasi xml yang penting dalam membuat cache. Yaitu ehcache.xml, disana disimpan nama cache serta lama waktu hidupnya

<?xml version="1.0" encoding="UTF-8"?>
<!--
    caching configuration
-->
<ehcache>
    
    <defaultCache
        maxElementsInMemory="5000"
        eternal="true"
        overflowToDisk="false"
        memoryStoreEvictionPolicy="LRU"
    />
    
    <!--2menit-->
    <cache name="duaMenit"
           maxElementsInMemory="10000"
           timeToLiveSeconds="120"
           memoryStoreEvictionPolicy="LRU" 
    />
    
    <!--1menit-->
    <cache name="satuMenit"
           maxElementsInMemory="10000"
           timeToLiveSeconds="60"
           memoryStoreEvictionPolicy="LRU" 
    />
</ehcache>

Nah sekarang kita buat class java-nya, dimulai dari service layer. Perhatikan annotation Cacheable, konfigurasi tersebut adalah konfigurasi yang menandakan lokasi method yang kita cache.

package com.edw.mavenproject5.service;

import com.edw.mavenproject5.bean.Dosen;
import java.util.List;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
public class DosenService {

    @Autowired
    private SessionFactory sessionFactory;
    
    @Cacheable(key = "#root.methodName", value = "duaMenit")
    public List<Dosen> getDosens() {
        return sessionFactory.getCurrentSession().createCriteria(Dosen.class).list();
    }
    
    @Cacheable(key = "#root.methodName + #idDosen", value = "satuMenit")
    public Dosen getDosen(String idDosen) {
        return (Dosen) sessionFactory.getCurrentSession().get(Dosen.class, idDosen);
    }
    
}

Yang kemudian dipanggil dari controller,

package com.edw.mavenproject5.controller;

import com.edw.mavenproject5.service.DosenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class IndexController {
    
    @Autowired
    private DosenService dosenService;
    
    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String index(ModelMap modelMap){
        modelMap.put("dosens", dosenService.getDosens());
        return "index";
    }
    
    @RequestMapping(value = "/get", method = RequestMethod.GET)
    public String get(ModelMap modelMap, String id){
        modelMap.put("dosen", dosenService.getDosen(id));
        return "index";
    }
}

Dan berikut adalah ui yang digunakan untuk menampilkan hasil query didatabase,

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>JSP Page</title>
    </head>
    <body>
        <h1>Hello World!</h1>
        
        <c:if test="${not empty dosens}">
        <table class="table table-hover table-striped">
            <thead>
                <tr>
                    <th>ID Dosen</th>
                    <th>Nama Dosen</th>
                </tr>
            </thead>
            <tbody>
            <c:forEach items="${dosens}" var="dosen">
                <tr>
                    <td>${dosen.iddosen}</td>
                    <td>${dosen.namadosen}</td>
                </tr>
            </c:forEach>
            </tbody>
        </table>
        </c:if>
        
        <c:if test="${not empty dosen}">
            <h1>Hello Dosen ${dosen.namadosen}!</h1>
        </c:if>
    </body>
</html>

Hasil akhirnya adalah sebagai berikut, data dengan kode 02 dan 321 sudah diubah secara langsung didatabase, namun walaupun browser di-refresh berulang kali, tetap menampilkan data yang lama, yaitu “Dodol” dan “Testing 123 123″.
capture1

Untuk sourcecode lengkapnya bisa diunduh di github, https://github.com/edwinkun/CachingWithSpringMVC. (*)

Google+

A Simple MyBatis Caching using Redis

Mybatis recently introduce a new caching mechanism using Redis (http://mybatis.github.io/redis-cache/index.html), and in this tutorial i’m trying to create a simple demonstration for it. MyBatis’ Redis caching is a little bit different with other MyBatis’ caching provider, such as EhCache or OsCache because you need to install Redis server before using it.

Because Redis project does not officially support Windows and i need Redis to run as a windows service so im downloading redis-service.exe from this url (https://github.com/rgl/redis/downloads), install and run it. You can see Redis is running on port 6379.

Because MyBatis Redis Cache hasnt available on Maven repository, so next step is download MyBatis Redis Cache project from github (https://github.com/mybatis/redis-cache), build it into jar and install it manually into our pom.xml file from local folder.

Okay, so here is my pom.xml file, you can see im manually installing mybatis-redis from local repository,

<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.edw</groupId>
    <artifactId>MybatisRedisExample</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    
    <repositories>
        <repository>
            <id>local</id>
            <url>file://${basedir}/lib</url>
        </repository>
    </repositories>
    
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.7</maven.compiler.source>
        <maven.compiler.target>1.7</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.2.2</version>
        </dependency>
        
        <dependency>
            <groupId>org.mybatis.caches</groupId>
            <artifactId>mybatis-redis</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
            <version>2.3</version>
        </dependency>
        
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.6.2</version>
        </dependency>
    </dependencies>
</project>

Now create a database and a simple table for example,

CREATE DATABASE `test`;
DROP TABLE IF EXISTS `testing`;
CREATE TABLE `testing` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL,
  `address` varchar(50) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1;


INSERT INTO `testing` VALUES ('1', 'edw', '123');
INSERT INTO `testing` VALUES ('2', 'pepe', 'epep');
INSERT INTO `testing` VALUES ('3', 'dodol', 'dodll');

And a java bean for our table representation

package com.edw.mybatisredisexample.bean;

public class Testing {

    private String name;
    private String address;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public Testing() {
    }
}

And an xml file and a java file, named TestingMapper to handle all the queries,

package com.edw.mybatisredisexample.mapper;

import com.edw.mybatisredisexample.bean.Testing;
import java.util.List;
import java.util.Map;

public interface TestingMapper {
    void insert(Testing testing);
    List<Map> select();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.edw.mybatisredisexample.mapper.TestingMapper" >
    
    <cache type="org.mybatis.caches.redis.RedisCache" />
    
    <select id="insert" parameterType="com.edw.mybatisredisexample.bean.Testing" >
        insert into testing (name, address)
        values ( #{name,jdbcType=VARCHAR}, #{address,jdbcType=VARCHAR} )
    </select>
    
    <select resultType="java.util.Map" id="select" >
        SELECT
            *
        FROM
            testing
    </select>
</mapper>

Dont forget to register our xml file on MyBatis’ Configuration.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="UNPOOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost/test"/>
                <property name="username" value="root"/>
                <property name="password" value="password"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="TestingMapper.xml" />
    </mappers>
</configuration>

And a java class to load our MyBatis xml configuration

package com.edw.mybatisredisexample.config;

import java.io.IOException;
import java.io.Reader;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class MyBatisSqlSessionFactory {

    private static final SqlSessionFactory FACTORY;

    static {
        try {
            Reader reader = Resources.getResourceAsReader("Configuration.xml");
            FACTORY = new SqlSessionFactoryBuilder().build(reader);
        } catch (IOException e) {
            throw new RuntimeException("Fatal Error. Cause: " + e, e);
        }
    }

    public static SqlSessionFactory getSqlSessionFactory() {
        return FACTORY;
    }
}

Next is creating our Redis connection configuration, redis.properties

redis.host=localhost
redis.port=6379
redis.timeout=5000
redis.password=
redis.namespace=edw
redis.database=0

After everything is ready, next is create a Main file

package com.edw.mybatisredisexample;

import com.edw.mybatisredisexample.config.MyBatisSqlSessionFactory;
import com.edw.mybatisredisexample.mapper.TestingMapper;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;

public class Main {

    private Logger logger = Logger.getLogger(this.getClass());

    public static void main(String[] args) throws Exception {
        new Main().testQuery();
    }

    private void testQuery() throws Exception {
        logger.debug("start ----");

        for (int i = 0; i < 10; i++) {
            SqlSession sqlSession = null;
            try {
                sqlSession = MyBatisSqlSessionFactory.getSqlSessionFactory().openSession(true);
                TestingMapper testingMapper = sqlSession.getMapper(TestingMapper.class);
                List<Map> maps = testingMapper.select();

                for (Map map : maps) {
                    logger.debug(map);
                }
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
            } finally {
                if (sqlSession != null) {
                    sqlSession.close();
                }
            }
            
            Thread.sleep(5000);
        }

        logger.debug("end ----");
    }
}

Using monitor command, this is what you will see on your Redis console,
redis console

And on my netbeans console,mybatis redis result log 2

It will keep showing the same value despite i’ve delete and update some values on database directly,
mysql data redis

You can see my complete sourcecode on my github page (https://github.com/edwinkun/MybatisRedisExample).

Google+

Create an Expirable Token Using Java

On this article, im trying to create a simple java app that will hold a value that have an expiration time. I usually use this for handling request session or to hold a value that have a time limitation. Btw, i’m using EhCache for handling expiration time.

First is a simple 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/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.edw</groupId>
    <artifactId>ExpirableToken</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>ExpirableToken</name>
    <url>http://maven.apache.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache</artifactId>
            <version>2.7.4</version>
        </dependency>           
    </dependencies>
</project>

And a simple xml file to hold ehcache configurations,

<ehcache updateCheck="false" monitoring="off" dynamicConfig="false">
    
    <diskStore path="java.io.tmpdir" />
    
    <defaultCache maxEntriesLocalHeap="0" eternal="false"
                  timeToIdleSeconds="1200" timeToLiveSeconds="1200">
    </defaultCache>
    
    <!-- a 3 seconds -->
    <cache name="token" maxElementsInMemory="1000" eternal="false"
           overflowToDisk="true" diskPersistent="false" timeToIdleSeconds="3" 
           timeToLiveSeconds="3" memoryStoreEvictionPolicy="LFU" statistics="false" transactionalMode="off">       
    </cache>                
    
    <!-- a 10 seconds -->
    <cache name="action" maxElementsInMemory="1000" eternal="false"
           overflowToDisk="true" diskPersistent="false" timeToIdleSeconds="10" 
           timeToLiveSeconds="10" memoryStoreEvictionPolicy="LFU" statistics="false" transactionalMode="off">       
    </cache>        
    
</ehcache>

And this is my java class that i use for testing,

package com.edw.expirabletoken;

import java.util.Date;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;

public class App {

    private CacheManager cacheManager= CacheManager.create();
    private Cache tokenCache = cacheManager.getCache("token"); // a 3 seconds cache
    private Cache actionCache = cacheManager.getCache("action"); // a 10 seconds cache

    public App() {
    }

    private void action() throws Exception {
        // add into a 3 seconds cache
        Element regElement = new Element("name", "edwin 3 seconds");
        tokenCache.put(regElement);
        
        // add into a 10 seconds cache
        Element regElement2 = new Element("name", "edwin 10 seconds");
        actionCache.put(regElement2);
        
         // 2 seconds sleep
         Thread.sleep(2000);
         getCacheContent();
         
         // 5 seconds sleep
         Thread.sleep(5000);
         getCacheContent();
         
         // 10 seconds sleep
         Thread.sleep(10000);
         getCacheContent();
    }
    
    private void getCacheContent() {
        Element element3 = tokenCache.get("name"); // get value from a 3 seconds cache
        if (element3 != null) {
            String value = (String) element3.getObjectValue();
            System.out.println(new Date()+" -- "+value);    
        } else {
            System.out.println(new Date()+" -- Empty Cache");
        }
        
         Element element10 = actionCache.get("name"); // get value from a 10 seconds cache
         if (element10 != null) {
            String value = (String) element10.getObjectValue();
            System.out.println(new Date()+" -- "+value);    
        } else {
            System.out.println(new Date()+" -- Empty Cache");
        }
    }
    
    public static void main(String[] args) throws Exception {
        App app = new App();
        app.action();
    }
}

This is what the result on my Netbeans’ console,

This is my Netbeans’ project structure,

Well i hope this helped others, have fun using ehcache.

Google+

A Simple Caching Example on MyBatis using EhCache

Today i will show a simple example on how to combine ehcache caching framework with MyBatis ORM. I use Maven as my build tool, and Netbeans as my IDE.

Okay, here is my 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/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.edw.ehcache</groupId>
    <artifactId>MyBatisEhCache</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>MyBatisEhCache</name>
    <url>http://maven.apache.org</url>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.2.2</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.caches</groupId>
            <artifactId>mybatis-ehcache</artifactId>
            <version>1.0.2</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>      
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache</artifactId>
            <version>2.7.0</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.5</version>
        </dependency>     
    </dependencies>
</project>

And i create a simple mysql table,

CREATE TABLE `testing` (
  `Id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(30) NOT NULL DEFAULT '',
  `address` varchar(255) NOT NULL DEFAULT '',
  PRIMARY KEY (`Id`),
  UNIQUE KEY `ix` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=latin1;

and a simple bean and xml representation from my table,

package com.edw.mybatisehcache.bean;

import java.io.Serializable;

public class Testing implements Serializable  {

    private Integer id;
    private String name;
    private String address;

    // setter and getter

    @Override
    public String toString() {
        return "testing{" + "id=" + id + ", name=" + name + ", address=" + address + '}';
    }            
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.edw.mybatisehcache.mapper.TestingMapper" >
  
     <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
    
    <resultMap id="Testings" type="com.edw.mybatisehcache.bean.Testing" >
        <id column="id" property="id" jdbcType="BIGINT" />
        <result column="name" property="name" jdbcType="VARCHAR" />
        <result column="address" property="address" jdbcType="VARCHAR" />
    </resultMap>  

    <select id="select" resultMap="Testings">
        select 
        *
        from testing    
    </select>   
</mapper>

An xml configuration to load my database connection,

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
       <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="UNPOOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost/test"/>
                <property name="username" value="root"/>
                <property name="password" value=""/>
            </dataSource>
        </environment>          
    </environments>
     <mappers>        		
        <mapper resource="com/edw/mybatisehcache/xml/TestingMapper.xml" />  
    </mappers>              		
</configuration>

A java code to load my xml configuration,

package com.edw.mybatisehcache.config;

import java.io.Reader;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class MyBatisSqlSessionFactory {

    private static final SqlSessionFactory FACTORY;

    static {
        try {
            Reader reader = Resources.getResourceAsReader("com/edw/mybatisehcache/xml/Configuration.xml");
            FACTORY = new SqlSessionFactoryBuilder().build(reader);
        } catch (Exception e){
            throw new RuntimeException("Fatal Error.  Cause: " + e, e);
        }
    }

    public static SqlSessionFactory getSqlSessionFactory() {
        return FACTORY;
    }
}

A java interface to do handle queries,

package com.edw.mybatisehcache.mapper;

import com.edw.mybatisehcache.bean.Testing;
import java.util.List;

public interface TestingMapper {
    public List<Testing> select();    
}

And this is my ehcache.xml configuration,

<?xml version="1.0" encoding="UTF-8"?>
<!--
    caching configuration
-->
<ehcache>
    
    <diskStore path="F:\\cache" />
    
    <defaultCache eternal="true" maxElementsInMemory="1000"
                   overflowToDisk="true" diskPersistent="true" timeToIdleSeconds="0"
                   timeToLiveSeconds="0" memoryStoreEvictionPolicy="LRU" statistics="true" />
</ehcache>

This is my main java class, as you can see i try to do a repeated simple select queries,

package com.edw.mybatisehcache.main;

import com.edw.mybatisehcache.bean.Testing;
import com.edw.mybatisehcache.config.MyBatisSqlSessionFactory;
import com.edw.mybatisehcache.mapper.TestingMapper;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.log4j.Logger;

public class Main {

    private static Logger logger = Logger.getLogger(Main.class);

    public static void main(String[] args) {

        for (int i = 0; i < 3; i++) {
            SqlSessionFactory sqlSessionFactory = MyBatisSqlSessionFactory.getSqlSessionFactory();
            SqlSession sqlSession = sqlSessionFactory.openSession();
            TestingMapper testingMapper = sqlSession.getMapper(TestingMapper.class);

            List<Testing> testings = testingMapper.select();
            for (Testing testing : testings) {
                logger.debug(testing);
            }
            sqlSession.close();

            try {
                Thread.sleep(3000);
            } catch (Exception e) {
                logger.error(e, e);
            }
        }
    }
}

This is what is written on my netbeans’ console,

2013-07-25 15:30:10,648 [Segment] DEBUG net.sf.ehcache.store.disk.Segment:779 - fault removed 0 from heap
2013-07-25 15:30:10,648 [Segment] DEBUG net.sf.ehcache.store.disk.Segment:796 - fault added 0 on disk
2013-07-25 15:30:13,722 [Cache] DEBUG net.sf.ehcache.Cache:1970 - Cache: com.edw.mybatisehcache.mapper.TestingMapper store hit for 2026218237:1652924294:com.edw.mybatisehcache.mapper.TestingMapper.select:0:2147483647:select 
        *
        from testing
2013-07-25 15:30:13,722 [Main] DEBUG com.edw.mybatisehcache.main.Main:24 - testing{id=1, name=edw, address=Ciledug}
2013-07-25 15:30:13,722 [Main] DEBUG com.edw.mybatisehcache.main.Main:24 - testing{id=2, name=kamplenk, address=Cikokol}
2013-07-25 15:30:13,722 [Main] DEBUG com.edw.mybatisehcache.main.Main:24 - testing{id=3, name=nugie, address=Pamulang}
2013-07-25 15:30:13,722 [Main] DEBUG com.edw.mybatisehcache.main.Main:24 - testing{id=4, name=tebek, address=Karawaci}

Here is my Netbeans project structure

Have fun (K)

Google+

How to Add Expiry Date on Your Static Files using Java’s Servlet Filter

Perhaps there would be some people wondering, why should i use expiry date on my static files? Well for a first-time visitor to your page may have to make several HTTP requests to load all your web page’s content, but by using the Expires header you make those components cacheable. This avoids unnecessary HTTP requests on subsequent page views. Expires headers are most often used with images, but they should be used on all components including scripts, stylesheets, and Flash components.

But dont forget, using a far future Expires header affects page views only after a user has already visited your site. It has no effect on the number of HTTP requests when a user visits your site for the first time and the browser’s cache is empty. Therefore the impact of this performance improvement depends on how often users hit your pages with a primed cache.

Okay, so basically i use a simple ServletFilter to add Expiry header on each static contents, on this example would be .css, .png and .gif files. So here is my code,

package com.edw.fw.server.filter;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;

public class ExpiryFilter implements Filter {

	// add a five years expiry
	private Integer years = 5;

	@Override
	public void destroy() {
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		if (years > -1) {
			Calendar c = Calendar.getInstance();
			c.setTime(new Date());
			c.add(Calendar.YEAR, years);

			// HTTP header date format: Thu, 01 Dec 1994 16:00:00 GMT
			String o = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss zzz")
					.format(c.getTime());
			((HttpServletResponse) response).setHeader("Expires", o);
		}

		chain.doFilter(request, response);
	}

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {		
	}
}

And dont forget to register your Filter to your web.xml file

		<!--  
			expiration date filter
		-->
	  	<filter>
			<description>Set cache expiry for static content</description>
			<filter-name>ExpiryFilter</filter-name>
			<filter-class>com.edw.fw.server.filter.ExpiryFilter</filter-class>
		</filter>
		<filter-mapping>
			<filter-name>ExpiryFilter</filter-name>
			<url-pattern>*.css</url-pattern>
			<dispatcher>REQUEST</dispatcher>
		</filter-mapping>
		<filter-mapping>
			<filter-name>ExpiryFilter</filter-name>
			<url-pattern>*.png</url-pattern>
			<dispatcher>REQUEST</dispatcher>
		</filter-mapping>
		<filter-mapping>
			<filter-name>ExpiryFilter</filter-name>
			<url-pattern>*.gif</url-pattern>
			<dispatcher>REQUEST</dispatcher>
		</filter-mapping>

This is what it looks like on my browser’s http log,

as you can see, my http request get 304 not modified header, due to accessing my browser’s cache instead of the targetted web page.

Okay, i hope this helped other. Have Fun :)

Google+