CrashPlan on Mavericks with Java 1.7

Last month Apple released OS X Mavericks (10.9) and again removed Java 1.6 from your system during the upgrade. Since Oracle stopped support for Java 1.6 I think this is a good thing, and I want to avoid installing Java 1.6 for any application. At first CrashPlan seemed to work with Java 1.7 just fine, Crashplan’s menubar icon told me it was backing up and I didn’t get any alerts from Crashplan about my backups either. However today I noticed that CrashPlan will not start the client. When I start the CrashPlan client I’m greeted by a dialog that tells me that I need Java 1.6. When I looked at the CrashPlan support site it told me to install Java 1.6, what a shame! CrashPlan does offer an alternative, but it comes with a warning that it might cause problems with other applications and involves changing the application settings for Java 1.7. Since these settings are stored in a Info.plist file within the Java installation directory I would have to remember to set this each time I install a Java update. All in all not a good proposition, so armed with the knowledge that the application should work with Java 1.7 I started to look for a way to start it.

Piecing it together

The first clues for figuring out how to start the application come from the file /Applications/CrashPlan.app/Contents/Info.plist. This file holds information that is used by the system to determine how to start an application. For a java application like CrashPlan it tells about specific Java options and arguments to the Java Virtual Machine. The part that was of particular interest was the following:

<key>Java</key>
<dict>
	<key>ClassPath</key>
	<array>
		<string>$JAVAROOT/lib/com.backup42.desktop.jar</string>
		<string>$JAVAROOT/skin</string>
		<string>$JAVAROOT/lang</string>
	</array>
	<key>JVMVersion</key>
	<string>1.5*</string>
	<key>JVMArches</key>
	<array>
		<string>i386</string>
		<string>ppc</string>
	</array>      
	<key>MainClass</key>
	<string>com.backup42.desktop.CPDesktopWrapper</string>
	<key>StartOnMainThread</key>
	<true/>
	<key>VMOptions</key>
	<array>
		<string>-Dfile.encoding=UTF-8</string>
		<string>-DappBaseName=CrashPlan</string>
		<string>-Dsun.net.inetaddr.ttl=300</string>
		<string>-Dnetworkaddress.cache.ttl=300</string>
		<string>-Dsun.net.inetaddr.negative.ttl=0</string>
		<string>-Dnetworkaddress.cache.negative.ttl=0</string>
		<string>-Djava.net.preferIPv4Stack=true</string>                        
	</array>  
	<key>WorkingDirectory</key>
	<string>$APP_PACKAGE/Contents/Resources/Java</string>
</dict>

In this block some crucial information is set. The name of the main class that has to be invoked (MainClass key), the class path used by the application (ClassPath key), options to the JVM (VMOptions key) and the working directory of the application (WorkingDirectory key).

Exceptions…

With this information I created a first version of a shell script to start the application. However it quickly failed with an Exception:


[11.06.13 22:12:15.057 ERROR   main                 root                                    ] Failed to launch CPDesktop; java.lang.NoClassDefFoundError: org/eclipse/swt/widgets/Display
Exception in thread "main" java.lang.NoClassDefFoundError: org/eclipse/swt/widgets/Display
	at com.backup42.desktop.CPDesktop.<init>(CPDesktop.java:266)
	at com.backup42.desktop.CPDesktop.main(CPDesktop.java:200)
	at com.backup42.desktop.CPDesktopWrapper.main(CPDesktopWrapper.java:23)
Caused by: java.lang.ClassNotFoundException: org.eclipse.swt.widgets.Display
	at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
	... 3 more

So the application was not finding all the classes. An ls on the lib directory gave a list of additional jar files.

darwin:Java enckevort$ ls -l lib/
total 40568
-rw-r--r--  1 root       staff  1290808 18 apr  2013 c42_protolib.jar
-rw-r--r--  1 root       staff  5716574 18 apr  2013 com.backup42.desktop.jar
-rw-r--r--  1 root       staff   215243 18 apr  2013 com.jniwrapper.jniwrap.jar
-rw-r--r--  1 root       staff  1285054 18 apr  2013 com.jniwrapper.macpack.jar
-rw-r--r--  1 root       staff   706581 18 apr  2013 com.jniwrapper.winpack.jar
-rw-rw-r--  1 enckevort  staff   268794 16 mrt  2011 commons-jxpath-1.1.jar
-rw-r--r--  1 enckevort  staff   946973 16 mrt  2011 jna-3.2.5.jar
-rw-r--r--  1 enckevort  staff    41829 16 mrt  2011 json-20070829.jar
-rw-r--r--  1 root       staff   159123 27 mrt  2012 json-lib-2.4.jar
-rw-rw-r--  1 enckevort  staff    41291 16 mrt  2011 jtux.jar
-rw-r--r--  1 root       staff   481534 27 mrt  2012 log4j-1.2.16.jar
-rw-rw-r--  1 enckevort  staff    69028 16 mrt  2011 miglayout15-swt.jar
-rw-r--r--  1 root       staff   108731 18 apr  2013 org.eclipse.core.commands_3.6.1.v20120814-150512.jar
-rw-r--r--  1 root       staff   106763 18 apr  2013 org.eclipse.equinox.common_3.6.100.v20120522-1841.jar
-rw-r--r--  1 root       staff  1090959 18 apr  2013 org.eclipse.jface_3.8.101.v20120817-083647.jar
-rw-r--r--  1 root       staff  1396459 18 apr  2013 org.eclipse.osgi_3.8.1.v20120830-144521.jar
-rw-r--r--  1 root       staff   450279 27 mrt  2012 protobuf-java-2.4.1.jar
-rw-rw-r--  1 enckevort  staff    81591 16 mrt  2011 sbbi-upnplib-1.0.4.jar
-rw-r--r--  1 root       staff    25496 18 apr  2013 slf4j-api-1.6.1.jar
-rw-r--r--  1 root       staff     9753 18 apr  2013 slf4j-log4j12-1.6.1.jar
-rw-r--r--  1 root       staff  1664400 18 apr  2013 swt-64.jar
-rw-r--r--  1 root       staff  1760744 18 apr  2013 swt.jar
-rw-r--r--  1 root       staff  2522902 27 mrt  2012 trove-3.0.2.jar
-rw-r--r--  1 root       staff   284059 18 apr  2013 twitter4j.jar

So my second attempt was to just add all those jar files to the classpath. Unfortunately, and slightly surprisingly that didn’t work! I got a new exception:

[11.06.13 22:15:31.797 ERROR   main                 com.backup42.desktop.CPDesktop          ] Failed to launch CPDesktop; java.lang.UnsatisfiedLinkError: Cannot load 32-bit SWT libraries on 64-bit JVM, java.lang.UnsatisfiedLinkError: Cannot load 32-bit SWT libraries on 64-bit JVM
java.lang.UnsatisfiedLinkError: Cannot load 32-bit SWT libraries on 64-bit JVM
	at org.eclipse.swt.internal.Library.loadLibrary(Unknown Source)
	at org.eclipse.swt.internal.Library.loadLibrary(Unknown Source)
	at org.eclipse.swt.internal.C.<clinit>(Unknown Source)
	at org.eclipse.swt.widgets.Display.<clinit>(Unknown Source)
	at com.backup42.desktop.CPDesktop.<init>(CPDesktop.java:266)
	at com.backup42.desktop.CPDesktop.main(CPDesktop.java:200)
	at com.backup42.desktop.CPDesktopWrapper.main(CPDesktopWrapper.java:23)

Okay, so it is trying to load the wrong libraries… what’s going on?

I decided to check if the application had a manifest file. This is a file that can be present in a jar file and describes the additional jar files to add to the classpath when trying to load a class. The file has a fixed location and name: META-INF/MANIFEST.MF. So I checked for the presence with the following command:

unzip -l lib/com.backup42.desktop.jar META-INF/MANIFEST.MF

I was in luck, the jar file did indeed come with a manifest file. I extracted it and got the following file:

Manifest-Version: 1.0
Ant-Version: Apache Ant 1.8.2
Created-By: 1.6.0_31-b04-415-11M3646 (Apple Inc.)
Built-By: Code 42 Software
Class-Path: c42_protolib.jar com.jniwrapper.jniwrap.jar com.jniwrapper
 .macpack.jar com.jniwrapper.winpack.jar comfyj-2.10.jar commons-colle
 ctions-3.2.1-mini.jar commons-jxpath-1.1.jar jna-3.2.5.jar json-20070
 829.jar json-lib-2.4.jar jtux.jar log4j-1.2.16.jar miglayout15-swt.ja
 r org.eclipse.core.commands_3.6.1.v20120814-150512.jar org.eclipse.eq
 uinox.common_3.6.100.v20120522-1841.jar org.eclipse.jface_3.8.101.v20
 120817-083647.jar org.eclipse.osgi_3.8.1.v20120830-144521.jar protobu
 f-java-2.4.1.jar rhino-1.7r3.jar sbbi-upnplib-1.0.4.jar slf4j-api-1.6
 .1.jar slf4j-log4j12-1.6.1.jar trove-3.0.2.jar twitter4j.jar

When I created the classpath based on this list I got again one step further. However I still hadn’t solved the puzzle, because I got another Exception:

***WARNING: Display must be created on main thread due to Cocoa restrictions.
[11.06.13 22:27:46.058 ERROR   main                 root                                    ] Failed to launch CPDesktop; org.eclipse.swt.SWTException: Invalid thread access
Exception in thread "main" org.eclipse.swt.SWTException: Invalid thread access
	at org.eclipse.swt.SWT.error(Unknown Source)
	at org.eclipse.swt.SWT.error(Unknown Source)
	at org.eclipse.swt.SWT.error(Unknown Source)
	at org.eclipse.swt.widgets.Display.error(Unknown Source)
	at org.eclipse.swt.widgets.Display.createDisplay(Unknown Source)
	at org.eclipse.swt.widgets.Display.create(Unknown Source)
	at org.eclipse.swt.graphics.Device.<init>(Unknown Source)
	at org.eclipse.swt.widgets.Display.<init>(Unknown Source)
	at org.eclipse.swt.widgets.Display.<init>(Unknown Source)
	at org.eclipse.swt.widgets.Display.getDefault(Unknown Source)
	at com.backup42.desktop.CPDesktop.<init>(CPDesktop.java:354)
	at com.backup42.desktop.CPDesktop.main(CPDesktop.java:200)
	at com.backup42.desktop.CPDesktopWrapper.main(CPDesktopWrapper.java:23)

I was pretty convinced that I had the options right, and the warning message told me I was looking at a OS X specific issue, so I did a quick internet search for the message “***WARNING: Display must be created on main thread due to Cocoa restrictions.”. Quickly I found out that I was missing one OS X specific setting: -XstartOnFirstThread. When I added it my last exception was gone and I could start the CrashPlan client!

The script

The final version of the shell script lives in my /usr/local/bin and looks like this:

#!/bin/bash -e
APP_PACKAGE='/Applications/CrashPlan.app'

JAVAROOT="${APP_PACKAGE}/Contents/Resources/Java"
cd "${JAVAROOT}"

CLASSPATH="${JAVAROOT}/skin:${JAVAROOT}/lang"
JARS="com.backup42.desktop.jar c42_protolib.jar com.jniwrapper.jniwrap.jar com.jniwrapper.macpack.jar com.jniwrapper.winpack.jar comfyj-2.10.jar commons-collections-3.2.1-mini.jar commons-jxpath-1.1.jar jna-3.2.5.jar json-20070829.jar json-lib-2.4.jar jtux.jar log4j-1.2.16.jar miglayout15-swt.jar org.eclipse.core.commands_3.6.1.v20120814-150512.jar org.eclipse.equinox.common_3.6.100.v20120522-1841.jar org.eclipse.jface_3.8.101.v20120817-083647.jar org.eclipse.osgi_3.8.1.v20120830-144521.jar protobuf-java-2.4.1.jar rhino-1.7r3.jar sbbi-upnplib-1.0.4.jar slf4j-api-1.6.1.jar slf4j-log4j12-1.6.1.jar trove-3.0.2.jar twitter4j.jar"

for JAR in ${JARS}
do
  CLASSPATH="${JAVAROOT}/lib/${JAR}:${CLASSPATH}"
done

java -XstartOnFirstThread -Dfile.encoding=UTF-8 -DappBaseName=CrashPlan -Dsun.net.inetaddr.ttl=300 -Dnetworkaddress.cache.ttl=300 -Dsun.net.inetaddr.negative.ttl=0 -Dnetworkaddress.cache.negative.ttl=0 -Djava.net.preferIPv4Stack=true  -cp "${CLASSPATH}" com.backup42.desktop.CPDesktopWrapper

Conclusion

I can now start the CrashPlan client with the command crashplan in Terminal. Trying to start CrashPlan from the menubar icon or from /Applications still gives the same message about needing Java 1.6. This script shows however that there is no reason why CrashPlan shouldn’t support Java 1.7 and I hope it will help convince Code 42 to resolve this issue. It is already three years ago that Apple announced that it would phase out support for its Java Virtual Machine, so it is really a shame that Code 42 hash’t made an effort to support Java 1.7.

One thought on “CrashPlan on Mavericks with Java 1.7”

  1. Thanks for this! I had no other (obvious) way to exclude my WWDC2014 videos from backing up 😉

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.