디자인 패턴》(Design Patterns, ISBN 0201633612)은 소프트웨어 설계에 있어 공통된 문제들에 대한 표준적인 해법과 작명법을 제안한 이다. 이 분야의 사인방(Gang of Four, 줄여 GoF)으로 불리는 에리히 감마(Erich Gamma), 리처드 헬름(Richard Helm), 랄프 존슨(Ralph Johnson), 존 블리시데스(John Vlissides)가 같이 썼고, 한국어 판은 김정아의 번역으로 피어슨 에듀케이션 코리아를 통해 출판되었다.

책의 첫 번째 반절은 다양한 디자인 패턴의 정의에 할애하고 있고, 나머지 반절은 실제적으로 유용한 디자인 패턴들을 나열하고 있다. 책의 예제들은 객체지향적인 언어인 C++과 스몰토크로 제시되고 있다. 다음은 책에서 언급되고 있는 패턴의 리스트이다.

생성 패턴(Creational Patterns)[편집]

구조 패턴(Structural Patterns)[편집]

  • 어댑터 패턴: 인터페이스가 호환되지 않는 클래스들을 함께 이용할 수 있도록, 타 클래스의 인터페이스를 기존 인터페이스에 덧씌운다.
  • 브리지 패턴: 추상화와 구현을 분리해 둘을 각각 따로 발전시킬 수 있다.
  • 합성 패턴: 0개, 1개 혹은 그 이상의 객체를 묶어 하나의 객체로 이용할 수 있다.
  • 데코레이터 패턴: 기존 객체의 매서드에 새로운 행동을 추가하거나 오버라이드 할 수 있다.
  • 파사드 패턴: 많은 분량의 코드에 접근할 수 있는 단순한 인터페이스를 제공한다.
  • 플라이웨이트 패턴: 다수의 유사한 객체를 생성·조작하는 비용을 절감할 수 있다.
  • 프록시 패턴: 접근 조절, 비용 절감, 복잡도 감소를 위해 접근이 힘든 객체에 대한 대역을 제공한다.

행위 패턴(Behavioral Patterns)[편집]




출처 - http://ko.wikipedia.org/wiki/%EB%94%94%EC%9E%90%EC%9D%B8_%ED%8C%A8%ED%84%B4_(%EC%B1%85)







'Design Pattern > Common' 카테고리의 다른 글

디자인 패턴 - 종류  (0) 2013.07.26
Programming paradigms  (0) 2013.01.01
DSL(domain-specific language)  (0) 2013.01.01
Core J2EE Patterns  (0) 2012.11.30
Rich Domain Model  (0) 2012.11.05
Posted by linuxism
,



Evaluate after the method has been called

Hooray, as of Spring 3.2 the framework allows for this using Spring SPEL and unless. Note from the java doc surrounding Cacheable:

http://static.springsource.org/spring/docs/3.2.x/javadoc-api/org/springframework/cache/annotation/Cacheable.html

public abstract String unless

Spring Expression Language (SpEL) attribute used to veto method caching.

Unlike condition(), this expression is evaluated after the method has been called and can therefore refer to the result. Default is "", meaning that caching is never vetoed.

The important aspect is that unless is evaluated after the method has been called. This makes perfect sense because the method will never get executed if the key is already in the cache.

So in the above example you would simply annotate as follows (#result is available to test the return value of a method):

@Cacheable(value="defaultCache", key="#pk", unless="#result == null")
public Person findPerson(int pk) {
   return getSession.getPerson(pk);
}

I would imagine this condition arises from the use of pluggable cache implementations such as Ehcache which allows caching of nulls. Depending on your use case scenario this may or may not be desirable.


출처 - http://stackoverflow.com/questions/12113725/how-do-i-tell-spring-cache-not-to-cache-null-value-in-cacheable-annotation








Using Spring and Ehcache

Introduction

Ehcache has had excellent Spring integration for many years. This page demonstrates two new ways of using Ehcache with Spring.

Spring 3.1

Spring Framework 3.1 added a new generic cache abstraction for transparently applying caching to Spring applications. It adds caching support for classes and methods using two annotations:

@Cacheable

Cache a method call. In the following example, the value is the return type, a Manual. The key is extracted from the ISBN argument using the id.

@Cacheable(value="manual", key="#isbn.id")
public Manual findManual(ISBN isbn, boolean checkWarehouse)

@CacheEvict

Clears the cache when called.

@CacheEvict(value = "manuals", allEntries=true)
public void loadManuals(InputStream batch)

Spring 3.1 includes an Ehcache implementation. See the Spring 3.1 JavaDoc.

It also does much more with SpEL expressions. See http://blog.springsource.com/2011/02/23/spring-3-1-m1-caching/ for an excellent blog post covering this material in more detail.

Spring 2.5 - 3.1: Ehcache Annotations For Spring

This open source, led by Eric Dalquist, predates the Spring 3.1 project. You can use it with earlier versions of Spring or you can use it with 3.1.

@Cacheable

As with Spring 3.1 it uses an @Cacheable annotation to cache a method. In this example calls to findMessage are stored in a cache named "messageCache". The values are of type Message. The id for each entry is the id argument given.

@Cacheable(cacheName = "messageCache")
public Message findMessage(long id)

@TriggersRemove

And for cache invalidation, there is the @TriggersRemove annotation. In this example, cache.removeAll() is called after the method is invoked.

@TriggersRemove(cacheName = "messagesCache",
when = When.AFTER_METHOD_INVOCATION, removeAll = true)
public void addMessage(Message message)

See http://blog.goyello.com/2010/07/29/quick-start-with-ehcache-annotations-for-spring/ for a blog post explaining its use and providing further links.



출처 - http://ehcache.org/documentation/2.5/integrations/spring






SPRING 3.1 M1: CACHE ABSTRACTION

Costin Leau

One of the major features added in Spring Framework 3.1 M1 is the generic cache abstraction for transparently applying caching to Spring applications. Just like the transaction support, the caching abstraction allows consistent use of various caching solutions with minimal impact on the code.

Purpose

Caches are in general used to improve application performance by transparently serving frequently accessed data in a faster fashion such as serving data from local memory rather than from the network. Many of you have already used caching, whether knowingly or not: most ORM/JPA frameworks provide dedicated caching functionality (also known as the 2nd-level cache). Spring 3.1 M1 however introduces a generic cache mechanism that can be applied to any Java class, method or library: one can use it in conjunction with an existing caching infrastructure, to add caching to APIs without such support (for example JDBC) or simply to improve the performance of a slow, time-consuming and resource-hungry method.

Meet @Cacheable@CacheEvict and SpEL

Let us see what it takes to cache an arbitrary method:

@Cacheable("books")
public Book findBook(ISBN isbn) {...}

By marking the method with the @Cacheable annotation, we tell the container that thefindBook method is backed by the cache entry books. That is each time the method is called, a cache lookup is performed using as key the method parameters (in this case theisbn argument). If a value is found, it will be returned and the method execution skipped. However, if the key is not found, the method is executed as usual and its result stored in the cache so the next time the method is invoked, the result can be returned without actually executing the (expensive or slow) method.

In practice not all methods have only one argument or, worse yet, the parameters are not suitable as cache keys – take for example a variation of the method above:

public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

In such cases, one can use Spring 3 Spring Expression Language or SpEL to cherry pick the proper arguments, navigate the object tree

// use property 'rawNumber' on isbn argument as key
@Cacheable(value="book", key="#isbn.rawNumber")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

or compute the key on the fly, even call arbitrary methods without having to write any code:

// get the key by calling someType#hash(isbn)
@Cacheable(value="book", key="T(someType).hash(#isbn)")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

Additionally, one can specify when or if the caching should occur: whether the cache should be inspected or completely disregarded and the method executed normally. It's up to the developer to decide what the criteria is: can be anything from the key size or type to the time of day or result of arbitrary methods: SpEL supports it all:

// cache only names shorter then 32 chars
@Cacheable(value="book", condition="#name.length < 32")
public Book findBook(String name)
// do not cache on weekends
@Cacheable(value="book", condition="!T(TimeUtils).isWeekend()")
public Book findBook(String name)

The cache abstraction also supports eviction of cache entries or of an entire cache through the @CacheEvict annotation. To evict a cache once it becomes invalid (for example because the cached data has been updated) one can use the following:

// evict all cache entries
@CacheEvict(value = "books", allEntries=true)
public void loadBooks(InputStream batch)

Once the annotations are in place, one can simply "enable" the caching functionality with one line (or three if you count the schema declaration):

Just like the rest of the annotation-driven element, the cache one uses defaults in its simplest form but can be used to pick between proxy and byte-code weaving of cached classes or to wire in the desired cache implementation.

Declaring a cache implementation

So far, we discussed the declarative aspect of the caching abstraction: how to add and remove data from the cache based on your POJOs. But what are the backing cache implementations that one can use?
Out of the box, Spring provides integration with ehcache and JDK ConcurrentHashMapgreat for small, non-distributed environments or testing:

<!-- generic cache manager -->
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
  <property name="caches">
    <set>
      <bean class="org.springframework.cache.concurrent.ConcurrentCacheFactoryBean" p:name="default"/>
      <bean class="org.springframework.cache.concurrent.ConcurrentCacheFactoryBean" p:name="books"/>
    </set>
  </property>
</bean>


What about [xx] library – when will it be supported ?

For the moment we do not plan to support other caching libraries inside Spring Framework simply because of the sheer number of options out there, the dependency impact (many are bigger in size than the cache abstraction), and the maintenance and licensing issues. To plug-in a custom cache provider, we encourage developers to look at the caching SPIpackage and its two interfaces: CacheManager and Cache. Besides the implementations available out of the box, one can look at the GemFire implementation, scheduled for the next major release version of Spring GemFire.

How does the caching abstraction compare to other caches (e.g. JPA 2nd-level cache) ?

In general, the two caching mechanisms can happily coexist as long as the developer pays attention to any domain overlap. Taking the example of the JPA 2nd-level cache, one can used it for data access through JPA while using Spring caching for the web-tier or remote service calls. One can go a step further by reusing the backed cache between the two mechanisms if that applies.

Summary

I hope you enjoyed this quick introductory entry to the new caching feature in Spring 3.1. For more information, please see the relevant reference documentation chapter and the SPIjavadoc. And do let us know what you think – we are interested in your feedback! You can reach us through the forum, blog post comments, our issue tracker or yours truly on Twitter.

SIMILAR POSTS



출처 - http://blog.springsource.com/2011/02/23/spring-3-1-m1-caching/






Quick start with method caching using Spring 3.1 and Ehcache


A while ago I wrote about method caching in Java using Ehcache Annotations for Spring. However, Spring 3.1 release, among many enhancements, brings native support for method caching with so-called cache abstraction. The main usage scenario of cache abstraction is to reduce the number of expensive executions and/or slow methods returning the same result for given parameters. The usage of Spring cache abstraction consists of two aspects: cache declaration and configuration. Spring provides many ways for declaring and configuring caching in an application: annotation-based, XML-based or mixed. In this article I will create an application that utilizes cache using Java-based approach. The example used in this article is a bit modified project taken from the original article.

Create a new web project

To create a project we use spring-mvc-quickstart Maven2 archetype that can be found here:spring-mvc-quickstart-archetype. After installing the archetype in your local repository we run the following command to create new Spring 3.1 MVC project:
    mvn archetype:generate
        -DarchetypeGroupId=com.github.spring-mvc-archetypes
        -DarchetypeArtifactId=spring-mvc-quickstart
        -DarchetypeVersion=1.0.0-SNAPSHOT
        -DgroupId=my.groupid
        -DartifactId=my-artifactId
        -Dversion=version
Once the project is created it may be imported to Eclipse/STS using “Import Existing Maven Projects”.

Ehcache dependency

Spring cache abstraction currently supports two cache storages: JDK ConcurrentMap-based Cache and Ehcache-based Cache that will be used in this example. In order to use Ehcache-based Cache storage in our application we need to add appropriate dependency on Ehcache in POM file:
1
2
3
4
5
<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache-core</artifactId>
    <version>2.5.0</version>
</dependency>

Cache configuration

Our application is configured via Spring @Configuration classes and the cache configuration will be configured the same way thanks to @EnableCaching annotation that enables Spring annotation-driven cache management capability to the application.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
@Configuration
@EnableCaching
public class CachingConfig {
    @Bean
    public EhCacheManagerFactoryBean ehCacheManagerFactoryBean() {
        EhCacheManagerFactoryBean ehCacheManagerFactoryBean = new EhCacheManagerFactoryBean();
        ehCacheManagerFactoryBean.setConfigLocation(new ClassPathResource("ehcache.xml"));
        return ehCacheManagerFactoryBean;
    }
    @Bean
    public CacheManager cacheManager() {
        EhCacheCacheManager cacheManager = new EhCacheCacheManager();
        cacheManager.setCacheManager(ehCacheManagerFactoryBean().getObject());
        return cacheManager;
    }
}
To finish the configuration we need to create ehcache.xml file that configures Ehcache. The file should be in the classpath (according to the above configuration), so we place it in src/main/resources:
1
2
3
4
5
<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
    <defaultCache eternal="true" maxElementsInMemory="100" overflowToDisk="false" />
    <cache name="messageCache" maxElementsInMemory="10" eternal="true" overflowToDisk="false" />
</ehcache>
CachingConfig class may be now imported by our WebMvcConfig class:
1
2
3
4
5
6
7
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = { "com.github.springmvcdemos.cachedemo" }, excludeFilters = @Filter(type = FilterType.ANNOTATION, value = Configuration.class))
@Import({PersistanceConfig.class, CachingConfig.class})
public class WebMvcConfig extends WebMvcConfigurerAdapter {
    // ...
}

Cache declaration

Now it’s time to add caching capabilities to the application. We declare caching onMessageRepository by setting the following rules:
  • use cache with name messageCache for caching messages for invocations offindByRecipient(String)
  • use cache with name messagesCache for caching messages for invocations of findAll()
  • remove all elements from both caches after the invocation of createMessage(Message)
To declare method cacheable we use theorg.springframework.cache.annotation.Cacheable annotation – method or type level annotation indicating the method or multiple methods can be cached. To invalidate the cache we useorg.springframework.cache.annotation.CacheEvict annotation:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Repository
public class MessageRepository {
    @PersistenceContext
    private EntityManager entityManager;
    @CacheEvict(value = { "messageCache", "messagesCache" }, allEntries = true, beforeInvocation = false)
    public Message createMessage(Message message) {
        entityManager.persist(message);
        return message;
    }
    @Cacheable(value = "messagesCache")
    public List findAll() {
        return entityManager.createQuery("select m from Message m",
                Message.class).getResultList();
    }
    @Cacheable(value = "messageCache")
    public List findByRecipient(String recipient) {
        return entityManager.createQuery("select m from Message m where m.recipient = :recipient",
                Message.class).setParameter("recipient", recipient).getResultList();
    }
    public Message findById(Long id) {
        return entityManager.find(Message.class, id);
    }
}
Note that both caches used in the above example appeared ehcache.xml file shown in previous section. To quickly see the result of the method caching we may create a @Controller that calls our repository methods:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
@Controller
@Transactional(readOnly = true)
public class MessageController {
    private MessageRepository repo;
    @Autowired
    public MessageController(MessageRepository repo) {
        this.repo = repo;
    }
    public MessageController() {
    }
    @RequestMapping(method = RequestMethod.GET, value = "/")
    public ModelAndView index() {
        return new ModelAndView("index");
    }
    @RequestMapping(method = RequestMethod.GET, value = "/message/add")
    public ModelAndView messageForm() {
        return new ModelAndView("message-form", "command", new Message());
    }
    @RequestMapping(method = RequestMethod.POST, value = "/message/add")
    @Transactional
    public ModelAndView addMessage(@ModelAttribute Message message) {
        message = repo.createMessage(message);
        return addMessageToModel(message);
    }
    @RequestMapping(method = RequestMethod.GET, value = "/message/{id}")
    public ModelAndView getMessageById(@PathVariable("id") long id) {
        Message message = repo.findById(id);
        return addMessageToModel(message);
    }
    private ModelAndView addMessageToModel(Message message) {
        ModelAndView mav = new ModelAndView("message-details");
        mav.addObject("message", message);
        return mav;
    }
    @RequestMapping(method = RequestMethod.GET, value = "/message", params= "recipient")
    public ModelAndView getMessagesByRecipients(@RequestParam String recipient) {
        return addMessagesToModel(repo.findByRecipient(recipient));
    }
    @RequestMapping(method = RequestMethod.GET, value = "/message")
    public ModelAndView getAllMessages() {
        return addMessagesToModel(repo.findAll());
    }
    private ModelAndView addMessagesToModel(Collection messages) {
        ModelAndView mav = new ModelAndView("messages");
        mav.addObject("messages", messages);
        return mav;
    }
}
On application startup we may observe that Ehcache manager is actually created and also cachable methods are created:
INFO : org.springframework.cache.ehcache.EhCacheManagerFactoryBean - Initializing EHCache CacheManager
DEBUG: org.springframework.cache.annotation.AnnotationCacheOperationSource - Adding cacheable method 'findByRecipient' with attribute: [CacheableOperation[public java.util.List com.github.springmvcdemos.cachedemo.domain.MessageRepository.findByRecipient(java.lang.String)] caches=[messageCache] | condition='' | key='']
DEBUG: org.springframework.cache.annotation.AnnotationCacheOperationSource - Adding cacheable method 'createMessage' with attribute: [CacheEvictOperation[public com.github.springmvcdemos.cachedemo.domain.Message com.github.springmvcdemos.cachedemo.domain.MessageRepository.createMessage(com.github.springmvcdemos.cachedemo.domain.Message)] caches=[messageCache, messagesCache] | condition='' | key='',true,false]
DEBUG: org.springframework.cache.annotation.AnnotationCacheOperationSource - Adding cacheable method 'findAll' with attribute: [CacheableOperation[public java.util.List com.github.springmvcdemos.cachedemo.domain.MessageRepository.findAll()] caches=[messagesCache] | condition='' | key='']
Please note that the method will be cached using default key – meaning that the method parameter is considered as a key and default cache condition – meaning that the method is always cached. Consult API docs for more details regarding these attributes. When calling the method for the first time we may observe in logs that the method was actually executed (hibernate.show_sql property is set totrue):
Hibernate: select message0_.id as id0_, message0_.body as body0_, message0_.recipient as recipient0_ from Message message0_ where message0_.recipient=?
Another call will not execute the method, instead its result will be returned from cache. And that’s it!

Summary

Spring cache abstraction brings to developers the possibility of method caching in applications with a minimal code changes and simple configuration to control the cache behavior. Comparing to already mentioned Ehcache Annotations for Spring it has one big advantage in my opinion: Spring provides an abstraction, which makes it extensible to support more cache managers in future or to provide your own ones.

References


출처 - http://blog.goyello.com/2012/01/20/quick-start-with-methods-caching-using-spring-3-1-and-ehcache/






Spring 3.1 M1 is out with some very useful features. One of the coolest feature in the latest release is comprehensive Caching support!

Spring Framework provides support for transparently adding caching into an existing Spring application. Similar to the transaction support, the caching abstraction allows consistent use of various caching solutions with minimal impact on the code.

The cache is applied to Java methods, reducing the number of executions based on the information available. Spring checks if the given method is already executed for given set of parameters. If the method is already executed, Spring uses the cache value and returns it to caller instead of calling the method again. This is a write through cache. This way, expensive methods (whether CPU or IO bound) can be executed only once for a given set of parameters and the result reused without having to actually execute the method again. The caching logic is applied transparently without any interference to the invoker.

Adding Cache support to Spring project

In order to add Cache support to any Spring based project, one needs to declare the configuration using new Spring tag in the schema declaration.

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

Note the cache:annotation-driven tag in above declaration enables the caching in given Spring project.

Using @Cacheable and @CacheEvict annotations

Spring 3.1 M1 provides two very useful Java annotations: @Cacheable and @CacheEvict which allow methods to trigger cache population or cache eviction. Let us take a closer look at each annotation:

@Cacheable annotation

This annotation mark a method cacheable. Thus the result from this method call will be stored into the cache on subsequent invocations with same arguments.

     
@Cacheable("persons")
public Person profile(Long personId) { ... }

In the above code snippet, method profile is marked cacheable using @Cacheable annotation. Also the method is associated with a cache named “persons“. Whenever method profile is called, the Spring framework will check if cached entry is available in persons cache and returns the same without calling profile method.

It is also possible to provide multiple cache names if you have multiple caches declared in your application. For example:

     
@Cacheable({"persons", "profiles"})
public Person profile(Long personId) { ... }

In above code snippet, we provide two cache names persons and profiles. Spring framework will check in all the caches if entry is available for given method call with argument personId, if at least one cache is hit, then the associated value will be returned.

@CacheEvict annotation

Cache eviction is removing of any unused or stale data from the cache. Opposed to @Cacheable, annotation @CacheEvict demarcates methods that perform cache eviction, that is methods that act as triggers for removing data from the cache. Just like its sibling, @CacheEvict requires one to specify one (or multiple) caches that are affected by the action, allows a key or a condition to be specified but in addition, features an extra parameter allEntries which indicates whether a cache-wide eviction needs to be performed rather then just an entry one (based on the key):

@CacheEvict (value = "persons", allEntries=true)
public List<Person> listPersons()

This annotation is very useful when an entire cache region needs to be cleared out. The Spring framework will ignore any key specified in this scenario as it does not apply.

Using Default key

The cache is nothing but a key-value store which stores the data based on certain key. In Spring framework based caching, the method arguments of cached method acts as the source of Key generation. Every key is essentially the Hash-code of these arguments. This approach works well for objects with natural keys as long as the hashCode() reflects that. If that is not the case then for distributed or persistent environments, the strategy needs to be changed as the objects hashCode is not preserved. In fact, depending on the JVM implementation or running conditions, the same hashCodecan be reused for different objects, in the same VM instance.

To provide a different default key generator, one needs to implement theorg.springframework.cache.KeyGenerator interface. Once configured, the generator will be used for each declaration that does not specify its own key generation strategy.

By default, all the method arguments are used in Key generation logic. In practice not all methods have only one argument or, worse yet, the parameters are not suitable as cache keys – take for example a variation of the method above:

     
@Cacheable(value="persons", key="personId")
public Person profile(Long personId, Long groundId) { ... }

Here we are using just personId in key generation ignoring groupId altogether.

Understand Conditional caching

Spring framework also supports conditional caching letting user to cache certain methods based on some conditions. For example, in following code snippet we cache profiles only for those users who have profileId greater than 50:

     
@Cacheable(value="persons", condition="personId > 50")
public Person profile(Long personId) { ... }

Currently supported libraries

There are probably hundreds of cache libraries available which can be used in your JEE project. For now the Spring framework supports following implementations:

  1. JDK ConcurrentMap based Cache
  2. Ehcache based Cache

JDK ConcurrentMap based Cache

The JDK-based Cache implementation resides under org.springframework.cache.concurrent package. It allows one to use ConcurrentHashMap as a backing Cache store.

<!-- generic cache manager -->
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
  <property name="caches">
    <set>
      <bean class="org.springframework.cache.concurrent.ConcurrentCacheFactoryBean" p:name="default"/>
      <bean class="org.springframework.cache.concurrent.ConcurrentCacheFactoryBean" p:name="persons"/>
    </set>
  </property>
</bean>

In above code snippet, we use SimpleCacheManager class to create a CacheManager. Note that we have created two caches in our application, one is default and second is persons.

Ehcache based Cache

The Ehcache implementation is located under org.springframework.cache.ehcache package. Again, to use it, one simply needs to declare the appropriate CacheManager:

<bean id="cacheManager" class="org.springframework.cache.ehcache.EhcacheCacheManager" p:cache-manager="ehcache"/>
 
<!-- Ehcache library setup -->
<bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:config-location="ehcache.xml"/>

This setup bootstraps ehcache library inside Spring IoC (through bean ehcache) which is then wired into the dedicated CacheManager implementation. Note the entire ehcache-specific configuration is read from the resource ehcache.xml.

References

출처 - http://viralpatel.net/blogs/cache-support-spring-3-1-m1/






TUESDAY, OCTOBER 4, 2011

EhCache Integration with Spring and Hibernate. Step by Step Tutorial

EhCache is a very popular open source caching framework widely used for Java based Caching.

Currently it’s owned by Terracotta.org and 2.5 beta releases is available for download.

In this article I just focused on EhCache Integration with Spring and Hibernate also I just explained few basic concepts which are required to understand Code. For detailed Caching Concepts Please visit http://ehcache.org/

Cache Manager, Caches and Elements are main entities of EhCache

EhCache consists of a CacheManager, which manages Caches. Caches contain Elements, which are essentially name value pairs. Caches are physically implemented either in-memory, or on disk.

Integration with Spring

EhCache integration with Spring is quite simple. You just need to define some properties in spring configuration xml file and its ready to use. Spring is using Annotation to integrate EhCache and by this we can add caching to any method results

EhCache Annotations for Spring is available via maven, simply add the following dependency to your pom.xml
 
<!-- EhCache -->
 <dependency>
      <groupId>com.googlecode.ehcache-spring-annotations</groupId>
      <artifactId>ehcache-spring-annotations</artifactId>
      <version>1.2.0-M1</version>
 </dependency>
<!—CGLib is also required for EHCache -->
<dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>            
      <version>2.2.2</version>
</dependency> 
   

Next Step is to perform changes in your spring Configuration file. Below is my Spring configuration file which contains EhCache configurations

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans" 
xmlns:context="http://www.springframework.org/schema/context" 
xmlns:mvc="http://www.springframework.org/schema/mvc" 
xmlns:p="http://www.springframework.org/schema/p" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:ehcache="http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring"   
xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-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/mvc 
 http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
  http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring 
  http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring/ehcache-spring-1.1.xsd">
          
    <context:component-scan base-package="com.eiconsulting"/>
    <mvc:annotation-driven  />
    <ehcache:annotation-driven cache-manager="cacheManager" />
    <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">   
         <property name="configLocation" value="/WEB-INF/ehcache.xml" />    
    </bean> 
</beans>



Important point to note here is that Cache Manager Name and its Configuration Location. We can use cacheManager in our code to store and retrieve information from Cache.

ehcache.xml file is configuration file for EhCache where we can define configuration details of EhCache. In my case here is content of my ehcache.xml file

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

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">    

    <defaultCache eternal="true" maxElementsInMemory="100" overflowToDisk="false" />           

    <cache name="customer" maxElementsInMemory="10" eternal="true" overflowToDisk="false" />  

 </ehcache>

 


Here it’s clear that we have created a cache with name Customer, I will use this Cache to store Customer details

Next step is to use it in your code. In my example I created a simple Spring based application where we have Customer Controller which is used to retrieve Customer Information

We have Customer Object with Customer Id, name and address details and related setter getter methods

public class Customer {
      public String address;
      public String name;
      public String id;




Next step is to create Controller. Customer Controller is very simple. I am using Spring Restful Web services to retrieve Customer Information. In my Controller I have no logic related to EhCache. I just have two versions of getCustomer method (one with Annotation and one without Annotation), save Customer and Clear Cache method. Code is simple and self explanatory for any Spring MVC developer.

package com.eiconsulting.web;

import net.sf.ehcache.CacheManager;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

import com.eiconsulting.beans.Customer;
import com.eiconsulting.services.CustomerService;

@Controller
public class CustomerController {
@Autowired @Qualifier("CustomerService")
public CustomerService customerService;

@RequestMapping(value="/GetCustomer/{customerId}",method={RequestMethod.GET})
@ResponseStatus(HttpStatus.OK)
public @ResponseBody String getCustomer(@PathVariable String customerId){
Customer c=customerService.getCustomer(customerId);
return c.toString();
}
@RequestMapping(value="/GetCachedCustomer/{customerId}",method={RequestMethod.GET})
@ResponseStatus(HttpStatus.OK)
public @ResponseBody String getCachedCustomer(@PathVariable String customerId){
Customer c=customerService.getCachedCustomer(customerId);
return c.toString();
}
@RequestMapping(value="/ClearCache",method={RequestMethod.GET})
@ResponseStatus(HttpStatus.OK)
public @ResponseBody String clearCache(){
boolean result=customerService.clearCache();
if(result) return "Sucessfully Cache Cleaned";
else return "Not able to Clean Cache";
}
@RequestMapping(value="/SaveCustomer/{customerId}",method={RequestMethod.GET})
@ResponseStatus(HttpStatus.OK)
public @ResponseBody String saveCustomer(@PathVariable String customerId){
Customer c=new Customer();
c.setId(customerId);
c.setAddress("New Address");
c.setName("New Name");
boolean result=customerService.saveCustomer(c);
if(result) return "Sucessfully Saved Customer";
else return "Not able to Save Customer";
}
}





If you notice Controller is calling Customer Service. Customer Service is our main class and it’s used to get Customer Information. It can get information from Database using Hibernate or from any backend. Here I just provided basic implementation. Idea is to understand usage of EhCache
Following Service methods are used to retrieve and store information in Cache

@Autowired
public CacheManager cacheManager;
public boolean saveCustomer(Customer customer){
Cache cache=cacheManager.getCache("customer");
cache.put(new Element(customer.getId(),customer));
return true;
}
public Customer getCachedCustomer(String customerId){
Cache cache=cacheManager.getCache("customer");
return (Customer)cache.get(customerId).getObjectValue();
}


     
Previously we have defined “cacheManager” object in our Spring.xml file and also we have defined a Cache with name “customer” in ehcache.xml file. Its time to use both these configurations
getCache method is use to retrieve Cache Object and we are storing new Element in this cache. Element requires key, value pair so I am using id filed as a key. Later on we can use same key to retrieve Customer Object from Cache.

Another great way of using EhCache is Spring Annotation

@Cacheable(cacheName="customer")
public Customer getCustomer(String customerId){
Customer c=new Customer();
c.setId(customerId);
c.setName("Adeel Shafqat");
c.setAddress("Address");
return c;
}

   

Add Cacheable Annotation to methods you would like to cache. In our case we are using it for getCustomer method. If we call getCustomer method with customerId 1, first time complete method will be called. If we will call this method again with same customerId then response will be returned from Cache.

To Clear Cache we can just use TriggerRemove Annotation

@TriggersRemove(cacheName = "customer", when = When.AFTER_METHOD_INVOCATION, removeAll = true) 
public boolean clearCache(){
return true;
}



Complete Source Code is present at following location

https://docs.google.com/viewer?a=v&pid=explorer&chrome=true&srcid=0B0YFdqXJcI3mY2ZiYzA4NWQtNmQ0ZS00ZWM0LTlkMzktMmM3YmJmZjUzNDEy&hl=en_US

Integration with Hibernate as a Second Level Cache

Hibernate uses different type of Caches

·         The first cache type is the session cache. The session cache caches object within the current session.
·         The second cache type is the query Cache. The query cache is responsible for caching queries and their results.
·         The third cache type is the second level cache. The second level cache is responsible for caching objects across sessions.
EhCache is used as second level cache. EhCache integration with Hibernate is quite simple as well. You just need to define some properties in persistence.xml file.
EhCache jars are available via maven, simply add the following dependency to your pom.xml  

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


   
Next step is to perform changes in persistence.xml  

<property name="hibernate.cache.region.factory_class" value="net.sf.ehcache.hibernate.EhCacheRegionFactory"/>
<property name="hibernate.cache.use_query_cache" value="true"/>
<property name="hibernate.cache.use_second_level_cache" value="true"/>
<property name="hibernate.cache.provider_class" value="net.sf.ehcache.hibernate.EhCacheProvider" /> 
<property name="hibernate.cache.provider_configuration_file_resource_path" value="ehcache.xml" /> 



 Put ehcache.xml at your classpath. It could be in classes or WEB-INF folder

<?xml version="1.0" encoding="UTF-8"?> 
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">    
 
  <defaultCache eternal="true" maxElementsInMemory="100" overflowToDisk="false" />    
  <cache name="messages" maxElementsInMemory="10" eternal="true" overflowToDisk="false" />  

 </ehcache>



In you Entity just add Annotations related to ehcache and thats it.

@Entity
@Table(name="MESSAGES")
@Cache(region = "messages", usage = CacheConcurrencyStrategy.READ_WRITE)

public class Message {



Complete Source Code is present at following location

https://docs.google.com/viewer?a=v&pid=explorer&chrome=true&srcid=0B0YFdqXJcI3mMDJiMDU4MmUtZWFkYy00MTAxLWFjMmYtMWYyMGNhMWY2Mjdj&hl=en_US


출처 - http://eiconsulting.blogspot.kr/2011/10/ehcache-implementation-in-spring.html






Cache abstraction in Spring 3

In this article we’ll have a look at the new Spring 3 feature: caching by annotation.

I Cache abstraction layer

Caching is an important feature for all applications needing high performance. Many open-source frameworks are available: EhCacheJBoss CacheOSCacheOpen Terracota …

Their integration with the application is sometimes easy sometimes more complex, depending on the desired features and use (distributed cache ? cache JMX management …)

Spring 3 introduces a new abstraction layer for caching services. The idea is to provide a set of common features, mainly annotations, to activate and manage the caches.

Since it is only an abstract layer, Spring 3 caching still need a concrete implementation to work. The entry point for cache implementation is the CacheManager interface. By default 2 concrete implementation of CacheManager are provided:

  • EhCacheCacheManager: default implementation for EhCache
  • ConcurrentMapCacheManager: default implementation using Java ConcurrentHashMap as cache store

 

II Configuration

For this example we’ll use EhCache as cache implementation.

First you need to import the Spring cache namespace and add the &th;cache:annotation-driver> tag.

1
<cache:annotation-driven cache-manager="cacheManager" mode="proxy" order="1" />
  • cache-manager: id or name of the bean implementing the CacheManager interface. Can be omitted because by convention Spring will look for any bean with the name “cacheManager
  • modeproxy or aspectj. “proxy” will use Spring AOP framework to proxy any class having a caching annotation (see below). “aspectj” will rely on AspectJ aspect for cache management
  • order: optional. If you have more than one aspect declared on the same method, it can be useful to specify the order in which they execute

Then you need to declare the cacheManager bean and implementation instance (or factory bean)

1
2
3
4
5
6
7
8
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
    <property name="cacheManager" ref="ehcache"/>
</bean>
 
<!-- Ehcache library setup -->
<bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
    <property name="configLocation" value="classpath:ehcache.xml"/>
</bean>

First we declare the EhCacheCacheManager as default cache manager. Then we inject theEhCacheManagerFactoryBean so it can retrieve an instance of EhCache class.

Please notice the very confusing naming. The EhCacheCacheManager instance is given the id “cacheManager” as per Spring convention but it also has a cacheManager property. This property is indeed an instance ofnet.sf.ehcache.CacheManager, different from the org.springframework.cache.CacheManager interface.

Last but not least, you can specify the config file for ehcache with the property configLocation ofEhCacheManagerFactoryBean. If not declared it will default to “ehcache.xml

 

III Cache annotations

By default Spring proposes several annotations to manage your caches:

  • @Cacheable: put the method returned value(s) into the cache
  • @CacheEvict: remove an entry from the cache
  • @CachePut: force a cache entry to be updated

And that’s pretty much. But don’t be fooled by their apparent simplicity, they offer a lot of possibilities to fine tune your caching.

A @Cacheable

When annotating a method with @Cacheableits returned value will be put into the cache provided it meets some condition (if any). Consequently it does not make sense to annotate a void method.

So what can be put in cache ? Pretty much anything, an Object, a collection (List, Map, Set, Array)…

When is the cache activated ? All subsequent calls on the same method with the same arguments or cache key (we’ll see it later) will trigger the cache. Instead of executing the method, the cache is scanned to check whether a matching entry can be found, if yes then it is returned. Otherwise the method is really executed and its result put into the cache.

Below is the pseudo-code:

  • Method is called with arguments args
  • Use the args hashCode or extract the cache key from the args to look for an entry in the cache
  • If a corresponding entry is found
    • Return the cached entry
  • Else
    • Execute really the method
    • Put the method returned value into the cache using the args hashCode of extracted cache key

So what is the cache key and why do we need to use args hashCode ? We’ll see it shortly. First the @Cacheableannotation offers the following attributes:

  • value: mandatory, the name of the cache to work on
  • key: optional, the key used to store and fetch data from the cache
  • condition: optional, specifies the condition to verify before caching an item

Example:

1
2
3
4
5
6
7
8
9
@Cacheable(value = "user-cache", key="#userSearchVO.name", condition="#supportUser == false")
public User findUser(UserSearchVO userSearchVO, boolean supportUser)
{
    User foundUser = null;
    ...
    ...
    ...
    return foundUser;
}

In the above example

  • value = “user-cache” indicates the name of the cache in which entries are stored
  • key=”#searchVO.name” defines the key to lookup into the cache. Here it is the name property of theUserSearchVO object
  • condition=”#supportUser == false” provides the condition to trigger the cache. The cache lookup and the method result caching are triggered only when the supportUser flag is set to false

Please notice that for the key and condition attributes, we are using SpEL to process the method arguments.

This is very convenient because the SpEL expression language is extremely powerfull. For example, if theUserSearchVO has an attribute searchType of enum type, we can do the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Cacheable(value = "user-cache", key="#userSearchVO.name", condition="#userSearchVO.searchType == ${T(com.doan.spring.cache.SearchType).CLASSIC")
public User findUser(UserSearchVO userSearchVO, boolean supportUser)
{
    User foundUser = null;
    ...
    ...
    ...
    return foundUser;
}
 
public enum SearchType
{
    CLASSIC,
    ADVANCED,
    SUPPORT
}

In this case the cache is triggered only when the search is of type CLASSIC.

Note that we use == instead of equals() to check for the searchType. Since the attribute searchTypeis an enum, we want to check object identity (same reference) with == rather than object equality (same value) with equals().

Another simple example:

1
2
3
4
5
6
7
8
9
@Cacheable(value = {"user-cache1","user-cache2"})
public User findUserByLoginAndName(String login, String name)
{
    User foundUser = null;
    ...
    ...
    ...
    return foundUser;
}

In this example, we declare more than one cache (“user-cache1″,“user-cache2″) and no key information.

By default if no cache key is provided Spring will compute a hash code of all method arguments (here it is login &name) and use it as a key for cache lookup. If your method arguments are not of primitive type and you want them to be used as cache key, you should redefine properly the hashCode() method.

Last but not least, Spring will scan each cache for key lookup, if an entry is found in any declared cache, it will be returned, subsequent caches are skipped. If all caches are scanned and no entry is found, the method will be executed and the result added to all the declared caches.

 

B @CacheEvict

The @CacheEvict is used to trigger explicit cache eviction. By default most of caching frameworks expire the cache data after some defined duration. The @CacheEvict annotation is usefull when you want to control explicit cache eviction upon some method calls.

1
2
3
4
5
6
@CacheEvict(value = "user-cache", key = "#user.login")
public void updateUser(User user)
{
    ...
    ...
}

The above code is quite self-explanatory. The @CacheEvict annotation exposes the following attributes:

  • value: mandatory, the name of the cache to evict entry
  • key: optional, the key used to lookup data from the cache
  • condition: optional, specifies the condition to verify before evicting a cache entry
  • allEntries: optional, indicates that all entries from the cache should be removed
  • beforeInvocation: optional, indicates whether the cache evict should be done before or after method call

Obviously the key and allEntries attributes are mutually exclusive.

 

C @CachePut

The @CachePut annotation allows you to “update” a cache entry. This is very similar to @Cacheable but for entry update. The @CachePut annotation has exactly the same attributes than @Cacheable.

1
2
3
4
5
6
7
8
9
10
@CachePut(value = "user-cache", key = "#user.login")
public User updateUserName(User user,String newName)
{
    ...
    ...
    user.setName(newName);
    ...
    ...
    return user;
}

In the case of @CachePutthe method is always executed and its returned result is put into the cache, using the provided key (or arguments hash), replacing the old entry if necessary.

The only case where the method is not executed is when you provide an optional @CachePut condition and the condition is not met.
 

IV Multiple caching policies

Let’s suppose than we have an application with 2 cache regions: “user-cache” and “user-details-cache”.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Cacheable(value = "user-cache", key="#login")
public User findUserByLogin(String login)
{
    ...
    ...
    return foundUser;
}
...
...
 
@Cacheable(value = "user-details-cache", key="#login")
public UserDetails findUserDetailsByLogin(String login)
{
    ...
    ...
    return userDetails;
}

It is possible to trigger the eviction from both caches with the same key:

1
2
3
4
5
6
7
8
9
10
@CacheEvict(value =
{
    "user-cache",
    "user-details-cache"
}, key="#login")
public void updateUserDetails(String login, UserDetails newUserDetails)
{
    ...
    ...
}

In this particular example, it’s working very well because we access both caches with the same key. What if we want to evict from those caches with different keys ?

1
2
3
4
5
6
7
@CacheEvict(value = "user-cache", key="#login")
@CacheEvict(value = "user-details-cache", key="#newUserDetails.id")
public void updateUserDetails(String login, UserDetails newUserDetails)
{
    ...
    ...
}

It’s simply not possible because Java does not allow you to have more than one type of annotation on the same method.

For this kind of use case, Spring provides a generic @Caching annotation. This annotation simply allows you to group caching annotations of same type like @Cacheable@CacheEvict or @CachePut.

With this said, our example becomes:

1
2
3
4
5
6
7
8
9
@Caching(evict = {
    @CacheEvict(value = "user-cache", key="#login"),
    @CacheEvict(value = "user-details-cache", key="#newUserDetails.id")
})
public void updateUserDetails(String login, UserDetails newUserDetails)
{
    ...
    ...
}

And you’re done!

Of course if you try to mix caching annotation of different type in the same @Caching, needless to say that you are running into big trouble…

 

V Object mutability gotcha

So far the caching abstraction infrastructure proposed by Spring is very convenient. However, for having used it in a real project, I’ve spotted 1 pain point : object mutability issues, thought this point is not Spring’s specific but common to all caching frameworks.

Indeed when you get an instance of User after calling findUserByLogin(String login), this instance may come from the cache directly.

If you are modifying this User instance (changing an user property for example), you are indeed modifying the object which is in the cache directly !!!

The immediate consequence is that on the same server, if another client looks for the same user, the cache will give him the same User instance that has been modified earlier…

There are 2 solutions for this issue.

  1. The first idea is to use objects returned from cache as read-only but this rule cannot be enforced easily. Suppose that you put @Cacheable annotation on a Repository or DAO method, the developper that calls this method from Service layer may not be aware that he’s getting a cached instance and may modify it.
  2. The second fix is to perform a deep copy of the returned object. But again, implementing deep copy is not an easy task (as I mentioned in my article about Object Immutablity) and the deep copy should be done by thecaller (Service layer in our example). The same issue with “knowing that we’re dealing with cached instances” mentioned above also applies.

Fortunately, latest EHCache versions provide a very usefull feature: copyOnRead.

1
2
3
4
5
6
7
8
9
<cache name="user-cache"
    ...
    eternal="false"
    timeToIdleSeconds="10"
    timeToLiveSeconds="1800"
    overflowToDisk="false"
    copyOnRead="true"
>
</cache>

With this flag turned on, EHCache will return you a deep copy of the object instance that is in the cache, not the instance itself. So mutability issue is solved.

Of course there is no free lunch, this convenient feature comes with its counterparts, the cost of serialization/deserialization because the default implementation is plain Java object serialization.


출처 - http://doanduyhai.wordpress.com/2012/07/01/cache-abstraction-in-spring-3/







@Caching With Multiple Keys


I have a service that takes in a DTO and returns some result:

@Override
public int foo(Bar bar) {
    ....
}

Bar is as follows (simplified):

public class Bar {
    public int id;
    public String name;
    public String baz;

    @Override
    public int hashCode() {
        //this is already being defined for something else
        ...
    }

    @Override
    public boolean equals(Object o) {
        //this is already being defined for something else
        ...
    }
}

I want to use @Cacheable on the foo method; however, I want to hash on the id and name properties, but not baz. Is there a way to do this?

share|improve this question

Yes, you can specify using a Spring-EL expression along these lines:

@Override
@Cacheable(key="#bar.name.concat('-').concat(#bar.id)")
public int foo(Bar bar) {
    ....
}

or define a modified hashCode on bar and call that:

@Override
@Cacheable(key="#bar.hashCodeWithIdName")
public int foo(Bar bar) {
    ....
}



출처 - http://stackoverflow.com/questions/13381731/caching-with-multiple-keys







Posted by linuxism
,


How should I implement request response with JMS?

The simplest solution is to use Camel as a Spring Remoting provider which allows you to hide all the JMS API from your business logic and letting Camel provide the request/response handling code for you.

However if you wish to write the JMS client code yourself, please read on how it works...

Using the JMS API to implement request-response

You might think at first that to implement request-response type operations in JMS that you should create a new consumer with a selector per request; or maybe create a new temporary queue per request.

Creating temporary destinations, consumers, producers and connections are all synchronous request-response operations with the broker and so should be avoided for processing each request as it results in lots of chat with the JMS broker.

The best way to implement request-response over JMS is to create a temporary queue and consumer per client on startup, set JMSReplyTo property on each message to the temporary queue and then use a correlationID on each message to correlate request messages to response messages. This avoids the overhead of creating and closing a consumer for each request (which is expensive). It also means you can share the same producer & consumer across many threads if you want (or pool them maybe).

The Lingo library is an implementation of Spring remoting using JMS. (Spring remoting is a kind of POJO based remoting where the remoting code is invisible to your business logic code).

It uses exactly this pattern; of using correlation IDs to correlate requests to responses. The server side just has to remember to put the inbound message's correlation ID on the response.

The actual class which does this is the MultiplexingRequestor . It may be just using Spring remoting with Lingo is the simplest way of implementing request response - or maybe you could just use Lingo's Requestor interface to keep the JMS semantics.

More details here

Client side

So the client side creates a consumer on a temporary queue as follows...

// client side
Destination tempDest = session.createTemporaryQueue();
MessageConsumer responseConsumer = session.createConsumer(tempDest);
...

// send a request..
message.setJMSReplyTo(tempDest)
message.setJMSCorrelationID(myCorrelationID);

producer.send(message);

Server side

public void onMessage(Message request) {

  Message response = session.createMessage();
  response.setJMSCorrelationID(request.getJMSCorrelationID())

  producer.send(request.getJMSReplyTo(), response)
}

Full Examples

Server Side

import org.apache.activemq.broker.BrokerService;
import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

public class Server implements MessageListener {
    private static int ackMode;
    private static String messageQueueName;
    private static String messageBrokerUrl;

    private Session session;
    private boolean transacted = false;
    private MessageProducer replyProducer;
    private MessageProtocol messageProtocol;

    static {
        messageBrokerUrl = "tcp://localhost:61616";
        messageQueueName = "client.messages";
        ackMode = Session.AUTO_ACKNOWLEDGE;
    }

    public Server() {
        try {
            //This message broker is embedded
            BrokerService broker = new BrokerService();
            broker.setPersistent(false);
            broker.setUseJmx(false);
            broker.addConnector(messageBrokerUrl);
            broker.start();
        } catch (Exception e) {
            //Handle the exception appropriately
        }

        //Delegating the handling of messages to another class, instantiate it before setting up JMS so it
        //is ready to handle messages
        this.messageProtocol = new MessageProtocol();
        this.setupMessageQueueConsumer();
    }

    private void setupMessageQueueConsumer() {
        ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(messageBrokerUrl);
        Connection connection;
        try {
            connection = connectionFactory.createConnection();
            connection.start();
            this.session = connection.createSession(this.transacted, ackMode);
            Destination adminQueue = this.session.createQueue(messageQueueName);

            //Setup a message producer to respond to messages from clients, we will get the destination
            //to send to from the JMSReplyTo header field from a Message
            this.replyProducer = this.session.createProducer(null);
            this.replyProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);

            //Set up a consumer to consume messages off of the admin queue
            MessageConsumer consumer = this.session.createConsumer(adminQueue);
            consumer.setMessageListener(this);
        } catch (JMSException e) {
            //Handle the exception appropriately
        }
    }

    public void onMessage(Message message) {
        try {
            TextMessage response = this.session.createTextMessage();
            if (message instanceof TextMessage) {
                TextMessage txtMsg = (TextMessage) message;
                String messageText = txtMsg.getText();
                response.setText(this.messageProtocol.handleProtocolMessage(messageText));
            }

            //Set the correlation ID from the received message to be the correlation id of the response message
            //this lets the client identify which message this is a response to if it has more than
            //one outstanding message to the server
            response.setJMSCorrelationID(message.getJMSCorrelationID());

            //Send the response to the Destination specified by the JMSReplyTo field of the received message,
            //this is presumably a temporary queue created by the client
            this.replyProducer.send(message.getJMSReplyTo(), response);
        } catch (JMSException e) {
            //Handle the exception appropriately
        }
    }

    public static void main(String[] args) {
        new Server();
    }
}

Client Side

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;
import java.util.Random;

public class Client implements MessageListener {
    private static int ackMode;
    private static String clientQueueName;

    private boolean transacted = false;
    private MessageProducer producer;

    static {
        clientQueueName = "client.messages";
        ackMode = Session.AUTO_ACKNOWLEDGE;
    }

    public Client() {
        ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616");
        Connection connection;
        try {
            connection = connectionFactory.createConnection();
            connection.start();
            Session session = connection.createSession(transacted, ackMode);
            Destination adminQueue = session.createQueue(clientQueueName);

            //Setup a message producer to send message to the queue the server is consuming from
            this.producer = session.createProducer(adminQueue);
            this.producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);

            //Create a temporary queue that this client will listen for responses on then create a consumer
            //that consumes message from this temporary queue...for a real application a client should reuse
            //the same temp queue for each message to the server...one temp queue per client
            Destination tempDest = session.createTemporaryQueue();
            MessageConsumer responseConsumer = session.createConsumer(tempDest);

            //This class will handle the messages to the temp queue as well
            responseConsumer.setMessageListener(this);

            //Now create the actual message you want to send
            TextMessage txtMessage = session.createTextMessage();
            txtMessage.setText("MyProtocolMessage");

            //Set the reply to field to the temp queue you created above, this is the queue the server
            //will respond to
            txtMessage.setJMSReplyTo(tempDest);

            //Set a correlation ID so when you get a response you know which sent message the response is for
            //If there is never more than one outstanding message to the server then the
            //same correlation ID can be used for all the messages...if there is more than one outstanding
            //message to the server you would presumably want to associate the correlation ID with this
            //message somehow...a Map works good
            String correlationId = this.createRandomString();
            txtMessage.setJMSCorrelationID(correlationId);
            this.producer.send(txtMessage);
        } catch (JMSException e) {
            //Handle the exception appropriately
        }
    }

    private String createRandomString() {
        Random random = new Random(System.currentTimeMillis());
        long randomLong = random.nextLong();
        return Long.toHexString(randomLong);
    }

    public void onMessage(Message message) {
        String messageText = null;
        try {
            if (message instanceof TextMessage) {
                TextMessage textMessage = (TextMessage) message;
                messageText = textMessage.getText();
                System.out.println("messageText = " + messageText);
            }
        } catch (JMSException e) {
            //Handle the exception appropriately
        }
    }

    public static void main(String[] args) {
        new Client();
    }
}

Protocol Class

This class is needed to run the client/server example above. Delegating the handling of messages to a seperate class is solely a personal preference.

public class MessageProtocol {
    public String handleProtocolMessage(String messageText) {
        String responseText;
        if ("MyProtocolMessage".equalsIgnoreCase(messageText)) {
            responseText = "I recognize your protocol message";
        } else {
            responseText = "Unknown protocol message: " + messageText;
        }
        
        return responseText;
    }
}


출처  - http://activemq.apache.org/how-should-i-implement-request-response-with-jms.html

'OpenSource > Common' 카테고리의 다른 글

open source issue and bug tracking software  (0) 2014.01.09
elasticsearch 사용  (0) 2014.01.08
Lisog - Open Source Business Alliance  (0) 2013.03.25
list of PDF software  (0) 2013.03.25
OpenSource 소프트웨어의 개요  (0) 2012.12.29
Posted by linuxism
,