Recent Changes - Search:

edit SideBar

MacJavaApps

How to bundle your Java application on the Mac.

Summary

In the past, we have provided a Mac OS X .app that would invoke Ptolemy.

The way this is done differs between Apple Java (version 1.6) and Oracle Java (1.7 and later).

Ptolemy II now requires Java 1.7 (though it can be built under Java 1.6). Some portions of Ptolemy II require Java 1.8 (accessors use Nashorn). We will be shipping Java 1.8. So, we want to deploy a Mac .app file that uses Java 1.8.

Unfortunately, Oracle has some fairly heavyweight and incomprehensible system to create the .app directory.

So, we use universalJavaApplicationStub

How we built apps in Ptolemy 10.0

$PTII/bin/makeapp is a script that created $PTII/bin/Vergil.app etc.

When ./configure is run, $PTII/bin/ptinvoke.in is read and $PTII/bin/ptinvoke is created.

When make is run in $PTII/bin, the makefile creates a symbolic link from $PTII/bin/makeapp to $PTII/bin/ptinvoke.

When $PTII/bin/makeapp is invoked, the $PTII/bin/ptinvoke script checks the name under which it was invoked and runs the makeapp portion of the script.

universalJavaApplicationStub

Fortunately, https://github.com/tofi86/universalJavaApplicationStub defines a shell script that can be used to invoke Java

cd $PTII/vendors
git clone https://github.com/tofi86/universalJavaApplicationStub.git

creates vendors/universalJavaApplicationStub/src/universalJavaApplicationStub

Unfortunately, the syntax that it expects for bin/Vergil.app/Contents/Info.plist is different than what we generate.

Jarbundler

universalJavaApplicationStub suggests using https://github.com/tofi86/Jarbundler, which is a fork of Jarbundler.

Installed with:

cd $PTII/vendors
git clone https://github.com/tofi86/Jarbundler.git
cd Jarbundler/
ant
sudo -i cp /Users/cxh/ptII/vendors/Jarbundler/build/jarbundler-2.4.0.jar /opt/local/share/java/apache-ant/lib/

Ptplot

As a test, I used a Ptplot installation:

cd $PTII/ptolemy/plot
make dists
mkdir tmp
cd tmp
tar -zxf ../ptplot5.11.devel.tar.gz
cd ptplot5.11.devel
export PTII=`pwd`
./configure
ant

Then, at the end of build.xml:

  <taskdef name="jarbundler"
           classname="net.sourceforge.jarbundler.JarBundler" />

  <target name="app">
    <jarbundler
        dir="."
        mainclass="ptolemy.plot.plotml.EditablePlotMLApplication"
        name="Ptplot"
        shortname="Ptplot"
        stubfile="/Users/cxh/ptII/vendors/universalJavaApplicationStub/src/universalJavaApplicationStub"
        useJavaXKey="true"
        >
      <jarfileset dir="/Users/cxh/ptII/ptolemy/plot/tmp/ptplot5.11.devel">
        <include name="**/*.jar" />
      </jarfileset>
    </jarbundler>
  </target>

Running ant app created Ptplot.app, which works!

Ptplot.app/Contents/Info.plist looks like:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<plist version="1.0">
  <dict>
    <key>CFBundleName</key>
    <string>Ptplot</string>
    <key>CFBundleShortVersionString</key>
    <string>1.0</string>
    <key>CFBundleAllowMixedLocalizations</key>
    <string>false</string>
    <key>CFBundleInfoDictionaryVersion</key>
    <string>6.0</string>
    <key>CFBundleExecutable</key>
    <string>universalJavaApplicationStub</string>
    <key>CFBundleDevelopmentRegion</key>
    <string>English</string>
    <key>CFBundlePackageType</key>
    <string>APPL</string>
    <key>CFBundleSignature</key>
    <string>????</string>
    <key>JavaX</key>
    <dict>
      <key>MainClass</key>
      <string>ptolemy.plot.plotml.EditablePlotMLApplication</string>
      <key>JVMVersion</key>
      <string>1.3+</string>
      <key>ClassPath</key>
      <array>
        <string>$JAVAROOT/config/jarTest.jar</string>
        <string>$JAVAROOT/ptolemy/plot/compat/compat.jar</string>
        <string>$JAVAROOT/ptolemy/plot/demo/demo.jar</string>
        <string>$JAVAROOT/ptolemy/plot/img/img.jar</string>
        <string>$JAVAROOT/ptolemy/plot/plot.jar</string>
        <string>$JAVAROOT/ptolemy/plot/plotapplet.jar</string>
        <string>$JAVAROOT/ptolemy/plot/plotapplication.jar</string>
        <string>$JAVAROOT/ptolemy/plot/plotml/plotml.jar</string>
        <string>$JAVAROOT/ptolemy/plot/plotmlapplet.jar</string>
        <string>$JAVAROOT/ptolemy/plot/pxgraphapplet.jar</string>
      </array>
      <key>Properties</key>
      <dict>
        <key>com.apple.mrj.application.apple.menu.about.name</key>
        <string>Ptplot</string>
      </dict>
    </dict>
  </dict>
</plist>

Problem: Mac App fails with "LSOpenURLsWithRole() failed with error -10810"

If the Mac App fails with LSOpenURLsWithRole() failed with error -10810, then try making universalJavaApplicationStub executable

bash-3.2$ open Vergil.app/
LSOpenURLsWithRole() failed with error -10810 for the file /Applications/Ptolemy/ptII11.0.devel07Sep/bin/Vergil.app
bash-3.2$ ls -l Vergil.app/Contents/MacOS/
total 168
-rw-r--r--  1 cxh  admin  18604 Aug  8 08:25 JavaAppLauncher
-rwxr-xr-x  1 cxh  admin  45920 Aug  8 08:25 JavaApplicationStub
-rw-r--r--  1 cxh  admin  12397 Sep  7 00:55 universalJavaApplicationStub
bash-3.2$ chmod a+x Vergil.app/Contents/MacOS/universalJavaApplicationStub
bash-3.2$ open Vergil.app/

Problem: $JAVAROOT and $APP_ROOT are not expanded in the Properties element.

I'm using the Apple format with JavaX instead of Java so as to avoid #9. However $JAVAROOT and $APP_ROOT were not being expanded.

So, I forked universalJavaApplicationStub, see https://github.com/cxbrooks/universalJavaApplicationStub

Use:

  git clone https://github.com/cxbrooks/universalJavaApplicationStub.git

Below is the diff:

--- a/src/universalJavaApplicationStub
+++ b/src/universalJavaApplicationStub
@@ -158,6 +158,8 @@ if [ $exitcode -eq 0 ]; then

        # read the JVM Options
        JVMOptions=`/usr/libexec/PlistBuddy -c "print ${JavaKey}:Properties" "${InfoPlistFile}" 2> /dev/null | grep " =" | sed 's/^ */-D/g' | tr '\n' ' ' | sed 's/  */ /g' | sed 's/ = /=/g' | xargs`
+       # replace occurances of $APP_ROOT with its content
+       JVMOptions=`eval "echo ${JVMOptions}"`

        # read StartOnMainThread
        JVMStartOnMainThread=`/usr/libexec/PlistBuddy -c "print ${JavaKey}:StartOnMainThread" "${InfoPlistFile}" 2> /dev/null`
@@ -181,9 +183,11 @@ if [ $exitcode -eq 0 ]; then

        # read the JVM Arguments
        JVMArguments=`/usr/libexec/PlistBuddy -c "print ${JavaKey}:Arguments" "${InfoPlistFile}" 2> /dev/null | xargs`
+       # replace occurances of $APP_ROOT with its content
+       JVMArguments=`eval "echo ${JVMArguments}"`

-    # read the Java version we want to find
-    JVMVersion=`/usr/libexec/PlistBuddy -c "print ${JavaKey}:JVMVersion" "${InfoPlistFile}" 2> /dev/null | xargs`
+        # read the Java version we want to find
+        JVMVersion=`/usr/libexec/PlistBuddy -c "print ${JavaKey}:JVMVersion" "${InfoPlistFile}" 2> /dev/null | xargs`

 # read Info.plist in Oracle style
 else
@@ -200,7 +204,7 @@ else

        # read the JVM Options
        JVMOptions=`/usr/libexec/PlistBuddy -c "print :JVMOptions" "${InfoPlistFile}" 2> /dev/null | grep " -" | tr -d '\n' | sed 's/  */ /g' | xargs`
-       # replace occurances of $APP_ROOT with it's content
+       # replace occurances of $APP_ROOT with its content
        JVMOptions=`eval "echo ${JVMOptions}"`

        JVMClassPath="${JavaFolder}/*"
@@ -210,7 +214,7 @@ else
        # read the JVM Arguments
        JVMArguments=`/usr/libexec/PlistBuddy -c "print :JVMArguments" "${InfoPlistFile}" 2> /dev/null | tr -d '\n' | sed -E 's/Array \{ *(.*) *\}/\1/g' | sed 's/  */ /g' | xargs`
-       # replace occurances of $APP_ROOT with it's content
+       # replace occurances of $APP_ROOT with its content
        JVMArguments=`eval "echo ${JVMArguments}"`
 fi

My patch was merged in to the trunk in October, 2015.

Sharing a common JDK

One issue is that for each of the *.app directories, we have an individual jre directory. We need this jre directory because we want to include various shared libraries.

https://github.com/tofi86/universalJavaApplicationStub was updated to support JAVA_HOME:

"There is some foo happening to determine which Java versions are installed – here's the list in which order system properties are checked:"
"system variable $JAVA_HOME"
"can also be set to a relative path using the <LSEnvironment> Plist dictionary key which allows for bundling a custom version of Java inside your app!"

https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/LaunchServicesKeys.html#//apple_ref/doc/uid/20001431-106825 says:

"LSEnvironment (Dictionary - macOS) defines environment variables to be set before launching this app. The names of the environment variables are the keys of the dictionary, with the values being the corresponding environment variable value. Both keys and values must be strings."
"These environment variables are set only for apps launched through Launch Services. If you run your executable directly from the command line, these environment variables are not set."

https://github.com/tofi86/universalJavaApplicationStub/pull/26 discusses how the feature was implemented.

JDK Implementation

I edited $PTII/bin/CapeCode.app/Contents/Info.plist:

    <key>LSEnvironment</key>
    <dict>
        <key>JAVA_HOME</key>
        <string>../jdk</string>
    </dict>

However, it seemed that the $PTII/bin/CapeCode.app/Contents/MacOS/universalJavaApplicationStub script was not recognizing JAVA_HOME. I edited universalJavaApplicationStub:

# first check system variable "$JAVA_HOME"                                                                                                                
echo "$0: JAVA_HOME: $JAVA_HOME" > /tmp/j.out
if [ -n "$JAVA_HOME" ] ; then

The /tmp/j.out file was created, but JAVA_HOME was not set:

   /Users/cxh/ptII/bin/CapeCode.app/Contents/MacOS/universalJavaApplicationStub: JAVA_HOME:

https://www.cpume.com/question/eizoseg-set-environment-variables-on-mac-os-x-lion.html suggested running lsregister:

/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister -v -f ~/ptII/bin/CapeCode.app
Edit - History - Print - Recent Changes - Search
Page last modified on May 12, 2017, at 10:08 pm