Wednesday, January 31, 2007

Quick BeanWrapper tip

If you ever find yourself in the situation where you want to replace a single collaborator property in an otherwise fully wired environment, and all your beans are proxied by AOP, here's a handy tip.

Suppose you're testing your service component Foo, which has a dependency on service component Bar, both interfaces and transactionally proxied. Foo is wired to the actual DAOs and other service collaborators and whatnot, including Bar, but for this test purpose you'd like to replace the Bar dependency with an empty stub that doesn't do anything, or returns a fixed value or whatever. The problem is that you don't have any setBar(Bar bar) property on your Foo interface (you don't, do you? It belongs on the implementation!), so you have to employ some sort of reflection trick. After having wrapped with BeanWrapper, listed PropertyDescriptors, wrapped again, listed more PropertyDescriptors and so on, I finally found a way to modify the property on the actual Foo implementation found behind the transactional proxy:

// Here's my stub
Bar stubBar = new Bar() {
public void interactWithComplexStuff() {
// Just skip this
}
}
// Wire the new Bar
new BeanWrapperImpl(this.foo).setPropertyValue("targetSource.target.bar", stubBar);

As you can see, the property to set on the proxy is "targetSource.target.<name of actual property>".

Monday, January 29, 2007

Assorted hacks

I've been coding batch services the last couple of days, and found a few useful tricks and tools for testing that I would to share with you, the reader :-).

The task is basically to extract a number of filenames from an XML file (part of a complete processing of the file), and to download those files from a web server and store locally. We're already using XmlBeans to map the XML to JavaBeans, so I can stub the elements that I'm interested in for a particular test. XmlBeans generates interfaces for every node, so I let Eclipse create default implementations of the interfaces I need, which I then override inline like this:

Foo foo = new Foo() {
@Override
public Bar getBar() {
return new Bar() {
@Override
public String getStringValue() {
return "test-bar-value";
}
}
}
...
}

to represent (stub out) an XML fragment on this form:

<foo>
<bar>test-bar-value</bar>
...
</foo>

I've decided that I want to write my test against an actual HTTP server, since mocking that part is more work and less return on investment. Now, I don't want to run the test against the production server, and I don't want to rely on the fact that a dedicated Apache has been started either. The solution is to run an HTTP server as part of the test!

There are a number of different Java implementations of HTTP servers, ranging from microscopic to full-blown. I settled for Jetty, which is small, fast, has a nice programmatic API and you can rely on it to handle request-headers correctly, which might not be the case for YetAnotherSuperSmallHttpServer.sourceforge.net.

Anyway, a minimal HTTP file server can be accomplished with the following code:

Server server;

public void setUp() {
server = new Server(55555);
ResourceHandler resourceHandler =new ResourceHandler();
resourceHandler.setResourceBase(
new ClassPathResource("webroot").getFile().getAbsolutePath());
server.addHandler(resourceHandler);
server.start();
}

public void tearDown() {
if (server != null) {
server.stop();
}
}

The resource base is set to the directory "webroot" in the class path (ClassPathResource is a Spring class), which resolves to src/test/resources/webroot when you're using the Maven standard directory layout (like I do). The server runs on localhost on port 55555 for the duration of the test.

Make sure that you have execution rights on all directories from the root down to the actual directory you serve. Many Linux distributions (Fedora, Debian) sets 700 on the home directories. If you want to browse the server, add a simple System.in.read(); after server.start(). At least in Eclipse you can press any key when the console window is active to continue, I'm sure the other two have similar capabilities.

The actual downloading is done using Jakarta HttpClient. I don't have anything particular to say about that, it has a nice API and seems very solid and broadly used.

One important aspect of the task at hand is to not download images unless they are newer than the copy that we already have, so I'm using the "If-Modified-Since" header. Finding the correct date format was harder than I expected (the RFC specs don't count, who reads 'em anyway). Here's a snippet that seems to do the trick:

private static final SimpleDateFormat httpDateFormat = new SimpleDateFormat("EEE, dd-MMM-yy HH:mm:ss zzz", Locale.US);
static {
httpDateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
}

Finally, a rant and a warning about java.util.Calendar: if you populate your database with a timestamp "2007-01-02 12:30:00", here's how you do an assertEquals() in a test:

Calendar calendar = Calendar.getInstance();
// Yes, only month numbering starts with 0
calendar.set(2007, 0, 2, 12, 30, 0);
// Yes, there's no set() that includes milliseconds
calendar.set(Calendar.MILLISECOND, 0);
// Yes, we have no bananas
assertEquals(calendar, myLoadedCalendarValue);

This last thing is nothing new, but it deserves a daily beating.

Friday, January 05, 2007

Making transitive ASM dependencies work with Spring, Hibernate and AspectJ

I've recently had the opportunity to work with the new AspectJ integration in Spring 2.0, and it's been, for the most part, a pleasure. A very brief three-step tutorial could look something like this:

  1. Create a POJO that holds all your pointcuts, annotate the class with @Aspect and start defining your pointcuts:

    package se.petrix.stuff.aop;

    @Aspect
    public class MyPointcuts {

    @Before("execution (* * se.petrix.stuff.dao.*.*(..))")
    public void beforeAnyDaoMethod() {}

    }

    A pointcut answers the question of where the advice should be applied. In this case, the pointcut is defined in the AspectJ expression language and means "before the execution of any method, in any class in the package se.petrix.stuff.dao that takes any argument". It's the annotation that does all the work here, the actual method doesn't do anything, as you can see. It should however be namned in a self-explanatory way, since we will be referring to it later on.

  2. Write another class, which will become the actual advice. This class is also annotated with @Aspect:

    package se.petrix.stuff.aop;

    @Aspect
    public class DaoCallCounterAspect {

    private int callCount = 0;

    @Pointcut("se.petrix.stuff.aop.MyPointcuts.beforeAnyDaoMethod()")
    public void countDAOCall() {
    logger.info("DAO call count is now: " + (callCount++));
    }

    public int getDaoCallCount() { return callCount; }

    }

    This is now a before-advice, and refers to the previously created pointcut. All it does is count the number of calls the the DAO layer, which is yet another pointless example but at least it's not just logging. There's also a way to read the current call count.

  3. XML configuration!

    <bean class="se.petrix.stuff.aop.DaoCallCounterAspect"/>

    <aop:aspectj-autoproxy/>

    Not too bad, eh? You need the aop namespace configured of course. All weaving is automatic, based on the pointcut expressions.


It's completely straightforward the aspect class itself into another bean, for example you might want a controller to be able to read the DAO call count and display to a web page. And vice versa, you can wire other beans into the aspect.

It is of course possible to place pointcut definitions in the same class as the advice, and/or define pointcuts in XML and so on. This is just one way to organize it all. Read this chapter for more information, and take a look at the original AspectJ documentation.

Now to the main subject of this post: the transitive Maven jar dependency conflicts, or rather version mismatches, between Spring, Hibernate, CGLIB and ASM. We're using Spring 2.0 and Hibernate 3.2.0.ga (that's "general availability", not "Gavin Approved" contrary to popular belief), and AspectJ 1.5.3 (weaver and rt jars). This pulls in ASM 1.5.3 and CGLIB 2.1_3 through transitive dependencies defined in the Maven poms for those packages. But this will result in ASM classes not being found (something about node visitor, don't remember exactly), or if you're not careful when overriding dependencies, method signature mismatch (method not found). I don't have the details on which library requires which version, or the exact error messages, at the time of this writing, but I aim to provide a working solution (well, it works for us anyway). Maybe if I have the time, I'll do a more careful dependency analysis.

Anyway, here it goes: first of all, exclude the CGLIB dep from Hibernate, and replace it with the "cglib-nodep" version which includes the ASM files needed:

<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>2.1_3</version>
</dependency

and

<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate</artifactId>
<version>3.2.0.ga</version>
<exclusions>
<exclusion>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
</exclusion>
</exclusions>
</dependency>

Then we specify ASM version 2.2.3 manually:

<dependency>
<groupId>asm</groupId>
<artifactId>asm</artifactId>
<version>2.2.3</version>
</dependency>
<dependency>
<groupId>asm</groupId>
<artifactId>asm-all</artifactId>
<version>2.2.3</version>
</dependency>
<dependency>
<groupId>asm</groupId>
<artifactId>asm-attrs</artifactId>
<version>2.2.3</version>
</dependency>

The reason why we have an overlap with asm, asm-all and asm-attrs is that they are pulled in by another library (not sure which one), so we have to override the version to avoid ending up with different versions of these jars. Another way would probably be to exclude them and only have asm-all 2.2.3. As I said, I don't have the details, but we've struggled quite a bit to get this working and I thought it might useful to provide at working example, even without explaining exactly why it works :-).