Flex Builder 3 for Linux on hold

April 22nd, 2009

Yesterday, 21st of April, Ben Forta came to London to give a talk about ColdFusion 9 new features. It was really good session, Ben demoed all new major ones. Integration with Hibernate, new Adobe AIR ColdFusion services and of course brand new ColdFusion IDE codenamed Bolt (which does look pretty solid by the way) were most exciting.

After the talk I managed to ask Ben if he knows what is going on with Flex Builder 3 for Linux. The answer was not what I was expecting to hear. Unfortunately the project is currently on hold. Quoting Ben there is not enough requisition for the product to continue its development.

Without a doubt this is really bad news for anyone who invested in infrastructure for building AIR and Flex applications on Linux, it may happen that we are going to stay without the tools. I still believe that Adobe is going to rethink its decision. If they could only fix this one issue: debugger keeps disconnecting from the debugging session… I think most of the developers would be happy then.

Railo, BlazeDS and CFC instances

April 20th, 2009

There is a lot of buzz recently around Railo since version 3.1 finally went open source and many well known CF developers joined the project. This gave me the motivation to finally see how Railo really works. I decided to give it a shot on one of already running applications. The choice was made, I selected a Flex application powered by ColdFusion 8.1 utilizing Adobe ColdFusion classes, providing 20 different gateways, using ODBC data sources and operated with very rich and sophisticated Flex UI exchanging data using typed VOs.

First experience was great. No issues at all, application started very quickly, what takes about 20 seconds on ColdFusion is almost immediate on Railo. All CFCs worked fine, even those using internal ColdFusion classes. Overall performance was fantastic, all JDBC ODBC connections running smoothly. I have to admit that Railo guys have done really great job.

It was time to test Flex UI, a major issue popped out straight away. Railo provides integration with Adobe’s open source Blaze Data Services. It appears to work perfectly fine with all standard CFML types, strings, structures, arrays, etc., however not with CFCs. At the current state Railo is not able to serialize and deserialize them. Below is the sample test case.

Service.cfc that Flex talks to:

<cfcomponent>

  <!---
    Accepts a CFC instance: --->
  <cffunction name="getCfc" access="remote" output="false" returntype="string">
    <cfargument name="inst" type="com.gruchalski.railo.test.TestCFC" required="true" />
    <cfreturn "ok" />
  </cffunction>

  <!---
    Returns a CFC instance on request: --->
  <cffunction name="outCfc" access="remote" output="false" returntype="com.gruchalski.railo.test.TestCFC">
   <cfset var inst = createObject("component", "com.gruchalski.railo.test.TestCFC") />
   <cfset inst.property1 = "Hello from Railo" />
   <cfset inst.property2 = 20 />
   <cfreturn inst />
  </cffunction>

</cfcomponent>

And here is the source of com.gruchalski.railo.test.TestCFC:

<cfcomponent>
  <cfproperty name="property1" type="string" />
  <cfproperty name="property2" type="numeric" />
  <cfset this.property1 = "" />
  <cfset this.property2 = 0 />
</cfcomponent>

To access the service I am using following Flex application:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">

  <mx:Script>
    <![CDATA[
      import mx.utils.ObjectUtil;
      import mx.rpc.events.ResultEvent;
      import mx.controls.Alert;
      import mx.rpc.events.FaultEvent;
      import com.gruchalski.railo.test.TestCFC;

      private function sendInst():void {
        var inst:TestCFC = new TestCFC();
        inst.property1 = "Ħello Railo!";
        inst.property2 = 10;
        testService.getCfc( inst );
      }

      private function loadInst():void {
        testService.outCfc();
      }

      private function onFault(event:FaultEvent):void {
        Alert.show(event.fault.message);
      }
      private function onResult(event:ResultEvent):void {
        Alert.show( ObjectUtil.toString( event.result ) );
      }

    ]]>
  </mx:Script>

  <mx:RemoteObject id="testService" source="Service" destination="ColdFusion"
    endpoint="http://localhost:8888/flex2gateway"
    fault="onFault(event);"
    result="onResult(event);" />

  <mx:Button label="Get CFC from Railo" click="loadInst();" />
  <mx:Button label="Send CFC to Railo" click="sendInst();" />

</mx:Application>

with this com.gruchalski.railo.test.TestCFC VO:

package com.gruchalski.railo.test {

  [RemoteClass(alias="com.gruchalski.railo.test.TestCFC")]
  public class TestCFC {
    public var property1:String = "";
    public var property2:Number = 0;
  }
}

When loading a CFC instance following issue is reported:

faultCode:Server.Processing faultString:’An error occurred while serializing server response(s).’ faultDetail:’null’

In case of sending a VO:

faultCode:Server.Processing faultString:’flex.messaging.MessageException: railo.runtime.exp.UDFCasterException : invalid call of the function getCfc, first Argument (INST) is of invalid type, can’t cast Object type [Struct] to a value of type [com.gruchalski.railo.test.TestCFC]‘ faultDetail:’null’

First one is obvious, BlazeDS does not know how to serialize CFC provided by Railo. Second issue is probably caused by the fact that Railo does not treat magic structures as CFCs. What I call a magic structure looks like this:

<cfscript>
  struct = structNew();
  struct.property1 = "Some property";
  struct.property2 = 20;
  struct.__type__ = "com.gruchalski.railo.test.TestCFC";
</cfscript>

When structure like that comes from the outside world or is being sent to outside world (for example BlazeDS iteraction or SOAP request) Adobe ColdFusion treats it as a CFC instance.

Despite the issue I think Railo is a very good choice in its current state. For most small applications current BlazeDS integration level is enough, new applications can easily use what is there right now. And I am sure that Railo team is working hard to enable full support.

It is good however to know the limitations before making the switch to Railo, specially when so many people consider to do so.

So what is going on with Flex Builder for Linux?

April 20th, 2009

First, I have to say that I am big fan of Flex technology. I have worked a little bit with Flex 1, then 1.5 and now full time with Flex 2 and 3. I saw how the platform evolved, I saw Macromedia’s and Adobe’s enthusiasm when they have been announcing every new release. I am really greatful to those who delivered this great product.

But I have a question, first however I want to explain what made me to ask it.

If you are a Flex developer you are probably aware that in year 2007 Adobe released Flex Builder alpha for Linux. I just went to Adobe Flex Builder for Linux Forums looking for some sort of solution to an issue that keeps bugging me recently. I would say that FB3 Alpha for Linux works perfectly fine, there is no visual editor but I do not really need it, I could even buy it in the current form. But only when one issue would be fixed – the debugger that keeps disconnecting all the time.

The issue is that for unknown reason the debugger just disconnects when debugging AIR or Flex applications on Fedora, Suse, Kubuntu, Ubuntu and probably any other distro. It seems like it is a tough one or no one really cares anymore about FB3A. Just take a look at this thread http://forums.adobe.com/thread/112489. It was created on 28th of November 2007 and verified by someone from inside (http://forums.adobe.com/message/750458#750458, based on the fact that he says "We are investigating on this issue "). That person confirmed as well that there is a JIRA ticket for the very same problem. It can be found here http://bugs.adobe.com/jira/browse/FBE-184, opened on 19th of September 2007. It hangs on Internal Review status. Never resolved. For last 18 months.

It generally happens to be a major issue for all Flex developers working on Linux platform. The simpliest answer possibly is “we are using alpha software, we cannot expect it to work”. Fair enough. But here is the point of this post.

So what is going on with Flex Builder 3 for Linux? Is it abandoned? Someone asked that question a month ago http://forums.adobe.com/thread/203362. Never got a reply. It was not confirmed nor demented.

So please Adobe. Speak up! Paraphrasing Steve Ballmer: to push Flex you need developers, developers, developers!. You have plenty on Windows and OSX. You may have more on Linux. Just give them answers and show some support.

MySQL on AIR website is live

April 18th, 2009

I have just finished MySQL on AIR project website. It is available here: http://mysqlonair.gruchalski.com/. No release download yet, not all information is there. It is just a first step.

First release build should appear there withing next few days.

Mosso Cloud Servers DNS records tip

April 17th, 2009

If it happens that you use Mosso Cloud Servers and its DNS management panel remember to always set different name for each record. Otherwise if you want to remove one of them, if there is another record with same name control panel with return an error. In such case the only way to fix your DNS records is to drop whole domain, add it again and recreate all records from scratch. Unfortunately.

GitHub gets an issue tracker

April 16th, 2009

This appeared on Twitter about an hour ago, GitHub has an issue tracker now. Follow the link http://github.com/blog/411-github-issue-tracker to watch the video and read more about it.

Local function variables and parameters in ColdFusion and Railo

April 16th, 2009

Today I was doing some heavy refactoring on a project I am working for quite a long time. Moving methods, changing arguments, removing unnecessary CFCs. We are using PDF and XML merging so the project utilizes Adobe ColdFusion. Now, I am using Adobe CF for like 7 years now, I thought I know it well. While testing changes I have been stopped by an error saying "local variable X declared twice&quot. Something wasn’t right there, I just moved few methods to different components and changed few argument names. It took me good 5 minutes to figure out (even though, what I found out later, the error message was explicitly saying it) that the problem was caused bu using the same function parameter and local variable names. This was very confusing, in any other programming language I used before such an issue doesn’t exist, that was probably why I did not notice I am doing something wrong. To make sure I am right I created this sample CFC.

<cfcomponent>
  <cffunction name="testFunc">
    <cfargument name="arg1" type="numeric" />
    <cfset var arg1 = "" />
  </cffunction>
</cfcomponent>

And I called it in the browser. ColdFusion confirmed my assumption. My first thought was "hang on, is this a general CFML issue or just another bit that isn’t working correctly just in Adobe ColdFusion"? I started Railo and created exactly the same CFC. It appears that in Railo it is working as expected, calling the CFC with ?wsdl option returns WSDL definition.

Definitely a hint to remember.

AIR HTTPService does not return response headers

April 16th, 2009

Using Mosso Cloud Servers and Cloud Files for few days now I thought it would be useful to have AS3 library that would let me accessing my Cloud Files account directly from AIR. Just after spending last 2 hours writing prototype I came to the point when I wanted to read response headers that Cloud Files REST API returns and it appears to be impossible (Cloud Files returns authentication data using headers). According to what other people say HTTPService has some major limitations.

It got more interesting when I looked at HTTPStatusEvent.responseHeaders and HTTPRequestMessage.recordHeaders properties. Flex is able to determine proxy server headers only and only when using NetworkMonitor. Otherwise responseHeaders is always null.

I can understand why this limitation exists for Flex applications running in the browser. Not all browsers have the ability to access response headers and Adobe wants to keep Flash Player consistent across browsers. But why this wasn’t fixed for AIR where Adobe has full control on where and how Flash Player is running? And why this isn’t left in competence of the developer as HTTPStatusEvent.status is? I have no idea.

This is really disappointing. Does it mean people have to craft there REST for AIR? I hope someone at Adobe is going to have a look at this issue.

Safely storing configuration with passwords in Adobe AIR applications

April 15th, 2009

Almost every application designed to manipulate users data requires storing custom settings, often with passwords. MySQL on AIR is no different. One of the major features is saving connections settings locally. The most obvious way is to use XML but quickly an issue pops out – how to store passwords? XML is saved as open text unless some kind of encryption/decryption mechanism is implemented. But is it worth implementing such a solution when in most cases it will require storing hash keys on the hard drive or compiled directly in the application? Fortunately Adobe AIR provides mechanism called Encrypted Locale Store. It allows for saving critical bits of information in completely isolated, safe place. I won’t go into details how it works, . I rather focus on how I solved the problem with XML for all noncritical settings and Encrypted Local Store for passwords without the need of compiling any hash into the application itself.

The key lays inside the uk.co.riait.controllers.ConnectionsController class. Basically the idea is to store some unique ID in connection nodes and use the same unique ID as an EncryptedLocalStore.setItem() name argument. Let’s see how it’s done.

package uk.co.riait.controllers {
  import flash.data.EncryptedLocalStore;
  import flash.filesystem.File;
  import flash.filesystem.FileMode;
  import flash.filesystem.FileStream;
  import flash.utils.ByteArray;

  import org.swizframework.controller.AbstractController;

  public class ConnectionsController extends AbstractController {

    [Bindable] public var connections:XML = <root />;
    private var _initialized:Boolean = false;

    public function init():void {
      if (!_initialized) {
        var dir:File = File.userDirectory.resolvePath( ".mysqlonair" );
        if (!dir.exists) {
          dir.createDirectory();
          writeXml();
        } else {
          var file:File = File.userDirectory.resolvePath( ".mysqlonair/connections.xml" );
          if ( !file.exists ) {
            writeXml();
          }
          var stream:FileStream = new FileStream();
          stream.open( file, FileMode.READ );
          connections = new XML(stream.readMultiByte(stream.bytesAvailable, "utf-8"));
          stream.close();
        }
        _initialized = true;
      }
    }

    public function addConnection(
      type:String, host:String, port:Number, user:String, pass:String, schema:String, name:String, uid:String, existing:Object ):void {
      if ( existing != null ) {
        dropConnection(XML(existing).@uid.toString());
      }
      var node:String = "<node dbtype='" + type + "' host='" + host + "' port='" + port + "' "
        + " user='" + user + "' schema='" + schema + "' uid='" + uid + "' name='" + name + "' />";
      var xml:XML = new XML(node);
      connections.appendChild(xml);
      var passwordBytes:ByteArray = new ByteArray();
      passwordBytes.writeUTFBytes( pass );
      EncryptedLocalStore.setItem(uid, passwordBytes);
      writeXml();
    }
  }
}

Let’s scan through the code quickly, but focus on addConnection() method first. All connection parameters and uid are passed as arguments. First the method checks if connection with given uid exists. If it does it is going to delete it ensuring there is always just one instance of given connection. Next it createss new connection XML node, password is not saved there. Instead uid attribute is set on XML node. Right after appending newly created nodea ByteArray is used to save the password in the local store under uid key and XML is saved to the file.

It is time to focus on init() method. First the method checks it ConnectionsController is already initialized. If it is not it verifies existence of .mysqlonair directory in user’s home directory, creates it if necessary and writes an empty XML file with just a root node. Otherwise, if the directory was found it is going to check if the file is still there. If it is missing it is going to be created, again just root node is going to be saved, then all connections settings will be loaded. So the question now is how the password is accessed?

    [Bindable("dataChange")] public function getConnectionPassword(uid:String):String {
      var passwordBytes:ByteArray = EncryptedLocalStore.getItem(uid);
      return passwordBytes.readUTFBytes(passwordBytes.length);
    }

Above method is the answer. This method takes uid argument and reads the password from Encrypted Local Store. To see how it is executed let’s take a look at following test class (my wrapper class does not use events so I’m going to simulate time lapse using timers):

package uk.co.riait.tests {
  import flash.events.TimerEvent;
  import flash.utils.Timer;

  import mx.utils.UIDUtil;

  import uk.co.riait.controllers.ConnectionsController;

  public class ConnectionsControllerTest {

    private var ctrl:ConnectionsController = new ConnectionsController();
    private var uid:String = UIDUtil.createUID();

    public function runTest():void {
      ctrl.init();
      var timer:Timer = new Timer(1000, 1);
      timer.addEventListener(TimerEvent.TIMER_COMPLETE, onInitializedTimer);
      timer.start();
    }

    private function onInitializedTimer(event:TimerEvent):void {
      ctrl.addConnection(
        "MySQL", "someHost", 1000, "username", "P4ssw0rd", "mysql", "Some connection",
        uid,
        null);
      var timer:Timer = new Timer(1000, 1);
      timer.addEventListener(TimerEvent.TIMER_COMPLETE, onSavedTimer);
      timer.start();
    }

    private function onSavedTimer(event:TimerEvent):void {
      trace(ctrl.getConnectionPassword(uid));
    }
  }
}

Running the test class creates new ConnectionsController and initializes it. Next in onInitializedTimer() the connection is added and new timer is spawn. In onSavedTimer() method the string P4ssw0rd is displayed. And that was the password I used for new connection.

To delete a connection from XML file a dropConnection() method is used:

    public function dropConnection(uid:String):void {
      delete connections.node.(@uid==uid)[0];
      EncryptedLocalStore.removeItem(uid);
      writeXml();
    }

Thanks to this approach MySQL on AIR stores passwords safely without having any knowledge about IDs being used. The only place where IDs are stored is XML file.

What is MySQL on AIR?

April 12th, 2009

It is an Adobe AIR client for MySQL databases. About 3 months ago I moved to Ubuntu as my primary OS and the only thing I was missing was nice MySQL tool. I found few different projects, most obvious ones are MySQL Query Browser and phpMyAdmin, but there are always these small issues… MySQL Query Browser doesn’t allow resizing query area (really annoying when writing large queries) and phpMyAdmin, well… it is phpMyAdmin and it is to big for what I needed.

Remembering that someone created MySQL driver in ActionScript (asSQL) I decided to write an application that will be cross platform and is going to offer as much as any other desktop application would.

Now please remember, this is still work in progress, the code is not well structured and it is changing very often but some major functionality is already there.

MySQL on AIR interface

Following features are currently available:

  • query window with results data grid
  • new table window
  • edit table window
  • SQL editor with code coloring (please check notes on performance below), line numbering and selected query part execution (feature known from MSSQL Management Studio)
  • multiple windows
  • connections management, data stored in XML file, passwords in encrypted storage
  • number of context navigation options (for details please visit following URL http://wiki.github.com/radekg/mysqlonair/todo)

Here is a raw list of options I plan to implement in near feature:

  • query window progress bar and cancelling running query
  • wire all context navigation options
  • create and restore CSV, XML, SQL and YAML backups
  • handling binary files
  • query result data grid improvements, cell alignment and sort support depending on column data type
  • query editor improvement – code hints (similar to what can be found in MSSQL Management Studio 2008)
  • diagrams and foreign keys support

Notes on performance

While developing the application I discovered following performance issues. Code coloring works perfectly on Windows, there is no speed problems even for queries 500 lines long (I didn’t tested larger ones yet). Unfortunately on Linux with Flash Player 10 I could not get good TextRange performance (more information and sample code soon in separate post) so code coloring is by default disabled on Linux (you can enable it if you want). I had no chance to test it on OSX. Second performance issue is results data grid. I tried running large queries and it appears that asSQL driver handles them very well but ArrayCollection does not. I would not try running queries that return large result sets. Other than that everything seems to be working fine. I’m aware of some small driver issues that I’m going to track down and report to asSQL project owner.

License

MySQL on AIR project is available under MIT license, it is free software, you can download, use and modify it free of charge. But please bear in mind that there is no warranty at all and at this stage it is not ready for production environments.

Where is it

If you want give it a try you can check it out from github: git://github.com/radekg/mysqlonair.git. You will then need Flex SDK or Flex Builder to build it.