Monthly Archives: September 2011

Zeroing out times in java.util.Date the right way

File this one under The Mob Is Coming For java.util.Date Author Alan Liu With Flaming Torches And Pitchforks (one in a long series).

Folks who have worked with Java’s java.util.Date and java.util.Calendar classes know that when you create a new Date object without any arguments it is initialized with a long representing the number of milliseconds since the epoch.

Sometimes you need a Date that just has its date fields initialized. Here is the only way to do it properly, ensuring that all date fields are set to their defaults, and all time fields are set to their minimums. Anything else runs the risk of missing a time or date field or two, or uses a deprecated constructor, or is not performant, or all three.

final Calendar calendarNow = Calendar.getInstance();
assert calendarNow != null;
calendarNow.set(Calendar.HOUR_OF_DAY, calendar.getMinimum(Calendar.HOUR_OF_DAY));
calendarNow.set(Calendar.HOUR, calendar.getMinimum(Calendar.HOUR)); // for maximum correctness and safety you need to set both (!)
calendarNow.set(Calendar.MINUTE, calendar.getMinimum(Calendar.MINUTE));
calendarNow.set(Calendar.SECOND, calendar.getMinimum(Calendar.SECOND));
calendarNow.set(Calendar.MILLISECOND, calendar.getMinimum(Calendar.MILLISECOND));
calendarNow.set(Calendar.AM_PM, calendar.getMinimum(Calendar.AM_PM)); // this makes it "really correct" for future modifications
final Date now = calendarNow.getTime();
assert now != null;

Getting Jenkins Running On A Mac

I wanted to blog about how to get Jenkins running on a Mac using its installer.

Jenkins is a great product, but its frenetic and crazed (but cheerful and enthusiastic) development process often shows through.

Case in point: the default Mac installer, which you can download from the jenkins-ci.org website, sets up Jenkins to run as a Mac LaunchDaemon running as user daemon.

Now, there's nothing inherently wrong with this–indeed, it can be quite nice.?? You'll only have one instance of Jenkins running, and no user needs to be logged on for it to do its thing, and if Jenkins ever got hacked you're running as a low-privilege user rather than as some kind of full-fledged user with the ability to ruin your day.

However, this caused some weird problems, nullifying the entire intent of a one-click installer.?? These problems manifest themselves the moment you try to run a Maven build, which suggests to me that this (simple) smoke test is simply not run before new versions of the installer are released.?? Oh well, time to roll up our sleeves and turn the one-click installation process into an exercise in Mac system administration.?? 🙂

What's wrong with daemon?

The first thing to know about user daemon is that his home directory is /var/root.?? That should start to give you a funny feeling.

The reason that should give you a funny feeling is that Maven looks for its settings.xml file in $HOME/.m2.?? Which of course does not exist in /var/root.

So when Jenkins launches, it appears to come up fine.?? But if you try to run a Maven build, you'll get a lovely stack trace about how the file /var/root/.m2 couldn't be created.

When I first encountered this error, I just wanted to get the stupid thing working, so I did:


sudo mkdir -p /var/root/.m2

…and:


sudo chmod a+rwx /var/root/.m2

So this gets Jenkins-running-as-daemon past this problem, but now it wants to create temporary files in /Users/Shared/Jenkins/Home, which it doesn't own, and can't write to.

At any rate, I now realized that I didn't want this thing running as user daemon anyway, because I didn't want him doing anything to /var/root.?? And even if I could somehow tell him to use a different user directory so that $HOME/.m2/settings.xml would be resolved somewhere else, it was clear that I was going to have to edit .plist files.?? So, so much for the installer.?? And as long as the installer wasn't going to work, I decided that I wanted to make Jenkins run as a different kind of daemon user anyway.

This turned out (for this rookie Mac system administrator) to be quite difficult.

The steps involved are:

  1. Create a daemon user (I called mine _jenkins)
  2. Create a daemon group (I called mine???surprise!???_jenkins)
  3. Put the daemon user in the newly-created daemon group
  4. Create the home directory for the new daemon user (/Users/_jenkins in my case)
  5. chown the /Users/Shared/Jenkins directory so that its hierarchy is owned by your new user.
  6. edit /Library/LaunchDaemons/org.jenkins-ci.plist so that it reflects all this information.

Creating the user is a task that should not be accomplished through the usual Mac GUI methods.?? You need to use dscl instead.?? This is because you want to create a daemon user.?? I snooped around for a bit and came up with this lovely tutorial: http://www.minecraftwiki.net/wiki/Tutorials/Create_a_Mac_OS_X_startup_daemon#The_hard_.28and_correct.29_way.?? It walked me through steps 1-4 above.

Then I did:


sudo chown -R _jenkins:_jenkins /Users/Shared/Jenkins

Finally, my /Library/LaunchDaemons/org.jenkins-ci.plist looks like this:


<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
?????? <dict>
?????????????? <key>EnvironmentVariables</key>
?????????????? <dict>
?????????????????????????????? <key>JENKINS_HOME</key>
?????????????????????????????? <string>/Users/Shared/Jenkins/Home</string>
?????????????????????????????? <key>_JAVA_OPTIONS</key>
?????????????????????????????? <string>-Dfile.encoding=UTF-8</string>

?????????????? </dict>
?????????????? <key>GroupName</key>
?????????????? <string>_jenkins</string>
?????????????? <key>KeepAlive</key>
?????????????? <true/>
?????????????? <key>Label</key>
?????????????? <string>org.jenkins-ci</string>
?????????????? <key>ProgramArguments</key>
?????????????? <array>
?????????????????????????????? <string>/bin/bash</string>
?????????????????????????????? <string>/Library/Application Support/Jenkins/jenkins-runner.sh</string>
?????????????? </array>
?????????????? <key>RunAtLoad</key>
?????????????? <true/>
?????????????? <key>UserName</key>
?????????????? <string>_jenkins</string>
?????? </dict>
</plist>

I added the _JAVA_OPTIONS environment variable to force UTF-8 encoding.?? This is because no matter what kind of encoding you might specify in your Java code, Java-on-the-Mac's character encoding for what gets put out to the terminal is MacRoman by default (?!).?? You have to get the file.encoding property passed into the JVM early enough so that it is picked up by the rest of the JVM internals, and the only way to do that is to use the special _JAVA_OPTIONS environment variable picked up by all the Java tools in $JAVA_HOME/bin.?? The only unfortunate side effect of all this is that you get a warning printed to the screen on every JVM startup that says, effectively and incomprehensibly, I am using the environment variable you told me to.

Once you've done all this, you can simply stop the launch daemon and it will automatically restart with the new values:


sudo launchctl stop org.jenkins-ci

I hope that helps other Jenkins Mac users out.