Showing posts with label Spring. Show all posts
Showing posts with label Spring. Show all posts

Saturday, September 19, 2009

Annotation driven Caching with EhCache and Spring

Caching is the key of fast applications. The Spring modules framework provides us with an easy way to cache the return of our methods using java 5 annotations.

Here's an example :

ehCache.xml :

<ehcache>
<defaultCache
maxElementsInMemory="500"
eternal="true"
overflowToDisk="false"
memoryStoreEvictionPolicy="LFU" />

<cache name="getTestCache"
maxElementsInMemory="50"
eternal="true"
overflowToDisk="false"
memoryStoreEvictionPolicy="LFU" />
</ehcache>


applicationContext.xml :

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ehcache="http://www.springmodules.org/schema/ehcache"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springmodules.org/schema/ehcache
http://www.springmodules.org/schema/cache/springmodules-ehcache.xsd">

<ehcache:config configLocation="classpath:ehcache.xml" />
<ehcache:annotations>
<ehcache:caching id="getTestCacheModel" cacheName="getTestCache" />
<ehcache:flushing id="getTestFlushModel" cacheNames="getTestCache" />
</ehcache:annotations>

<bean id="customerManager" class="services.impl.CustomerManagerImpl"/>

</beans>

The CustomerManager interface :

import org.springmodules.cache.annotations.Cacheable;
import org.springmodules.cache.annotations.CacheFlush;
import vo.Customer;

public interface CustomerManager {

@Cacheable(modelId="getTestCacheModel")
public Customer load(long customerId);

@CacheFlush(modelId="getTestFlushModel")
public void add(Customer customer);
}

In order to do a simple test, we create a simple implementation of the CustomerManager interface :

import services.CustomerManager;
import vo.Customer;

public class CustomerManagerImpl implements CustomerManager {

public Customer load(long customerId) {
//This part should normally call a DAO
return new Customer("Rene", 34);
}

public void add(Customer customer) {
//This part should normally call a DAO
}
}

And the test class :

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import services.CustomerManager;

public class Main {

public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
CustomerManager customerManager = (CustomerManager) context.getBean("customerManager");

System.err.println("Loading customer");
customerManager.load(455L);

System.err.println("Loading customer again");
customerManager.load(455L);

System.err.println("Adding customer");
customerManager.add(new Customer("Jean",34));
}
}

The final step is to add this simple log4j.properties file to the classpath :

# Set root category priority to INFO and its only appender to CONSOLE.
log4j.rootCategory=DEBUG, CONSOLE

# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=- %m%n

Here's the log result of the execution of this class :
Loading customer
- Attempt to retrieve a cache entry using key <1187678266|32099260> and cache model
- Retrieved cache element
- Attempt to store the object in the cache using key <1187678266|32099260> and model
- Object was successfully stored in the cache
Loading customer again
- Attempt to retrieve a cache entry using key <1187678266|32099260> and cache model
- Retrieved cache element
Adding customer
- Attempt to flush the cache using model
- Cache has been flushed.

We can see that the first time we call the load method, the retrieved Customer object is stored in the cache. The second time, the object is directly retrieved from the cache. When we call the add method the cache is flushed.

Here's the maven pom file I used :

We'll use the 0.8 version of springmodules, the latest in the maven repo central. This version has dependencies on spring 2.0 and ehcahe 1.1. In order to use the latest library we will add our own version of spring (2.5.6) and ehcache (1.6.2) and excludes the old ones. We have to exclude also all the proprietary libraries which should be optional but are not.

<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
<version>2.5.6</version>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>1.6.2</version>
</dependency>
<dependency>
<groupId>org.springmodules</groupId>
<artifactId>spring-modules-cache</artifactId>
<version>0.8</version>
<exclusions>
<exclusion>
<artifactId>gigaspaces-ce</artifactId>
<groupId>gigaspaces</groupId>
</exclusion>
<exclusion>
<groupId>jini</groupId>
<artifactId>jsk-lib</artifactId>
</exclusion>
<exclusion>
<groupId>jini</groupId>
<artifactId>jsk-platform</artifactId>
</exclusion>
<exclusion>
<groupId>jini</groupId>
<artifactId>mahalo</artifactId>
</exclusion>
<exclusion>
<groupId>jini</groupId>
<artifactId>reggie</artifactId>
</exclusion>
<exclusion>
<groupId>jini</groupId>
<artifactId>start</artifactId>
</exclusion>
<exclusion>
<groupId>jini</groupId>
<artifactId>boot</artifactId>
</exclusion>
<exclusion>
<groupId>jini</groupId>
<artifactId>webster</artifactId>
</exclusion>
<exclusion>
<groupId>jboss</groupId>
<artifactId>jboss-cache</artifactId>
</exclusion>
<exclusion>
<groupId>jboss</groupId>
<artifactId>jboss-common</artifactId>
</exclusion>
<exclusion>
<groupId>jboss</groupId>
<artifactId>jboss-jmx</artifactId>
</exclusion>
<exclusion>
<groupId>jboss</groupId>
<artifactId>jboss-minimal</artifactId>
</exclusion>
<exclusion>
<groupId>jboss</groupId>
<artifactId>jboss-system</artifactId>
</exclusion>
<exclusion>
<groupId>jcs</groupId>
<artifactId>jcs</artifactId>
</exclusion>
<exclusion>
<groupId>xpp3</groupId>
<artifactId>xpp3_min</artifactId>
</exclusion>
<exclusion>
<groupId>ehcache</groupId>
<artifactId>ehcache</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>


In conclusion, annotation driven Caching with EhCache and Spring is very straightforward to implement.

However, be aware that the springmodules framework is in a dead status and have some unresolved issues you should be aware of. You can see those issues listed here. Take a look at it before using it though most of the issues have workarounds. There is also a 0.9 version you can only download because it has never been released in the maven central repository.

A project named Spring modules fork has been created to revive this useful project and to make it evoluate. They've created a 0.10-SNAPSHOT version hosted on their own server.

Sunday, April 5, 2009

Dependencies injection with Spring annotations (@Repository, @Service, @Autowired)

One of the big downfall with frameworks relying on XML configurations file is that they are not synchronized with your code. For example, if you are doing a refactoring of your code like renaming a package or a class, you need to update your XML files too. If you're not, you will end up with a configuration exception at runtime.

Spring 2.5 introduces injection dependencies by annotation with stereotypes annotation like @Repository, @Service, etc... and the @Autowired annotation.

Here's how you define your beans the old school way :

applicationContext.xml :

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

<bean id="beanDAO_1" class="com.yourcompany.dao.BeanDAO_1_Impl"/>
...
<bean id="beanDAO_N" class="com.yourcompany.dao.BeanDAO_N_Impl"/>

<bean id="beanServices_1" class="com.yourcompany.services.BeanServices_1_Impl">
<property name="beanDAO_1" ref="beanDAO_1"/>
</bean>
...
<bean id="beanServices_N" class="com.yourcompany.services.BeanServices_N_Impl">
<property name="beanDAO_N" ref="beanDAO_N"/>
</bean>

</beans>

BeanDAO_1_Impl.class :

package com.yourcompany.dao.impl;

public class BeanDAO_1_Impl implements BeanDAO_1 {
...
}

BeanServices_1.class :

package com.yourcompany.services.impl;

public class BeanServices_1_Impl implements BeanServices_1 {
private BeanDAO_1 beanDAO_1;
...
public setBeanDAO_1(BeanDAO_1 beanDAO_1) {
this.beanDAO_1 = beanDAO_1;
}
}


Here's how you define your beans with spring annotations :

applicationContext.xml :

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd"/>

<context:component-scan base-package="com.yourcompany.dao.impl"/>
<context:component-scan base-package="com.yourcompany.services.impl"/>
</beans>

BeanDAO_1_Impl.class :

package com.yourcompany.dao.impl;

@Repository("beanDAO_1")
public class BeanDAO_1_Impl implements BeanDAO_1 {
...
}

BeanServices_1.class :

package com.yourcompany.services.impl;

@Service("beanServices_1")
public class BeanServices_1_Impl implements BeanServices_1 {
@Autowired
private BeanDAO_1 beanDAO_1;
//No need of a setter anymore. Spring can inject the beanDAO_1
//even if it's a private properties
}

And that's how it's done... No more big XML configuration files.

Consult the spring documentation for a detailed explanation of the differences between the annotation sterotypes (@Repository, @Services, @Component ...)