Wednesday, June 11, 2008

Spring cache module catcha!

Caching using JDK 5.0 annotation with Spring seems to be easy. It has a catcha though, when you use the @Cacheable annotation in the second-level call of the object you want to cache. Let me explain it with an example. Consider this class:


class SampleRepositoryXmlImpl implements SampleRepository {
public Set getSamples(int n) {
Set samples = this.getSamplesFromDisk();
return getUpToNRandomSamplesFromSet(n, samples);
}

@Cacheable(groups="someCacheableGroup")
public Set getSamplesFromDisk() {
...
}
}


See, the innocent call to getSamplesFromDisk() from getSamples() should get invoked only when the return value expires in the cache. But let's see how Spring's JdkDynamicAopProxy works (excerpt from Spring's source code JdkDynamicAopProxy:189):


// Get the interception chain for this method.
List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

// Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
if (chain.isEmpty()) {
// We can skip creating a MethodInvocation: just invoke the target directly
// Note that the final invoker must be an InvokerInterceptor so we know it does
// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
} else {
// We need to create a method invocation...
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();
}


The "if (chain.isEmpty)" condition block evaluates to true when getSamples() is invoked but then in the inner call to getSamplesFromDisk(), the same condition block evaluates to false since it determines that there is no more chain, thus invokes the method directly, ignoring the interceptor created by @Cacheable annotation!

For now, to avoid the problem, you should only tag methods as @Cacheable only in the most external level call of the object. If necessary, refactor your code and extract the method to a Strategy object.

Tuesday, June 3, 2008

OSCache + Spring Framework

Since I didn't find a good reference to using OSCache with Spring after browsing the web for a day or so I thought that it would be useful to have this posted.

This example uses source level metadata (JDK 5.0 annotation) to tag methods as cacheable and configure the refreshPeriod for a particular OSCache's cache group (it's the same concept as cache region when using OSCache with Hibernate).

We first tag the methods with annotations:
@Cacheable(modelId = "testCaching")
public List<Listing> getListings() {
// some implementation
}

@Cacheable(modelId = "testFlushing")
public void saveListings(List<Listing> listing) {
// some implementation
}

And then we add the following to the application-context.xml for Spring:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
<!-- Domain -->
<bean id="listingRepositoryXmlImpl"
class="com.myco.service.ListingRepositoryXmlImpl"/>

<!-- Cache Setup -->
<bean id="autoproxy"
class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />

<!--
The created cache manager is a singleton instance of
com.opensymphony.oscache.general.GeneralCacheAdministrator
-->
<bean id="cacheManager"
class="org.springmodules.cache.provider.oscache.OsCacheManagerFactoryBean">
<!-- Optional properties -->
<property name="configLocation"
value="classpath:oscache.properties" />
</bean>

<bean id="cacheProviderFacade"
class="org.springmodules.cache.provider.oscache.OsCacheFacade">
<property name="cacheManager" ref="cacheManager" />
</bean>

<!-- Cache-feature Weaving -->
<bean id="cachingAttributeSource"
class="org.springmodules.cache.annotations.AnnotationCachingAttributeSource">
</bean>

<bean id="cachingInterceptor"
class="org.springmodules.cache.interceptor.caching.MetadataCachingInterceptor">
<property name="cacheProviderFacade" ref="cacheProviderFacade" />
<property name="cachingAttributeSource"
ref="cachingAttributeSource" />
<property name="cachingListeners">
<list>
<!-- We don't have any listener for now
<ref bean="cachingListener" />
-->
</list>
</property>
<property name="cachingModels">
<props>
<!--
Bean properties are set here for the underlying CachingModel implementation (OsCacheCachingModel).
Here we are setting the "group" and "refreshPeriod".
Note that the PropertyEditor will translate the semicolon separated key-values into a java.util.Map
-->

<prop key="testCaching"><!-- Here we map the modelId "testCaching" (Spring) to group "testCache" (OSCache), we also set the refresh period -->
groups=testCache;refreshPeriod=5
</prop>
</props>
</property>
</bean>

<bean id="cachingAttributeSourceAdvisor"
class="org.springmodules.cache.interceptor.caching.CachingAttributeSourceAdvisor">
<constructor-arg ref="cachingInterceptor" />
</bean>

<bean id="flushingAttributeSource"
class="org.springmodules.cache.annotations.AnnotationFlushingAttributeSource">
</bean>

<bean id="flushingInterceptor"
class="org.springmodules.cache.interceptor.flush.MetadataFlushingInterceptor">
<property name="cacheProviderFacade" ref="cacheProviderFacade" />
<property name="flushingAttributeSource"
ref="flushingAttributeSource" />
<property name="flushingModels">
<props>
<!-- Refresh period is set here for the underlying CachingModel implementation (OsCacheCachingModel) -->

<prop key="testFlushing"><!-- Here we map the modelId "testFlushing" (Spring) to group "testCache" (OSCache) -->
groups=testCache
</prop>
</props>
</property>
</bean>

<bean id="flushingAttributeSourceAdvisor"
class="org.springmodules.cache.interceptor.flush.FlushingAttributeSourceAdvisor">
<constructor-arg ref="flushingInterceptor" />
</bean>

</beans>

And that's all there is to it. Happy caching!

Sunday, October 21, 2007

Hibernate

According to Hibernate Sessions and transactions, implementing unit of works using Session-per-conversation is preferred over session-per-request-with-detached-objects. In Chapter 11 of hibernate manual, it's stated that CurrentSessionContext should be implemented but I haven't found the example on the wiki. Help?

Grid computing / distributed computing frameworks

  • Globus Toolkit:
  • Terracota: Transparent Clustered JVM
  • GigaSpaces
  • Coherence

Terracota

http://www.terracotta.org/

Doubts:

Monday, September 17, 2007

Hibernate Shards

From: http://www.hibernate.org/hib_docs/shards/reference/en/html/shards-shardstrategy.html

"It's important to note that if a multi-level object graph is being saved via Hibernate's cascading functionality, the ShardSelectionStrategy will only be consulted when saving the top-level object. All child objects will automatically be saved to the same shard as the parent. You may find your ShardSelectionStrategy easier to implement if you prevent developers from creating new objects at more than one level in your object hierarchy. You can accomplish this by making your ShardSelectionStrategy implementation aware of the top-level objects in your model and having it throw an exception if it encounters an object that is not in this set. If you do not wish to impose this restriction that's fine, just remember that if you're doing attribute-based shard selection, the attributes you use to make your decision need to be available on every object that gets passed to session.save()."

If you were to save child objects in a different shard, will you be able to enforce foreign key constraint? More generally, is it possible to enforce cross-shard integrity reference? Answer below (http://www.hibernate.org/hib_docs/shards/reference/en/html/shards-resharding.html):

"Unfortunately, the problem of changing object-shard mappings becomes even worse once we consider the fact that Hibernate Shards does not support cross-shard relationships (meaning FKs). This limitation prevents us from moving a subset of an object graph from one shard to another."

Thursday, December 21, 2006

Great openings!

And welcome! In this tiny space I write reviews about frameworks I've tried and tested, mainly about Java Presentation but some full stack frameworks are also tested. Happy reading!