How to format your project’s Java code with git hooks and finally get rid of annoying IDE specifics

In most of our projects, every developer can use the IDE of their choice which quickly leads to quite some issues with code formatting. Therefore, we needed a solution to automatically format the code before a commit is pushed to the central git repository. For JavaScript we already implemented that a long time ago and the feedback was great. There’s no more discussion about formatting in code reviews (some exceptions if a //doNotFormat comment is used) anymore, it’s not necessary to create checks on CI level etc. We also added some linting that prevents the commit if there’s a violation detected and even the critics started to like this approach :)
Now, it was time to have a look at some non-JavaScript: Some Java using maven…



At first, we looked for a maven plugin that is able to format the files with a standard Eclipse formatter configuration. We decided for spotless for the time being:

            <!-- excluding some auto generated files -->
  • java-formatter-config.xml is an Eclipse formatter configuration and can also be imported into most IDEs.
  • spotless.include is a glob pattern that defines which files should be formatted. Since we only want to format the files touched in a commit, it’s necessary to define these precisely when calling mvn spotless:apply.
  • One downside of spotless is its performance. Currently, it runs for about 3-4 seconds, so we might switch to something else at some point in the future. For the time being, it’s okay.

Calling it as a pre-commit hook

Our key requirement is that it shouldn’t be necessary to set up the git hooks manually for each developer. In the JavaScript parts, we’re using lint-staged with husky to achieve this, which is working fine for us. Thanks to a colleague, I was made aware of frontend-maven-plugin, which is capable of installing node+npm and execute a npm install afterwards. So we added the following pluing to our pom.xml:

         <id>install node and npm</id>
         <id>npm install</id>

So once a user calls mvn generate-source, node+npm will be installed locally into the project. To install lint-staged and husky and configure them, we added a minimal package.json:

  "name": "ClientProject",
  "private": true,
  "version": "1.0.0",
  "description": "ClientProject",
  "scripts": {
    "precommit": "lint-staged"
  "author": "",
  "license": "UNLICENSED",
  "devDependencies": {
    "husky": "0.14.3",
    "lint-staged": "7.1.3"
  "lint-staged": {
    "linters": {
      "*.java": [
        "git add"

This defines that lint-staged is called as a precommit hook, which checks if .java files were changed. If yes, it passes the changed files as a parameter to the as well as the git add afterwards. The workaround through the is required, as the plugin passes the full path of the files as arguments, while spotless requires the files to be formatted as a single comma-separated string relative to the location of the project root.


function join_by {
  local IFS="$1";
  echo "$*";

FILES=$(join_by "," "$@")

mvn spotless:apply -f $PROJECT_DIR/pom.xml -Dspotless.include=${FILES//$PROJECT_DIR/}


In case you don’t want your commit to be formatted, just run git commit with –no-verify, or disable the “run git hooks” in the respective IntelliJ pop-up. But be aware that you might be approached by the other developers ;)


Now, when committing any .java file, it is automatically formatted according to rules in java-formatter-config.xml without the user having to take care of this.

By the way, if you want to be notified for new posts, just sign up with your email address on the right and/or like my Facebook-Page :)

MacOS Context Switches mit ControlPlane

Täglich bewege ich mich mit meinem Laptop zwischen mehreren Orten hin und her. Im einen muss das WiFi an sein, im anderen aus. In manchen brauche ich eine VPN Verbindung, in anderen sollen alle bestehenden VPN-Verbindungen getrennt werden. Außerdem denke ich viel zu selten daran die PGP-Keys zu updaten. Gedruckt werden muss auch manchmal, aber welcher Drucker war es jetzt gerade noch in der Beta und welcher in der Lindenschmit? *nerv*

Beim Kunden und auch in diversen Workshops bei TNG hisse ich immer wieder die Flagge der Automatisierung wiederkehrender Aufgaben und was wäre ich für ein TNGler/Nerd/Consultant, wenn ich obiges "Problem" nicht automatisiert löse? ;)
Nach einigen Jahren verschiedenen Ideen/Tools bin ich nun seit einiger Zeit sehr zufrieden mit einer Kombination aus ControlPlane und “Otto – dem Automator” unterwegs.


Mit Hilfe von ControlPlane erstellt man sehr einfach Regeln+Gewichtung für verschiedene Contexte:

Diese werden in regelmäßigen Abständen überprüft und der Context bei Bedarf gewechselt. In Prozent ist hier angegeben, wie sicher ControlPlane ist, dass man sich imm Context befindet. Das ist v.a. hilfreich, wenn man an den Slidern rumspielt, um verschiedene Contexte besser unterscheiden zu können.

Soweit so nett. Was jetzt allerdings der eigentlich wichtige Teil ist, dass man in ControlPlane definieren kann, was "on arrival" oder "on departure" eines Contexts passieren soll:

Hier kann man auf diverse vorkonfigurierte Actions zurückgreifen, die über das Ausführen von Applikationen oder Shellscripts auch Dinge anbieten wie z.B. Radios aus- und einzuschalten. Leider umfasst das natürlich nicht jede beliebige Operation…

Otto – der Automator

So kommt man schnell zu "Otto – dem Automator". Mit Otto lassen sich nahezu beliebige Aktionen zusammenbauen und als Applikation speichern, die dann wiederum von ControlPlane ausgeführt werden kann. Ein einfaches Beispiel ist z.B. das Ausführen der

So wird jetzt jedes mal ein Keysync getriggert, wenn ich meinen Laptop in der Beta an meinem Schreibtisch anschließe und ich muss nicht mehr daran denken :P

Übrigens, wenn Du über meine neuen Posts benachrichtigt werden möchtest, einfach rechts Deine Emailadresse eintragen und/oder meine Facebook-Seite liken :)

Clean the slaves

In my current project we’re generating jobs for each branch of each project with the help of the Jenkins Job DSL. Branches are created for each feature and bugfix – hotfixes are branched from release branches but these are treated a little differently. Anyway, as you can imagine we tend to have a lot of branches even though we delete them once they’re merged back into the master. Since we also have a lot of different jobs for building/testing/deploying, the amount of jobs explodes quite quickly ;)
Also, to have acceptable job execution times and fast feedback for the developers, we’re using multiple slaves.


When deleting a job, e.g. because the branch is removed, its workspace on the slave is not deleted. This is a little annoying since it’s wasting a lot of space on the slaves’ disks and in the past required some manual effort to clean them up. Luckily, Jenkins provides some nice APIs :)


We created a job which is executing a “system groovy script” (important!) and must be run on the Jenkins master. It simply runs through all slaves, checks if their workspace contains a folder which is orphaned and deletes it. And yes, it is as simple as it sounds. Just a few lines of code which are run after the job generation and all orphaned workspaces are removed :mrgreen:

import hudson.model.*
import hudson.node_monitors.*
import jenkins.model.*

for (node in Jenkins.instance.nodes) {
    computer = node.toComputer()
    if (computer.getChannel() == null) {

    def rootPath = node.getWorkspaceRoot()
    def space = DiskSpaceMonitor.DESCRIPTOR.get(computer)

    println "Checking node ${}..."
    println "Got path ${rootPath} and remaining space ${space}"

    rootPath.list().sort().each { dir ->
        if (!Jenkins.instance.getItem( {
            println "${dir} orphaned. Deleting..."
        } else {
            println "${dir} still in use. Skipping."

Using your MacBook as Jenkins slave and thereby avoid any sleeping

As a requirement in my current project, we have to support IE11 and Safari 9 & 10. Since running Mac OS X virtualized comes with some legal issues, we decided to simply use a MacBookPro (El Capitan) as our Jenkins slave of choice. This works out of the box until the MacBook decides to hibernate, suspend or simply start any energy saving feature Mac OS offers. For normal usage, these are some great features but as a Jenkins slave, they are pretty annoying. So here are some hints you can follow to disable the annoying sleeping features:

At first, simply go to the energy preferences and set them up as shown here:
Energy System Settings

This is not enough though. To disable hibernation, you have to execute the following command:

sudo pmset -a autopoweroff 0

And again, this is not enough, as closing the lid will put the MacBook asleep… Therefore you should install a tool like NoSleep and configure it like this:

…but, Apple was pretty busy trying to avoid any kind of energy consuming background task. Since we want to test within Safari, we have to disable Safari’s background sleeping as well. So you have to run the following command:

sudo defaults write NSAppSleepDisabled -bool YES

Watch out with running this on your own machine though. Safari will drain your battery from now on ;)

From now on, your device will silently run and wait for work :mrgreen:


You can also set this globally:

defaults write NSGlobalDomain NSAppSleepDisabled -bool YES

Also, when using Safari for automated tests, you might want to set the following, to prevent Safari from reopening the latest windows:

defaults write ApplePersistenceIgnoreState YES

By the way, if you want to be notified for new posts, just sign up with your email address on the right :)


Aus Spaß haben wir mal wieder eine neue App gebastelt :mrgreen:

beertastic   beertastic2

Verfügbar für Android und iPhones kannst Du jetzt leicht tracken wieviel Du getrunken hast und weißt es besser als der Kellner! Außerdem bekommst Du je nach Pegel lustige Namenszusätze und siehst, was in Deinem Umkreis los ist! Sehr praktisch ;)
Es sind noch einige Features geplant und werden im Laufe der nächsten Wochen nach und nach kommen. Wünsche & Feedback sehr willkommen!


Übrigens, wenn Du über meine neuen Posts benachrichtigt werden möchtest, einfach rechts Deine Emailadresse eintragen :)

PDFme – first release

Since I didn’t program for Android natively for a while and I needed an app that simply turned a number of pictures into a pdf file, I wrote it myself. If you want to check it out, here’s the play store link: PDFme! Hope you enjoy it ;)


By the way, if you want to be notified for new posts, just sign up with your email address on the right :)

Recover stolen & remotely blocked iPhone

iphonedisabledMy wife’s iPhone 5S was stolen in a subway some weeks ago. As blocked it remotely, the thiefs apparently couldn’t or didn’t want to use it and dropped it. Luckily it was found and brought to Munich’s subway company’s lost & found office. Also, we triggered the remote wiping but as the phone was turned off, this never happened. So she picked it up on Friday and so my journey through the recovery began. After searching the internet for some time I came up with the following plan, which worked out quite well.

Maybe it’ll help someone else as well:

  • IMPORTANT: Remove the (in our case old and blocked by the provider) SIM card from the phone.
  • Start the phone and enter the PIN to unblock it.
  • Instantly put it into the Airplane mode to avoid it from being wiped.
  • Connect it to your computer and make a complete backup. On top, I imported all pictures and videos separately to iPhoto. Just to ensure not losing any of it, even if something else goes wrong.
  • Afterwards I put in the new SIM card, started the phone and connected it to our wireless. Remote wiping started instantly.
  • Now that there was a clean phone, I choose to do a complete reset. Therefore you have to disconnect it from your computer and restart it. While booting, hold down the home button until the iTunes logo appears. Then you connect it to your computer again and trigger the complete reset via iTunes. It’ll take some time.
  • Once that’s done, you can simply restore the backup from iTunes and there it is again. Freshly installed, completely clean and with all your data :)

All together took quite some time. Especially the wiping and recovering part is pretty time consuming. Anyway, seeing my happy smiling wife with her lost phone again was worth digging through this ;)

Keep in mind to do casual backups of your phone though. I know it’s annoying, but once you lost it, you’ll hate yourself for every picture you didn’t back up!

By the way, to get notified about new posts, just enter your email address on the right :)

The 7±2 reasons why gibbon’s five failed

Yes, gibbon’s five failed. Even though I am still convinced that our vision of providing very specific, high-value recommendations for users of what to do in their spare time was a great one, we did a lot of things wrong and did not manage to create enough growth and traction.

We’ve been working on our first product for quite some time and always got great feedback from the existing users. The servers ran fine and we made a lot of connections with local businesses in Munich. Nevertheless, we took our first product offline some weeks ago. It was a really tough step for us and even though we never really talked about it, I know it was pretty emotional for everyone of us. Now, some time later, one of the co-founders published an article about it on his blog. Of course he doubled checked with us ;)

Last but not least. Like almost all startups, we barely ever had technical problems in our way and tech was definitely not part of the problem.

On top I’d like to mention that I learned a lot of things you won’t find in books. You have to experience it yourself. And yes, I am willing to do it again and so are the others. Probably without doing the same mistakes again, but there will be mistakes and we know about that. But we improved our way of dealing with it ;)

By the way, to get notified about new posts, just enter your email address on the right :)

Some remarks on SubGit

I’ve been evaluating SubGit with a colleague for some time now. It seems to be a decent tool if you have to use Subversion and can’t simply switch to Git completely. Nevertheless, we stumbled on a few (pretty annoying) things:

First, SubGit writes changes to the Git tree right into Subversion. As Subversion doesn’t know things like fast-forwards, this results in funny commits with messages like “trunk:$rev replaced by branch:$rev“. That kinda freaked out some colleagues of mine before we understood what’s really going on[1]. A solution for this is the –no-ff flag when merging. You might just want to set it for the whole repository or even globally:

git config --add merge.ff false # add --global if you want to set it globally

Also, if you have a lot of Maven modules and want to ignore things like .classpath, .projects, target etc, this will result in SubGit trying to burn down your CPU to create a lovely .gitignore[2], for us with ~5500 lines. The solution is quite easy. Edit your $subgit_project_dir/subgit/config and add the following:

    ignores = false

This simply tells SubGit to stop translating the Subversion ignores into Git ignores. Now you have to manage the ignores yourself but at least for us it’s worth it ;)

Last but not least: Use the beta of version 3. It’s much more mature than the name suggests and for us it’s also more stable.



By the way, to get notified about new posts, just enter your email address on the right :)

My newest toy :P

After my Nexus 4 kinda passed away, I was looking for a new phone and finally switch my mind about non-Google Android phones. I decided to get a Sony Xperia Z3 compact and for now I’m so happy with this phone, I can’t event remember that I used to have another one! The design is amazing. It’s pretty light. Battery lasts for 3 days (!!) while using it as much as I used my Nexus 4 (lasted ~12h). And it’s so f***ing fast, it’s a pleasure to use it :D

Xperia Z3 compact

Damn it… Google’s spam detection annoys me!

For my fun project gibbon’s five, we’re using Google Apps to manage our emails etc. The other day I sent an email to one of our mailing lists and got a response that confused me at first:

Delivery to the following recipient failed permanently:


Technical details of permanent failure: 
Message rejected by Google Groups. Please visit to review our Bulk Email Senders Guidelines.

----- Original message -----

Received-SPF: softfail ( domain of transitioning does not designate as permitted sender) client-ip=;
       spf=softfail ( domain of transitioning does not designate as permitted sender)

So Google refused to deliver my emails to one of my mailing lists with an spf softfail? Why is this happening now and didn’t happen before even though I probably send 1321323 messages to that list? :(
I still have no clue why it never happened before, but if this is happening to you as well, the solution is quite easy:

You have to add your own mailserver – which I used to send the emails – to the TXT record:

@ IN TXT "v=spf1 ip4: ~all"

If you have multiple mailservers, you can easily add multiple ip4 or ip6 parts ;)

subversion, git, subgit & Windows

On client-side we’re currently working with subversion, which is a (serious) pain. On top we have Windows7 machines. To deal with the first problem, I used git-svn for quite some time and in combination with Sourcetree it worked quite well. When I came back from my xmas break last week, the subversion structure was changed drastically and git-svn didn’t work anymore. It couldn’t clone the new repository and died after hours of trying with weird error messages. As I wanted to try out subgit (“git mirror for svn”) for quite some time, I didn’t really dive into the git-svn problems but gave subgit a go for evaluation :)

First thing to do is quite obvious: Download and install subgit.
Afterwards you have to configure the repository you want to clone:

subgit configure --svn-url $YOUR_SVN_URL $DESTINATION

I also made some tiny changes to the $DESTINATION/subgit/config file. I enabled httpSpooling (httpSpooling = true) to avoid timeouts and added the following lines at the end of the file:


(More about line endings? -> Mind the end of your line)

Next, set your credentials within $DESTINATION/subgit/passwd. Careful: The (unlimited) evaluation version of subgit stores the passwords in plaintext.
Finally, tell subgit to “initialize” the project and start the demon – this might take some time, e.g. for me it took 9h:

subgit install $DESTINATION

Now you’re ready to use whatever git client you like to clone the repository from $DESTINATION and treat it like every other git repos you’re working with. Subgit is dealing with all the annoying svn stuff.
What I still need to test is good the branching and merging “translation” works.

If subgit is running on your dev system, you have to restart the demon by executing the install command again.

A small hint: If you want to use git 2.x on your Windows machine, you won’t find an official download for it yet. The current latest Windows version is 1.9.5. But you can install cygwin with git 2.x and simply add the $CYGWIN_DIR/bin to the Windows $PATH, as Cygwin provides you with a git.exe :P

1 2 3 13