Friday, August 17, 2018
Git and Command Line Productivity for Windows
Friday, March 2, 2018
Nested Enums
public class Foo { public static enum Bar { } } public class Foo { public enum Bar { } }
Thursday, May 18, 2017
File.listFiles Gotcha
/** * Returns an array of abstract pathnames denoting the files in the * directory denoted by this abstract pathname. ... * @return An array of abstract pathnames denoting the files and * directories in the directory denoted by this abstract pathname. * The array will be empty if the directory is empty. Returns * {@code null} if this abstract pathname does not denote a * directory, or if an I/O error occurs. * * @throws SecurityException * If a security manager exists and its {@link * SecurityManager#checkRead(String)} method denies read access to * the directory * * @since 1.2 */ public File[] listFiles() {Good luck trying to find out which I/O error occurred! I'm guessing just throwing an IOException would have been too easy. Luckily Java 8 provides Files.list as an alternative which does throw an IOException if needed.
Friday, November 25, 2016
Java 8 Update of Bitemporal Framework
- Use Java 8's new time classes instead of JodaTime
- Leverage Java 8's goodies where relevant: streams, diamond operator, ...
- Switched to Hibernate 5
- Use JUnit 4
Monday, May 23, 2016
Java Bean Validation Gotcha
public class Document { @NotNull private Status status; private Date signingDate; @AssertTrue(message = "A signed document should have a signing date") public boolean isASignedDocumentHasASigningDate() { return status != SIGNED || signingDate != null; } }The fact that the assertion method actually returns a boolean also makes unit testing easy. So far so good!
However, there is a little known gotcha related to this: the annotated validation method actually has to follow the naming conventions of a boolean bean property getter, i.e. getBla() or isBla(). In other words the following would not work:
@AssertTrue(message = "A signed document should have a signing date") public boolean aSignedDocumentHasASigningDate() { return status != SIGNED || signingDate != null; }That's quite unfortunate, and certainly doesn't follow the principle of least astonishment: we're explicitly annotating the method so there is no reason why the bean validator should not pick it up! Even worse, unless you're thoroughly testing all you bean validation annotations, it's easy to miss the fact that the validation is not actually happening. Something to keep in mind!
Friday, December 11, 2015
The Benefits of Building on Multiple Platforms
Making the switch to Windows unearthed a few subtle coding errors, mainly related to resource management. It's no secret Windows is a lot stricter when it comes to file manipulation. For instance, consider the following:
File f = new File("test.tmp"); try (FileOutputStream fout = new FileOutputStream(f)) { fout.write("foo".getBytes()); fout.flush(); Files.move(f.toPath(), new File("test.txt").toPath()); }Using Java 8, this runs fine on Linux. However, on Windows you get an error:
java.nio.file.FileSystemException: test.tmp -> test.txt: The process cannot access the file because it is being used by another process.Looking at the code again you can see that the file is being moved inside the try-with-resources block. In other words, we're attempting to move the file before we've closed the output stream!
In general I would advise different developers in your development team to use different platforms. That will highlight these kind of subtle errors early and will generally improve the quality of your system. It of course comes as little surprise that Juergen Hoeller (of Spring fame) told me on several occasions that he's still developing on Windows for exactly this reason! :-)
Friday, November 20, 2015
Bizar replaceAll tricks
"foo*bar".replaceAll("\\*", "\\\\*");To understand what's going on here, let's remind ourselves of what replaceAll() actually does:
/** * Replaces each substring of this string that matches the given regular expression * with the given replacement. * ... */ public String replaceAll(String regex, String replacement) {So that already explains why the first argument to replaceAll() is "\\*": for it to be a valid regular expression we need to escape the asterisk (which of course means zero or more times in a regular expression) using a backslash, and we all know that a backslash character in a Java String needs to be escaped.
But what about the second argument? Shouldn't that just be "\\*" also: a backslash followed by an asterisk? It turns out replaceAll() doesn't treat the replacement as a simple string literal. The Javadoc states the following:
* Note that backslashes (\) and dollar signs ($) in the * replacement string may cause the results to be different than if it were * being treated as a literal replacement stringSo just having "\\*" as the replacement string would mean we have a backslash in there which we again need to escape! Hence the "\\\\*". It's interesting to note that in a funny twist of fate this actually makes the code less bizar. If the replacement string would have been a simple literal the code would have been "foo*bar".replaceAll("\\*", "\\*");. Imagine coming across that gem when trying to maintain some old piece of code... :-)