I have been using Merapi for about six months now and so far I have been very pleased. However, in the project I have been working on there were two issues that we needed to solve that were not really addressed by Merapi or other members of the community.  They were:

  1. Installing Java and AIR with one installer
  2. Managing the Java and AIR sides of the application

Installation

This post is for Windows installers, for Mac-based installers, see my more recent Mac installer post. For the installation, the plan we came up with was that our installer would unpack 2 executables.  The first EXE would start the Java part of our application and would be the one that users would use to start the application (i.e. Shortcuts to this executable in the Quick Launch, Program Files, and the Start Menu).  The second EXE would be for the installed AIR application, and would be hidden from the user.  The jist of the plan was that the Java executable would start the AIR application. 

Here are the installation steps I took to accomplish this plan.

  1. Get the Adobe AIR Runtime for distribution
  2. Create an EXE to start the Java side of our application
  3. Package both the AIR and Java runtimes into an installer
  4. Figure out how to silently install the AIR runtime and our AIR application

Get the AIR Runtime

First, we obtained a license to distribute the Adobe AIR Runtime by applying here.  Once this was approved, we downloaded the Runtime, a sidecar installer, and some documentation from Adobe.  After evaluating the sidecar installer, we decided to create an NSIS installer for Windows instead.

Create an EXE to start the Java-side

Second, I created an EXE file out of our Java-side Jar file.  I used Launch4j to do this.  Launch4j allowed me the ability to reference the Merapi jar as well as other jars we are using for the project, and it allows you to reference the location for a Java Runtime that we could bundle in our installer.  We decided to package the JRE because it would give us more control over the version that all of our users had on their computer.

Package AIR and Java runtimes

Third, I had to package the Java and AIR runtimes in our installer.  This part was pretty easy.  Using NSIS, I bundled the JRE and AIR runtime into the installer.  The only trick was to make sure that my Launch4j configuration referenced the JRE installation directory. 

Silently install AIR runtime and AIR app

Finally, after packaging all of the necessary files into the installer, I had to instruct the installer to silently install the AIR runtime and the AIR-side of our application.  This was actually surprisingly easy to do.  The command to do a silent install from the Windows command line is:

1
ExecWait '"$INSTDIR\resources\AIR_Win_installer_files\Adobe AIR Installer.exe" -silent -location "$INSTDIR\resources" "$INSTDIR\resources\MyAirApp.air"'

$INSTDIR is a variable for the installation directory in NSIS.  The nice thing about this is that the user only needs to specify this directory one time.  Then, you can reference the Adobe AIR Installer.exe path in the installation directory.  The -silent option makes it so the user has no idea the AIR Runtime is being installed.  The -location option allows you to specify where the AIR application file should be installed to.  Finally, you can specify the location of the AIR file and the silent installer will automatically install it immediately following the silent install of the AIR Runtime.  Pretty sweet!

Managing Java and AIR

With the installation under control, we turned our attention to figuring out how to manage both sides of the installed application.  Our plan for this was that Java would immediately start the AIR application as soon as the user ran the Java executable.  Then, when AIR closed, it would send a message through Merapi to the Java-side telling it to close itself.  As a fail-safe, we also decided to include a kill switch for the Java-side in the event that somehow the AIR application had an exception and was unable to send a close message.

Here was the process in a nutshell:

  1. Start the AIR-side application from Java
  2. Kill the Java-side of the application when AIR closes
  3. Stop the Java-end if the AIR-side has an exception and is unable to tell Java it is closing

Start the AIR application

First, I figured out the path to the AIR executable by using the following line of code in Java:

1
String[] path = {System.getProperty("user.dir") + "\\resources\\MyAirApp\\MyAirApp.exe"};

The System Property: “user.dir” is the path of the Java executables installation location.  I used this to locate the AIR application’s executable.  Then I was able to start it by using:

1
Runtime.getRuntime().exec(path);

Kill Java-side when AIR closes

Second, to close the Java-side after AIR has closed, all I had to do was register an event handler to the close event on the Windowed Application in Flex.  In that handler, I sent a message to Java via Merapi to tell it to close itself.

Java Auto-Kill

Third, I built in an auto-kill switch on the Java process in the event that AIR closed without being able to dispatch a kill message to Java.  To do this, I used a Timer that would count down from 120 seconds.  Every time a message was received through Merapi, I would reset the Timer.  If that 120 seconds expired, I sent a ping to the AIR-side.  If AIR received a ping, it would immediately respond back to Java.  If Java didn’t hear back from AIR after 3 pings, it would close itself.

I hope this information is helpful for those who like to create an AIR app, but can’t rely on AIR alone.  Hopefully some of the shortcomings of AIR (starting/closing another programs, interacting with hardware, etc.) can be addressed and we won’t have to worry about having to integrate AIR with other technologies like Java.

Tags: , , , , , ,

29 Responses


  1. junmin on 13 May 2009

    do you want the nsis command to just install the air app:

    I have the following in the header script:

    1
    
    Exec "$INSTDIR\templateManager.air"

    but it doesn’t work for me. it doesn’t install the air app for me.

    thanks.

  2. junmin on 15 May 2009

    also if i understand your blog correctly, it only shows how to make installer for windows. my question is that: how do you make installer for Mac or linux? thanks

  3. [...] few weeks ago, I wrote a post detailing how to use Merapi in a Production Environment. That tutorial was primarily written to address managing Java and AIR on a Windows Platform [...]

  4. Nate on 15 May 2009

    @junmin

    For your first post, here is the NSIS command I used to install the AIR application

    1
    
    ExecWait '"$INSTDIR\resources\AIR_Win_installer_files\Adobe AIR Installer.exe" -silent -location "$INSTDIR\resources" "$INSTDIR\resources\MyAirApp.air"'

    This make the installer run silently, and passes it the location where the AIR application will be installed. Finally, I give it the path to the AIR application. The installer installs the Adobe AIR Runtime if it doesn’t exist on the machine, and then installs your AIR Application.

    For your second comment, I just finished writing a post on a Mac installer. Check it out here

  5. Merapi……

    Merapi…

  6. ekz on 04 Jun 2009

    Great post. Very thorough, yet to the point. This is very helpful.

    Question: How does one handle AIR app updates? When the java app launches the AIR app, and it detects and wishes to fetch a new version to be installed, can this be done without impacting the order of things? ie: Will the AIR app be aware of its location and drop all the new AIR bits in the precise place they should be… or is the NSIS installer the only guy that knows the folder structure?

    -ekz

  7. ekz on 04 Jun 2009

    I’ve got one other question… say this package was to be distributed in an office where users did not have admin rights on the machine; would this still work?

    thanks again!

    -ekz

  8. whoover on 16 Jun 2009

    I was able to download Adobe Air on the fly with the following:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    
    !define AIR_VERSION "1.5.1"
    !define AIR_URL "http://airdownload.adobe.com/air/win/download/${AIR_VERSION}/AdobeAIRInstaller.exe"
    Function GetAIR
      GetDllVersion "$COMMONFILES\Adobe AIR\Versions\1.0\Adobe AIR.dll" $R0 $R1
      IntOp $R2 $R0 / 0x00010000
      IntOp $R3 $R0 & 0x0000FFFF
      IntOp $R4 $R1 / 0x00010000
      IntOp $R5 $R1 & 0x0000FFFF
      ; StrCmp $R2 "0" +3 0
      #StrCpy $1 "$R2.$R3.$R4.$R5"
      StrCpy $1 "$R2.$R3"
     
      StrCmp $1 ${AIR_VERSION} End InstallAir
     
      InstallAir:
        Call ElevateToAdmin
        MessageBox MB_ICONINFORMATION "$(^Name) uses Adobe Air ${AIR_VERSION} (found $1), it will now be downloaded and installed."
        StrCpy $2 "$INSTDIR\AdobeAIRInstaller.exe"
     
        ; use http://nsis.sourceforge.net/Inetc_plug-in
        inetc::get ${AIR_URL} $2
        Pop $0
        StrCmp $0 "OK" dlok
            MessageBox MB_OK|MB_ICONEXCLAMATION "http upload Error, click OK to abort installation" /SD IDOK
            Abort
        dlok:
            ExecWait $2
        #Delete $2
     
      End:
        ; end
    FunctionEnd

    The only problem is detecting the Adobe Air version is not working :( I did find an interesting link regarding detecting the version .

  9. junmin on 18 Jun 2009

    hey, thank for the blog again.

    Similar to my question about Mac installer, could you give more details about how to bundle the JRE. For example, where can I download the JRE exe? How can I silently install JRE like you show for the AIR runtime exe?

  10. Nate on 20 Jun 2009

    @junmin

    Download the JRE you want from Sun’s site. There are several versions to chose from there. Note that the JRE is just a bunch of directories and files, not an EXE. For my installer, I used NSIS to package all of the JRE files and then unload them into a subfolder within Program Files on the user’s computer. Something like C:/Program Files/MyApp/Resources. That will get the files on the user’s computer.

    In order to make sure that your Java application will use the JRE you placed on the user’s computer, check out Launch4j. It is free, it can be used for commercial purposes, and it is very handy. If you package your Java application files into a .JAR file, you can have Launch4j wrap the .JAR into an .EXE. Also within Launch4j, there is an option to give a path to the JRE directory that your Java application will use. We used this because our software required JRE 6 Update 11 in order to work properly.

    In short, here are the steps I took:

    1. Download the JRE to my computer.
    2. Tell NSIS to package the JRE files within the installer.exe
    3. Tell NSIS where I want to unload the JRE files on the user’s computer (C:/Program Files/MyApp/Resources/jre_1.6.0_11 or relative Resources/jre_1.6.0_11 if your EXE is placed in the C:/Program Files/MyApp directory).
    4. Compile a .JAR file from the Java-side of my app.
    5. Tell Launch4j where the .JAR file is and where to expect the JRE on the user’s computer (Resources/jre_1.6.0_11).
    6. Create an .EXE using Launch4j.
    7. Tell NSIS to package the .EXE I made within the installer.exe.
    8. Create the installer.exe using NSIS.

  11. Nate on 20 Jun 2009

    @whoover

    Thanks for posting the script for downloading the AIR Runtime. That is a nice alternative to packaging the AIR Runtime in a distributable installer. If you figure out how to detect the AIR Runtime version, please let us know. Thanks!

  12. Nate on 20 Jun 2009

    @ekz

    I have not been able to find a way to support the Auto-Update feature in AIR because NSIS is the only one that knows the directory structure like you mentioned. As an alternative, I wrote a service which checks the version of the application against the latest version stored on our remote server. If an update is available, we prompt the user to visit our website to download a new installer. Each installer that we use checks for previous versions of the app and then removes them completely, so we have a fresh new version of the application.

    As far as your second question regarding administrator rights. I believe the AIR Runtime requires you to have these rights from what I can tell here. However, I’m not sure if that is just for the standard AIR Runtime installer, or for the redistributable Runtime as well. The application I wrote requires Admin rights anyways for other reasons, so I am not entirely sure because I haven’t had a reason to look into it yet.

  13. whoover on 09 Jul 2009

    @admin
    The previously posted nsis script does detect the version if you change

    !define AIR_VERSION “1.5.1″

    to

    !define AIR_VERSION “1.5″

    see: http://wildwinter.blogspot.com/2008/12/air-15-registry-key-changes.html

  14. whoover on 14 Jul 2009

    Uninstall Air Application w/o OSID.air:

    I also found that uninstalling the air application can be a pain. Even using the OSID air application (get link when signed up for a license to distribute) you have to know your air app id (in main-app.xml), the publisher id (in META-INF/AIR/publisherid of your app install dir), and then run the OSID air app. If your using NSIS I found a solution using the following snippet:

    # Search for the air application and uninstall it… see: http://nsis.sourceforge.net/Registry_plug-in and http://forums.winamp.com/showthread.php?postid=2489342

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    
    ${registry::Open} "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" "/K=0 /V=1 /S=0 /B=1 /N='DisplayName' /T=REG_SZ" $0
        StrCmp $0 0 0 loop
        MessageBox MB_OK|MB_ICONEXCLAMATION "Unable to find uninstall directory" /SD IDOK IDOK close
        StrCpy $R0 ""
        StrCpy $R9 ""
        loop:
            ${registry::Find} "$0" $1 $2 $3 $4
            ; need to track first find to prevent infinate loop
            ${If} $3 == "YOUR_AIR_APP_NAME_HERE"
                ReadRegStr $R0 HKLM $1 "UninstallString"
                MessageBox MB_OKCANCEL "$(^Name) is already installed. $\n$\nClick 'OK' to remove the previous version or 'Cancel' to cancel this upgrade." /SD IDOK IDCANCEL close
                ExecWait '$R0 /qb /quiet'
            ${Else}
                ${If} $R9 == ""
                    StrCpy $R9 $3
                ${ElseIf} $R9  $3
                    Goto close
                ${EndIf}
                Goto loop
            ${EndIf}
            ;MessageBox MB_OKCANCEL '$$1    "path"   =[$1]$\n $$2    "value" =[$2]$\n $$3    "string" =[$3]$\n $$4    "type"   =[$4]$\n $\n Find next?' IDOK loop
        close:
            ${If} $R0 == ""
                MessageBox MB_OK "$(^Name) is not installed." /SD IDOK
            ${EndIf}
            ${registry::Close} "$0"
            ${registry::Unload}
  15. Nate on 14 Jul 2009

    @whoover,

    I did the same thing. Here is what I came up with (Just a few modifications to your script with additional comments.)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    
    Function un.UninstallAIRApp
      ${registry::Open} "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" "/K=1 /V=1 /S=1 /B=1 /N='DisplayName'" $0
      StrCmp $0 0 endLoop loop
     
      # Loop through the registry looking for a registry key with a DisplayName of "MyApp"
      # $1 is the path of the key (ex. SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{B365B2A5-D2E0-2720-760C-67227F29C79F}
      # $2 is the name of the property (ex. "DisplayName")
      # $3 is the value of the property (ex. "MyApp")
      # $4 is the property value type (ex. [REG_DWORD])
      loop:
        ${registry::Find} "$0" $1 $2 $3 $4
     
        # If the property value type is an empty string, we have hit the end of the loop.
        StrCmp $4 '' endLoop
     
        # If the property value is "MyApp", we have found our key, so lets call it's uninstaller.
        StrCmp $3 "MyApp" foundEntry loop
     
      foundEntry:
        # Get the uninstall string and store it in variable $R4
        ReadRegStr $R4 HKLM $1 "UninstallString"
        # Execute the uninstall string silently (/qn)
        ExecWait "$R4 /qn"
        goto loop
     
      endLoop:
        ${registry::Close} "$0"
        ${registry::Unload}
    FunctionEnd
  16. Matt on 27 Aug 2009

    According to Adobe AIR’s distribution facts, a silent install is allowed, but states we can not bypass the EULA display. Was it possible with NSIS to just show that part of the AIR install process, or did you simply include a copy of the EULA in the shell installer process?

  17. Nate on 27 Aug 2009

    @Matt
    I actually just had a conversation with an Adobe rep about that exact thing a few days ago. If the AIR installer is run with a command-line option of -eulaAccepted, then the EULA is not supposed to show. However, even if I do not specify that option (which I am not doing), a license still does not appear.

    Adobe has logged this as a bug and will fix it in a future release. I also asked them if I should display the additional AIR license in the NSIS before installation and they said to not worry about it. In the meantime, they put our application on the AIR Marketplace even with this limitation.

  18. Paras on 28 Aug 2009

    Brilliant.

    We don’t have to worry about the EULA screen any more when it happens in the silent mode then. Awesome.

    Thank you.

  19. Nate on 28 Aug 2009

    @Paras,
    Yeah… that seems to be the case at least for now.

  20. Matt on 02 Oct 2009

    (First off, thanks Nate for your reply to my EULA question)

    I absolutely love this tutorial, but any idea how to use the “command-line args:” option in JCreator, or if it even works? I’m trying to include SSL in the java portion of my application. When I call my class with command-line, I simply include “-Djavax.net.ssl.trustStore=… -Djavax.net.ssl.trustStorePassword=…” and the program runs perfectly. But I’ve tried various syntaxes to get it to work in JCreator to no avail. I was even sure to copy my certificate to the JCreator folder as well as my .exe’s folder when compiling/testing, but still no luck.

    - Matt

  21. Merapi…

    Objetivo Merapi is a technology that can be used as a messaging bridge between applications that run in the Adobe Flash player or Adobe AIR and applications written in Java….

  22. Raghav on 07 Mar 2010

    Hi. This is really great article and this is something I was looking for. Unfortunately I couldn’t get the NSIS script right and my end.
    It will be great if you post you complete example, including NSIS script and the Java + AIR files.
    Like me many will really appericate that.

    Thank you once again for the article.

  23. Nate on 07 Mar 2010

    I wish I could submit the NSIS script and the Java/Air files I used but unfortunately it is all proprietary. The NSIS command I mentioned above was all I needed to get the Air app to install properly. The rest of my script dealt with registry keys and other stuff not directly related to installing the app. If you are having problems with your script ask questions on the NSIS forums. There are already tons of solutions available on the forum and the community will answer any questions you have much better than I will be able to :) .

    I also cannot include the Air distributable runtime. You have to fill out a form and receive approval from Adobe before they will give you access to the runtime. As far as the JRE is concerned, I just downloaded one from Sun.
    - Nate

  24. Sri Harsha on 26 Apr 2010

    Hi,
    Good Blog , currently me also developing a small application using merapi and Air,i followed the example which u explained,But here its not able to install the air application ,Its able to execute(AdobeAIRInstaller.exe) but unable to install Air(myProg.air) i mean its not able to get exe from air,Generally while i double click on Air automatically it will create a exe and will be showing the shortcut on desktop.But when i use NSIS me not able to get exe to be generated when i kept the option to silent.

  25. Davis on 18 Aug 2010

    Hey, I’m just wondering how you feel about taking this route now that Air2 is out and they support native installers with that?

    I see you posted another blog indicating how to create a native installer for Mac OS, but I guess the big limitation here is that you can’t auto-update the Air runtime like you can using the ADT native installers. You also have to setup, learn and maintain different installer tools for each platform, it seems.

    I’m about to solve this problem myself — having built an Air2 + Merapi/Java application, and I’m just looking for your thoughts on this approach vs. what is now available in Air2.

    Regards,
    Davis

  26. Nate on 23 Aug 2010

    Davis,

    I actually just released a Merapi replacement called Transmission. It uses native process to start up a Java jar and sends messages through standard in/out. Check it out here.

    This will allow you to use AIR 2.0 native installers and will make the auto-update mechanism possible. I’ve moved my Merapi projects in this direction.

  27. Davis on 14 Sep 2010

    Thanks for the info Nate. I’ve since plowed ahead, and I was able to setup ant scripts that create and package the native air installer for mac/linux/windows and include the jar, and I’m able to start the jar from the air app, and kill it on the close event.

    Seems to be working ok. I haven’t built in all the error handling etc., yet, and I’m just now building in a watchdog, but if I get bummed out with merapi, I’ll give your project a shot.

  28. sanjay on 11 Oct 2010

    Hi,
    How can i uninstall my air application using nsis script.

  29. Nate on 11 Oct 2010

    I looked up the registry key for the application using a registry plugin for NSIS and used it to find the path to the application. Once you have the path, you can remove the application. Just make sure you remember to clean up after yourself and delete the registry key and all application shortcuts.


Leave your comment