Wednesday, March 31, 2010

Part 3, Deploying Adobe AIR apps in a corporate environment

See Part 1 and Part 2 first.

A disclaimer before we create our AIR installers for Mac and Windows: this is for AIR 1.5! Adobe has not yet released the redistributable for AIR 2.0, though AIR 2.0 GA release is expected in the first half of 2010. However, everything we are doing here should work in AIR 2.0. The Native Installers in AIR 2.0 throw in a little twist, but is should be noted that the Native Installers a) will NOT install/update the AIR runtime, and b) also will NOT (I believe) support silent install/uninstall by themselves, but only via the AIR redistribution mechanism, outlined here.

Some tools we will need.

On Mac:
  1. XCode (free)
  2. A good text editor, such as TextWrangler (free)
  3. A log viewer; the Console App built into Mac works fine
  4. A Mac installer maker; PackageMaker (part of the XCode download) works fine
On Windows:
  1. Visual Studio 2008 (pay) or Visual C++ 2008 Express Edition (free)
  2. A good text editor, such as Notepad++ (free)
  3. A log viewer; you can use tail -f in cygwin (free), or use BareTail (free version)
  4. A Windows installer maker; I used InstallJammer (free)


Installer scenarios (work silently on both Mac and Windows):
  1. Machine lacks both AIR Runtime and AIR app.
  2. Machine has AIR Runtime, but is missing AIR app.
  3. Machine has both AIR Runtime and AIR app, but needs to upgrade app.


The Windows and Mac installers do the same actions:
  1. Copy AIR installer files to a temporary directory.
  2. Check if the AIR app is installed (via OS-specific calls) and, if it is installed, uninstall it (also OS-specific calls).
  3. Check the version of AIR Runtime currently installed (if any) and install or update as needed.
  4. Install the AIR app.
  5. Delete the temporary directory.


Step #2 above requires writing an OS-specific native program (in Visual Studio or XCode) and an OS-specific script file (BAT file or bash shell script).

It works like this:
  1. The installer program (created with InstallJammer or PackageMaker) calls the script file.
  2. The script file calls the native program to get information about the installed AIR app, if it is installed.
  3. The script file gets the exit code and stdout from the native program.
  4. If the exit code is 0, the stdout is the OS-specific app info (a GUID for Windows and an absolute path for Mac), and the script then uninstalls the AIR app (“msiexec /x {GUID}” on Windows, and “rm –rf {PATH}” on Mac).
  5. If the native program exit code is 1, the AIR app is not installed, and the script does nothing.
  6. Any other exit code is an error and is reported back to the installer and fails the install.
The Windows Installer

The Windows Installer depends upon a native app created by Oliver Goldman, in this post. You can download his code from his blog post.

I made one small change to remove the DLL dependency on msvcr90.dll (you can see this dependency with Dependency Walker). Msvcr90.dll is not guaranteed to be on all Windows machines, so it is better to not have the dependency. In the msiutils project properties, go to General Properties > C++ > Code Generation. In the Configuration drop-down, change to Debug if is not already displaying Debug. In the right pane, go to Runtime Library and change to "Multi-threaded Debug (/MTd)". Change the Configuration drop down to Release, and change Runtime Library to "Multi-threaded (/MT)". See this documentation at MSDN for more info. Build the Release version, and you can see with Dependency Walker that it no longer depends on mscvr90.dll; it depends (directly) only on msi.dll and kernel32.dll, which are on all Windows Machines (at least with XP or later OS). The trade-off is that msiu2p.exe is 53 KB as opposed to the 8 KB version from Oliver's example.

Next we need a Batch file named install.bat to drive the msiu2p.exe native program.





@echo off
cd %~dp0

:checkIfInstalled
REM echo Checking if older version installed
set OSID={8209FAF5-4714-5CB1-20B5-9D840B252637}
REM echo OSID=%OSID%
set AIR_APP=yourApp.air
REM echo AIR_APP=%AIR_APP%

REM must redirect stderr as well, or InstallJammer sees existence of any stderr as a failure
%~dp0msiu2p.exe %OSID% > msiu2p.out 2>&1

REM Bad error in msiu2p.exe, exit this script
if ERRORLEVEL 2 EXIT /B 1

REM ERRORLEVEL 1 just means the app was not previously installed, so go to install step
if ERRORLEVEL 1 goto install

:uninstall
REM echo Attempting to uninstall
SET /p PROD_GUID= < msiu2p.out
msiexec /q /x %PROD_GUID%

REM check to see if an error occurred in msiexec (error 1603 if not run as administrator)
if ERRORLEVEL 1 echo ERRORLEVEL=%ERRORLEVEL%
if ERRORLEVEL 1 EXIT /B 2

:install
REM echo installing
@call installer -silent -eulaAccepted -desktopShortcut -programMenu -location %1 %AIR_APP%
REM echo install ERRORLEVEL=%ERRORLEVEL%
REM ERRORLEVEL 1 indicates success, but reboot required
if ERRORLEVEL 2 EXIT /B 3

EXIT /B 0





You will need to make 2 changes to the BAT file:
  1. Change the line "set OSID=" to the Windows OSID of your AIR app (see Part 2 for how to get the OSID).
  2. Change the line "set AIR_APP=" to the correct name of your AIR file.
Make one temporary change, which is to comment out the line that begins "@call installer" by putting REM at the beginning of the line.

We will also creating another Batch file named test.bat to test install.bat.




@echo off
@call %~dp0install.bat "%ProgramFiles(x86)%"
echo ERRORLEVEL=%ERRORLEVEL%
pause




Save install.bat, msiu2p.exe, and test.bat in the same directory. Run a command line prompt, cmd.exe. Be sure to "Run as Administrator" on Vista and 7. Make sure your AIR app is currently not installed (Add/Remove Programs or Programs and Features in Control Panel). In the command line prompt, run test.bat. It should run with exit code (ERRORLEVL) 0. The Batch file determined your AIR app was not installed and did nothing.

Next, manually install your AIR app by double-clicking on the AIR file. Confirm the AIR app is installed. In the command line prompt, run test.bat. It should run with exit code (ERRORLEVEL) 0, AND your AIR app should be uninstalled.

Now, un-comment out the line that begins "@call installer" by removing the REM from the beginning of the line. Go into Control Panel and uninstall both your AIR app and the Adobe AIR runtime. In Part 2, I told you how to get the AIR runtime redistributable files, so you should have access to "AIR_Win_installer_files.zip". Extract the contents of the zip into the same directory as install.bat and test.bat (the zip has a directory named "Adobe AIR" and three files in the top directory, setup.msi, setup.swf, and "Adobe AIR Installer.exe". I renamed the file "Adobe AIR Installer.exe" to just installer.exe, but you can rename it to anything you like, just fix install.bat to call your executable name.

Now from the command line prompt, run test.bat. It should run with exit code (ERRORLEVEL) 0. The AIR runtime and your AIR app should be installed. Verify by making sure your AIR app can run.

Now uninstall just your AIR app in Control Panel. Run test.bat again and your AIR app should be installed once again.

Lastly, just run test.bat (without uninstalling anything). Your AIR app should be uninstalled and re-installed, which you can verify be seeing the difference in the file timestamps of your app in the Program Files (or Program Files (x86)) directory.

We have now tested all three install/update scenarios above.

The AIR installer can log messages to a file named .airinstall.log (note leading dot) in the user's home directory. On Windows, you can see the log file as the install is running with tail -f command in cygwin, or with the BareTail log viewer.

The last part of the Windows Installer is to package everything up with InstallJammer, which I will do in the next post. The InstallJammer installer will call the install.bat file. We will then move onto the Mac installer, which follows the same basic pattern.

Tuesday, March 30, 2010

Part 2, Deploying Adobe AIR apps in a corporate environment

Before we get started creating installers for Windows and Mac that will silently install and update both the AIR runtime and our app, it is important to understand a few concepts about how AIR apps get installed on Windows and Mac.

Explaining Mac is easier. The AIR app is associated with a Bundle ID, in Mac terminology. The Bundle ID looks like something like this:

HelloWorld.e146c51663394ee63585200f02ce0969fc8dd73c.1


When you look at an AIR app in Finder, it looks like a file, but it is actually a directory, and you can see the contents by right-clicking and selecting Show Contents. In Terminal, you can just cd to the Contents sub-directory. When you create your AIR file with Flash Builder or ADT command line, you can choose files and directories to bundle with you main SWF, and these get compressed into the AIR file, and decompressed into the Contents directory of the installed app.

On the Mac, the user can move apps around, since it is just moving or copying a directory. Unlike Windows, where locations of apps are stored in places like the Windows Registry and moving an app means it probably won't run in the new location, most Mac apps will run if you copy or move them elsewhere. Of course, you may need admin rights to move to/from certain directories.

Given that you can relocate an AIR app on Mac, how do you find it so you can uninstall it? The answer is the LSFindApplicationForInfo function. You pass the Bundle ID to LSFindApplicationForInfo, and you get back a URL to your app. The URL is hard to work with, so you can turn it into an absolute file path with a call to CFURLGetFileSystemRepresentation.

This means writing C code for Mac! I'll show you how to use XCode (which is free) to create a small command line app for Mac. We will do something similar for Windows with a C++ app written in Visual Studio 2008 (or Visual C++ 2008 Express Edition, which is free).

So on Mac, once we have used the AIR app Bundle ID to locate it's URL, and transformed the URL to a file path, uninstalling the AIR app on Mac is just the command line:

rm -rf path


Of course, you may need admin rights to remove the directory, so just precede the rm command with sudo. Note that this is analogous to the manual way of removing an AIR app on Mac, which is to just drag it to the Trash Can.

So, to uninstall an AIR app on Mac, you need the Bundle ID. How do you get the Bundle ID? The Mac Bundle ID is the same string as the Mac OSID for you AIR app, and you should use the OSID Generator to get both the Mac OSID and the Windows OSID (discussed later) for you app. The OSID Generator is a small AIR app that you get access to when you are accepted to Adobe's redistribution license for the AIR runtime.

The OSID Generator takes two inputs, the Application ID and the Publisher ID of your AIR app.

The Application ID is in your Flash Builder project in the <project_name>-app.xml file. You should never change this, because it needs to be constant to keep your Windows and Mac OSIDs constant, and you need these to be constant to uninstall you app. You can also get the Application ID from an installed AIR app in application.xml file found in the the META-INF/AIR sub-directory of your app installation directory.

The Publisher ID can be found in the publisherid file in the META-INF/AIR sub-directory of your app installation directory. Note that you AIR app must be code-signed with a real security certificate (not a self-signing certificate) to get the Publisher ID.

You will need to manually install your AIR app (clicking on the AIR file) to get the publisherid file that contains the Publisher ID. Like the Application ID, you should never change the security certificate, because then you get a new Publisher ID (at least I believe that is correct), and the Publisher ID is used to generate the

If your AIR app is an internal app, you may get some resistance to the expense and trouble of code signing. There are lots of good reasons to sign apps distributed over the public Internet, and I think some of those reasons apply in corporate environments, but the biggest argument for signing your app is you must sign it if you wish to be able to silently update or uninstall the app (remember from my last post that a silent update is really a silent uninstall of the old version and a silent install of the new version).

Here is the argument for signing your AIR app, step by step:

  1. To silently upgrade or uninstall an AIR app, you need the Mac OSID or Windows OSID, depending on the OS.
  2. The OSID is generated off the Application ID (which you know) and the Publisher ID.
  3. To get the Publisher ID, you must sign you AIR app with a real security certificate.
  4. Thus, in order to have the ability to silently upgrade or uninstall an AIR app, you must sign the AIR app with a real security certificate.
If you do not have a security certificate yet, you can still practice creating an installer with an AIR app which is signed, and there are plenty of those available on the web, just Google. Just manually install the AIR app and look in the META-INF/AIR sub-directory for the Application ID and Publish ID (as described above) to plug into the OSID Generator to find the OSID for Mac and Windows.

On to Windows, the concepts of how an AIR app is installed and uninstalled are a little more complicated. On Windows, an AIR app is installed and uninstalled using Windows Installer. Oliver Goldman explains it all very well in this blog entry. The only thing I can add is that what Oliver calls Upgrade Codes and Product Codes are also referred to as Upgrade GUIDs and Product GUIDs in other documentation.

For those who don't know, a GUID will look something like this:

{8209FAF5-4714-5CB1-20B5-9D840B252637}
To paraphrase Oliver, your AIR app is associated with a Product GUID that changes with every build of your AIR app, and an Upgrade GUID that does not change. In fact, the Windows Upgrade GUID is the same string as the Windows OSID for you AIR app, and you use the OSID Generator to get the Windows OSID as I described above for Mac.

Once you know the Upgrade GUID, you can call a Windows function MsiEnumRelatedProducts
to get the corresponding Product GUID, and you can then uninstall the AIR app with the following command line (run as Administrator):

msiexec /x ProductGUID


To call MsiEnumRelatedProducts on Windows requires native code, and Oliver has a small C++ program with his blog post that works fine, and which I use, though I made just one small change, which I will get to.

Note that calling "msiexec /x" is analogous to manually removing an AIR app in Windows via Add/Remove Programs (or Programs and Features) in Control Panel.

Now that I have covered the concepts, next post I will get into building the actual installers.

One more quick note, see this article on how to manually uninstall the AIR runtime and an AIR app for both Windows and Mac. You will be doing a lot of this as you test your installers.

Monday, March 29, 2010

Deploying Adobe AIR apps in a corporate environment

So, you have an Adobe AIR app that you need to deploy in your corporate environment, by which I mean a bunch of locked-down Windows and Mac machines on which the end-user does not have admin rights.

As a result of not having admin rights, the end-user CANNOT:

1) Install or update the AIR runtime - see here - Does the Adobe AIR runtime require administrative privileges for installation? - Yes. The runtime requires an administrator privileges to install.

2) Install or update an AIR app - see here - Do Adobe AIR applications require administrative privileges for installation? - Yes. Applications require admin privileges to install just like other native desktop applications.

3) Run the AIR Updater framework - see here - "On Mac OS, to install an updated version of an application, the user must have adequate system privileges to install to the application directory. On Windows and Linux, a user must have administrative privileges."

What you really want to do is silently install the AIR runtime and your AIR app on the end-user's machine, via an enterprise deployment tool. My employer uses Altiris for Windows machines and Casper Suite for Macs.

Well, you can do this (see here), but you have to sign up for Adobe's redistribution program, and it will be some work.

Silently installing is fairly easy. But how do you silently update your AIR app? You don't. See this PDF, page 5: You cannot use the silent installer option to update an installed AIR application. This is because the AIR installer cannot determine whether the version to be installed is newer than the installed version, and it would pose a security risk to downgrade the application.

However, you can silently uninstall your AIR app (though it is complicated), so the "upgrade path" is to uninstall the old version of your AIR app and install the new one.

Over the next few blog posts, I will show how I created installers for Windows and Mac that can silently install and/or update the AR runtime and your app. I got a lot of help from Google and articles by other folks, notably Adobe AIR Application Distribution and Installation. Hopefully, this series of blog posts will be useful.

Introduction

My names is Robert Burke, and I am a computer programmer. I have worked for Turner Broadcasting in Atlanta, GA for nearly 10 years.

My degree is in Mathematical Programming, which was basically solving math problems in C and Fortran. In school I learned Pascal, BASIC, Fortran, COBOL, Visual Basic, HTML, C, and C++. In my working career, I have primarily worked with C++, C#, JavaScript, and ActionScript. I am also proficient in XML, XSL, BATCH file scripting, WSH scripting, Apache Ant, etc. I have worked a little with Java, primarily writing custom Ant tasks.

Most of my experience is in front-end programming, though only a small part of that is actual UI coding. I consider myself more of a framework programmer, creating frameworks used by others for front-ends.

At my current job at Turner, I have primarily worked on the Turner CMS used by various websites owned or produced by Turner to publish the website - cnn.com, si.com, nascar.com, nba.com, etc. I have worked on a few other projects, notably the Pipeline Video Player for CNN.com (a down-loadable .NET app), and the web-based Video Player used for a few years by CNN (this was a large JavaScript framework). But most of my work has been around the Turner CMS. I have worked on three major versions of the Turner CMS, with the imaginative names of CMS1, CMS2, and CMS3.

I inherited the front-end of CMS1. This was a C++ CORBA back-end, Oracle DB, and an MFC front-end. My first task at Turner was to rewrite the front-end to dis-entangle the MFC UI logic from the CORBA business logic.

CMS2 was the next and current version. The back-end is Weblogic EJB and Oracle DB. The front-end is a complex C++ framework on top of MFC and ATL. There is also Java code running on the front-end, using a product called JIntegra to run a JVM within our C++ process, and wrap Java classes in COM proxies. The front-end application is deployed on Citrix terminal servers, so that our Windows and Mac users can both use it.

CMS3 is the future version we are working on now. The current plan in is an Adobe AIR front-end and REST services as the back-end, with CouchDB as the data store.

Coming from Visual Studio and .NET, I have some pretty strong opinions about ActionScript, Flash Builder, etc. From a developer's standpoint, Adobe is light-years behind Microsoft in terms of runtimes, frameworks, IDEs, etc. However, Adobe is ahead in terms of creating skinable, cross-platform desktop apps. Our new app must run on both Mac and Windows, since we want to stop using terminal servers (particularly important for all our video editing requirements - you cannot scale terminal servers if users are doing CPU-intensive stuff like watching video).

We considered Silverlight. I love C# programming and the .NET framework, and Expression Blend is far superior to Catalyst. However, we need the power of a desktop app, and Silverlight running out of the browser is in the same security sandbox as when it does run in the browser.

We also do not want an in-browser app, because of the security sandbox, the problem of staying on the right page, the browser's chrome (which is just confusing) and the fact you cannot have any keyboard shortcuts for power users since the browser has mapped most of them.

We also briefly considered Eclipse RCP, thought we did not look too much. We are very impressed by the OSGI plugin architecture of Eclipse, and we need our app to be modular in a similar fashion. However, most RCP apps look like IDEs. The big problem is the lack of declarative GUIs like XAML or MXML. This allows for cleaner separation of developer and designer concerns. I have heard the next version of RCP will have declarative GUIs, but I believe the UI will still be form-based (SWT wrappers around OS Native windows), instead of the vector-graphic UI of WPF/Silverlight or Flash. I am not a big believer in app chrome that does not match the OS chrome, but it is great in WPF or Flash to be able to easily create very custom UIs and animations.

We also considered a .NET desktop app that could run on Mono on Macs. However, Mono has not ported WPF and probably never will, and we want the nicer UIs.

So, the logical choice is Adobe AIR, though I am still somewhat blah about the decision. C# is a better language, .NET is a better framework, Visual Studio is a better IDE, Expression Blend is a better designer tool, and on and on. Unfortunately, Microsoft does not want to play in the cross-platform desktop app arena.

Enough for tonight.