Thursday, March 19, 2009

Unordered collections

Suppose we have domain classes with fields of type Collection. We also need to implement equals on these classes, which can be easily done using org.apache.commons.lang.builder.EqualsBuilder.
However, the EqualsBuilder is not smart enough to treat our data as an unordered collection (unordered is the lowest common denominator when the implemented interface is a Collection and not a List). This means that we should wrap our collections into UnorderedCollection
when comparing, which can be done like so:

public static <T, E extends T> Collection<T> asCollection(final Collection<E> ts) {
return new UnorderedCollection<T>() {

@Override
public Iterator<T> iterator() {
return new UnmodifiableIterator<T>() {
private Iterator<E> iterator = ts.iterator();

public boolean hasNext() {
return iterator.hasNext();
}

public T next() {
return iterator.next();
}

};
}

@Override
public int size() {
return ts.size();
}

@Override
public String toString() {
return ts.toString();
}
};
}


private static abstract class UnorderedCollection<T> extends AbstractCollection<T> {
/**
* Taken from {@link AbstractSet#equals} implementation.
* <p>
* <b>Important!</b> not associative when compared with ordered
* {@link Collection} (such as {@link List}).
* </p><p>
* E.g.
* <code>CollectionUtils.asCollection().equals(new ArrayList()) == true</code>

* but
* <code>(new ArrayList()).equals(CollectionUtils.asCollection()) != true</code>

*
* @see java.lang.Object#equals(java.lang.Object)
* @see java.util.collection.AbstractSet#equals(java.lang.Object)
*/
@Override
public boolean equals(final Object o) {
if (o == this) {
return true;
}

if (!(o instanceof Collection)) {
return false;
}
Collection c = (Collection) o;
if (c.size() != size()) {
return false;
}
try {
return containsAll(c);
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
return false;
}
}
}

The implementation of equals was adopted from AbstractSet as the comments state.

private static abstract class UnmodifiableIterator<T> implements Iterator<T> {
public void remove() {
throw new UnsupportedOperationException(
"Collection was created using CollectionUtils#asCollection which does not support element removal.");
}
}

This way we can wrap any Collection with an UnorderedCollection and enjoy the equality between
Arrays.asList(1, 2, 3)

and
Arrays.asList(3, 2, 1)

Monday, March 9, 2009

Java web apps in Tomcat

Put as little external libraries as you can into $TOMCAT/commons/lib or $TOMCAT/shared/lib. When time to update comes each shared dependency has a chance of crippling your efforts by breaking seemingly independent applications. In general, you shouldn't add any libraries to aforementioned folders. If your applications must share some of their dependencies - set up a simlink outside of the Tomcat. This way you will have a guarantee that there will be no issues related to the Tomcats classloader.

If you want to maintain j2ee 1.4 compatibility and avoid problems with dependencies on jsp/jstl libraries (dreaded NoClassDefFoundError: Class not found javax/el/ValueExpression) I suggest you use this configuration:

<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>

Dirty scripting

Java permits both
new Object[][] { new Object[] { ... } }
and
new Object[][] { { ... } }
Suppose we wanted to transform the former to the more concise latter form. Naturally, going through all of the source files is not an option, so we use the tools at hand:
grep -lr 'new Object\[\] {' . | xargs sed -i 's/new Object\[\] {/{/g'
grep locates files matching the pattern (note that this command might introduce errors in case of:
Object[] wontCompile() {
return new Object[] { };
}
). -r tells grep to recurse into subdirectories and -l - to output only the filenames. Sed takes the filenames and performs a substitution writing result to the same file (-i flag).