Wednesday, February 28, 2007

StringBuilder vs. StringBuffer and the Java compiler

Since Java 5, a non-synchronized sibling to the popular StringBuffer class exists - StringBuilder. It's API compatible, and migration can be done with search-and-replace. StringBuilder is not thread-safe, but that's rarely a problem. A popular myth among Java developers is that this:

String comma = ", ";
String slow = "Hello" + comma + " world!";

is slower than this:

String comma = ", ";
String fast = new StringBuffer().append("Hello").append(comma).append("world!").toString();

The fact is that the Java compiler automatically converts the first line to the equivalent of the second, resulting in the same bytecode. That is, until Java 5, when the compiler instead selects StringBuilder.

The twist here is that if you're using Java 5 and a library that's very heavy on string handling (contcatenation) which is compatible with Java < 5, it might be a good idea to recompile it with the -target 1.5 parameter to convert all string concatenations to use StringBuilder. Of course, you should also replace any explicit usage of StringBuffer with StringBuilder.

I figured this out while profiling an application using Freemarker, which has a standard distribution that's compatible with Java 1.2.

Thursday, February 22, 2007

MySQL mini-FAQ

I've spent some time reading about MySQL and the JDBC driver, and here are a few long-standing questions that I found the answer to:

  1. What is the difference between org.gjt.mm.mysql.Driver and com.mysql.jdbc.Driver?

    - They are the same, the former extends the latter with nothing but a no-args constructor for backwards compatability.

  2. What is the difference between MySQL Connector/J 3.1.x and 5.0.x?

    - The 5.0.x version has support for XA (distributed) transactions.

  3. Where can I find a reference guide for configuring the JDBC driver?

    - Here.

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 :-).

Thursday, December 21, 2006

Through the looking glass

(Congratulations to me, the 1,000,000th person to use this title in this context)

Project Looking Glass, Sun's Java 3D desktop (Vista Killer) has finally released 1.0.0, and of course I had to check it out. Here's a screenshot:

Wednesday, December 20, 2006

Transactions and exceptions revisited

As Jonas (Hello, world!) embarrasingly enough pointed out, the default Spring transaction semantics with respect to exceptions are identical with EJB's (you can't escape EJB!). So that's why Spring doesn't roll back on checked exceptions, but the question remains: why would anyone ever not want to roll back a transaction when an exception is thrown, checked or unchecked?

We had a long discussion which I'm going to try and sum up here. Obviously you (almost) never want an exception to leave the transactional layer without rolling back the transaction, since that means that something went wrong, but the transaction commited anyway. Suppose you have something like this:

@Transactional
public void registerNewUser(User user) throws LDAPException {
log.info("Registering user: " + user);
userDao.insertUser(user);
ldapService.registerEntry(user.getFirstName(), user.getLastName()); // Let's assume this throws a (checked) LDAPException
}

in the service layer, and then you call this method from outside the service layer, for example in an MVC controller. Now if the call to create the LDAP entry fails (throws a checked exception), the user data is stored even though an exception is thrown out to the calling controller, which can't do anything about it.

This scenario could be handled in two different ways, depending on business requirements: either a failed LDAP registration is fatal, and we need to roll back the transaction to avoid storing user data, or an LDAP exception is not fatal and we should catch and gracefully handle the failure inside the registerNewUser() service method.

If we decide to roll back, we have a few options:
So far it seems that simplifying the rollback rules to "always roll back on an Exception, I don't care if it's checked" is a good idea. But if it's the case that an LDAPException isn't fatal after all, the situation becomes a little more complicated. We modify the method like this:

@Transactional
public void registerNewUser(User user) {
log.info("Registering user: " + user);
userDao.insertUser(user);
try {
ldapService.registerEntry(user.getFirstName(), user.getLastName()); // Let's assume this throws a (checked) LDAPException
} catch (LDAPException e) {
// Log and move on
log.error("LDAP registration failed: " + e.getMessage());
}
}

No checked exception is thrown anymore. Furthermore, assume that the LDAP service call is also transactional, and that we're using the default propagation behaviour, REQUIRED, so that the LDAP service call participates in the same transaction as the registerNewUser() started. If the rule is to always roll back on any transaction, the exception thrown in the LDAP service call will mark the current transaction for rollback, even though we catch it in the user service method! That means that we have lost the ability to pass on failure messages between horizontal transactional calls (service-to-service).

So to conclude:
  1. We need to keep a way to pass on failure messages between calls inside the same transaction, and checked exceptions are a good candidate for that (certainly better than returning a negative integer).
  2. You should not expose service methods that throw checked exceptions to layers above/outside the transactional layer.

Thursday, December 14, 2006

This you need to know

Section 9.5.3 in Spring's reference documentation explains how and when transactions are rolled back, and also states the default behaviour with respect to rolling back transactions. The behaviour might not be what you expect (quoted):

However, please note that the Spring Framework's transaction infrastructure code will, by default, only mark a transaction for rollback in the case of runtime, unchecked exceptions; that is, when the thrown exception an instance or subclass of RuntimeException. (Errors will also - by default - result in a rollback.) Checked exceptions that are thrown from a transactional method will not result in the transaction being rolled back.

Note the very last sentence (my bold). Checked exceptions do not trigger a transaction rollback!

There is of course a way change this behaviour by listing an array of Exception classes for which to roll back for, the details are in section 9.5.3 and below.

Personally I find this quite counter-intuitive, but I suppose there is good reason for this default behaviour. In either case, it's very important to be aware of how it works.

Wednesday, December 13, 2006

Faster DAO testing

If you're prepared to step away just a little bit from how the production environment is set up, here's one way to speed up Hibernate DAO testing considerably:

First of all, don't add a (Annotation)LocalSessionFactoryBean to your test context, just a DataSource and a hibernate.cfg.xml, and don't add any mapped/annotated classes to the configuration file either. Then create a base DAO test class that looks something like this:

public class AbstractDaoTest {

SessionFactory sessionFactory;

public AbstractDaoTest(Class... classes)
{
// Read hibernate.cfg.xml from root of classpath, for example src/test/resources
Configuration configuration = new Configuration();
for (Class clazz : classes) {
configuration.addClass(clazz);
}
sessionFactory = configuration.buildSessionFactory();
}

}

Then place all your DAOs and the DataSource in the test application context, and start writing your DAO tests like this:

public class ParentDaoTest extends AbstractDaoTest {

ParentDao dao;

public ParentDaoTest() {
// Parent has a relation to the Child class, so we need two classes mapped
super(Parent.class, Child.class);
}

public void setParentDao(ParentDao dao)
this.dao = dao;
}

public void testSomething() {
// Stuff that involves Parent and Child
}

}

Now there's only two classes that Hibernate needs to parse metadata for and create CRUD SQL for and so on, which can speed up application context startup considerably compared to mapping all domain classes, if you have 10-20 classes in you domain model.

Sunday, December 10, 2006

YES

He's on his way!

Go Christer!

Thursday, December 07, 2006

En vän har gått ur tiden

Min katt Morrissey dog idag. Han hade en medfödd, obotlig njursjukdom som till slut blev för svår att uthärda, och han somnade in på sin lurviga fäll hemma i soffan efter en överdos sömnmedel.


Tack Yvonne på Djurdoktorn för all hjälp.



Lillmosse, 2002-07-04 - 2006-12-07

Friday, November 24, 2006

The dark side

Christmas came early this year:









Monday, November 20, 2006

One less headache thanks to Tiger

You are familiar with the old Arrays.asList(Object[]) call for converting an array to a List. You're also sick of doing this:

List list = new ArrayList();
list.add(Integer.valueOf(1));
list.add(Integer.valueOf(2));
list.add(Integer.valueOf(3));

Well, thanks to autoboxing and varargs, you can do this instead:

List list = Arrays.asList(1, 2, 3);

Sure, it's not a huge deal, but it's just better in every sense of the word.

Saturday, November 11, 2006

Testing Hibernate DAOs with Spring

Spring has an enormous amount of utility/help/glue code, that can simplify a lot of common tasks, but it's not easy to get a good overview of everything that's available. One of the core areas of Spring is of course testing, and I'm going to show one way to write unit tests (or integration tests, if you insist) for Data Access Objects implemented using Hibernate, against a real database.

To begin with, most of the work has already been done by Spring. In the
org.springframework.test package there are a number of helper classes on top of JUnit for in-container testing. We are going to make use of an application context, a data source and transactions, so we're going to with AbstractTransactionalDataSourceSpringContextTests, which is coincidentally the longest class name in Spring. This class has a number of cool features:

  • It keeps a static cache of application contexts based on the string array of file names defining the context, so you only need to initialize the context once per test class*.
  • It autowires the test itself, by type. (It's also possible to change to autowire-by-name, or turn it off)*
  • It runs every test inside a transaction, which is rolled back after each test (unless you say otherwise).
  • It makes a JdbcTemplate available for low-level database operations.
  • Instead of the setUp()/tearDown() callbacks, it has callbacks before transactions start, inside the transaction but before the test, after the test but inside the transaction and finally after the transaction has been rolled back.
(* Thanks Robert for the pointers.)

I usually organize my applicationContext files so that every environment-dependant bean - such as the data source, SMTP server or whatever - is in a separate file called applicationContext-env.xml. Also, each of the dao-, service- and mvc layer have their own context file. Using that setup we would use the applicationContext-dao.xml file, and a test-specific applicationContext-env.xml. If you keep your context files in the classpath and use Maven and the standard directory layout, overriding context files work out of the box. Specifically, you can keep one version of applicationContext-env.xml in src/main/resources pointing at a JNDI exposed data source that's picked up from the application server, and another in src/test/resources looking somthing like this:

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="jdbc:postgresql:myapp_test"/>
<property name="driverClassName" value="org.postgresql.Driver"/>
<property name="username" value="my_db_user"/>
<property name="password" value="******"/>
</bean>

In addition to this, you can have a test-specific hibernate.properties in src/test/resources that turns on SQL debugging and other things:

hibernate.show_sql=true
hibernate.format_sql=true

Don't deviate too much from the production environment though, by doing things like turning off the second-level cache or whatever, since that decreases the value of the tests. We want our testing to be as close to reality as possible, that's why where using the same database (application) as in production, instead of something like Hypersonic.

Now we're ready to implement our first method, which we'll do in a base class for all our daos. You ususally have a number of daos that use the same combination of context files, the same batch file with test data and which all need a reference to the SessionFactory. So, here's our AbstractDaoIntegrationTest class:

public abstract class AbstractDaoIntegrationTest extends AbstractTransactionalDataSourceSpringContextTests {

SessionFactory sessionFactory;
SimpleJdbcTemplate jt;

public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}

@Override
protected String[] getConfigLocations() {
return new String[] {
"applicationContext-dao.xml",
"applicationContext-env.xml"
};
}

@Override
protected void onSetUpBeforeTransaction() throws Exception {
jt = new SimpleJdbcTemplate(jdbcTemplate);
}

@Override
protected void onSetUpInTransaction() throws Exception {
super.onSetUpInTransaction();
// Load a batch of test data in the transaction
executeSqlScript("classpath:test-data.sql", false);
}

// Utility method
protected void flush() {
sessionFactory.getCurrentSession().flush();
}

}


The SessionFactory bean obviously needs to be available in one of the context files, I usually keep ORM configuration along with dao declarations. As you can see, an SQL file of test data is loaded at the beginning of the transaction. This data is rolled back at the end, eliminating the need for any manual cleanup. This works well for any reasonable amount of test data, but maybe not if you need a million records loaded. The actual test will be hard-coded against the test data file, when verifying load and finder methods. We're also wrapping the JdbcTemplate with the new and simpler (!) SimpleJdbcTemplate, which makes use of varags for parameter matching for example.

Now we can start writing dao tests on top of the above class. I'm going to use an EmployeeDao for this blog entry.

public class EmployeeDaoTest extends AbstractDaoIntegrationTest {

EmployeeDao employeeDao;

public void setEmployeeDao(EmployeeDao employeeDao) {
this.employeeDao = employeeDao;
}

}

This is all we need to get started with the actual tests! Very clean and simple. Let's look at load, store and delete testing in turn.

public void testLoadEmployee() throws Exception {
Employee employee = employeeDao.load(7L);

assertEquals("197708170000", employee.getUsername());
assertEquals("test-password", employee.getPassword());
assertEquals("test.realm", employee.getRealm());
assertEquals("197708170000@test.realm", employee.getJid());
assertEquals("test-firstName test-familyName", employee.getName());
// Assert every property
}

The important principle in action here is that you test one ORM operation against a known database state, namely the test batch file.

Note that unless we close the Hibernate session manually, the session remains open during the test, so that lazy relational restrictions will not be effective here. We'll test that later though.

Store testing is similar, we build an Employee in Java code, store it using the ORM, and the compare against the actual database state.

public void testStore() throws Exception {
Employee employee = new Employee();
employee.setUsername("197708306621");
employee.setPassword("_password");
employee.setRealm("_realm");
employee.getRoles().add(User.Role.CUSTOMER);
employee.getRoles().add(User.Role.EMPLOYEE);
employee.getRoles().add(User.Role.TRANSLATOR);
employee.getVcard().setFirstName("_firstName");
employee.getVcard().setFamilyName("_familyName");

// Store
employeeDao.store(employee);
// Flush manually, to make sure the SQL is issued
flush();

// Start assertions by loading data from the database using the JdbcTemplate
assertNotNull(employee.getId());
Map authregMap = jt.queryForMap(
"select * from authreg where id = ? " +
"and class = 'se.petrix.iaba.model.user.Employee'", employee.getId());

assertEquals("197708306621", authregMap.get("username"));
assertEquals("_password", authregMap.get("password"));
assertEquals("_realm", authregMap.get("realm"));

// Roles
List<Map<String,Role>> roles = jt.queryForList(
"select role from authreg_roles where authreg ? order by role",
employee.getId());

assertEquals(3, roles.size());
assertEquals(Role.CUSTOMER.name(), roles.get(0).get("role"));
assertEquals(Role.EMPLOYEE.name(), roles.get(1).get("role"));
assertEquals(Role.TRANSLATOR.name(), roles.get(2).get("role"));
}


Here you can see the SimpleJdbcTemplate in action. It's really simple to read data for verifying against what we fed the Employee instance with.

Next comes deletion, which is fairly simple in comparison.

public void testDelete() throws Exception {
employeeDao.delete(7L);
flush();

// Employee
assertEquals(0, jt.queryForInt("select count(*) from authreg where id = 7"));
// Roles
assertEquals(0, jt.queryForInt("select count(*) from authreg_roles where authreg_id = 7"));
}

Here's where you would test your cascade-delete mapping settings, by making sure that data in related tables is or isn't deleted, according to your configuration.

Finally we'll take a look at how you can test lazy relations. It could be argued that this belongs in the service layer tests, but the same method can easily be applied to the service layer, just include applicationContext-service.xml and write tests against that layer.

Suppose that Employee has a Set<Customer> of customers that he or she is responsible for, and that the relation is lazy. An Employee also has a Set<Role> of roles, that isn't lazy.

public void testEmployeeRelations() throws Exception {
Employee employee = employeeDao.load(7L);
// Manually end the transaction, which closes the Hibernate session
endTransaction();

try {
Role role = employee.getRoles().iterator.next();
// Success
} catch (LazyInitializationException e) {
fail("Employee.roles relation should not be lazy");
}

try {
Customer customer = employee.getCustomers().iterator.next();
fail("Employee.customers relation should be lazy");
} catch (LazyInitializationException e) {
// Success
}
}

That's it for today, good luck and as always, comments are welcome.

Monday, November 06, 2006

November spawned a Florence

Robert och Joanna blev föräldrar idag, till en liten flicka som ska döpas till Florence (eller kanske phl0renZ)? Jag hoppas få se henne senare i veckan, det ska bli spännande. Stort grattis och lyckönskningar från mig och Sofia!

För fratida referens noterar vi att den 6 november 2006 är Elfsborg nyblivna svenska mästare i fotboll, Fredrik Reinfeldt nybliven statsminister och Googles senaste påhitt en mobil Java-klient till Gmail.

Sunday, October 29, 2006

Continuum and SSL authentication solved

We got SSL authentication working in Continuum this Friday, by applying this one-line patch that we found in the JIRA. These are the steps that we followed, roughly:
  1. Checkout the source:

    svn co http://svn.apache.org/repos/asf/maven/continuum/tags/continuum-1.0.3/
  2. Apply the patch (it's just one line, so it's trivial to apply it by hand).
  3. Build the whole thing with Maven:

    mvn -Dmaven.test.skip=true -DappProperties=app.properties install
    Some tests failed, and I didn't have the time to investigate further, and one of the modules failed to find the app.properties file automatically. Also, you may need to download and install some Sun jars manually, but that's not a big problem since Maven hands you a command line to use.
Now Continuum is ready to run, if the build succeeds. There is a build guide, and a README that contains additional information. In our case, we have a hierarchy of projects which are automatically identified and set up in Continuum. Very handy!

Sunday, October 22, 2006

New job

I've just completed the first week on my new assignment: consulting team member on a medium sized project for Eniro. It's been a great experience so far, and although I can't reveal the exact nature of the project, I can say that it's based on high profile open source Java EE components such as Spring, Hibernate, MySQL, Linux, Velocity and Maven.

I've worked quite a bit with most of the stuff before, but one thing that's new to me is Maven multi-module project hierarchies which works quite well, and makes Maven look even better. Another nice thing that I've never used before is Maven Proxy.

One thing we decided on early on was to use contiuous integration, and since we're heavy users of Maven already, Continuum seemed like a sane choice for server. Unfortunately we've having problems getting it to work with SSL+authentication...

The project is based on an older application which has been split into three major parts, with each part having two or three sub-parts. The older application did have a partial test suite, but it hadn't really been kept up to date the latter half, so the first step will be to resurrect and/or rewrite the testsuite. This situation is fairly common in my experience, where you start out writing tests for everything, but as the project progresses, people pay less and less attention to testing, and the suite drifts further away from the code base. Hopefully CI will counter this behaviour :-)

My methodology for writing DAO testing against a database was accepted with some enthusiasm. I've written a little about it in "Rolling with Spring and Hibernate", but I'm planning a larger entry on that subject. An update on that article is planned, too. Stay tuned!

Friday, October 06, 2006

Patch accepted

I got my first patch accepted into Spring today, a small fix for the FreeMarker view class. It'll be present in 2.0.1. Very fast response from the core team (Jürgen), impressive!

I've also worked a little on the CSS of this blog. I really think serif fonts are more readable in the long run. Gotta do something about that awful header though. This was the only Blogger template I could find that spanned the full width of the page, so I figured I'd start from there and customize it later on.

Thursday, October 05, 2006

About the "Rolling with..." article

For those of you interested in reading the "Rolling with Spring and Hibernate" article series, here's a quick recap of the contents:
  1. The first part is just about APIs and development environment.
  2. The second part begins the building of the model, with Hibernate and transaction configuration and a basic CRUD-able entity class.
  3. In the third part a controller and a few views are added, and the application is deployed for the first time. Also a few notes about AspectJ weaving and Jetty.
  4. Part four adds a second class to the model, and a relation between the two model classes. Here we can see what amount of work is needed to add one more model class to an existing application.
  5. Testing is covered in part five, both mock testing and integration testing against a database.
  6. And in the final part I comment a little on the differences between Rails and the stack at hand, and some suggestions for Spring improvement.
Any comments and suggestions are welcome. The code is available as a Google Code project. Embarassingly enough, I've misspellt "Recipe" as "Recipie" thoughout the application, but the blog text is corrected. Refactoring recipie->recipe is left as en excercise to the reader :-)

Update: those of you who find this article interesting, might also enjoy this more recent in-depth look at implementing the ActiveRecord pattern using Spring and Hibernate.

Monday, September 25, 2006

Rolling with Spring and Hibernate, part 6: summary and reflections

This series of blog entries was meant to compare speed and ease of development of the Spring 2.0 and Hibernate/JPA combination with Ruby on Rails, and also to take a closer look at the new Rich Domain Model support in Spring 2.0. I am by no means an expert on RoR, I've mostly tinkered with a few toy projects to get a basic hang of it. My experience with Spring and Hibernate is a lot more extensive, going back to a few months before Spring 1.0 was released in March of 2004.

Obviously it's a lot more work to get started with this stack than RoR, but that can be vastly improved with a good prototype project. There are a number of options, ranging from a slightly expanded Maven standard archetype to Appfuse. Usually you can modify an existing project, maybe maintain a company-wide standard application - or even use spring-cookbook :-). In either case, it's not a big deal in a real world situation, since the time spent in this phase is a tiny, tiny part of the whole project. It does matter for newbies, though.

What's more important and interesting is the time and effort needed to make a change to the application. You often see the argument that RoR is more productive because you need to write less code to accomplish the same feat. Case in point: the infamous Hello World application...Ruby first:

puts "Hello, World!"

And the same thing in Java:

public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, world!");
}
}

Clearly a big advantage for Ruby, wouldn't you say? ;-)

Jokes aside, code-completion compensates for more verbose syntax IMHO. Actually writing code isn't really time-consuming, but the number of places where you have to edit code for a particular change in the application to happen, does matter. It matters a lot. Suppose you have a Car in your domain model, and you want to add a color property. In RoR, you add the column "color" to the table "cars" in the database. That's it. There's no updating the ORM mapping files, no adding of a member , getter and setter for color in the Car class, no updating of DTOs or form beans, no extra handling of parameters. Unless you're using view scaffolding you will need to modify the views, however. This is a great boost to productivity, read- and maintainability of the application.

When working with Hibernate/JPA annotations, you get fairly close to this behaviour . Hibernate can auto-update the schema on changes to the Java model, and you only need to set the @Entity annotation on the class level for properties to be persistent by default. And there's no need for maintaining separate DTOs, as when working with EJB 2.x. If you're using the Spring MVC framework, you can even use arbitrary classes as form beans, and the domain model is of course a great candidate for that. So we can honestly say that we have a fairly competitive setup at hand.

A nice thing in Rails is the automatic conversion from id parameter to a related persisted entity, for example the category of a recipie in this particular application. The HTML form for editing a Recipe has a drop-down list where the parameter value is the id of the Category. Using Spring MVC, we need to write a (short) PropertyEditor to load and join the Recipie and the Category.

When it comes to URL mapping and controllers, Spring 2.0 brings a fantastic new addition to the table: ControllerClassNameHandlerMapping. Together with the MultiactionController we can work similarly to Rails: Any request that matches /car/* is handled by a CarController, and specifically by the method that has the same name as the end part of the url - /car/edit is handled by the edit() method, and so on. It works for other controller types as well, so you can have a SimpleFormController named EditCarController that's mapped to /editCar.

The edit-save-reload-cycle in the web server is almost zero in RoR, since Ruby is a interpreted language. Traditionally, this cycle has been very slow in Enterprise Java development, and even if you work extensively with out-of-container testing, you will need to see and test the actual application from time to time. Until we have on-the-fly class reloading in the JVM, we're going to have to reload the application after making changes to Java code. We can ease the pain by using a smart directory structure (as in this application) and a fast, handy servlet container that scans for changes and triggers reloads automatically. Another thing that's new in Spring 2.0 is the support for beans defined in script languages such as BeanShell, Groovy or even Ruby. You could for example write the controllers in Groovy and keep the service layer and everything below in Java. The possibilites are endless, and the big advantage of Spring is that you can gradually migrate to a simpler and more modern architecture while preserving and interacting with legacy components.

Working with a Rich Domain Model and IoC using the new @Configurable technique, is quite nice. It does require a few tricks and redundant configuration though. Passing the -javaagent parameter to the JVM requires setting shell variables or editing the server startup script, which is acceptable but easy to forget.

It's also unfortunate that there's no domain-specific shorthand XML configuration for the AspectJ transaction aspect, like <aop:aspectj-configured/>.

In general, it would be nice if you didn't have to specify what classes should be persisted twice, first with the @Entity annotation and the again the in SessionFactory configuration. Same thing with classes that need to be weaved with the IoC-aspect: first with @Configurable, and again in classpath:META-INF/aop.xml. With no detailed technical insight in the matter, here's how I'd like to write my configuration, using the same recursive package syntax as AspectJ:

<hibernate3:sessionFactory mappedClasses="se.petrix.cookbook.model..*"/>

<aop:aspectj-configured configuredClasses="se.petrix.cookbook.model..*"/>

<aop:aspectj-transactions/>

Better get to work on that patch then :-)

There is a lot of room for configuration improvment everywhere, now that the domain-specific XML framework is in place. My guess is that we'll see a lot of new XSDs during the 2.x series.

Mock testing is a little bit more difficult, but still manageable. If you're wiring a DAO or service layer into the model, you won't have to deal with chained interface calls either, such as sessionFactory.getCurrentSession().getCriteria(clazz).

How to write the CRUD methods is another difficult question. At first thought, the load operation isn't tied to a particular instance of an entity, since we don't have one yet. Therefore it should be static:

public static Recipe load(Long id) { ... }

Recipe r = Recipe.load(1L);

But there are two problems with this implementation: first, you don't want to make the persistence collaborator (the SessionFactory in this application, maybe a DAO layer or a DataSource in others) a static member. So, we have to wrap the static call with an instantiation:

public static Recipe load(Long id) {
return new Recipe().doLoad(id);
}

And secondly, you don't want to implement this method in every persistent domain class, instead you move it to the BasicEntity class. But how do we instantiate the parameterized class in a static context? We want something like this (in BasicEntity):
   
public static T load(Long id) {
return T.class.newInstance().doLoad(id);
}

Of course, that's not possible (T.class is illegal). I haven't found any way around this.

The choice, as I see it, is between wrapping load and other static methods such as finders in every persistent class, or completely skip static methods and instead choose one of the following styles:

// 1:
Category c = new Category().load(1L);
// 2:
Category c = new Category(1L).load();
// 3:
Category c = new Category(); c.setId(1L); c.load();
// 4: (perform a load operation in the constructor)
Category c = new Category(1L);

If anyone has opinions on this matter, please post a comment. I'm not convinced about the superiority of any of these solutions, but I've chosen the simplest one, number 3, in this application.