Tomcat Launcher: Sample Application using the AIR 2.0 Native Process API

Tomcat Launcher: Sample Application using the AIR 2.0 Native Process API

I have been playing with the new Native Process API of AIR 2.0, and here is a first sample application I wrote. “Tomcat Launcher” is like a mini Tomcat Console that allows you to start / stop Tomcat and view the standard output log without opening a DOS box or Terminal window. It also allows you to open that log in your default Text Editor which is another new feature of AIR 2.0 that I already explored in my “Open in Excel” sample application. Tomcat Launcher also provides a generic example showing how to use the AIR 2.0 native process API to execute Java code.

Installation Instructions

  1. Download the AIR 2.0 beta runtime here.
  2. Because Tomcat Launcher uses the Native Process API, I had to create native installers:

After starting the application, enter the paths to your Java home folder and to the Tomcat installation you want to start, then click the Start button. The application will remember the folders you entered the next time you use the application. If you get a BindingException, make sure Tomcat isn’t already started. You can also click the Stop button to stop a running instance.

You can download the project here: TomcatLauncher.fxp.zip. If you just want to take a quick peak at the code, here is TomcatLauncher.mxml:

<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
					   xmlns:s="library://ns.adobe.com/flex/spark"
					   xmlns:mx="library://ns.adobe.com/flex/halo"
					   title="Tomcat Launcher"
					   height="700" width="500"
					   applicationComplete="init()">
	<s:layout>
		<s:VerticalLayout paddingLeft="8" paddingRight="8" paddingBottom="8"/>
	</s:layout>

	<fx:Script>
		<![CDATA[
			import mx.controls.Alert;

			private var startTomcatProcess:NativeProcess;
			private var stopTomcatProcess:NativeProcess;

			public function init():void
			{
				if (!NativeProcess.isSupported)
				{
					Alert.show("NativeProcess not supported");
				}

				// Read the last home used to start Tomcat (if any)
				var xml:XML = readConfig();
				if (xml != null)
				{
					javaHome.text = xml.javaHome;
					tomcatHome.text = xml.tomcatHome;
					return;
				}

				// If no known last home, present default/sample values
				if (Capabilities.os.toLowerCase().indexOf("win") > -1)
				{
					// default/sample values for Windows users
					javaHome.text = "C:\\Program Files\\Java\\jdk1.6.0";
					tomcatHome.text = "C:\\tomcat";
				}
				else if (Capabilities.os.toLowerCase().indexOf("mac") > -1)
				{
					// default/sample values for Mac users
					javaHome.text = "/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0";
					tomcatHome.text = "/Applications/tomcat/";
				}

			}

			public function startTomcat():void
			{
				// Write the Java and Tomcat home paths to config.xml so that they can be presented
				// to the user the next time the application is started
				writeConfig();

				log.text = "Starting Tomcat..." + File.lineEnding;
				startTomcatProcess = new NativeProcess();
				execute(startTomcatProcess, "start");
			}

			public function stopTomcat():void
			{
				log.text = "Stopping Tomcat..." + File.lineEnding;
				stopTomcatProcess = new NativeProcess();
				execute(startTomcatProcess, "stop");
			}

			public function execute(process:NativeProcess, arg:String):void
			{
				// Get a file reference to the JVM
				var file:File = new File(javaHome.text);
				if (Capabilities.os.toLowerCase().indexOf("win") > -1)
				{
					file = file.resolvePath("bin/javaw.exe");
				}
				else
				{
					file = file.resolvePath("Home/bin/java");
				}

				var tomcatHomeFile:File = new File(tomcatHome.text);

				// Start the process
				try
				{
					var nativeProcessStartupInfo:NativeProcessStartupInfo = new NativeProcessStartupInfo();
					nativeProcessStartupInfo.executable = file;
					nativeProcessStartupInfo.workingDirectory = tomcatHomeFile.resolvePath("bin");
					var processArgs:Vector.<String> = new Vector.<String>();
					processArgs[0] = "-Dcatalina.home="+tomcatHome.text;
					processArgs[1] = "-classpath";
					processArgs[2] = tomcatHomeFile.resolvePath("bin/bootstrap.jar").nativePath;
					processArgs[3] = "org.apache.catalina.startup.Bootstrap";
					processArgs[4] = arg;
					nativeProcessStartupInfo.arguments = processArgs;
					startTomcatProcess = new NativeProcess();
					startTomcatProcess.start(nativeProcessStartupInfo);
					startTomcatProcess.addEventListener(ProgressEvent.STANDARD_OUTPUT_DATA,
						outputDataHandler);
					startTomcatProcess.addEventListener(ProgressEvent.STANDARD_ERROR_DATA,
						errorOutputDataHandler);
				}
				catch (e:Error)
				{
					Alert.show(e.message, "Error");
				}
			}

			public function outputDataHandler(event:ProgressEvent):void
			{
				var process:NativeProcess = event.target as NativeProcess;
				var data:String = process.standardOutput.readUTFBytes(process.standardOutput.bytesAvailable);
				log.text += data;
			}

			public function errorOutputDataHandler(event:ProgressEvent):void
			{
				var process:NativeProcess = event.target as NativeProcess;
				var data:String = process.standardError.readUTFBytes(startTomcatProcess.standardError.bytesAvailable);
				log.text += data;
			}

			private function readConfig():XML
			{
				var file:File = File.applicationStorageDirectory.resolvePath("config.xml");
				if (file.exists)
				{
					var fileStream:FileStream = new FileStream();
					fileStream.open(file, FileMode.READ);
					var xml:XML = XML(fileStream.readUTFBytes(fileStream.bytesAvailable));
					fileStream.close();
					return xml;
				}
				else
				{
					return null;
				}
			}

			private function writeConfig():void
			{
				var xml:String = '<?xml version="1.0" encoding="utf-8"?>' + File.lineEnding;
				xml += "<config>" + File.lineEnding;
				xml += "<javaHome>" + javaHome.text + "</javaHome>" + File.lineEnding;
				xml += "<tomcatHome>" + tomcatHome.text + "</tomcatHome>" + File.lineEnding;
				xml += "</config>" + File.lineEnding;

				var file:File = File.applicationStorageDirectory.resolvePath("config.xml");
				var fileStream:FileStream = new FileStream();
				fileStream.open(file, FileMode.WRITE);
				fileStream.writeUTFBytes(xml);
				fileStream.close();
			}

			private function openWithDefaultEditor():void
			{
				var file:File =
					File.createTempDirectory().resolvePath("tomcat_launcher_console.txt");
				var fileStream:FileStream = new FileStream();
				fileStream.open(file, FileMode.WRITE);
				fileStream.writeUTFBytes(log.text);
				fileStream.close();
				file.openWithDefaultApplication();
			}

		]]>
	</fx:Script>

	<mx:Form width="100%">
		<mx:FormItem label="Java Home" width="100%">
			<s:TextInput id="javaHome" width="100%"/>
		</mx:FormItem>
		<mx:FormItem label="Tomcat Home" width="100%">
			<s:TextInput id="tomcatHome" width="100%"/>
		</mx:FormItem>
	</mx:Form>

	<s:HGroup>
		<s:Button label="Start" click="startTomcat()"/>
		<s:Button label="Stop" click="stopTomcat()"/>
		<s:Button label="Clear Console" click="log.text=''"/>
		<s:Button label="Open in Default Text Editor"
				  click="openWithDefaultEditor()"/>
	</s:HGroup>
	<s:TextArea id="log" width="100%" height="100%"/>

</s:WindowedApplication>
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • DZone
  • LinkedIn
  • StumbleUpon
  • Twitter
This entry was posted in Flex. Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

7 Comments

  1. Posted December 3, 2009 at 8:23 am | Permalink

    Wow. Interesting use-case. This really opened my eyes to the usefulness of the Native Process API. Imagine having AIR delegate tasks which it cannot accomplish alone to Java, such as communicating via bluetooth, infrared, comm-ports and the like. Our imagination is the only limitation. Thanks for sharing

  2. MZ
    Posted December 3, 2009 at 10:31 am | Permalink

    Hi,
    although I have the latest (3.12.09 current) Air Runtime installed, I am getting the following error message:
    “This application requires an update to Adobe Air that is not avaiable for your system”
    Then the TomcatLauncher.exe does not start.
    Do you know why?
    TIA
    MZ

  3. Henrik
    Posted December 8, 2009 at 11:48 am | Permalink

    I really like this but could you please tell me how to open the project file on a mac. It would be nice if you could ditribute the source in another format. I haven’t found anything that takes a .fxp-file.

  4. Henrik
    Posted December 8, 2009 at 4:26 pm | Permalink

    Sorry, I realized .fxp is the new FlashBuilder project format.

  5. Posted December 9, 2009 at 6:58 am | Permalink

    I think there is a copy-paste typo in your code. Line 066 should read

    execute(stopTomcatProcess, "stop");

    Note the name of the process passed.

    Stu

  6. Posted December 21, 2009 at 1:27 pm | Permalink

    Christophe,

    As always, thanks for the great examples.

    Quick question: since nativeProcess requires using a native installer, how would this affect applicationUpdaterUI, since it traditionally points to an .air file for updates?

    Can .air files be used to update an app after first install? It would seem strange to refer to an .exe in the update-config.xml file on the server, since if you’re supporting multiple platforms you’d need to point to three different installers…something I don’t think applicationUpdaterUI handles.

    Thanks for any thoughts and the great blog.

    Daniel

  7. Cosma Colanicchia
    Posted January 11, 2010 at 4:47 am | Permalink

    @Daniel,

    I had the same question, and found out that it simply doesn’t work.. quite a big issue for us, I hope that this is due to the beta status, and that it will be fixed in the final release.

    I filed a bug in the Adobe issue tracker: https://bugs.adobe.com/jira/browse/SDK-24824

    Please vote/suggest any workaround :)

    Cosma

6 Trackbacks

  1. [...] Christophe Coenraets Rich Internet Applications, Flex, AIR, Java Skip to content BioUsing Flex with Spring « Tomcat Launcher: Sample Application using the AIR 2.0 Native Process API [...]

  2. By AIR 2.0 Web Server using the New Server Socket API on December 7, 2009 at 2:46 pm

    [...] exploring Java integration using the new Native Process API (here and here), Excel integration using the new file.openWithDefaultApplication(), and the new [...]

  3. [...] API -Screenrecording app with AIR 2.0 Beta -Embedding Tomcat and BlazeDS in an AIR 2.0 Application -Tomcat Launcher: Sample Application using the AIR 2.0 Native Process API – “Open in Excel”: Another AIR 2 Mini Sample -Voice Notes: Record Voice Notes and [...]

  4. [...] Tomcat Launcher: Sample Application using the AIR 2.0 Native Process API [...]

  5. [...] Tomcat Launcher: Sample Application using the AIR 2.0 Native Process API [...]

  6. By links for 2010-05-26 « sySolution on May 26, 2010 at 10:01 am

    [...] Tomcat Launcher: Sample Application using the AIR 2.0 Native Process API (tags: air) [...]

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>