Java: How to handle drop events to your Mac OS X Dock icon (Part 2)

In Part 1 of this tutorial I shared the Java source code you need to handle drag and drop events on Mac OS X. But as I mentioned in that tutorial, the recipe to handle files that are dropped onto your Dock application icon requires a little more work, in particular an Ant build process that uses the JarBundler task. So here in Part 2 of this tutorial, I'm sharing my Ant build script that completes this recipe.

A working Ant build script with the JarBundler task

To build an application on Mac OS X that can handle drag and drop file events, you need to make your Java application more like a native Mac application. But fear not, this isn't hard, you just need an Ant build script that uses the JarBundler task to create a native application, and I just happen to have one of those that I'm going to share here.

My Ant build script

Here is the source code for my Ant build.xml script that I use to build the Java application that I shared in Part 1 of this tutorial:

<project name="JavaMacDockTest" default="do-bundle" basedir="..">

  <!-- use the 'jarbundler' project to make our Java/Mac application -->
  <taskdef name="jarbundler" classname="net.sourceforge.jarbundler.JarBundler" />

  <path id="class.path">
    <fileset dir="lib">
      <include name="**/*.jar"/>
    </fileset>
  </path>

  <target name="init">
    <tstamp/>
  </target>

  <!-- place our compiled java classes here -->
  <target name="create-classes-dir" depends="init">
    <mkdir dir="classes"/>
  </target>

  <target name="clean">
    <delete dir="classes"/>
  </target>

  <!-- compile to the classes directory -->
  <target name="compile" depends="clean,create-classes-dir">
    <javac destdir="classes" source="1.5" >
      <src path="src"/>
      <classpath refid="class.path"/>
    </javac>
    <copy todir="classes">
      <fileset dir="src">
        <include name="**/*.gif"/>
        <include name="**/*.jpg"/>
        <include name="**/*.png"/>
      </fileset>
    </copy>
  </target>

  <!-- create a jar file in the 'jar' directory with this task -->
  <target name="create-jar" depends="compile">
    <jar basedir="classes" jarfile="jar/MacDock.jar" manifest="build/macDock.manifest"/>
    <copy todir="jar">
      <fileset dir="lib">
        <include name="**/*.jar"/>
        <include name="**/*.zip"/>
      </fileset>
    </copy>
  </target>

  <!-- create a mac bundle (application) with this task -->
  <target name="do-bundle" depends="create-jar">
    <jarbundler dir="release"
                name="JavaMacDockTest"
                shortname="MacDock"
                signature="Alvin Alexander"
                mainclass="com.devdaily.macdocktest.JavaMacDockTest" 
                icon="build/macDock.icns"
                jvmversion="1.5+"
                version="0.92"
                infostring="Java Mac Dock Test, v0.92, June, 2009"
                build="2190"
                bundleid="com.devdaily.macdocktest.JavaMacDockTest" >

      <!-- this documenttype lets me received drag/drop events from safari to my dock icon -->
      <documenttype name="Allow any document type"
                    ostypes="****" 
                    role="Viewer"/>

      <!-- this doesn't do anything for my app, but i wanted to show this -->
      <!-- as an example of the "mimetypes" keyword.                      -->
      <documenttype name="Image types we support"
                    mimetypes="image/jpeg image/png image/jpg" 
                    role="Viewer"/>

      <jarfileset dir="jar">
        <include name="**/*.jar" />
      </jarfileset>

      <!-- include all the regular properties to make my java/swing applications -->
      <!-- look like native mac os x applications.                               -->
      <javaproperty name="apple.laf.useScreenMenuBar" value="true"/>
      <javaproperty name="apple.awt.brushMetal" value="true"/>
      <javaproperty name="apple.awt.showGrowBox" value="true"/>
      <javaproperty name="com.apple.mrj.application.apple.menu.about.name" value="Mac Dock Test"/>
      <javaproperty name="apple.awt.textantialiasing" value="true"/>
    </jarbundler>
  </target>

</project>

I've tried to document my code pretty well, so I don't have much to add about the build process here. The only real trick to getting this to work -- besides being comfortable with Java, Ant, and Mac OS X -- is that you need to install JarBundler in the right location. All that means, however, is that you need to install the JarBundler jar file in the lib directory of your Ant installation, so even that is pretty easy.

One other thing: This build script assumes the directory structure for your project looks like this:

build
classes
lib
jar
release
src

I've document these directories in the build script, but here are some short definitions for them:

build     your project build directory, where build.xml goes
classes   my build script compiles your java classes to this folder
lib       put any required jar files here
jar       my build script builds an application jar file to this directory
release   my build script places the "mac application" version of your program here
src       your source code goes in this folder

To build your project with my Ant build script, just change the variables in the script to match your project, then move to the build script, and run Ant, like this:

cd build
ant

If everything works, this will convert your Java application into what is essentially a native Mac OS X application, or bundle. You'll find this bundle in the release directory. With my current variables, my bundle ends up being named JavaMacDockTest.app.

Download the complete project

If you'd like to experiment with all of this, I'm sharing my entire project with the Download link shown below:

I use this project with Eclipse, and I've run my ant commands from the Mac OS X Terminal, and it all works for me, so hopefully it will work for you as well.

Special thanks

A special thanks goes out to "Jerry" on the Mac/Java mailing list. He was very helpful, pointing me in the right direction several times as I tried to figure out how to make this work.