Naming Maven repositories

Finding the root cause of a Maven build issue made me question my sanity, as seemingly everything I knew about Maven seemed to be wrong. Luckily, in the end logic was victorious, and quite a nasty bug was found.

Motto:

Caches are bugs waiting to happen. Rob Pike

The last two days I was hunting an issue in the Maven/Tycho based build of VIATRA: some artifacts from Maven Central were not found anymore, seemingly without any related change. A more detailed analysis has shown that the issue is specific to the build server at the Eclipse Foundation: in all other cases the builds run successfully.

When looking at the debug output of the build (the -X switch for mvn is truly a killer feature…) it was interesting that the build did not try to download the dependency from Maven Central nor from the mirror set up for builds at the Hudson instance at eclipse. Given that the first twenty-two modules compiled (with dependencies to Maven Central) this made no sense.

After managing the sanity loss and calling for help (thanks Ábel and Balázs) we have managed to identify that the settings.xml used on Hudson caused the problem. After calling for more help (thanks for the quick help from webmaster) I have finally managed to find out the root cause of the issue: repository identifier clash caused by the maven central mirror and a dependency issue.

For performance and network utilization reasons a mirror for maven central was set up about a month ago using the following fragment:

<mirrors>
    <mirror>
      <id>repo.eclipse.org</id>
      <name>Eclipse Central Proxy</name>
      <url>https://repo.eclipse.org/content/repositories/maven_central/</url>
      <mirrorOf>central</mirrorOf>
    </mirror>
</mirrors>

Our problematic bundle had a repository declaration as follows:

<repositories>
    <repository>
        <id>repo.eclipse.org</id>
        <url>https://repo.eclipse.org/content/groups/releases/</url>
    </repository>
</repositories>

What happened here is that we declared some repositories with the same identifier as the proxy repository declared by webmaster, and Maven got confused, and thought that everything from Central is reachable from our declared dependency repository. Changing the identifier to avoid the name clash solved this issue nicely.

Now only one question remains: why was this issue not found earlier. The answer is trivial: the plugins were available in the local repository, effectively hiding the unresolvable dependencies. A completely unrelated cleanup of the local repository however has brought forth this issue, causing much headache to find.


The main lesson we learned here: DO NOT reuse the same repository identifier for multiple repositories. It can cause very subtle, hard to debug issues in the long run. However, there are a few other aspects you have to keep in mind:

  • All repositories and plugin repositories defined in your parent project are also inherited, so their name should also be unique.
  • Identifier clash might not be a problem in case of deployment repositories, but have not tested this aspect yet.
  • A more specific lesson for Eclipse projects building on Foundation Servers: DO NOT use the identifier “repo.eclipse.org” for your repositories. Maybe it is worth checking whether the identifier is used (e.g. use following the github search link to check projects developed or mirrored to the Eclipse Github organization), and update accordingly. Update: the mirror repository id was updated to “eclipse.maven.central.mirror” that should not clash with manual repository names.

Language Features Enhancing Trust

Alex just facepalmed.

if ( Boolean.TRUE.equals(employee.isHappy) ) {

“Wow, the human invention never ceases to come up with new ways to adorn boolean expressions! Too bad Eclipse doesn’t have a Quick Assist for simplifying them…”
Ah, one of those WWTC moments. That’s when the Show Annotation feature of Subversive comes in handy… which revealed that Bob is the author (unless he only adjusted the whitespaces in that line). “But he already left the office for today. Now, I’ll just simplify it and similar occurrences manually and tomorrow, I’m going to considerately mention to him that he could have written this condition umm… more concisely.” Alex pondered a bit over the commit message, but practiced self-restraint and wrote simply

Simplify boolean expressions...

(though he couldn’t help omitting those ellipses).
Next day, some of the tests of Bob’s module were failing.
– Hi Bob, could we have a look at your code? First, there are some red tests, and…
– Hmm, but I haven’t modified that module since yesterday. Let’s see the exception!
– Okay…

Exception in thread "main" java.lang.NullPointerException
at EmployeeLoader.loadEmployee(EmployeeLoader.java:41)

“Hmm, exactly the line I modified. But…” – thought Alex.
– …how could a simple condition without a single dereferencing cause a NPE?! – asked Bob, confused just like Alex.
– Uh-oh, I think I have a suspicion… Please show the declaration of isHappy

private Boolean isHappy;

Alex facepalmed once again, but this time the cause was himself.
– NOOO! The dreaded autoboxing! But again, why isn’t it a primitive boolean?
– Hey, now I remember! Employees are parsed from XML using an autogenerated schema. The attribute isHappy is optional, so it might very well be null.
– Sorry, Bob. I feel silly for acting without asking you in advance or running the tests before committing. See, I couldn’t have imagined how this kind of change could break.
– Take it easy, Alex. 🙂 Now you can. In fact, I thought Boolean.TRUE would refer to the attribute’s type being non-primitive unambigously, and the Yoda condition would evoke the possibility of the null value immediately.
– I understand you, but to avoid such misunderstandings in the future, would you mind writing

if ( (employee.isHappy == null) && employee.isHappy ) {

to make it totally explicit that isHappy can be null? Or rather set an optional false value for this attribute in the schema?
– OK, I’ll consider.
Fortunately, Bob took the incident very lightly and his commit message was just:

Revert Alex's "simplifications" :)

The first thing Alex did was to set a warning for boxing and unboxing among the Java compiler settings. As he was accepting the changes, he thought: “Null and primitive types as well are billion dollar mistakes coming from arbitrary language design decisions which reflect implementation details. But at least they tought me to trust my fellow’s code – or at least to inspect the types before refactoring.”

Lesson of the day: Beware the indirect dependencies in Eclipse 4.2

Executive summary: If you cannot move or minimize your views in Eclipse 4.2, always check whether the org.eclipse.e4.ui.workbench.addons.swt bundle is present in your run configuration.

A view misses its minimize icon

Today Eclipse surprised me with a nice feature. I tried to reconfigure my runtime workbench to create some screenshots, but I couldn’t. The views were missing the minimize button, and dragging view parts between groups did not work.

I had some minor issues with the new workbench model, so first I tried to check everything is at its base setting. I reset the perspective, closed all perspectives, restarted the workbench, but nothing helped. Finally, I noticed a short error message in the Error log:

Unable to retrieve the bundle from the URI:
bundleclass://org.eclipse.e4.ui.workbench.addons.swt/org.eclipse.e4.ui.workbench.addons.minmax.MinMaxAddon

That provided the required hint: to conserve memory, I usually start a runtime Eclipse with a reduced set of plug-ins – possibly a dependency is missing. Looking at the uri from the error message, I found, that the org.eclipse.e4.ui.workbench.addons.swt plug-in was not added to the Run configuration. I checked the corresponding box, and voilá – everything worked as expected.

What makes this case more interesting is that the built-in dependency validation report no issues. I had my own debugging sessions because of missing dependencies, so I set all my Run configurations to validate every dependency at startup. This means, this plug-in is not needed for executing the platform (technically true), however, when it is missing, it strongly reduces the usability.

Alltogether, what I am missing is some kind of higher-level validation that could report such issues. But in the meantime, I publish this, hoping, someone else finds it useful.

Java primitive type comparison – A wat look

Not long ago, we had a not too nice issue related to mismatch between int and long variables. As the concrete variables were coming from 1) a model created by others and 2) another model created by us, the solution did not appear to be simple.

We were thinking, if we could compare the variables by value consistently without the concrete type equality, we would not need to support both int and long numbers in our model, so we asked our collegue, Gábor Bergmann to experiment a bit with the comparison.

Sadly, the result is that there is no reasonable way to compare longs and integers by value. However, we found a truly nice wat moment, almost fitting to the issues presented by Gary Bernhardt in a talk called WAT at CodeMash 2012 (or instead of the video, you could have a look at http://www.shopify.com/technology/5370262-wat-a-funny-look-at-ruby-and-javascript-oddities).

Because of Java is statically typed, most inconsistencies of Javascript do not apply here. However, the implicit contracts of Java are sometimes broken, just as the following code snippet shows (it is Gábor’s work entirely, I only changed only the white spaces):

Basically, when comparing primitive types and their boxed values, if you have two different types, all hell’s might break loose. Sometimes two values are considered equal when using ‘==’ but not when using Object.equals. Even worse, transitivity can also be broken.

I cannot think about any possible reason for this behavior, but we could reproduce it issue consistently across different computers using Java 1.6, so this appears to be according to be designed that way.

If someone could provide some reasonable explanation, I would be glad for it. Then we could learn something nice instead of just laughing a bit. Otherwise, be careful with primitive comparison…