During my career I've seen too many database-driven maven2 projects that use maven 2 filtering of resources for jdbc properties in order to externalize the username, password, jdbc url and other JDBC specific configuration to $USER/.m2/settings.xml. The problem with this approach is that we become locked-in to maven2 and won't be able to run a simple JUnit test without first running "mvn clean install", so that the placeholders in jdbc properties file get replaced with the appropriate values from settings.xml.
In my opinion, jdbc properties file should be avoided at all if possible. In a web application for example, it's possible to create a datasource at the container (e.g. Tomcat) and then reference this datasource from the application via JNDI. By doing this, we can happily use JDBC datasources without having to run "mvn clean install", making the life for those folks building inside Eclipse much easier.
Now, we don't always want to run our code inside a container. One such example is when we are running unit tests. For those cases we should create a base JUnit class that is specific to our module, say "MyProjectModuleTestCase" and inject JNDI datasource as explained in Randy Carver's blog post Injecting JNDI datasources for JUnit Tests outside of a container.
Monday, December 1, 2008
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:
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):
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.
class SampleRepositoryXmlImpl implements SampleRepository {
public SetgetSamples(int n) {
Setsamples = this.getSamplesFromDisk();
return getUpToNRandomSamplesFromSet(n, samples);
}
@Cacheable(groups="someCacheableGroup")
public SetgetSamplesFromDisk() {
...
}
}
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:
And then we add the following to the application-context.xml for Spring:
And that's all there is to it. Happy caching!
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?
Labels:
hibernate
Grid computing / distributed computing frameworks
- Globus Toolkit:
- Terracota: Transparent Clustered JVM
- GigaSpaces
- Coherence
Terracota
http://www.terracotta.org/
Doubts:
Doubts:
- Question: What if you don't want to replicate state through all nodes in the grid? HTTP Session replication can be VERY expensive.
Answer: http://www.terracotta.org/confluence/display/orgsite/HTTP+Session+Clustering - Question: How about resources such as JDBC connections? Will each node be able to establish their own connections? Furthermore, can they have their own configurations?
Answer: http://www.terracotta.org/confluence/display/orgsite/Hibernate
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."
"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."
Labels:
hibernate shards
Subscribe to:
Posts (Atom)
