So we decided it was time to upgrade our project to Spring 3. The last time I gave this a try it was a pretty bad experience because at the time I was unable to get the upgraded maven packages without switching to the OSGi names. I'm not sure whether that was an issue with our archiva server or Spring but I didn't run into it this time.
This update also including updating Tiles, Junit and Spring Security.
The latter was not really required and upgrading it turned out to be quite a hassle. Though I guess that hassle had to be dealt with at some point anyway.
Spring Security underwent some major refactoring for version 3, changing a lot of the packages around and also making some minor improvements to parts of the API (UserDetails.getAuthentication and some changes to voters). Adjusting to that was mostly just a matter of organizing imports in eclipse and thankfully most of our access of the relevant classes was wrapped at a few key points.
But it turned out that Webflow hasn't been upgraded to support either Spring Security 3 or Tiles 2.1. Their JIRA has tickets for both and I was able to hack something together from that. I'm curious if there is some better way to handle these versioning issues. Explicitly requiring external dependencies (instead of marking them optional) is too inflexible, if the upgrade is minor. Yet it would be nice to see incompatible changes. Maybe one could put meaning into major and minor version numbers? Oh well, versioning is always tough.
In the case of Tiles, actually removing methods from their api instead of just deprecating them and letting them return null would have brought this particular issue to light more quickly, since Webflow 2.0.9 simply wouldn't have compiled. Why even bother with separate api and core/impl packages?
Other maven changes were limited to removing spring-security-core-tiger (yay to less jdk-specific packages) in favor of spring-security-config. I also had to add commons-codec because it seems to have previously been implicitly required from somewhere else and only been needed since some point after I last ran check-dependencies.
In the end all of this this turned out to be less problematic than I had feared. The knowledge gained from the last attempt (i.e. spring-test update also necessitating a junit update) and the good test coverage helped to sort out most issues before even trying to run the application.
Friday, April 23, 2010
Tuesday, April 6, 2010
Working around type erasure in Java
Labels:
java
Due to how generics are implemented in Java, there is no way to determine at runtime, e.g. the type of objects contained in a collection. This can be a bit of an issue if you want to use that type information to look up a specific converter or repository for that type.
What I didn't know was that while the type information is lost on the actual instance, there is still the possibility to get it from the surrounding class declaration. This does require that the instance is declared in a Field and that that Field contains the generic type declaration. Similarly, for classes extending/implementing generic classes or interfaces you can also access type parameters via reflection.
Here's a messy example that hopefully should illustrate both points:
What I didn't know was that while the type information is lost on the actual instance, there is still the possibility to get it from the surrounding class declaration. This does require that the instance is declared in a Field and that that Field contains the generic type declaration. Similarly, for classes extending/implementing generic classes or interfaces you can also access type parameters via reflection.
Here's a messy example that hopefully should illustrate both points:
public class LongToStringList extends AbstractList<String> implements List<String> {
private List<Long> someList = new ArrayList();
@Override
public String get(int index) {
return Long.toString(someList.get(index));
}
@Override
public int size() {
return someList.size();
}
@Test
public void test() throws Exception {
// alternatively this.getClass().getGenericInterfaces()[0]
ParameterizedType superclass = (ParameterizedType) this.getClass().getGenericSuperclass();
assertArrayEquals(new Type[]{String.class}, superclass.getActualTypeArguments());
Field field = this.getClass().getDeclaredField("someList");
ParameterizedType fieldType = (ParameterizedType) field.getGenericType();
assertArrayEquals(new Type[]{Long.class}, fieldType.getActualTypeArguments());
}
}
I'm always a bit scared of reflection and so I only learned about it browsing through my colleague Markus' code and then again while stumbling through some of the code in Spring-Binding (which lead here). The latter can use this to determine which converter to use to map form values from an array into a collection on a bound model. And that is pretty nifty if not without its problems.
Subscribe to:
Posts (Atom)