April 20, 2015

Packaging up a Java application on OSX with recent versions of Java

So, I've started hacking on a very, very old project of mine that is a Swing-based Java application. I got it running again on OSX but things have changed a lot since I last was doing much coding on it. It used to be that Apple vended Java for OSX, but now Oracle does, and the way that you package up a Java application as an OSX application has changed.

Oracle has some documentation on how to package up an application, but I have to admit that it took me a lot longer than it should have to get this to work. First, Oracle distributes an Ant task for packaging the JAR. I don't use Ant for my project. Actually, I don't remember how I was packaging up the application before. I think I just hand-crafted some directories and dropped an Info.plist file in there that worked.

I was a bit intimidated by that, but it does look like someone has done things by hand. This also looked pretty complicated.

So I ended up installing ant. I had previously installed homebrew on my machine, so that was as simple as "brew install ant". Then I needed to put together a build.xml. I know a bit about that, but not much. I use eclipse for this project, but haven't done anything smart with how it is set up. It just uses the default java builder. I think I even have it set up to put the compiled code in the existing directory structure with the Java files. Not really great. Anyway, I made a simple build.xml that pulls in the libraries I need and some other resources. When I tried to run the resulting application though, I get a failure:

LSOpenURLsWithRole() failed with error -10810

That was not helpful. Running from the command line didn't help, just output that error. Running via java -jar did help though: it couldn't find some classes it needed. Oh, right, I need to set the classpath in the Manifest file. So if you get that error, check to see if your classpath is set correctly. Remember that when you run something via -jar, the -classpath option is completely ignored, and it takes the classpath from the JAR's manifest file. Here is what my final build.xml looked like:

<project name="GMAO" default="bundle-GMAO" basedir=".">        
    <taskdef name="bundleapp"
             classname="com.oracle.appbundler.AppBundlerTask"   
             classpath="../GMAO_libs/appbundler-1.0.jar" />
    <!-- See the lib reference here, this is why you need to use the lib directory! -->

	<path id="build.classpath">
	  <fileset dir="${basedir}">
	     <include name="lib/*.jar"/>
	  </fileset>
	</path>

	<pathconvert property="manifest.classpath" pathsep=" ">
	  <path refid="build.classpath"/>
	  <mapper>
	    <chainedmapper>
	       <flattenmapper/>
	       <globmapper from="*.jar" to="*.jar"/>
	    </chainedmapper>
	  </mapper>
	</pathconvert>
	
	<target name="create-jar" description="Create GMAO Jar">
		<jar destfile="GMAOGUI.jar" basedir="." includes="**/**.class,../common/**/**.class,images/**,docs/**,*html,*xml,*xsl">
			<manifest>
				<attribute name="Main-Class" value="com.FuguTabetai.GMAO.GMAOGUI"/>
				<attribute name="Class-Path" value="${manifest.classpath}"/>
			</manifest>
		</jar>
	</target>
	
    <target name="bundle-GMAO" depends="create-jar">
        <delete dir="appBundle" failonerror="false"/>
        <mkdir dir="appBundle"/>
    	<echo message="JAVA_HOME is set to = ${java.home}" />
        <bundleapp outputdirectory="appBundle"
            name="GMAO"
            displayname="GMAO"
            identifier="com.FuguTabetai.GMAO.GMAOGUI"
            mainclassname="com.FuguTabetai.GMAO.GMAOGUI"
        	icon="images/GMAOGUI.icns">
        	<runtime dir="${java.home}/.."/>
        	<option value="-Dswing.volatileImageBufferEnabled=false"/>
            <!-- The following is important and should point to your build -->
            <classpath file="GMAOGUI.jar" />
            <!-- You can have multiple instance of classpath if you 3rd party or
                 dependent jars in different locations -->
        	<classpath file="lib/TableLayout.jar" />
        	<classpath file="lib/commons-logging-1.2.jar" />
        	<classpath file="lib/helpgui-1.1.jar" />
        	<classpath file="lib/jcommon-0.7.0.jar" />
        	<classpath file="lib/jfreechart-0.9.3.jar" />
			<classpath file="lib/jnlp.jar" />
    		<classpath file="lib/skinlf.jar" />
    		<classpath file="lib/swingfx.jar" />
    		<classpath file="lib/xnap-commons-0.9.5.jar" />
        	<classpath file="lib/xpp3_min-1.1.3.4.0.jar" />
        	<classpath file="lib/xstream-1.2.jar" />
        	<classpath file="lib/gmao_common.jar" />
        </bundleapp>
    </target>
</project>
The Appbundler documentation was useful in adding some additional properties. I know that the build.xml could be better, and I'll probably improve it, but I wanted to note it here because I know in a few years I will want to figure out why I did this.


Comments

Provide your email address when commenting and Gravatar will provide general portable avatars, and if you haven't signed up with them, a cute procedural avatar with their implementation of Shamus Young's Wavatars.

Re: Packaging up a Java application on OSX with recent versions of Java
Of course, not too long after figuring this out there was a discussion on the java-dev@lists.apple.com mailing list about how to package up an application. It looks like there is another way that seems more supported than the appbundler approach. Here is a great summary message from David DeHaven:

Now I have more time for a better answer... :)

I mention it because it's actively developed and supported by Oracle and specifically builds application bundles for MAS. We have online documentation on using it [1] as well as a man page [2]

You'll want to use the latest possible JDK to create app bundles as MAS distribution is a moving target and we have to keep chasing it around, older releases may not work.

The maintainers are on openjfx-dev [3] and are generally responsive unless on vacation or traveling.

-DrD-

[1] https://docs.oracle.com/javase/8/docs/technotes/guides/deploy/self-contained-packaging.html
[2] https://docs.oracle.com/javase/8/docs/technotes/tools/unix/javapackager.html
[3] http://mail.openjdk.java.net/mailman/listinfo/openjfx-dev
Posted 2 years, 6 months ago by FuguTabetai • • wwwReply
Re: Packaging up a Java application on OSX with recent versions of Java
Hi,

I hope you'll do an updated release of gmao. I used it for a few projects in the past (8-10 years ago, I suppose) and I haven't found any software with as good a workflow since, even for purely local stuff. Unfortunately I ran into some quirks with later versions of java (might've been linux specific), so I had to stop using it. I miss it a lot and it would be awesome if a new version were released.

Nice writeup by the way.

Posted 2 years, 5 months ago by Anonymous • • • Reply
Re: Packaging up a Java application on OSX with recent versions of Java
Hi Anonymous. Get in touch with me - fugu at this domain via email, or the email I'm using for this comment. I am actively using GMAO, but on OSX. I should be able to get something working on Linux. What environment are you using? Distro, java version, that kind of thing. I have access to linux machines, so it shouldn't be hard to make things work.
Posted 2 years, 5 months ago by Fugu Tabetai • @wwwReply
Re: Packaging up a Java application on OSX with recent versions of Java
Dear FuguTabetai.com

Nice to talk to you.
My name is Yuta Sugawara.

How are you doing?
I hope you are doing well.

Currently, I live and work in Tokyo.
First, sorry to message you all of a sudden.

I'm here to ask your permission to use your blog content on our Facebook page.

We made a Facebook page for conveying the allure of Japan to those who don't know about Japan.
At this point, we have about 20,000 fans from all over the world on our Facebook page.
Since we are going turn it into a web service with its own site at the end of March, we need native English bloggers to help get people interested.


URL : https://www.facebook.com/TokyoLocalGuide/?ref=ts&fref=ts

I looked at your blog posts about Japan, which are so organized and easy to read with your fantastic description of each location.
Thus, I thought the concept of your blog matches right up with the concept for our Facebook page.
This is why I'm contacting you, to ask permission to share posts from your blog onto our Facebook page. Of course, we'd provide a link back to your original post each time.

Or even better, if you could write posts for your blog on our web service.
It'd be great if you could collaborate with us on making content for the site as a writer.
In the process, we'd be totally open to hearing input from you, like how the design on a blog page should look.

If you're up for it, I'd really like to have you on our team.
Let’s make a wonderful blog platform together.
When it comes to attracting customers,
I’m willing to go the extra mile.

I look forward to hearing from you.

Thanks

Yuta Sugawara
Posted 1 year, 6 months ago by Yuta Sugawara • @ • • Reply

Add Comment

( to reply to a comment, click the reply link next to the comment )

 
Comment Title
 
Your Name:
 
Email Address:
Make Public?
 
Website:
Make Public?
 
verification image
Image verification:
 
Comment:

Allowed XHTML tags : a, b, i, strong, code, acrynom, blockquote, abbr. Linebreaks will be converted automatically.