Thursday, October 10, 2013

Simple caching with a TTL

Spring's cache abstraction framework is hugely useful for declarative caching as outlined in previous posts. As we start to use more caches, an inherent requirement that arises is periodic and on-demand purging. For the first kind of purge, you require a Time To Live (TTL) to be associated with your cache. External solutions like ehcache provide configuration to be able to do this. There are host of other parameters, like writing to disk, disk location, buffer sizes, max limits that can be configured using the configuration. However, what if your requirement is simpler and don't want to marry into ehcache just yet.
Spring's ConcurrentMapCacheFactoryBean has been made nicely pluggable where you can plug in any backing store that you want to use for the concurrent map based caching. So, here we can plug in our own TTLAwareConcurrentMap. But, I don't want to write TTL logic myself, right? Sure, just use the constructs available from guava. Guava gives a TTL backed map with a CacheBuilder which looks as simple as:
return CacheBuilder.newBuilder().expireAfterWrite(duration, unit).build().asMap();
All we need to do now is create FactoryBean in Spring that will be injected with the duration (and unit of the duration) of the TTL and it will vend out the store that will be used by Spring's caching framework. Sample FactoryBean isTTLAwareConcurrentMapFactoryBean.
For on-demand spring cache flushes, we can have a JMX operation defined on a custom CacheManager that is injected with the Spring caches at startup. On invocation, the specific cache or all caches may be flushed by invoking the Cache.clear() method. Due credit to this SO question.
Hope this helps!
References:

Wednesday, September 25, 2013

Generating JAX-RS client stubs

In the CXF JAX-WS world, when giving out API's to the clients to use, the steps to be followed were:
  • Annotate the service and service methods with the @WebService and@WebMethod annotation in the business module
  • Generate a WSDL corresponding to the given service methods using the java2wsdlutility as part of the maven build of the business module
  • Define a separate ws-api module that uses the wsdl2java utility to create the client stubs using the WSDL file generated by the business module as an input. The target of this module is what is given to the outside world
How do we do this in a CXF JAX-RS world where though we have a wadl2java utility, we don't have a java2wadl utility - and where concept of WADL by itself is largely contentious?
Suppose, we have an interface that we wish to expose in a JAX-RS way - say SpecialService.java. Now this service interface can expose methods which produce complex java objects which in turn may have other non-primitive fields. The rs-api jar that we wish to give to the clients of this interface should contain only the relevant objects required and should be spared from the implementation classes that are used by the business module itself (like done by the ws-api jar in the JAX-WS world). ProGuard is one utility that helps us out here. ProGuard is more known for being a class file obfuscator for distributing android packages (now being superseded by DexGuard) so that clients are not able to reverse-engineer the original logic. However, it is also a pretty nifty optimizer - in the sense that it can do static code analysis to see which class files are needed corresponding to a given starting point. And that is really what we wanted - we want to see which classes are needed given SpecialService class as the starting point.
The configuration is as simple as:
    <configuration>
        <obfuscate>false</obfuscate>
        <injar>../../proguard-tester-business/target/proguard-tester-business-0.0.1-SNAPSHOT.jar</injar>
        <inFilter>!**.xml</inFilter>
        <outjar>${project.build.finalName}.jar</outjar>
        <outputDirectory>${project.build.directory}</outputDirectory>
        <options>
            <option>-dontnote</option>
            <option>-keepattributes</option>
            <option>-keep @javax.ws.rs.Path public interface com.kilo.SpecialService { *;}</option>
            <option>-keepclassmembers class * { *;}</option>
            <!-- Additional classes as needed -->
            <option>-keep public class com.kilo.MixinSetter { *;}</option>
            <option>-keep public class com.kilo.ApplicationParamConverterProvider { *;}</option>
        </options>
    </configuration>
This configuration will create a stub for all the methods in the interface that have been annotated with the @Path annotation and will pull in its dependent classes. Hence, if you have certain methods as part of your interface which are not annotated will not figure in the interface that is being provided to your clients - which is great. But, one should also consider why both methods are part of the same interface if they are serving such varied needs - anyway, that is upto the designers of the interface. Any additional classes needed can also be mentioned and the rs-api jar is thus self-sufficient and directly usable by the client. And no dependency on WADL whatsoever. Sample setup available here.
The ProGuard usage manual has an exhaustive explanation of the different options and I found community support for this good as well.
Next step would be to see how we can get a source jar generated for these client stubs so that debugging would be useful, but that is for another post. Hope this helps!

References:

Thursday, July 4, 2013

Quick embedded db for integration tests

Our implementation of bitemporal library had a sample client. The integration test cases in the client project run nightly to affirm the veracity of the actions. To see the client in action, it needed an underlying table on a dataserver. To get the ball rolling, we initially housed it in the sandbox DB of a dev dataserver. However, it regularly went missing giving us monday morning pains. Next, we decided to move it to one of the databases that we owned on the dev dataserver. However, the monday morning pain changed to a monthly pain coinciding with the data server refresh cycles when the tables on the database would go missing again. We could have considered moving these tables to the production data server, but it would be undesired clutter to a newcomer. As an alternate route, we just pointed it to a local dataserver instance running on one of our colleagues machines. And yes, you guessed it right – when the machine was down for reboots or other maintenance, again failures though we needed to be especially unlucky for that to happen! Here an embedded database seemed to fit the bill nicely

Monday, June 24, 2013

Mixin it up Jackson style

We have already discovered the goodness of Jackson for vending out JSON data in a JAX-RS setup. In many cases, our domain objects have references to other domain objects that are not under our control (e.g. classes from third-party JARs). We can easily add annotations to our domain objects to indicate which fields to ignore, but what do we do for the third party objects? One could choose to write custom bean serializers, but that is pretty onerous on the part of the user and one that requires higher maintenance since it is away from the original class. Here is where Jackson Mixins comes to the rescue.

Wednesday, June 12, 2013

Know your disk latency numbers

I was looking around for a tool that will help quantify the latency difference between a NFS-mounted location to a local location and came across ioping. As the documentation indicates, it tries to represent disk latency in pretty much the same way as the ping command shows network latency to a desired host.
I had to build it from source for our linux installation - but the process went about without a hitch. I first tried it on my NFS mounted home drive where the checked-out code, eclipse workspace and maven repository reside and is the hotspot for IO activity when developing. Using a sample invocation from the documentation site, I tried it for a chunk of size 1MB, ten times.
$>ioping -c 10 -s 1M
--- /home/kilo(nfs fs1.kilo.com:/vol/home/kilo) ioping statistics ---
10 requests completed in 9101.5 ms, 102 iops, 102.1 mb/s
min/avg/max/mdev = 9.5/9.8/10.0/0.2 ms
Next was to test out the performance of our local partition with the same parameters:
--- /local/kilo (ext4 /dev/ssda) ioping statistics ---
10 requests completed in 9052.4 ms, 201 iops, 200.8 mb/s
min/avg/max/mdev = 4.8/5.0/5.6/0.2 ms
Result was roughly a speed-up of 200%!
For kicks, I tried it out on a /tmp location as well with the same parameters:
--- /tmp (tmpfs none) ioping statistics ---
10 requests completed in 9004.1 ms, 5219 iops, 5219.2 mb/s
min/avg/max/mdev = 0.1/0.2/0.3/0.0 ms
That was roughly 30 times faster - but hold on - what is that tmpfs that is mentioned. Digging further, it seems tmpfs is a special filesystem that doesn't reside on a physical disk but rather stored in the physical memory (albeit volatile) - so the speed-up in access is kind of expected. Which also means that we should be extra nice about what goes in /tmp in such a setup. Keeping a lot of garbage in there and not cleaning up will come and bite us at unpredictable times. Guess many people already know about this
Coming back to the utility itself, there are options to change working set sizes, offsets into files that wish to be seeked, etc. One particularly interesting feature is the option to use write IO, but that option was not available in the 0.6 that I downloaded. An issue indicates that this is a yet to be released feature and will be interesting to see it in action.
I think this will a good utility to have in our linux installations by default. If there are other utilities that do similar stuff but comes out of the box from a stock RHEL installation, please let me know. Hope this helps!
References:

Monday, May 27, 2013

Lets go uber!

ow do you run your non-webapp maven-based java program from command line? One might be using exec:java route and specify the main class. The only sticking point here is that the classpath will be filled with references to the user’s local repository (usually NFS mounted). I would be much more comfortable if I had all the set of jars that I would be dependent on packaged WAR-style and be ready at my disposal. In that sense, the application would be much more self-contained. It also becomes that much easy for someone to testdrive your application. Hence the concept of an “uber” jar – a unified jar that will house all classes/resources of the project along with classes/resources of its transitive dependencies.

Thursday, March 14, 2013

Blocked persistence nuance with GET*DATE()


Noticed an interesting (and desired) behavior when dealing with persisting timestamps that is blocked by a competing transaction, which might be useful for others as well. For unitemporal and bitemporal tables, we frequently update the knowledge_date as either GETDATE() or GETUTCDATE(), but it is not guaranteeing that the wall-clock time of when record got persisted to the db is the same as the value noted in the knowledge_date column.
To illustrate this, let's say we have a table as
CREATE TABLE special_table (
special_key INT PRIMARY KEY,
special_value INT,
knowledge_date DATETIME)
An we insert a few values into it:
INSERT INTO special_table VALUES(1, 100, GETUTCDATE())
INSERT INTO special_table VALUES(2, 100, GETUTCDATE())
INSERT INTO special_table VALUES(100, 10000, GETUTCDATE())
Now, let's say, we've got two competing transactions, one a read and another a write with the read preceding the write and the read transaction taking a lot more time to finish (simulated with a WAITFOR DELAY).
Read Transaction (at isolation level repeatable read):
BEGIN TRANSACTION
SELECT GETUTCDATE() --ts1
SELECT * FROM special_table WHERE special_key = 2
WAITFOR DELAY '00:00:10'
SELECT * FROM special_table WHERE special_key = 2
SELECT GETUTCDATE() --ts2
COMMIT
Write Transaction (at isolation level read committed):
BEGIN TRANSACTION
SELECT GETUTCDATE() --ts3
UPDATE special_table
   SET special_value = special_value + 1, knowledge_date=GETUTCDATE()
 WHERE special_key = 2
SELECT GETUTCDATE() --ts4
SELECT * FROM special_table WHERE special_key = 2
COMMIT
Execute these two batches in two windows of SSMS with the read preceding the write. Since the read started before the write, ts1+10 ~= ts2 because the read transaction will experience no blocking. The write operation was kicked off a little after read was kicked off (say with interval d). Hence ts1 + d = ts3.
Question: will the knowledge_date updated be closer to ts3 or ts4?
One might think that the knowledge_date value is closer to ts4 when the write transaction actually gets unblocked, however, this is not the case. For Sql Server itself to figure out whether or not the transaction needs to get blocked (because of the default page level locking scheme followed), the query needs to be evaluated and hence the value to be assigned to knowledge_date has to be evaluated at a time closer to ts3 itself. Hence the knowledge_date timestamp will be persisted to the DB at a wall-clock time closer to ts4even if the DB claims the timestamp as closer to ts3.
This can be verified with the output from the read and writes where there is a marked delay between the knowledge_date updated and the ts4. This becomes even more interesting when you have multiple updates in the same write transaction - some of which can proceed - till the point that it gets blocked because of the read and one can notice varying knowledge_date across records even though they were all kicked off in the same transaction.
BEGIN TRANSACTION
SELECT GETUTCDATE() --ts1
UPDATE special_table
   SET special_value = special_value + 1, knowledge_date=GETUTCDATE() --ts
 WHERE special_key = 100
SELECT GETUTCDATE() --ts2
UPDATE special_table
   SET special_value = special_value + 1, knowledge_date=GETUTCDATE() --tss
 WHERE special_key = 2
SELECT GETUTCDATE() --ts3
SELECT * FROM special_table WHERE special_key = 2
COMMIT
Here, knowledge_date for key 100 would be closer to ts1 due to it not getting blocked by the read and the knowledge_date for key 2 would be closer to ts3 and away from ts2 since it was blocked by the read.
BTW, this should not haunt the trigger based td_bl temporal tables as the trigger gets only fired after the base table is updated and effectively captures the timestamp of when the base table was changed (but may not be the exact time when the temporal record got persisted to the db due to blocking concerns).
Hope this helps!
References:

Saturday, March 9, 2013

Quick headless JAX-RS servers with CXF


If one needs to vend out JSON data in a JAX-RS compatible way with minimal setup fuss, CXF + Spring provides good out-of-the-box solution for you.
The steps would be:
  1. Write your service class (interface and impl preferably)
  2. Annotate your service impl methods with
    1. @Path annotation indicating the URI on which it will serve the resource
    2. @Get/@Post indicating the HTTP method which it serves
    3. @Produces("application/json") indicating that the output format is JSON
  3. Define a jaxrs:server directive in your spring context file indicating the address and resource path on which the service is hosted
  4. Add maven dependencies of javax.ws.rs-api (for annotations), cxf-rt-core (for stubbing RS communication over a http conduit) and cxf-rt-transports-http-jetty (for embedded jetty)
and voila you are done.
Concretely:
public interface SpecialService {
    String getSomeText();
}
public class SpecialServiceImpl implements SpecialService {

    @GET
    @Produces("application/json")
    @Path("/someText/")
    @Override
    public String getSomeText() {
        return "kilo";
    }
}
    <bean id="specialService" class="com.kilo.SpecialServiceImpl"/>

    <bean id="inetAddress" class="java.net.InetAddress" factory-method="getLocalHost" />

    <jaxrs:server id="specialServiceRS"
        address="http://#{inetAddress.hostName}:${com.kilo.restful.port}/specialServiceRS">
        <jaxrs:serviceBeans>
            <ref bean="specialService" />
        </jaxrs:serviceBeans>
    </jaxrs:server>
And now hit http://yourhostname:yourportnum/specialServiceRS/someText to get the response as "kilo". If you examine the request via some developer tools, you will see that the content type is application/json.
CXF JAX-RS uses an embedded jetty as the http container, so we don't really need a tomcat for setting this up. This might bring up the question of Tomcat vs Jetty overall and here are my thoughts:
Tomcat
  • Lightweight
  • Servlet 3 style async threading in the works
  • Known beast in terms of configuration
Jetty
  • Even more lightweight
  • Implements servlet 3 style async thread allocation (like node) and hence more responsive and efficient
  • Easy to have an embedded server with cxf (embeddability is synonymous with jetty)
  • Ability to have multiple simple java processes that act as headless servers quickly
Overall, I believe we should give Jetty a chance and see how it performs. If it ever lets us down, it is easy to take the process and house it in a Tomcat container.

We will try to cover some more involved use cases of passing in inputs via JAX-RS, dealing with complex objects, CORS and GZIP in subsequent posts (the samples already have them explained).

References:

Friday, March 8, 2013

Staging paradigms universe in MS SQL Server


How do you pass bulk data from the application to a staging table in the database layer in a typical 3-tier java application?
Options:
  1. Use multiple INSERT clauses (with iBATIS/spring-jdbc/jdbc)
  2. Use INSERT clause with multiple values clauses [SQL Server 2008 onwards] (withiBATIS/spring-jdbc/jdbc)
  3. Use batching of INSERT clause with varying batch sizes (with iBATIS/spring-jdbc/jdbc)
  4. Create a bcp file and use the bcp executable
  5. Create a bcp file and use the BULK INSERT T-SQL command (with iBATIS/spring-jdbc/jdbc)
  6. Create a bcp file and use the OPENROWSET BULK T-SQL command (withiBATIS/spring-jdbc/jdbc)
  7. XML shredding (with iBATIS/spring-jdbc/jdbc)
We will analyze each of these options and try to establish benchmarks in trying to stage data of two data sets sized 1k and 100k records having columns belonging to a motley of data types and try to establish best practices as to where what should be used.
The staging candidate chosen looks something like this:
CREATE TABLE motley
  (
     date           DATETIME,
     name           VARCHAR(50),
     id             INT,
     price          NUMERIC(18, 4),
     amount         NUMERIC(18, 4),
     fx_rate        NUMERIC(18, 7),
     is_valid       TINYINT,
     knowledge_time DATETIME
  )
The stats were as follows:



As you may imagine, there are pros and cons with each approach which I have tried to outline here.

Feel free to suggest ways to further fine-tune each of these approaches or suggest brand new ones or plainly comment on the comparison metrics or factors.
Hope this helps!
References:

Monday, February 4, 2013

Experimenting with MyBatis


iBATIS has been the workhorse for our data heavy applications for quite some time, and we were largely happy with it – till MyBatis took it to the next level. While iBATIS has been decommissioned circa mid 2010 (and now lives in the apache attic), the real trigger to move to see what MyBatis offered, was Spring’s decision to deprecate support for iBATIS in Spring 3.2.0.RELEASE and all of my DAO classes would show those pesky warnings in eclipse. Some time ago, we had a training on the nuances of iBATIS  in which we outlined a few examples. I thought that converting this same setup to MyBatis would be a good exercise to  see the features in action and how these two are similar and where they differ.
To start off, I fired up the migration tool viz ibatis2mybatis on my existing XML files in the previous setup. The tool is pretty decent and gets us almost 90% there. The major changes that still needed to be done were:
  1. Add namespacing to be consistent with mapper package name
  2. Changing of primary key attributes of a result map from <result> tag to <id> tag (needed for group by operations and performance optimizations)
  3. Strict ordering of elements in the resultMap tag so that <id/> is followed by <result/> followed by <association/> followed by <collection/> (not sure why the DTD has been defined thusly)
  4. Replace intermediate resultMap created for collections by providing the type information to the <collection/> tag itself. For an example see userWithLoginLocationsAndUniqueSites resultMap.
  5. Cache settings are not migrated and need to be defined by hand again. But that is extremely simple now — <cache/> and you have a cache local to the mapper!
After this, the only other changes were to the config file to disable the default lazyLoading. We prefer the objects to be POJO’s and also helps in reflective comparison of objects for hashCode and equals. Since we use mybatis-spring integration (provided by mybatis), we don’t need to deal with the SqlSesssionFactory ourselves (and hence worry about clean closing).
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="testerDataSource" />
        <property name="configLocation" value="classpath:com/kilo/dao/sqlmap-config.xml" />
        <property name="mapperLocations" value="classpath:com/kilo/dao/mapper/*.xml" />
    </bean>

    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.kilo.dao.mapper" />
    </bean>
The mappers just need to be interfaces with the names of the methods matching the sql fragment that needs to be fired. Hence, no need for any DAOImpl’s whatsover! However, it has its downside as you may have already imagined – now you can’t craft fancy params to pass down to your mybatis sql fragment as parameters.
References:

Tuesday, January 29, 2013

CORS filters choices


Have you ever had to workaround Javascript’s Same origin policy and felt the need to take a shower with an industrial grade disinfectant immediately after? Well fear no more, as HTML5 to the rescue – CORS - Cross Origin Resource Sharing. Almost every conceivable hack that web developers used to do is being standardized as a feature spec in HTML5 and I thought CORS was a good feature. While there is good merit in the same origin policy, with the ubiquity of data vending servers that are being re-used in other data vending servers, there need to be a straight-forward solution to transitive data sharing. The workarounds of setting document.domain property, using JSONP or using crossdomain.xml (flex only) were not seamless. By making this a HTML5 spec and conveying it by means of headers, there is now, a definite method in the madness. This tutorial does a very good job of explaining nuances related to preflight, credentials (cookies) and caching policies along with all its usages via JQuery. The obvious concern of security has been aptly addressed in the following snip from the tutorial.

A WORD ABOUT SECURITY

While CORS lays the groundwork for making cross-domain requests, the CORS headers are not a substitute for sound security practices. You shouldn’t rely on the CORS header for securing resources on your site. Use the CORS headers to give the browser directions on cross-domain access, but use some other security mechanism, such as cookies or OAuth2, if you need additional security restrictions on your content.
What was missing was a good server side filter that will enable this for a typical java webapp. While the spec is pretty straight forward for the vanilla use cases, it quickly gets involved once we get into the details. There are two alternatives out there that I could find:
  1. com.thetransactioncompany.cors.CORSFilter
    1. Pros:
      1. Hugely popular
      2. Additional support for allowing subdomains in allow list
      3. Frequently updated for latest specs and bug fixes
      4. Liberal license (Apache)
    2. Cons:
      1. Not from a stable house
  2. org.eclipse.jetty.servlets.CrossOriginFilter
    1. Pros:
      1. From Jetty
      2. Liberal license (Eclipse)
    2. Cons:
      1. Not very popular
      2. May seem somewhat unintuitive if we use jetty related artifacts in a tomcat container environment
      3. Not aggressively updated
So, overall, it seems that the CORS filter from dzhuvinov is the winner, but would be happy to know if others feel differently.
References:

Monday, January 21, 2013

Some more fun with @Cacheable


Let's say we configure a cache for methods for a third-party service class that is not under your direct control via AOP (as discussed in the previous post).
    <cache:advice id="externalFibonacciCacheAdvice" cache-manager="externalConcurrentMapFibonacciCacheManager">
        <cache:caching>
            <cache:cacheable method="*" cache="externalConcurrentMapFibonacci" />
        </cache:caching>
    </cache:advice>
Now say that this class has another method similar to getFibonacci(int):long as getNonFibonacci(int):long which returns a non-fibonacci constant number of -1.
    /**
     * Constantly returns -1
     * 
     * @param index
     * @return
     */
    public long getNonFibonacci(int index) {
        return -1;
    }
Now, I will modify the driver as:
        long fibonacci = ((ExternalFibonacciServiceImpl) externalFibonacciService)
                .getFibonacci(45);
        LOG.info("Fibonacci number is " + fibonacci);
        long nonFibonacci = ((ExternalFibonacciServiceImpl) externalFibonacciService)
                .getNonFibonacci(45);
        LOG.info("Non-fibonacci number is " + nonFibonacci);
What is the output?
[ 2013-01-21 21:21:48,496 [main] driver.SpringCacheTesterDriver.main():36  INFO ]: Fibonacci number is 1836311903
[ 2013-01-21 21:21:48,501 [main] driver.SpringCacheTesterDriver.main():39  INFO ]: Non-fibonacci number is 1836311903
What the hell?! Further investigation led me to SPR-8933 where it seems that this was by design. The default key generator only takes the method arguments into account (which seems to make sense when you define your services close to your domain objects - but goes awfully awry when you have a facade service like ReferenceDataService which will vend out data like countries as well as currencies). 
Thankfully there is a workaround (and a nice way to introduce a feature) - enter key-generator. The default key generator implementation used is org.springframework.cache.interceptor.DefaultKeyGenerator. We can define a custom MethodAwareCacheKeyGenerator that extends the DefaultKeyGenerator and takes the method name also into account. Just beanify it and use it
    <bean id="methodAwareCacheKeyGenerator" class="org.springframework.cache.interceptor.MethodAwareCacheKeyGenerator" />

    <cache:advice id="externalFibonacciCacheAdvice" cache-manager="externalConcurrentMapFibonacciCacheManager"
        key-generator="methodAwareCacheKeyGenerator">
        <cache:caching>
            <cache:cacheable method="*" cache="externalConcurrentMapFibonacci" />
        </cache:caching>
    </cache:advice>
And voila...
[ 2013-01-21 21:40:17,419 [main] driver.SpringCacheTesterDriver.main():36  INFO ]: Fibonacci number is 1836311903
[ 2013-01-21 21:40:17,419 [main] driver.SpringCacheTesterDriver.main():39  INFO ]: Non-fibonacci number is -1
It is important to note that though the problem was demonstrated with the AOP route, it can also easily happen if you define it via annotations on methods having same datatypes.
References:
  • https://jira.springsource.org/browse/SPR-8933
  • Code available at https://github.com/kilokahn/spring-testers/tree/master/spring-cache-tester

Saturday, January 19, 2013

More fun with @Cacheable


I am assuming the audience has already got themselves familiar with the previous post where vanilla cache operations were demonstrated. The scope of this post will be:
  • Using multiple cache managers in a context
  • Using aspectj to cache self calls
  • Using cache:advice to cache non-owned calls
Using multiple cache managers in a context
So continuing from the fibonacci example from before, let's say that I wish to use both the simple cache manager as well as the ehcache cache manager? As with the pitfalls in using tx:annotation-driven, only one manager can effectively parade the annotations in the desired way. In tx:annotation-driven, we specify the transaction manager to use in the annotation itself, whereas such a thing is not available in the cache:annotation-driven (yet). Hence, when two cache managers wish to operate in tandem, enter org.springframework.cache.support.CompositeCacheManager. This specifies the list of cache managers that need to be used.
    <bean id="compositeCacheManager" class="org.springframework.cache.support.CompositeCacheManager">
        <property name="cacheManagers">
            <array>
                <ref bean="ehcacheManager" />
                <ref bean="concurrentMapCacheManager" />
            </array>
        </property>
    </bean>
The directive to process the annotation will refer to this compositeCacheManager as the sole manager
    <cache:annotation-driven cache-manager="compositeCacheManager" proxy-target-class="true"
        />
The output can be verified as showing each of the two cache managers operating without treading on each other's toes.
[ 2013-01-18 15:28:36,226 [main] driver.SpringCacheTesterDriver.doHeavyLifting():52  INFO ]: com.kilo.driver.ConcurrentMapFibonacciServiceImpl$$EnhancerByCGLIB$$a3241f9a took 7.586 seconds
[ 2013-01-18 15:28:36,227 [main] driver.SpringCacheTesterDriver.doHeavyLifting():52  INFO ]: com.kilo.driver.ConcurrentMapFibonacciServiceImpl$$EnhancerByCGLIB$$a3241f9a took 0.001 seconds
[ 2013-01-18 15:28:36,227 [main] driver.SpringCacheTesterDriver.doHeavyLifting():52  INFO ]: com.kilo.driver.ConcurrentMapFibonacciServiceImpl$$EnhancerByCGLIB$$a3241f9a took 0.0 seconds
[ 2013-01-18 15:28:36,227 [main] driver.ConcurrentMapFibonacciServiceImpl.flushFibonacciCache():30  INFO ]: Royal ConcurrentMap flush
[ 2013-01-18 15:28:43,797 [main] driver.SpringCacheTesterDriver.doHeavyLifting():52  INFO ]: com.kilo.driver.ConcurrentMapFibonacciServiceImpl$$EnhancerByCGLIB$$a3241f9a took 7.569 seconds
[ 2013-01-18 15:28:43,797 [main] driver.ConcurrentMapFibonacciServiceImpl.flushFibonacciCache():30  INFO ]: Royal ConcurrentMap flush
[ 2013-01-18 15:28:51,247 [main] driver.SpringCacheTesterDriver.doHeavyLifting():52  INFO ]: com.kilo.driver.EhcacheFibonacciServiceImpl$$EnhancerByCGLIB$$73a5e038 took 7.449 seconds
[ 2013-01-18 15:28:51,247 [main] driver.SpringCacheTesterDriver.doHeavyLifting():52  INFO ]: com.kilo.driver.EhcacheFibonacciServiceImpl$$EnhancerByCGLIB$$73a5e038 took 0.0 seconds
[ 2013-01-18 15:28:51,247 [main] driver.SpringCacheTesterDriver.doHeavyLifting():52  INFO ]: com.kilo.driver.EhcacheFibonacciServiceImpl$$EnhancerByCGLIB$$73a5e038 took 0.0 seconds
[ 2013-01-18 15:28:51,253 [main] driver.EhcacheFibonacciServiceImpl.flushFibonacciCache():30  INFO ]: Royal Ehcache flush
[ 2013-01-18 15:28:58,850 [main] driver.SpringCacheTesterDriver.doHeavyLifting():52  INFO ]: com.kilo.driver.EhcacheFibonacciServiceImpl$$EnhancerByCGLIB$$73a5e038 took 7.597 seconds
[ 2013-01-18 15:28:58,853 [main] driver.EhcacheFibonacciServiceImpl.flushFibonacciCache():30  INFO ]: Royal Ehcache flush
Using aspectj to cache self calls
Now let's get into some AspectJ magic! It would be worthwhile to spend a few minutes knowing how load time weaving (LTW) lends itself to processing annotations. There is anentire section in the Spring documentation that outlines the interactions in detail. In a nutshell, contrasting with the proxy based approach, the logic before/after/around a call is embedded in the bytecode of the class as the class is being loaded into the JVM. The JVM provides hook-ins via the java.lang.instrument.ClassFileTransformers route which can be configured per classloader. Since this logic is now part of the bytecode loaded in the JVM itself, any and all calls will have access to the logic coded for before/after/around calls. We will discover more as we take the tour. In our example for the concurrent map and ehcache based cached services, we were only storing the results for the fibonacci with index 45 and not the ones preceding it - and we gave the reason as the mode being proxy which doesn't support self calls (recursive or public/private/protected). Let's say we do want to achieve the benefits of the intermediate fetches as well, we should switch over to using the aspectj mode. Here are the set of steps you need to do and why:
Add maven dependencies for spring-aspects (which pulls in aspectj related jars)
        <!-- for mode aspectj -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.6.11</version>
            <scope>runtime</scope>
        </dependency>
    <cache:annotation-driven cache-manager="compositeCacheManager" mode="aspectj" />
Informs Spring that we need to use aspectj for processing the annotations.
    <context:load-time-weaver/>
Inform spring to load time weave the bytecode for the beans that have been annotated. IMHO, specifying #2 should have automatically caused #3, but it is not - so we live with it.
-javaagent:/u/baigm/winnt/.m2/repository/org/springframework/spring-instrument/3.1.3.RELEASE/spring-instrument-3.1.3.RELEASE.jar
Specify the javaagent that will do the actual work of instrumenting the bytecode. From Java 5 onwards, java has built in support for agents that can help with instrumentation related activities which come out-of-the-box in most JEE containers. If you are not running in a JEE container (like Tomcat) or running a standalone java application, Spring provides a lightweight instrumentation jar that provides the implementation of a LoadTimeWeaver (viz. InstrumentationLoadTimeWeaver). So you need to add this to your launch configuration in your eclipse or to your invocation if outside eclipse.
And you are all set! Run the driver to see that the initial run itself now finishes in much under a second.
[ 2013-01-18 16:05:10,306 [main] driver.SpringCacheTesterDriver.doHeavyLifting():52  INFO ]: com.kilo.driver.ConcurrentMapFibonacciServiceImpl took 0.074 seconds
[ 2013-01-18 16:05:10,306 [main] driver.SpringCacheTesterDriver.doHeavyLifting():52  INFO ]: com.kilo.driver.ConcurrentMapFibonacciServiceImpl took 0.0 seconds
[ 2013-01-18 16:05:10,306 [main] driver.SpringCacheTesterDriver.doHeavyLifting():52  INFO ]: com.kilo.driver.ConcurrentMapFibonacciServiceImpl took 0.0 seconds
[ 2013-01-18 16:05:10,313 [main] driver.ConcurrentMapFibonacciServiceImpl.flushFibonacciCache_aroundBody2():30  INFO ]: Royal ConcurrentMap flush
[ 2013-01-18 16:05:10,317 [main] driver.SpringCacheTesterDriver.doHeavyLifting():52  INFO ]: com.kilo.driver.ConcurrentMapFibonacciServiceImpl took 0.004 seconds
[ 2013-01-18 16:05:10,317 [main] driver.ConcurrentMapFibonacciServiceImpl.flushFibonacciCache_aroundBody2():30  INFO ]: Royal ConcurrentMap flush
[ 2013-01-18 16:05:10,354 [main] driver.SpringCacheTesterDriver.doHeavyLifting():52  INFO ]: com.kilo.driver.EhcacheFibonacciServiceImpl took 0.037 seconds
[ 2013-01-18 16:05:10,355 [main] driver.SpringCacheTesterDriver.doHeavyLifting():52  INFO ]: com.kilo.driver.EhcacheFibonacciServiceImpl took 0.001 seconds
[ 2013-01-18 16:05:10,355 [main] driver.SpringCacheTesterDriver.doHeavyLifting():52  INFO ]: com.kilo.driver.EhcacheFibonacciServiceImpl took 0.0 seconds
[ 2013-01-18 16:05:10,387 [main] driver.EhcacheFibonacciServiceImpl.flushFibonacciCache_aroundBody2():30  INFO ]: Royal Ehcache flush
[ 2013-01-18 16:05:10,397 [main] driver.SpringCacheTesterDriver.doHeavyLifting():52  INFO ]: com.kilo.driver.EhcacheFibonacciServiceImpl took 0.008 seconds
[ 2013-01-18 16:05:10,400 [main] driver.EhcacheFibonacciServiceImpl.flushFibonacciCache_aroundBody2():30  INFO ]: Royal Ehcache flush
The call after the flush however returns faster than the first call - which may be due the the instrumentation stuff bootstrapping and subsequent calls reusing cached instrumented code.
Using cache:advice to cache non-owned calls
The annotation route is good when we are owning the services that need caching. If you don't own the services that vend out the data, you are forced to wrap it in a facade and place your annotation on this facade. The other route may be to use the cache:advice directive. This is similar to the tx:advice which we use to wrap calls around certain pointcuts based on fully qualified classnames, methods, arguments and argument types. So first off, we define a separate concurrent cache to be used for this external service.
    <bean id="externalConcurrentMapFibonacci"
        class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" />
    <bean id="externalConcurrentMapFibonacciCacheManager"
        class="org.springframework.cache.support.SimpleCacheManager">
        <property name="caches">
            <set>
                <ref bean="externalConcurrentMapFibonacci" />
            </set>
        </property>
    </bean>
Then we define a cache advice (pointcut) with the cache manager to be used and defining which methods need to be cached and which backing cache store is to be used.
    <cache:advice id="externalFibonacciCacheAdvice" cache-manager="externalConcurrentMapFibonacciCacheManager">
        <cache:caching>
            <cache:cacheable method="*" cache="externalConcurrentMapFibonacci" />
        </cache:caching>
    </cache:advice>
Here we are asking Spring to cache all methods (via *) using the externalConcurrentMapFibonacciCacheManager cache manager and to use the externalConcurrentMapFibonacci cache as the underlying store.
    <aop:config proxy-target-class="true">
        <aop:pointcut id="externalFibonacciCacheOperation" expression="execution(* com.kilo.external..*.*(..))"/>
        <aop:advisor advice-ref="externalFibonacciCacheAdvice" pointcut-ref="externalFibonacciCacheOperation"/>
    </aop:config>
Now, we ask aop to to bind the pointcut to work on all beans of package com.kilo.external and use the advice defined before. We have to give the proxy-target-class="true" to use CGLIB based proxies (we can choose to use aspectj mode for reasons mentioned before).
You may verify the working via the output:
[ 2013-01-18 17:20:50,374 [main] driver.SpringCacheTesterDriver.doHeavyLifting():55  INFO ]: com.kilo.driver.SimpletonFibonacciServiceImpl took 7.248 seconds
[ 2013-01-18 17:20:57,615 [main] driver.SpringCacheTesterDriver.doHeavyLifting():55  INFO ]: com.kilo.driver.SimpletonFibonacciServiceImpl took 7.241 seconds
[ 2013-01-18 17:21:04,863 [main] driver.SpringCacheTesterDriver.doHeavyLifting():55  INFO ]: com.kilo.driver.SimpletonFibonacciServiceImpl took 7.247 seconds
[ 2013-01-18 17:21:04,863 [main] driver.SimpletonFibonacciServiceImpl.flushFibonacciCache():27  INFO ]: No-op
[ 2013-01-18 17:21:12,119 [main] driver.SpringCacheTesterDriver.doHeavyLifting():55  INFO ]: com.kilo.driver.SimpletonFibonacciServiceImpl took 7.255 seconds
[ 2013-01-18 17:21:12,119 [main] driver.SimpletonFibonacciServiceImpl.flushFibonacciCache():27  INFO ]: No-op
[ 2013-01-18 17:21:12,161 [main] driver.SpringCacheTesterDriver.doHeavyLifting():55  INFO ]: com.kilo.driver.ConcurrentMapFibonacciServiceImpl took 0.042 seconds
[ 2013-01-18 17:21:12,161 [main] driver.SpringCacheTesterDriver.doHeavyLifting():55  INFO ]: com.kilo.driver.ConcurrentMapFibonacciServiceImpl took 0.0 seconds
[ 2013-01-18 17:21:12,161 [main] driver.SpringCacheTesterDriver.doHeavyLifting():55  INFO ]: com.kilo.driver.ConcurrentMapFibonacciServiceImpl took 0.0 seconds
[ 2013-01-18 17:21:12,167 [main] driver.ConcurrentMapFibonacciServiceImpl.flushFibonacciCache_aroundBody2():30  INFO ]: Royal ConcurrentMap flush
[ 2013-01-18 17:21:12,172 [main] driver.SpringCacheTesterDriver.doHeavyLifting():55  INFO ]: com.kilo.driver.ConcurrentMapFibonacciServiceImpl took 0.004 seconds
[ 2013-01-18 17:21:12,172 [main] driver.ConcurrentMapFibonacciServiceImpl.flushFibonacciCache_aroundBody2():30  INFO ]: Royal ConcurrentMap flush
[ 2013-01-18 17:21:12,210 [main] driver.SpringCacheTesterDriver.doHeavyLifting():55  INFO ]: com.kilo.driver.EhcacheFibonacciServiceImpl took 0.038 seconds
[ 2013-01-18 17:21:12,211 [main] driver.SpringCacheTesterDriver.doHeavyLifting():55  INFO ]: com.kilo.driver.EhcacheFibonacciServiceImpl took 0.0 seconds
[ 2013-01-18 17:21:12,211 [main] driver.SpringCacheTesterDriver.doHeavyLifting():55  INFO ]: com.kilo.driver.EhcacheFibonacciServiceImpl took 0.0 seconds
[ 2013-01-18 17:21:12,242 [main] driver.EhcacheFibonacciServiceImpl.flushFibonacciCache_aroundBody2():30  INFO ]: Royal Ehcache flush
[ 2013-01-18 17:21:12,250 [main] driver.SpringCacheTesterDriver.doHeavyLifting():55  INFO ]: com.kilo.driver.EhcacheFibonacciServiceImpl took 0.008 seconds
[ 2013-01-18 17:21:12,253 [main] driver.EhcacheFibonacciServiceImpl.flushFibonacciCache_aroundBody2():30  INFO ]: Royal Ehcache flush
[ 2013-01-18 17:21:19,873 [main] driver.SpringCacheTesterDriver.doHeavyLifting():55  INFO ]: com.kilo.external.ExternalFibonacciServiceImpl$$EnhancerByCGLIB$$6337baf took 7.62 seconds
[ 2013-01-18 17:21:19,874 [main] driver.SpringCacheTesterDriver.doHeavyLifting():55  INFO ]: com.kilo.external.ExternalFibonacciServiceImpl$$EnhancerByCGLIB$$6337baf took 0.0 seconds
[ 2013-01-18 17:21:19,874 [main] driver.SpringCacheTesterDriver.doHeavyLifting():55  INFO ]: com.kilo.external.ExternalFibonacciServiceImpl$$EnhancerByCGLIB$$6337baf took 0.0 seconds
[ 2013-01-18 17:21:19,874 [main] external.ExternalFibonacciServiceImpl.flushFibonacciCache():28  INFO ]: No-op
[ 2013-01-18 17:21:19,874 [main] driver.SpringCacheTesterDriver.doHeavyLifting():55  INFO ]: com.kilo.external.ExternalFibonacciServiceImpl$$EnhancerByCGLIB$$6337baf took 0.0 seconds
Hope this helps!
There is one gotcha with the cache mapping that I will try to cover in a new post.
References:
  • http://static.springsource.org/spring/docs/3.1.0.M1/spring-framework-reference/html/aop.html#aop-aj-ltw
  • http://www.eclipse.org/aspectj/doc/released/devguide/ltw.html
  • http://stackoverflow.com/questions/8658789/using-spring-cache-annotation-in-multiple-modules
  • Test bed available at https://github.com/kilokahn/spring-testers/tree/master/spring-cache-tester