0

I am trying to bundle my Java application to an Mac OS X .app bundle. I am currently using appbundler and I followed Oracle's guide. The Ant task works fine in Netbeans and the corresponding .app package is built correctly (on Windows). But when trying to execute the .app package to my MacBookPro (OS X 10.7.5), I get the following error in the log:

[0x0-0x72072].ch.lawsuite.core.Servitus: Error: Unable to access jarfile LawSuiteSE.jar

I already checked the permissions for the .jar file and the execution bits are set for the main-jar and all the jars in the lib-folder.

The application just runs fine when typing the following to the terminal:

java -jar /Applications/Servitus.app/Contents/Java/LawSuiteSE.jar --installer

For clarification, here's the final folder structure:

Servitus.app
    |_Contents
        |_Java
            |_LawSuiteSE.jar
            |_lib
                |_lib1.jar
                |_lib2.jar
                    |_subfolder1
                        |_lib3.jar
                    |_subfolder2
                        |_lib4.jar
                |_etc...
            |_MacOS
                |_JavaAppLauncher
            |_PlugIns
            |_Resources
                |_en.lproj
                    |_Localizable.strings
                |_kplus.icns
        |_Info.plist
        |_PkgInfo

By the way, my application has lots of external dependencies, all located in the "lib" folder which are properly linked by the class-path property within the MANIFEST.MF file of the executable main-jar file "LawSuiteSE.jar". Do I need to copy these libs within the appbundle-ant-task or is it okay to copy them subsequently by hand?

I also tried to build the application and the corresponding .app package on the MacBook Pro, but the same error message.

Here's my Ant-task:

<!-- Define the appbundler task -->
<taskdef name="bundleapp" classname="com.oracle.appbundler.AppBundlerTask"/>
<!-- Create the app bundle -->
<target name="bundle-macosx">
    <bundleapp outputdirectory="macosx"
        name="Servitus"
        displayname="Servitus"
        identifier="ch.lawsuite.core.Servitus"
        shortversion="1.0"
        applicationCategory="public.app-category.business"
        icon="src/ch/lawsuite/data/icons/gmbh/osx/kplus.icns"
        mainclassname="ch.lawsuite.core.Servitus">
        <classpath file="dist/LawSuiteSE.jar"/>
        <argument value="--installer"/>
    </bundleapp>
</target>

EDIT: I meanwhile replaced the JavaAppLauncher in Contents/MacOS with a small run script (start.sh):

#!/bin/bash
java -Xms256m -XX:PermSize=128m -jar /Users/salocinx/Workspace/LawSuiteSE.app/Contents/Java/LawSuiteSE.jar --installer

Then I also replaced the value for the key CFBundleExecutable in Info.plist from JavaAppLauncher to start.sh.

Now it works fine, but how can I use a relative path to the jar-executable? Something like "../Java/LawSuiteSE.jar" doesn't work and gives me the same error as before. Is there any constant pointing to the current app-folder, something like "$APP_ROOT" so that the script could look like:

#!/bin/bash
java -Xms256m -XX:PermSize=128m -jar $APP_ROOT/Contents/Java/LawSuiteSE.jar --installer

Any ideas?

salocinx
  • 3,715
  • 8
  • 61
  • 110
  • Are the symlinks working? Generally, when building on windows, the symlinks are broken. – nbz Aug 14 '14 at 14:04
  • Okay I checked and updated my symlinks on the MacBook Pro by following this guide: http://www.monkehworks.com/set-java-7-as-default-jvm-on-mac-osx-mountain-lion. But I does still not work. Next I will try to build my app on the MacBook. – salocinx Aug 14 '14 at 14:32
  • Building the entire project and bundling the package on the Mac did not help... still same problem. – salocinx Aug 14 '14 at 14:54

1 Answers1

3

Do I need to copy these libs within the appbundle-ant-task

Yes, you should mention all the required libraries in classpath elements. The bundler will then copy them all to the appropriate place inside the bundle (only JAR files that are directly under Contents/Java will go onto the classpath when the bundle is run, JARs in subdirectories are ignored). You should also use dots instead of slashes in the mainclassname.

<bundleapp outputdirectory="macosx"
    name="Servitus"
    displayname="Servitus"
    identifier="ch.lawsuite.core.Servitus"
    shortversion="1.0"
    applicationCategory="public.app-category.business"
    icon="src/ch/lawsuite/data/icons/gmbh/osx/kplus.icns"
    mainclassname="ch.lawsuite.core.Servitus">
    <classpath file="dist/LawSuiteSE.jar"/>
    <classpath dir="lib" includes="*.jar" />
    <argument value="--installer"/>
</bundleapp>

Edit

Now it works fine, but how can I use a relative path to the jar-executable

You can use dirname $0 to get the path to your bundle's Contents/MacOS, and take a relative path from there. You should probably also make an explicit reference to the public JRE rather than relying on java being on your PATH, as that will only work if the target machine has a JDK installed, not if it only has a JRE. Installing the JDK will install the public JRE as well, so it's safe in that case too.

#!/bin/bash

SCRIPT_DIR="`dirname $0`"
exec "/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java" \
  -Xms256m -XX:PermSize=128m -jar "$SCRIPT_DIR/../Java/LawSuiteSE.jar" --installer
Ian Roberts
  • 120,891
  • 16
  • 170
  • 183
  • Hi Ian, thanks for your suggestion. Now the bundler copies and flattens all the .jar files within my lib folder to Contents/Java. I also adjusted my MANIFEST.MF to directly looking for the .jars in the app-root folder instead of app-root/lib. But still the very same problem. Btw. is there really no way to keep up the folder structure within the lib-folder? My external libs have themselves heavy dependencies to a lot of other jar-libs in a clearly defined folder structure within the lib-folder. Thanks in advance! – salocinx Aug 15 '14 at 10:06
  • @salocinx The native launcher stub inserted into the bundle by the bundler task creates it's classpath by enumerating all the jar files that are contained in the `Contents/Java` directory (and only that directory, no subdirs), and executes the mainclassname - this behaviour is hard coded in the stub and is not negotiable. – Ian Roberts Aug 15 '14 at 10:13
  • Thanks for the quick answer Ian. Okay these are bad news. Is there any work-around to launch my application with some kind of a script or something similar out of an .app-bundle? My application basically works just fine on the Mac when directly launching with "java -jar Servitus.jar --installer", but I'm really interested in an .app-bundle... :-) Any further ideas? – salocinx Aug 15 '14 at 11:00
  • @salocinx you can certainly construct a bundle by hand, and the main "binary" in `Contents/MacOS` can be a shell script that delegates to `/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java`. This is the approach I use for [GATE](http://gate.ac.uk), though it's a bit more complex in that case as there's a second level of indirection (it's a cross-platform tool, the script inside the `.app` bundle delegates to another shell script, which other platforms would call directly). – Ian Roberts Aug 15 '14 at 11:28
  • Hi Ian, that's exactly what I meant. Meanwhile I wrote a small start.sh script and linked it with the CFBundleExecutable key in the Info.plist. The only problem remaining is that I need to provide the full path to the .jar-executable within the script, with relative paths it doesn't work... is there a placeholder that points to the current .app-folder like $APP_ROOT or something similar? Please have a look on the original question above, I added an "EDIT" section to describe the problem in more detail. – salocinx Aug 15 '14 at 14:14
  • @salocinx I've added a suggestion using the fact that a script knows its own path via `$0` – Ian Roberts Aug 15 '14 at 14:26
  • Hey Ian - thank you very much, this issue had been on my todo list for 9 months now and I am very happy to have solved it finally :-D Btw. I checked GATE and this looks very promising, maybe I'll find an application in my project :-) – salocinx Aug 15 '14 at 14:49