I am Nicolas Labrot, a software engineer at 4Dconcept. I have interrest in Java, JEE (JSF, Hibernate...), Maven, Agile, etc. You can follow me on http://twitter.com/#!/NicolasLabrot Nicolas has posted 3 posts at DZone. View Full User Profile

How to Cache JSF Beans Getter with Spring AOP

01.05.2011
| 12333 views |
  • submit to reddit

Introduction

JSF can call a getter more than once per request.

For example in the following JSF file :

<html>
<body>
<h:dataTable var="car" value="#{carService.cars}">
<h:column>
<f:facet name="header">
Model
</f:facet>
<h:outputText value="#{car.model}"/>
</h:column>

<h:column>
<f:facet name="header">
Year
</f:facet>
<h:outputText value="#{car.year}"/>
</h:column>

<h:column>
<f:facet name="header">
Manufacturer
</f:facet>
<h:outputText value="#{car.manufacturer}"/>
</h:column>
</h:dataTable>
</body>
</html>

JSF will call the carService.cars getter more than once inside the same request.

In this example, JSF calls it three times :

13:39:40 DEBUG : CarService.getCars:17 - qtp17489534-18
13:39:40 DEBUG : CarService.getCars:17 - qtp17489534-18
13:39:40 DEBUG : CarService.getCars:17 - qtp17489534-18

 A getter obviously returns a value. This value can be a bean property or a calculated value.

If the value is calculated, this can potentially be a problem.

I googled a bit and found some workarounds.  

  1. The first one was to include a check and see if it has already been calculated.
  2. The second one was to use the getter only to access bean properties
  3. The third one was to use Spring AOP and cache the value.


For me, the last workaround is the best one because you do not have to modify the view.

And thanks to Spring, it's quite simple to add AOP with a few annotations.
                                          

The workaround

First we create the annotation Cacheable :

@Retention(RetentionPolicy.RUNTIME)
public @interface Cacheable {
}


This annotation will be used to cache the result of a method.

Then we create the annotation matching pointcut :

<bean class="org.springframework.aop.support.annotation.AnnotationMatchingPointcut" id="cachePointCut">
<constructor-arg index="0"><null/></constructor-arg>
<constructor-arg index="1" value="org.nigaju.cache.annotation.Cacheable" />
</bean>


And the method interceptor :

<bean id="cacheInterceptorAdvice" class="org.nigaju.cache.CacheAdvice"/>
With the associated class :
public class CacheAdvice implements MethodInterceptor {

private static Logger logger = LoggerFactory.getLogger(CacheAdvice.class);

@Autowired
private CacheService cacheService;

@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {

String key = methodInvocation.getThis() + methodInvocation.getMethod().getName();

String thread = Thread.currentThread().getName();

Object cachedValue = cacheService.getData(thread , key);

if (cachedValue == null){
cachedValue = methodInvocation.proceed();
cacheService.cacheData(thread , key , cachedValue);
logger.debug("Cache miss " + thread + " " + key);
}
else{
logger.debug("Cached hit " + thread + " " + key);
}
return cachedValue;
}


public CacheService getCacheService() {
return cacheService;
}
public void setCacheService(CacheService cacheService) {
this.cacheService = cacheService;
}
}

The key generator is quite simple (do not use it). The CacheService class embed a ConcurrentHashMap.

And finally the pointcut advisor :

<bean id="advisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="pointcut">
<ref bean="cachePointCut"/>
</property>
<property name="advice">
<ref bean="cacheInterceptorAdvice"/>
</property>
</bean>

The cache has to be flushed after each request. For that, I use a JSF lifecycle listener that flush the cache for the current request after the RENDER_RESPONSE phase.

Easy! I have attached a small Maven war project that contains the code of this article.

What is your opinion ?

Legacy
Article Resources: 
Published at DZone with permission of its author, Nicolas Labrot.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Andy Gibson replied on Wed, 2011/01/05 - 10:00am

Or just make it a request scoped JSF or CDI bean and it will exist for the remainder of the request.

What about clearing the cache when the list of cars changes mid-request (i.e. the user moves to the next page in the list of cars)? Your backing beans now have to suddenly be cache aware to know when to clear it.

No wonder people find JSF to be complex when they go to such lengths to solve problems that have already been solved.

 In this scenario, the only 'best practice' JSF or CDI would require of you to do this is to add a @RequestScoped annotation and to use lazy initialization for returning the list of cars. Voila, automatic request scoped caching.

Gerard COLLIN replied on Wed, 2011/01/05 - 10:50am

I agree with you Andy, as I understand a framework calling 3 times a getter to display a page doesn't seem very good to me, your solution is the obvious right one. Doing AOP for caching data hides this from the screen developer, and could be very difficult to debug. Without even writing about the performance impact of have yet another proxy layer in the code. Regards, Gérard.

Nicolas Labrot replied on Wed, 2011/01/05 - 12:13pm

On the contrary, this answer is simpler (because transparent) than adding request scoped beans with lazy loading to handle all getters (this statement fit into workaround 1 and 2). You annotate a method and voila.

People find JSF slow because they request the DB inside a getter. And after googled they find it complex because they need to create a request bean.

I can make the same statement about lazy loading if the list of cars changes mid-request.

This "how to" could be greatly improved. For example the cache could be activated only on the render phase. Of course this answer should be used with caution and it's not a solution that meets all of the problems. For example it is better to use a DataModel instead a list of object inside a datatable.

@Gerard : the performance impact is negligible.

 

Andy Gibson replied on Wed, 2011/01/05 - 2:13pm

Nicolas, you aren't adding a request scoped bean, you are making the existing backing bean request scoped (although I'm going to go out on a limb and say you are trying to bind JSF components to results from a stateless spring bean without holding it in a backing bean). In most cases, you should be lazy loading anyway, except for DAO type objects where you are actually doing the fetching which is used once per request to perform the loading (technically, it could be twice per request if the data was invalidated in the app).

This is the problem with people trying to mix JSF which is semi-stateful, and Spring which stateless. People spend their time trying to figure out how to bind JSF to stateless Dao type beans and end up with these kinds of concoctions, at which point, people start complaining about how messy it all is. It's also a further example of why people should match similar frameworks (stateful, stateless etc) instead of trying to shoehorn them together in ungodly formations. JSF and Wicket have the similar problems since bean references aren't meant to last multiple requests so there's a lifecycle impedence there.

Action type frameworks just grab the state, mix in some HTML and you have a page. Stateless Spring is great for this and they created web flow for when you want something more stateful, specifically for things like JSF.

 >I can make the same statement about lazy loading if the list of cars changes mid-request.

You can't though, when the backing bean changes the list of cars such as when the next () method is called to get the next set of data; it knows it needs to invalidate the list of cars that it is responsible for. Your cache is a totally separate concern which the backing bean shouldn't know about and now has to deal with whenever it changes the data. I think as a general rule, any object that can have an aspect applied to it should not have to be aware of that aspect.

People plugging their JSF components in to DB driven getters is not only a misuse of JSF (they put backing beans there for a reason), but also has other implications (your GUI is now bound to your service layer).

 

 

Jonathan Fisher replied on Wed, 2011/01/05 - 2:33pm

If you stick to an anemic domain model, this isn't a problem. If you mix business logic in your getters/setters, you deserve all the pain such practices should cause you!

-An angry (occasional) maintenance coder

Mario León replied on Wed, 2011/01/05 - 11:31pm

@Andy Gibson & @Jonathan Fisher

100% agree, the common problem is that the business logic is bound to the presentation layer, this is an architectural concern.

 JSF is intented for presentation layer, so you better decouple the back-end from the front-end, or you'll:

@Jonathan Fisher

 

... you deserve all the pain such practices should cause you.

Nicolas Labrot replied on Thu, 2011/01/06 - 4:39am

@Andy :

I donnot understand why you speak about Spring. Spring is used in this "how to" for AOP and for the EL resolver.

Why should I make my backed bean request scoped ? The backed bean can have a scope > request like a conversation scope or a session scope.

>>I can make the same statement about lazy loading if the list of cars changes mid-request.

With this pattern, you have to manage the cache manually and for all getter. You should be aware of the cache and the invalidation.

Again, this "how to" could be greatly improved. Another anotation could be add to invalidate the value cached. For example the getter method will be annoted with @Cachable and the addToList with @Invalidate. Transparent.

@Mario and  Jonathan : the topic of this article is not about layer, the topic is about "JSF can call a getter more than once per request" and the workaround. If I manually cache the result or if it is cached with AOP, I always need to query the business layer.

 

Cosmin Mutu replied on Thu, 2011/01/06 - 9:00am

@ManagedBean
@RequestScoped
public class MyBean {

private String myValue;


public String getMyValue() {
if (myValue == null || myValue.isEmpty()) {
myValue = getMyValueFromDB();
}
return myValue;
}

}
@Nicolas : why can`t I just do this? 
@Andy : this is what you meant, right? 

Nicolas Labrot replied on Thu, 2011/01/06 - 9:36am

You can do it. It will work.

Or you can do it with an init method if the getter is always called :

@ManagedBean
@RequestScoped
public class MyBean {

private String myValue;

@PostConstruct
public void init(){
myValue = getMyValueFromDB();
}

public String getMyValue() {
return myValue;
}

}

Or you can do with my annotation :

@ManagedBean
@RequestScoped
public class MyBean {

@Cacheable
public String getMyValue() {
return getMyValueFromDB();
}

}

 But what about if MyBean hasn't a RequestScoped with your code ?

 

 

Andy Gibson replied on Thu, 2011/01/06 - 1:24pm in response to: Nicolas Labrot

I mention spring because it is the only reason I can assume you aren't using request scoped beans, instead favouring getters  on stateless Spring beans and implementing your own request scoped state management.

>Why should I make my backed bean request scoped ?

Because then you won't have to use AOP to roll your own psedo request scope. Reuse is good.

 Cosmin, yes, that is what I meant, the lazy initilization means that the values are only calculated when needed for the first time and held for the duration of the request.

Nicolas, in the second example, I'm assuming you meant to remove the @requestscoped annotation since the fact that is it request scoped would mean you don't even need to cache it. If not, why would you just not hold the value in a field variable instead of holding it in a map? 

 

 

 

Nicolas Labrot replied on Thu, 2011/01/06 - 2:16pm

I don't use request scoped bean because my bean has a scope longuer than the request scope. 

The value returned by a getter need to be a precomputed value. The answer to this constraint (in my opinion) do not have to modify too much the backed bean. The code paste by Muffin modify too much the bean code with logic that do not participate to the UI logic.

For a @RequestScoped bean @PostConstruct or @Cacheable perform the same function.

Andy if I have a backed bean with a scope longuer than the request scope. This backed bean has a getter which returns a computed value. What is in your opinion the best pattern to cache the value of the getter ?

 

Cosmin Mutu replied on Fri, 2011/01/07 - 2:38am in response to: Nicolas Labrot

@Andy : danke, I was not clear what was all the fuss about :) that`s why I wanted to put a face on the issue with some code :)

@Nicolas : @ViewScoped, @CustomScoped, @ApplicationScoped, @SessionScoped :

-----------------------------------  @ViewScoped ------------------------------------

Managed beans registered in view scope remain available as long as the user stays on the same view. Navigating away from that view will cause beans registered in that view’s scope to be deallocated.

-----------------------------------  @CustomScoped------------------------------------

 If the content of the <managedbean-scope> element, or the value of the value attribute on the @CustomScoped annotation is an EL Expression, and the expression evaluates to a class that implements java.util.Map, then the runtime uses the map itself as the scope into which the instance of the managed bean will be placed.

-----------------------------------  @ApplicationScoped------------------------------------

 Managed beans registered with an application scope retain their values throughout the lifetime of the application and are available to all users.

-----------------------------------  @SessionScoped------------------------------------

Managed beans registered with a session scope will be stored on the HTTP session. This means that the values in the managed bean will persist beyond a single HTTP request for a single user. This is ideal for a shopping cart type of usage where values must be stored and made available during multiple requests.

-----------------------------------------------------------------------------------------

It`s all in the book :  Java Server Faces 2.0 - The Complete Reference

Nicolas Labrot replied on Fri, 2011/01/07 - 3:32am

I don't understand why you past this Cosmin. I don't have ask question about scope.

Cosmin Mutu replied on Fri, 2011/01/07 - 3:50am in response to: Nicolas Labrot

Then I missunderstood your dilema.

You have a problem with how many times a getter is called for a given property. This results in two options :

1) You have a problem with the number of times a getter is called - you actually want the getter to be called only once no matter what (1)

2) You have a problem with the number of times a getter is called - your getter can be called multiple times, but the computation should be performed only once (2) 

I was discussing about number (2)

Nicolas Labrot replied on Fri, 2011/01/07 - 4:14am

To be clear I don't have problem :-). I know the workarounds.

I would like to know pattern for the question  :

If I have a backed bean with a scope longuer than the request scope. This backed bean has a getter which returns a computed value. What is in your opinion the best pattern to cache the value of the getter ?

Cosmin Mutu replied on Fri, 2011/01/07 - 4:59am

Using @CustomScope :)

Try reading this : http://blogs.sun.com/rlubke/entry/custom_managed_bean_scopes

And then download this war and play around : http://blogs.sun.com/rlubke/resource/custom-bean-scope.war

Cheers ;)

Nicolas Labrot replied on Fri, 2011/01/07 - 7:05am

The pattern below is only correct, if the bean is request scoped :

 public String getMyValue() {
if (myValue == null || myValue.isEmpty()) {
myValue = getMyValueFromDB();
}
return myValue;
}

Because for a scope > request, the method will always return for all requests the cached value.

 

 

Cosmin Mutu replied on Fri, 2011/01/07 - 7:36am in response to: Nicolas Labrot

Then what is the purpose of caching, if for each request you wish to perform the computation?

Please provide a scenario of what you wish to accomplish.

 

"Because for a scope > request, the method will always return for all requests the cached value." - not quite, it will always return the cached value as long as the scope is not fulfilled (depend on how you implement CustomScope).

For example you have the following views A.html, B.html and C.html.

When you go to A.html your MyBean is loaded and also myValue is computed.

You go from A.html to B.html, do some stuff (doesn`t matter), and then you go from B.html to C.html.

In C.html you have a button which destroy the custom scope defined on MyBean :

 <h:commandButton id="destroy" value="Destroy Custom Scope" action="#{myBean.destroyScope}"/>

There, a scope > request is indeed returning the "cached" value for all requests untill the custom scope is fulfilled.

 

I hope this is what you are looking for, otherwise, I give up :)

Nicolas Labrot replied on Fri, 2011/01/07 - 8:28am

There, a scope > request is indeed returning the "cached" value for all requests untill the custom scope is fulfilled.

I want the getter return a fresh value for each request even if the getter is owned by a bean with a scope > request. The fresh value has to be calculated once per request. :D

 

Cosmin Mutu replied on Fri, 2011/01/07 - 9:35am in response to: Nicolas Labrot

I think this falls into category : Bad Design. :)

Make MyBean marked as RequestScoped (this bean will compute your fresh values)

Make YourBean marked with SCOPE > RequestScope and acces MyBean programatically to get your "FRESH" values.

In your view, map both beans :

<h:inputHidden id="myValue" value="#{myBean.value}" />

<h:inputWhatever id="otherValues" value="#{yourBean.otherValues}" />

When this view is loaded, both beans are instantiated, one is per request, the other is longer. 

 

 

Nicolas Labrot replied on Fri, 2011/01/07 - 10:06am

I agree with your proposal. But I dislike creating a bean just to handle this case.:)

Thanks Cosmin for your help and your patience :)

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.