OSX: Packaging Java .jar in an .app bundle!

Posted on October 31, 2016

No stub/launcher required!

All you need to do this is a jar file, and an installed Oracle JDK!

javapackager is cool

The magic is actually an Oracle JDK provided binary, “javapackager.” This file does what we want, and is capable of much more. Check the manpage! It is not OSX specific.

Do note that this file does not end up in your $PATH, you will need to call it directly. You can find it on-disk as such: /Library/Java/JavaVirtualMachines/jdk{version}/Contents/Home/ in the bin subdirectory, and the manpage in the man/man1 subdirectory. Do check the manpage, there’s lots of goodies.

working example

I will be packaging the DoD Software Protection Initiative’s public Encryption Wizard, which is distributed as a jar application. This is usable, but running jars on OSX is annoying - wouldn’t it be better to have a standard .app bundle? Yes, yes it would.

assumptions

I assume you have Xcode and Oracle JDK 8 installed. Xcode may not actually be required, so I’d suggest you try without installing it first, if you don’t already have it. I also extracted the splash screen PNG from the jar file and built a retina-compatible icns, but this is optional - you’ll get a generic java-themed icon if you don’t supply an alternative. I’ll cover creating one from scratch at the end of this post.

I also assume you realize that an .app “file” is really just a directory with a filesystem attribute set, and a standardized contents.

let’s do the thing already!

First, create a scratch directory. Copy your jar file (and any other items that it’ll need next to it) into a scratch dir, and open up a terminal. Change into the scratch directory. If you’re using a custom icon, put this there as well. Now, here’s the example invocation:

/Library/Java/JavaVirtualMachines/jdk1.8.0_112.jdk/Contents/Home/bin/javapackager \
  -deploy \
  -Bicon=TENS-EW.icns \
  -native image \
  -srcfiles EW-Public-3.4.11.jar \
  -appclass afrlew.CheckLoad \
  -name TENS-EW \
  -outfile TENS-EW \
  -outdir output \
  -v

Argument explanation:

  • -deploy: “Assembles the application package for redistribution.”
  • -Bicon: Hopefully obviously, the relative (or full) path and filename to the icon. In this invocation (which provides a .app) you must supply an icns file or nothing at all. If you omit this you’ll get a default icon. This is an argument for the -deploy command.
  • -native: “Generate self-contained application bundles (if possible).” … “If [a bundle type] is specified, then only a bundle of this type is created. If no type is specified, all is used.” - ‘image’ on OSX means .app bundle. Other options include things like a .dmg disk image, .pkg installer, .rpm or .deb package, and so on. (yes… it’ll create those too!)
  • -srcfiles EW-Public-3.4.11.jar: Pretty obvious. “List of files in the directory specified by the -srcdir option. If omitted, all files in the directory (which is a mandatory argument in this case) will be used. Files in the list must be separated by spaces.” - Note that I’m not specifying a -srcdir - you could totally put everything in there as necessary and then pass that, instead… but for a single-file build like this, K.I.S.S.
  • -appclass afrlew.CheckLoad: This is an important one. This is the main class, the class that Java starts in. This might not be required if the jar has it in it’s metadata (eg, if you can do just java -jar thing.jar and it works) but it certainly doesn’t hurt to be explicit. If you don’t know it, and need it… a jar is just a zip file. Poke around in the META-INF subdirectory inside of it and you’ll find it…
  • -name TENS-EW: Application name. Yes. What did you think this did?
  • -outfile TENS-EW: This is the filename that should be generated, without the extension.
  • -outdir output: Specify this and output files will be placed in the directory you specify.
  • -v: As you might guess, this enables verbose output.

Note: unless you make use of Xcode you probably don’t have a keypair or developer ID, and you’ll see Did not find a key matching 'Developer ID Application in your output. Do not panic - you can still execute the resulting output if you have Gatekeeper disabled. (this is code signing stuff). Note: if you are building a dmg instead of an .app, and you have Gatekeeper enabled locally, you have to sort this out. Else, disable Gatekeeper and deal with unsigned stuffs.

I made changes and they didn’t take effect!

OSX does some serious caching. If you make changes that effectively replace a .app, you need to either jump through hoops with finder, or just re-register the .app in the Launch Services database. I’m not sure if this takes just the filename, or a relative path, so I just run it with the .app in the current working directory. Here’s the command to do this:

/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister -f MyApp.app

I want an icon!

You can use this online tool to convert an image to an icns, but this won’t be retina-compatible out of the box. The ‘best’ way to do this is to do it manually (rather, if you were building the project via xcode it will do this for you, but since you have a jar already, you aren’t doing that). You’ll need Xcode installed for this. Find a suitable graphic - ideally something with a nice transparency gradient. Ideally you want a source image resolution of 1024x1024 or higher. Create the following PNG files, and place them in a directory with .iconset at the end of the name (yes, a directory with a file extension…)

  • icon_16x16.png (16x16 resolution)
  • icon_16x16@2x.png (32x32 resolution)
  • icon_32x32.png (32x32 resolution)
  • icon_32x32@2x.png (64x64 resolution)
  • icon_64x64.png (64x64 resolution)
  • icon_64x64@2x.png (128x128 resolution)
  • icon_128x128.png (128x128 resolution)
  • icon_128x128@2x.png (256x256 resolution)
  • icon_256x256.png (256x256 resolution)
  • icon_256x256@2x.png (512x512 resolution)
  • icon_512x512.png (512x512 resolution)
  • icon_512x512@2x.png (1024x1024 resolution)

You may not need all of these resolutions. The @2x variants are used for retina (high-dpi) display. Yes, this is redundant - I’m not sure why it can’t just use the ‘real’ resolution file, but this does work.

Now that you have a directory ending in .iconset with the PNG files (naming is critical! don’t flub it!) pass it through iconutil to get an icns file: iconutil -c icns WhateverYouCalledIt.iconset

Disclaimer/Copyright

The information, views, and opinions published on this website were done so in the author's personal capacity. The information, views, and opinions expressed in this article are the author's own and do not reflect the view of their employer, or any other entity unless explicitly stated otherwise.

All data and information provided on this site is for informational purposes only. This website and it's operators makes no representations as to accuracy, completeness, currentness, suitability, or validity of any information on this site and will not be liable for any errors, omissions, or delays in this information or any losses, injuries, or damages arising from its display or use. All information is provided on an as-is basis.

All original content on this website is, unless explicitly stated otherwise, licensed under the MIT license. Full license text is available here. Non-original content that is included on this website in whole or in part, linked, or otherwise made available remains under copyright of the original owners.