Thursday, August 25, 2011

@Nullable / @Null / @NotNull / @Notnull / @Nonnull / @CheckForNull annotations


Jetbrains never ceases to delight. As of 10.5, their IDE supports not only their own pioneering org.jetbrains.annotations Nullable annotations, but also any other arbitrary @Nullable annotations you decide to use.

Currently the most popular ones are the JSR-305 (javax.annotation) annotations, the FindBugs nullable annotations, and IntelliJ's own nullable annotations.

If you decide to use the Findbugs annotations (which are found in package edu.umd.cs.findbugs.annotations), keep in mind that you probably want to use @CheckForNull instead of @Nullable. By default IntelliJ includes @Nullable for Findbugs, but you should change that to @CheckForNull.

The reason is that @Nullable is all but ignored by Findbugs if you decide to use that as well. Findbugs' annotations seem badly named, but hey that's just the way it is.

On a related subject, be careful not to get the JSR-303 nullable annotations confused with the JSR-305 nullable annotations.

Here is a quick summary of the differences:

JSR-303

  • Used for runtime bean validation, NOT static code analysis
  • javax.validation.constraints.NotNull
  • javax.validation.constraints.Null


JSR-305

  • Used for Static code analysis - seems to be all but dead
  • javax.annotation.Nonnull
  • javax.annotation.Nullable


Findbugs

  • Used by the Findbugs static code analysis tool
  • edu.umd.cs.findbugs.annotations.NonNull
  • edu.umd.cs.findbugs.annotations.Nullable (Probably not what you want)
  • edu.umd.cs.findbugs.annotations.CheckForNull


IntelliJ

  • Used by IntelliJ IDEA, but also publicly available as a jar
  • org.jetbrains.annotations.Nullable
  • org.jetbrains.annotations.NonNull



As usual, IntelliJ's built-in IDE support for nullable is awesome, and Eclipse's is decidedly un-awesome.


Wednesday, June 22, 2011

FileNotFoundException when running a unit test in IntelliJ

If you are running a unit test in IntelliJ that needs to load a test file, you might come across this error.
java.io.FileNotFoundException: src\test\resources\testFile.xml (The system cannot find the path specified)
The problem may be that by default, IntelliJ sets the working directory to be the project root. If you are working on a maven project or something else with a complicated project structure, then you probably want it to look instead at the module root.

This is easy enough to configure. Simply go to:

Edit Configurations -> Defaults -> JUnit -> Working Directory

Set this value to $MODULE_DIR$ and you are done!

Also remember to delete any existing JUnit configurations so that it will pick up the default you have just set.

Friday, May 27, 2011

Spring Validation is still pretty dumb

HOW IT USED TO BE

One of the annoying things about Spring MVC 2.5 was the validation. Even Spring themselves have acknowledged it:

"In previous versions it was up to the developer to manually invoke validation logic"

HOW IT IS NOW

When Spring 3 rolled around, I was looking forward to some clever validator improvements. And Spring delivered!

You no longer have to manually invoke validators, but you can use the JSR-303 @Valid annotation to AUTOMAGICALLY invoke validation on the bean in your controller. Check it out below:

 @Controller  
 public class MyController {  
   @InitBinder  
   protected void initBinder(WebDataBinder binder) {  
     binder.setValidator(new FooValidator());  
   }  
   @RequestMapping("/foo", method=RequestMethod.POST)  
   public void processFoo(@Valid Foo foo) { ... }  
 }  

Awesome sauce!

WHY IT IS STILL DUMB

Yeah that's cool and all, but that's pretty much all they've done in the way of magic. When it comes to CONFIGURING validators (which is one place we could really use some magic), Spring Validation is still pretty stoopid.

You basically have two options - manually set the validator in your initBinder() (like you can see in the example above), or call setValidator (Validator) on the global WebBindingInitializer. Which looks ike this in the applicationContext.xml:

 <mvc:annotation-driven validator="globalValidator"/>  

This is pretty dumb too - having a single global validator for all models probably isn't very useful.

WHY IT IS NOT NORMALLY A PROBLEM

Now all of this isn't so bad when using Spring Validation with Spring MVC. Normally We have one Model per Controller, which is configured with one Validator (and incidentally one View - which is why it is called (yes you guessed it) MVC ).

So configuring a validator for a controller isn't too bad - we're already configuring the model and view for the contrller, so one more thing is not a problem.

WHEN IT IS A PROBLEM

It becomes a problem when you want to use it OUTSIDE of the MVC framework. When you don't have a controller to configure your validator and model to - how on earth do you wire this stuff up?

Suppose I have an arbitrary model that I want validated with Spring's framework - how do I do it? I don't need any of Spring's Binding facilities. And nothing magical like this seems to exist:

 @Autowired MagicalSpringValidationMonkey magicalSpringValidationMonkey;  
 public void myService(MyModel myModel){  
   Errors errors = magicalSpringValidationMonkey.validate(myModel);  
 }  

I would expect my Magical Spring validator to do three things:
1. Run any JSR-303 Bean Validation configured for the model
2. Find and run the appropriately configured Spring Validator for this model
3. Return me an errors instance containing all of those errors.


WHY IT SHOULDN'T BE A PROBLEM

JSR-303 Validation is configured on the model itself, so Spring should be able to figure out how to run that validation.

Spring Validators (my custom validation classes that implement Spring's Validator interface) are already configured to show which models they support, by means of the supports(Class clazz) method which must be implemented.

HMMM COME TO THINK OF IT

Actually now that I think about it, you know what? After writing this long rant, I figured out that it probably wouldn't be too hard for me to write the MagicalSpringValidationMonkey myself.

But you know what else? I'm annoyed that this isn't already there, and that I had to do so much investigation to find out how it works, so I'm not gonna do it. Plus I'm too lazy. And Margaret River is calling me.

I hope you enjoyed this rant.

The end.

Tuesday, December 14, 2010

Something I didn't (and still don't) know about SQL

Take this simple Database Table:

Field1
(null)
abc
xyz

Here's a test for you. What will the following query return?

select * from table1 where field1 like 'a%'

If you answered abc you are correct - congratulations!
Ok now here is the same query slightly modified - what will it return:

select * from table1 where not (field1 like 'a%')

Surprisingly (to me) it doesn't return (null) and xyz, it only returns xyz. If anyone knows why can you please explain in the comments?

Tuesday, October 12, 2010

The cause of TeamCity 5 slowness and CPU usage

Teamcity 5.1.2 was running like a dog for me - with skyrocketing CPU on the server.

The cause of this slowness was a slow network connection to Subversion. You see, by default Teamcity is configured to check for SVN updates every 60 seconds.

If, for some reason (like a slow network connection), it takes longer than 60 seconds to do this check, then the Teamcity svn update jobs will just keep backing up every 60 seconds until there is a whole queue of them slowing down the server.

There are a couple of things you can do:

1. Fix the cause of the network slowness
2. Change the "Default VCS changes check interval" under Administration -> Server Configuration
3. Make sure "Clean all files before build" is NOT checked in your build configuration.
4. Use the slowness as an excuse to request newer hardware :) Then do steps 1, 2 or 3.

Monday, August 30, 2010

Cached javascript files? A duh moment

How do you ensure that your users don't have an old cached version of your javascript files?

The answer might come to you straight away, but I'm not ashamed to admit that this problem perplexed me for quite a while. We had problems where the users' browsers were using old cached versions of some javascript files that we had updated. Thus the web application was behaving inconsistently for them.

We flirted with ideas about fiddling with the 'Cache-control' and 'Expires' headers, but it proved troublesome and we didn't want to disable caching completely as a lot of our users are still on dialup connections (yes that's how badly our life-saving Queensland firefighters are treated).

We even thought about writing some sort of script that would find and clear the browser's cache manually each time they logon to the department's network, but for obvious reasons that would be difficult and error-prone. Plus it wouldn't take effect until they log out and log back in.

Then it occurred to me - How does Google do this? They must have heaps of js files and must be rolling out updates all the time!

The answer is very simple. Each time we deploy a new version of the webapp we simply rename the "scripts" folder that contains all our javascript files. Something like appending the version number of the app to the scripts directory name works well.

This will ensure that each time a new version of the app is deployed, each user's browser will be forced to get the latest version of the scripts, solving the caching problem.

Duh!

Friday, August 27, 2010

Selenium and untrusted SSL Certificates

Our Web App uses SSL and is deployed daily to a development server that has a certificate signed by our internal Certification Authority. This causes some problems with Selenium, as this is essentially an "untrusted" certificate in the eyes of Firefox.

It is easy to get around this, you just answer the prompt that Firefox throws up and tell Firefox to trust this certificate. But this isn't good enough for an automated build, as Selenium can't answer this prompt for you every time.

So to get around this I am running my Selenium tests against a custom Firefox Profile that I have created and checked into subversion. There are instructions how to do this here. Inside this profile I have told Firefox to trust the certificate, and remember that to "permanently store this exception".

I found that the "Permanently store this exception" box was greyed out when I first tried this. This was as a result of previously configuring firefox not to save any history. The solution for this problem is this:

1. Enable History
2. Permanently store the exception (The box is not greyed out now)
3. Disable History

I only needed to do this once - Firefox seems to remember the certificate exception even though I set it to not remember History afterwards.