Wednesday, 1 March 2017

"I know.. instead of typing a forward-slash in a string, I'll declare a static variable for the "/" character"

Was looking at some code someone else wrote and came across:

public static final String SLASH = "/ ";     

FML. Dude. WTF were you even thinking? Why even have a constant? To save us from typing? Instead of one character you now have 5, and then the "+" symbol. What are the odds of making an error on a forward slash?  You're just using it in other static declarations anyway *in the same class*.

Friday, 3 February 2017

Java JPA and Generics and Type Erasure: Casting a List to List

Java Generics are a bundle of compromises and workarounds. Backwards compatibility is a PITA. We all know that. Anyway... 

I had a service class that, due to some refactoring, was now going to be returning a List<ParentClass> whereas before it could return a List<ChildClass>. We had some A/B testing inside the implementation, so in some cases, we would be actually returning a List<ChildClassTwo> from our DAO class.

My original fix for this was to create a new ArrayList and initialise it with the List of ChildClass or ChildClassTwo, but I didn't like the idea of having to create a new object just to change types.

But there's actually a better way, one that does not involve creating an extra wrapper object, that I found on StackOverflow.

To use the classic O-O hierarchy of  Animal and Dog classes:

List<Dog> dogsInLocation = dao.findDogs(location);

List <Animal> animalsInLocation  = (List<Animal>) (List) dogsInLocation;

So basically you are casting the List<Dog>  "up" to a List, and then casting it "down" to a List<Animal>

Trying to do 

 (List<Animal>)  dogsInLocation;

gives you an "Inconvertible types" error.  You can't directly go from a typed list of one type to a type of another. 

The workaround is to use type erasure, turn it into a standard List, which gets rid of the type information about List<Dogs>, and *then* cast it to a List<Animal>

Friday, 20 January 2017

Rebuilding Nexus - what we did wrong and how we fixed it

Nexus is just a searchable cache for all the various jars you either downloaded from the internet or generated from your code-base.

There are 2 main reasons you use a repository manager like Nexus or Artifactory:

* save on network requests, and make your builds faster
* predictability of builds, to serve as the 'source of truth' for all your release artifacts

Now we had a recent problem with Nexus. It got totally f*cked. I'm not privy to all the gory details, but we seem to have lost a few files.

This shouldn't be that big a deal, because we could just reconstruct Nexus from the files we already have in our Maven /.m2 directories -- we could take a new Nexus instance and populate it with the files we have. 

However, during the process we made a couple of errors, and it took us a few days as we discovered the problems day by day, depending on what projects we were rebuilding and what errors we were getting.

Now Nexus seems to have changed the way it works, and was no longer a system that could just be pointed to a file system to do a mass import. It looked like there wasn't actually any way to do bulk imports of an existing file system repository. So we had to do the import by individual files.

Most of the contents of Nexus were jars that came from either the main Maven repository, or other repos like Apache, Spring or Atlassian, so getting those wasn't an issue.

The main thing we had to repopulate were internal jars that we'd released and uploaded into Maven. One of the sysadmins wrote a script to recurse through internal jars from the /.m2/repository/ directory on our build server and then upload them into Nexus. However there were some bugs in the script, that resulted in people getting broken repositories.

The things we did wrong were:

1. Uploading jars into Nexus without specifying the pom file. By default, Nexus created a default pom file, but this only contained basic information like groupId, artifactId and version. No dependencies. 

Result: Transitive dependencies couldn't get resolved during builds in Maven, so compilation broke.

How We Found The Issue: Looking at the artifact directory in our local repo, and looking at the POM file. 

Fix: Update the command to deploy the file to specify both the Jar and Pom file.

2. Assuming that a .jar file was a java Jar. This was incorrect, because maven builds can also produce JAR containing source or javadoc. This resulted in the source or javadoc jar being mistakenly uploaded. 

Result: Builds broke because class files couldn't be found.

How We Found The Issue: Looking at the .jar file in local repo, opening it, and inspecting contents. No .class files - wrong jar upload

Fix: Update the command to exclude *-source.jar and *-javadoc.jar from deploys.

Thursday, 25 August 2016

Unit tests with mockito - how to verify that a parameter to a mock method call has an expected value

Use ArgumentCaptor, then in your verify() call for the method call you're setting up, use the argumentCaptor instead of the usual .class you'll be passing.

Then in your assert() to check the value, see if the argumentCaptor.getValue().get<field> has the value you're expecting

Basic idea is that you use ArgumentCaptor as the proxy to intercept the value the mock is getting.

Wednesday, 27 January 2016

JSTL transforms null BigDecimal to 0 and null String to ""

Was scratching my head why I was getting zero for a BigDecimal that I was returning as null, then realised I'd forgotten about JSTL behaviour that cleans up nulls to nicer values.

So anything extending type Numeric that is null gets shown as 0.

And anything that is a null String becomes a blank String.

As mentioned here:

After a bit of trial and error I discovered that the JSP tab library framework does a little bit of magic on known classes, such as Strings and Numericobjects. Classes of Numeric types that are null get converted to 0 andStrings get converted into the empty string ""

I discovered that if you change your internal value to anjava.lang.Object instead of a java.math.BigDecimal the magic doesn't know what to do and just passes null to your class.


Friday, 11 December 2015

Problem and solution: "Content not allowed in prolog" when loading XML as string, even without extra characters

I was parsing XML files in a unit test, but was doing it incorrectly.

I was loading them from classpath, then wanted to have them in a string and compare them using XMLUnit.


InputStream devStream = getClass().getClassLoader().getResourceAsStream("api-response--dev.xml");

String devXml = devStream.toString();

I was getting "Content not allowed in prolog" when I tried to parse this as XML, even though there was no BOM or extra characters in the source XML file.

Turns out I was doing it incorrectly. Because if you call toString(), you end up using the default encoding of your platform. 


InputStream devStream = getClass().getClassLoader().getResourceAsStream("api-response--dev.xml");

String devXml = CharStreams.toString(new InputStreamReader(devStream, "UTF-8"));

Thursday, 19 November 2015

How to fix "Unlink of file '.git/objects/pack/pack-(longhash)' failed. Should I try again? (y/n)

possible tags: Git GC, git cleanup repository, 

Every now and then while using Git, like say when you do a "git pull" on a repository which regularly gets a lot of changes, you will get a message from Git like this:

Auto packing the repository for optimum performance. You may also
run "git gc" manually. See "git help gc" for more information.
Counting objects: 499419, done.
Delta compression using up to 24 threads.
Compressing objects: 100% (164548/164548), done.
Writing objects: 100% (499419/499419), done.
Total 499419 (delta 238160), reused 493660 (delta 233258)

And then you might see this:

Unlink of file '.git/objects/pack/pack-061698b76ddcbea46c9d31874b73c9a66d87f790.pack' failed. Should I try again? (y/n)
Unlink of file '.git/objects/pack/pack-061698b76ddcbea46c9d31874b73c9a66d87f790.idx' failed. Should I try again? (y/n)

You'll find that if you press "n", you'll find yourself pressing "n" a *lot* of times... this is pretty much the point where you should just go:


and then just do a 

$ git gc

to manually pack your repository.

$ git gc
Counting objects: 499419, done.
Delta compression using up to 24 threads.
Compressing objects: 100% (159646/159646), done.
Writing objects: 100% (499419/499419), done.
Total 499419 (delta 238160), reused 499419 (delta 238160)
Removing duplicate objects: 100% (256/256), done.
Checking connectivity: 499419, done.