Subscribe to feed
Blog | About

Archive for Flex

Startup Technology Expenses

One aspect of a software startup that cannot be escaped is money must be spent on technology and development of technology. Whether this is a good or bad thing depends on if you ask the engineer or the accountant. My general rules of thumb are:

  • Purchases that help people do their jobs better or faster are worth paying for.
  • Before spending money on something look for an open source alternative that is cheap or free. Often you will find something better or only slightly inferior to the commercial item.
  • If you are going to spend money on something, the price-to-substance ratio is important.

And now a smattering of thoughts and plugs for each rule of thumb in the context of our company that is full of my personal opinions. I do realize that the earliest days of a startup largely must ignore most of this list. For example, when you don’t have an office yet (and everybody works from their homes) you don’t really worry about getting comfortable chairs, good machines, etc. for that office.

Purchases that help people work

  • Screen real estate is important. I used to think this meant 2 screens but have refined this to mean total resolution. With my macbook pro and spaces I went from using 2 computers and 3 monitors to just 1 laptop and I feel more efficient now. I like to give 2 monitors to any person that wants one - especially engineers, designers, and QA.
  • Good chairs are worth paying for. I’ve worked places in the past that gave their engineers hand me down garage sale garbage to sit on. The nature of a software company means people are going to spend a lot of time sitting and the chairs need to be good enough that people don’t notice them all day (and often longer given the nature of startups). Aerons are great if you can get a deal on them but there are solid options in the $200 - $300. CWC sells better quality furniture at the best price.
  • Don’t skimp on workstation hardware. I personally think the mac path is worth the premium for developers. On a per-item basis the price is virtually equivalent but given Dell’s willingness to haggle and price slash (especially if buying multiple items) a premium does remain. I think it is worth it.

Open Source

  • We use Java and I think it is better than .NET and it is free. You can build it on Windows/Linux/Mac and you can deploy it to all 3 as well. I think PostgreSQL is better than SQL Server (and MySQL). The Microsoft lock in has never made any sense to me and I feel the Java community is a great place in that the number of unqualified engineers is relatively small and it is full of extremely qualified people. Java also scales vertically or horizontally very, very well. It has the whole 10,000 frameworks/libraries to choose from “problem” that .NET does not have but that is okay in my opinion. We went with Spring/Hibernate/DWR and it has worked out great.
  • PostgreSQL is fantastic. The developers are accessible and helpful and the community is strong. We’ve run it up to a 1TB database and it handles it just fine. You obviously have to run it on a reasonable machine as load increases but it scales vertically wonderfully and there are addons for replication. Check out Slony and/or Mammoth Replicator if you need that replication, we haven’t yet. Visit this site for installing Postgres on your local mac workstation.
  • Linux is the way to go for servers. I don’t think the Linux/Dell combo can be beaten on the server side.

Price-to-Substance Ratio - Some Examples

  • IntelliJ IDEA is worth its cost. It is magical and exceeds a plugin-ridden eclipse install for features out of the box and I think the editing experience and source control interaction are superior.
  • Despite stability issues I think the Leopard incremental upgrade to OSX was worth it for productivity overall. Spotlight and Spaces have changed my workflow completely.
  • Dell provides a fantastic ratio here. I would strongly recommend them for server hardware, especially their latest models. Solid architecture, solid raid controllers, RAM, etc. If you go with Dell get in sync with a Small Business team. It will save you money and streamline the process as you get to talk to the same people every time. Their business lines of laptop (Latitude) and desktops (Optiplex) are also solid.
  • Good consultants and contractors are worth their rates for focused, time-constrained assistance. You have to be careful though because there are a large number of unqualified people posing as consultants and contractors that aren’t worth the time it takes to arrange a contract. If you find somebody you can work with and does a good job keep using them as needed.
  • Parallels is worth its very manageable price for providing IE6/IE7 testing to mac-using developers. See this post for help setting up the free VMs provided by Microsoft for doing this testing.
  • FlexBuilder isn’t worth the cost. When I used it a long while back it was $700+ with charting and had marginally more functionality than notepad2. Following that link, it looks like they are pumping Flex 3 now. The fact that Flex 2 has profound issues makes this especially troublesome.
  • Flex Data Services pricing defies all reasoning. $20k per CPU. Same for pretty much any other product that charges per-CPU. If anyone knows of ANY per-CPU product that is worth paying for let me know. I recently priced out a better WYSIWYG editor for portions of our product and they wanted pricing per CPU for a text editor.
  • And finally, I think sharp, qualfied engineers that you can interact with in person in the US are superior to any offshore team. When you consider the time differences, communication barriers, and general lack of quality offshore I believe a 5 man team of people that know what they are doing and work together here could out perform a 50 man team of offshore cube farm drones. I have 3 specific experiences (admittedly not that many) working with offshore teams. 2 ended in utter failure to complete the task, and 1 was bailed out of before it got too far along because even the onshore PM/BA assigned were completely clueless and ineffective. I feel like the offshoring development companies live in an alternative universe where you just keep a neutral look on your face through meetings and shuffle out inferior product making fixes until the customer is too frustrated, tired, or so accustomed to the low quality that they start to believe the software is good and consider the project a “success.”

So there you have a smattering of my thoughts. I expect to elaborate on many of these items in separate posts in the future. You can likely tell by the tones which items I find most interesting and/or alarming.

Comments (2)

Flex2 & DWR

In my last post I mentioned in passing some code we wrote to allow DWR calls inside Flex2 code. My goal with this post is to provide a short, clear example of doing this and then describe how we applied that to our project. This doesn’t handle timeouts, call batching, reverse Ajax/Comet, and probably some other DWR features I am forgetting. There are many improvements that could be made to it and it was only used in FireFox. You may want to be familiar with the ExternalInterface of Flash (which hopefully will have its bug fixed soon) and with Javascript and DWR before reading on. I apologize for any faulty formatting - still using vanilla, hosted wordpress and it doesn’t make code especially easy to work with.

There are 3 pieces to making it work:

  • An Html/JS container that your swfs must be run in.
  • An AS3 class that talks to the Html/JS container.
  • A layer of JS to make the DWR calls and route callbacks to AS3.

The Html/JS Container

For the sake of brevity here is a wrapper that will work. For a more robust wrapper take a look at the Html/JS wrappers that FlexBuilder generates and base your custom DWR wrapper files off of those. You need 2 wrappers - one for run mode and one for debug.

DWRContainer.html & DWRContainer-debug.html

<html>
<head>
  <!– Core JS Stuff –>
  <script type=”text/javascript” src=”dwrWrapper.js”>
  </script>
  <script type=”text/javascript” src=”/ajax/engine.js”>
  </script>

  <!– DWR Controllers –>
  <script type=”text/javascript” 
    src=”/ajax/someDWRController.js”></script>
</head>
<body style=”padding: 0px; margin: 0px”>
  <object id=’flexObject’
    classid=”clsid:d27cdb6e-ae6d-11cf-96b8-444553540000″
    codebase=’http://download.macromedia.com/pub/
      shockwave/cabs/flash/swflash.cab#
      version=9,0,0,0′ 
    height=’100%’
    width=’100%’>
  <param name=’src’ value=’MyFlexApp.swf’/>
  <param name=”allowScriptAccess” value=”always”/>
  <embed name=’flexObject’ src=’MyFlexApp.swf’
    allowScriptAccess=’always’ pluginspage=’http://
      www.macromedia.com/shockwave/download/
      index.cgi?P1_Prod_Version=ShockwaveFlash’
    height=’100%’
    width=’100%’/>
  </object>
</body>
</html>

The details to notice are the inclusion of dwrWrapper.js, the DWR engine.js file, and then any number of DWR controllers that you want to use in Flex2. Ideally these DWR controller JS files would have their content injected dynamically but they are small files so I don’t think it is a big issue to include them all and this makes the example simpler. Now to create the debug version just copy the above contents but use MyFlexApp-debug.swf for the src values. Once you have your wrappers built, you will need to tell FlexBuilder to use these when running/debugging. To set this go to the Run menu, click either the Run or Debug option that is expandable, then click Other. In the “Url or path to launch” section you can enter your run and debug wrapper locations.

Now lets look at the JS file.

The Javascript Layer

dwrWrapper.js


/**
 * Receives requests from AS3 for a Dwr call
 * @param wrapUUID The unique identifier for the wrapper
 *   used to make the call
 * @param jsMethodName Name of JS method to call
 * @param jsMethodArguments Arguments for JS method
 * @param asCallbacks The callback object containing AS3
 *   function UUIDs
 */
var dwrCall = function(wrapUUID, jsMethodName,
    jsMethodArguments, asCallbacks){

  var methodToCall = eval(jsMethodName);

  // add callback to arguments
  jsMethodArguments.push(buildDwrCallback(
    wrapUUID, asCallbacks.callback,
    asCallbacks.errorHandler));

  // call the desired JS method
  methodToCall.apply(null, jsMethodArguments);
}

/**
 * Constructs a callback map for a Dwr call.
 * @param wrapUUID The UUID of the AS3 wrapper
 * @param asCallback UUID of the AS3 function to call
 *   upon successful callback
 * @param asErrorHandler The UUID of the AS3 function to
 *   call if there is an error
 * @returns A JS callback object for a DWR call
 */
var buildDwrCallback = function(wrapUUID, asCallback,
    asErrorHandler){
  var jsCallback = handleDwrCallback;
  var jsErrorHandler = handleDwrError;

  var wrappedJsCallback = jsCallback;
  var wrappedJsErrorHandler = jsErrorHandler;

  // wrap the callbacks in functions that call them
  // with dwr response + AS3 callback info
  jsCallback = function(dwrResponse){
    return wrappedJsCallback.call(null, wrapUUID,
      dwrResponse, asCallback);
  }
  jsErrorHandler = function(dwrResponse){
    return wrappedJsErrorHandler.call(null, wrapUUID,
      dwrResponse, asErrorHandler);
  }

  return {callback: jsCallback,
    errorHandler: jsErrorHandler};
}

/**
 * Called when a DWR function returns -
 *   sends response to AS3 code
 * @param wrapUUID UUID for the wrapper
 * @param dwrResponse The actual response received
 *   from the server through Dwr
 * @param asCallback UUID for the AS3 function to call
 */
var handleDwrCallback = function(wrapUUID, dwrResponse,
    asCallback, extraArguments){

  var asCallbackCall = “document['flexObject'].
    handleJsCallback” + wrapUUID.replace(/-/gi, “”) +
    “(dwrResponse, asCallback)”;

  eval(asCallbackCall);
}

/**
 * Called when a DWR call fails -
 *   sends error to AS3 code
 * @param wrapUUID UUID for the wrapper
 * @param dwrResponse The actual error response received
 *   from the server through Dwr
 * @param asErrorHandler UUID for the AS3 function
 */
var handleDwrError = function(wrapUUID, dwrResponse,
    asErrorHandler){

  var asErrorCall = “document['flexObject'].
    handleJsError” + wrapUUID.replace(/-/gi, “”) +
    “(dwrResponse, asErrorHandler)”;

  eval(asErrorCall);
}

Some comments on this JS code:

  • Instead of passing the string names for the AS3 functions to use for callbacks and errors I used UUIDs. On the AS3 side a mapping from UUID to function reference is maintained.
  • I use eval above to call the AS3 functions registered on ExternalInterface because the apply function isn’t available.
  • I assigned each usage of the wrapper a UUID as well that is taken into account when calling back into AS3. By appending these wrapper UUIDs to the callback and errorHandler AS3 functions registered on the ExternalInterface we can ensure the Dwr callbacks are going to the correct AS3 code. Each DWR controller will have a separate wrapper instance on the AS3 side each with its own UUID.
  • In the buildDwrCallback function we are using the technique described here to pass the AS3 callback/errorhandler information through the DWR call such that we can use it in the handleDwrCallback/handleDwrError functions. If the above makes sense (and it will look weird to someone new to JS) you should be able to see how extra arguments could be passed on top of the callback info for the benefit of your AS3 code.
  • I strip the dashes from the wrapper UUIDs because they were causing problems with ExternalInterface and calling the registered AS3 functions. You will notice below that the dashes are stripped to match on the AS3 side of things.

So that covers the Html/JS wrappers for running your Flex2 code against DWR. The last step is the AS3 class that knows how to communicate with this wrapper.

The Actionscript Layer

DwrWrap.as

package someLib
{
  import flash.external.ExternalInterface;
  import mx.collections.ArrayCollection;
  import mx.utils.UIDUtil;

  /**
   * This class provides access to Dwr.  It assumes
   * that flex is being used in an Html container that
   * has access to dwrWrapper.js.
   * Each instance wraps a single Dwr controller.
   */
  public class DwrWrap
    private var functionMap:Array;
    private var wrapUID:String;

    public var controllerName:String;

    /**
     * Constructor for DwrWrap.  Initializes function
     * map and registers ExternalInterface callbacks
     * @param controllerName The name of the
     *   JS/Dwr controller to use
     */
    public function DwrWrap(controllerName:String){
      this.controllerName = controllerName;
      this.functionMap = new Array();
      this.wrapUID = UIDUtil.getUID(this);

      // register AS3 callbacks as name + UUID
      ExternalInterface.addCallback(”handleJsCallback” +
        this.wrapUID.replace(/-/gi, “”),
        handleJsCallback);
      ExternalInterface.addCallback(”handleJsError” +
        this.wrapUID.replace(/-/gi, “”),
        handleJsError);
    }

    /**
     * Method that calls JS using ExternalInterface.
     * @param jsMethodName JS/Dwr function to call
     * @param jsArgs A single argument or an
     *   ArrayCollection of arguments to use in JS call
     * @param callback An untyped object with callback
     *   and errorHandler properties
     */
    public function serverCall(jsMethodName:String,
        jsArgs:*, callback:Object = null):void{

      var jsCallback:Object = new Object();

      // map UUIDs to AS3 callback function references
      var callbackUID:String =
        UIDUtil.getUID(callback.callback);
      jsCallback.callback = callbackUID;
      functionMap[callbackUID] = callback.callback;

      var errorUID:String =
        UIDUtil.getUID(callback.errorHandler);
      jsCallback.errorHandler = errorUID;
      functionMap[errorUID] = callback.errorHandler;

      // make sure jsArgs is an array - assumption in JS
      if (jsArgs is ArrayCollection){
        jsArgs = jsArgs.toArray();
      }
      else if (!(jsArgs is Array)){
        var argArray:Array = new Array();
        argArray.push(jsArgs);
        jsArgs = argArray;
      }
      else if (jsArgs == null){
        jsArgs = new Array();
      }

      ExternalInterface.call(”dwrCall”, this.wrapUID,
        (controllerName + “.” + jsMethodName), jsArgs,
        jsCallback);
    }  

    /**
     * This method receives the callback from JS and
     * then calls the appropriate AS3 callback
     * @param dwrResponse The response from Dwr
     * @param asCallback UUID of AS3 function to call
     */
    public function handleJsCallback(dwrResponse:*,
        asCallback:String):void{

      // use the UUID to look up the function reference
      var callbackFunction:Function =
        functionMap[asCallback];

      if (callbackFunction != null){
        delete functionMap[asCallback];
        callbackFunction(dwrResponse);
      }
    }

    /**
     * This method receives errors from JS and
     * then calls the appropriate AS3 errorHandler
     * @param dwrResponse The error response from Dwr
     * @param asErrorHandler UUID of the AS3 function
     */		
    public function handleJsError(dwrResponse:*,
        asErrorHandler:String):void{

      // use the UUID to look up the function reference
      var errorFunction:Function =
        functionMap[asErrorHandler];

      if (errorFunction != null){
        delete functionMap[asErrorHandler];
        errorFunction(dwrResponse);
      }
    }
  }
}

Some comments about the AS3 code:

  • Notice that by routing all callbacks and errorHandlers through 2 AS3 functions we only have to register those 2 function on the ExternalInterface. These two functions need to be registered for each DWR controller so we append a UUID to the names to ensure uniqueness of function names.
  • AS3 functions to use for DWR callbacks and errors are sent to JS as UUID strings. When we get calls back from the JS we can then look up the actual function references and call them directly. This is nice because it allows the AS3 callback object to contain actual references and not strings.

Finally, you can make DWR calls from AS3 that look like this. Assume you have a DWR controller exposed as someDWRController.js and that this controller has an addNumbers method that takes 2 arguments and returns the sum.

Usage Example

// instantiate a wrapper for the DWR controller
var wrap : DwrWrap = new DwrWrap(”someDWRController”);
// call a function on that controller
wrap.serverCall(”addNumbers”, 5, 6,
  {callback: someASCallback, errorHandler: someASErrorHandler});

// the callback and errorHandler
public function someASCallback(theSum): void{
  Alert.show(”Should be 11: ” + theSum);
}
public function someASErrorHandler(errorInfo): void{
  Alert.show(”Stuff broke”);
}

This should work for making basic DWR calls. In our project Mark cleaned this code up, allowed extra passthrough arguments to be passed to callback functions, injected the DWR Controller JS file content dynamically, and then integrated this logic into our Cairngorm-based application. DWR services were then listed and looked up from a Services.mxml file and executed by delegates like any other service. The callback and errorhandler functions for DWR calls were bound to the result/fault methods of our command objects and upon return the results from Dwr were translated into typed objects. I won’t share that code because I didn’t write it.

Hopefully just this will help someone stuck with Flex2 in an environment where DWR is being used. If you haven’t already read my opinions on Flex2 and you are trying to decide between Flex2 and Html/JS you should.

If you are determined to use Flex2 with DWR you really need to read about the ExternalInterface bug that may force you to refactor your value objects depending on their complexity.

Comments (6)

Flex2 Opinions

At work we decided to give Flex2 a lengthy, honest trial run as a replacement of the Html/JS used for an important component of our application suite. It is an intranet sort of application where we controlled the environment and it seemed like a promising place to put Flex2 to work.

After months spent learning and becoming accustomed to Flex2 our team decided it simply is not a technology ready for building enterprise applications. We are now taking a pragmatic approach: continuing with Html/JS but using the Flex-Ajax bridge for charting, graphics, and multimedia. In my opinion these are things Flex2 is great for.

To anyone that may be considering the Flex2 platform, know that you will find large crowds of evangelists (many of them Adobe employees) on the web describing trivial applications and simple examples to make Flex2 sounds like a fantastic way to solve problems. When we starting dealing with hard problems our experience was extraordinarily negative.

My biggest issues with Flex2 are the following. My viewpoint here is largely a comparison of Flex2 against Html/JS for building rich internet applications. Though based on months of nontrivial development these are my individual opinions.

  • It is not production ready. It is not a stable product. We have an internal wiki page titled “Flex bugs & gotchas” and its content has grown to a substantial size. Take a look at the latest hotfix notes to see what sort of nontrivial issues are just being addressed.
  • The community is small and inexperienced. Flexcoders and Flexcomponents are great groups if you have trivial questions. We asked a half dozen hard questions over the last few months and those were met with either complete silence or confirmation from a Flex team member that we had bumped into another known bug with no confirmed fix date. Compare this to the mailing list and forums for Spring, Hibernate, JS/Dojo/Prototype/DWR, or PostgreSQL where your questions are answered so thoroughly that you feel questions far more difficult than the ones you face have already been conquered by people more knowledgeable. Patches to specific issues are also the norm in these communities. Maybe patching the Flex2 SDK is an easy process, I didn’t try it.
  • The livedocs are down more than they are up. These would be pretty helpful if they were reliable.
  • Existing examples of reasonably nontrivial Flex2 applications on the web are really buggy. Go visit the Flex2 search appliance or the golf store that someone made (can’t remember the url right now - will look it up) with a debug version of the flash player installed and watch the errors bubble out.
  • The tools are not established. Flex-Ajax bridge is bugged. ExternalInterface is profoundly bugged (see my earlier post about this). The Flex Ant tasks are bugged (only with the most recent update did a failed compile cause a build to fail and most of the command line arguments aren’t stubbed into their corresponding ant tasks). Want to display some HTML in your application? Get ready to wrap your application in an HTML container, invoke an iframe in that container, and then hack massage it into place over your application so that it looks like it belongs there. Granted, all of these tools are still in Adobe labs so it is fair to expect alpha-ish quality, but I need real tools now to build applications with.
  • Adobe’s priorities with this platform are exceedingly frustrating. They are working on Apollo and Flex3 while huge, show-stopping bugs exist in Flex2 and prevent it from being a viable alternative to more established technologies.
  • Flex Data Services might be a good thing. It might even make the platform reasonable for an enterprise-grade application but it costs $20k per CPU. I can buy a beefed out database server with dual procs, 16gb RAM, and a 16 disk sata raid array for less than the price of licensing one of its CPUs.
  • It is extremely difficult to find talent with Flex2 experience or a desire to obtain it. We want full-time, exceptionally intelligent software engineers. Our efforts to find Flex2 developers brought us contractors that refused to work onsite and people that think knowing individual, popular languages is more important that understanding the concepts that make them the same. We work with area recruiters and the vibe from them was much the same.
  • The evangelists drive me nuts. They are too worried about presenting Flex2 as the slickest piece of engineering ever created to discuss the real issues that prevent real work from being done with it.
  • While not unique to Flex2, the compile cycle is killer. I found over time, as our application grew, it completely crushed my productivity. Compare this to pressing F5 with the alternative.
  • FlexBuilder is worth $10 and not $749 (that’s with charting, but if you are using Flex2 you should take advantage of the charting). No refactoring, no find usages, no code generation, plugin problems (we had a hard time getting subclipse and some other plugins to work with it), awfully unimpressive automatic importing, ctrl-space completion, ctrl-click, etc, etc. Comparing the Java intelligence of IntelliJ/Eclipse to FlexBuilder isn’t fair or applicable, but comparing the Html/JS intelligence of those tools to FlexBuilder is and they put FlexBuilder to shame. The design view, though helpful in some situations for coders, is not a design tool. Our designer hated it. The debugger pales in comparison to a Firefox instance with Firebug and the web developer toolbar installed.
  • Flex2 without FDS does not play well with anything other than xml or web services. I can tell by the search terms used to find this blog that people are interested in using DWR with Flex2. We built an Html/JS container that provided a communication layer between Flex2 and our exposed DWR objects and though it worked for basic calls (did not handle timeouts, call batching, or reverse Ajax/Comet) it meant involving a 3rd debugger in development and it was really just very difficult to work with so many layers at the same time. Regardless I will try to post the code in the future to help anyone else in the same boat.
  • The whole platform feels disconnected. If you pull down the source code for the core Flex2 classes and take a peek, you will find big balls of logic commented out. It is as if they starting commenting out things that are broken until it was stable enough to ship. Also the impression I get from working with Flex2 is that Adobe has a grabbag of developers - some quite good, and some quite junior - and then turned them loose on different pieces of the compiler and FlexBuilder. The result is a language that is far less powerful than a fully dynamic language like Javascript and unawareness in some areas of the platform that other areas exist. A concrete example: Flex2 uses metadata internally ([Bindable], [ArrayElementType], etc) and essentially had a wonderfully powerful metadata construct written and working. You couldn’t use this metadata for your own code until 2.0.1 because they hadn’t thought to expose it. Even now you have to build with ANT and use some command line arguments that aren’t stubbed out in the Flex ANT tasks to make it work. Someone smart wrote the metadata code and it wasn’t leveraged in the rest of the platform. That feeling occurs repeatedly.

That said, Flex2 is great for making pretty things involving charting or multimedia. Our frustration began and grew as we tried to complete the nuts and bolts of a large enterprise application with it. If your application is heavy on the areas where Flex2 shines, then it may be worth the frustration. Perhaps your experience has been different? All I know is we are back to Html/JS using DWR for transport and Xulrunner/Firefox for the environment and getting work done again. Apollo sounds promising, but I am more excited about Xulrunner and Firefox3.

My advice is this: If your team has 1 or more of these following trends in place, I would strongly discourage a move to Flex2:

  • You are building an application for the public (you do not control the environment or user base). Flash player 9 is not ubiquitous and the 96% flash player penetration number that the evangelists trumpet include all versions of Flash player. Flash player 9 is no where near 96%. Though it wouldn’t be fair to not mention that MySpace and Youtube are going to help Flash player 9’s penetration number increase faster than version 8’s.
  • You have solid Html/Css/JS knowledge in place now. While I prefer this stack enourmously over Flex2 I also realize there is a learning curve involved. If you have already conquered that learning curve rest easy knowing you are using the superior tool.
  • You are building business applications or applications that aren’t heavy multimedia. Flex2 is great for multimedia and I don’t hesitate to admit its superiority here. Keep in mind that you can use the ExternalInterface or the Flex-Ajax bridge to use Flex2 widgets inside your Html/JS with relative ease.
  • You are building large applications. For smaller applications Flex2 is fine. Cairngorm helps (and in fact may help you keep your sanity) but it doesn’t solve all of the problems.
  • You don’t currently have nightmares about scrollbars and don’t want to start having them :) This will only make sense to someone who has worked with Flex2.

Needless to say, probably the last Flex-related post for this new blog except for the DWR bit or unless I have something interesting to share related to the charts and multimedia strengths we will continue to use. I tried to keep this post objective; it does represent 6 man months of experience. I love many of Adobe’s products and I recognize that there are several cases where Flex2 would be a great tool. Large, enterprise applications is not one of those areas.

That said, it is a new tool in my toolkit and because it has obvious strengths I can certainly see myself using it for those strengths in the future.

Comments (19)

Flex2 Resources

I mentioned flexcoders in my last post but didn’t provide a link or more information. I thought it would be helpful to list the Flex2 resources I rely on in one place as I know I struggled to find resources initially and the more places indexed in Google for people to find the better.

Point 1: Don’t use the forums at www.Adobe.com for Adobe Flex2 questions. I had 0 luck here and they are virtually silent.

Adobe Labs
This is a fairly helpful site both because it provides access to tools and utilities (like the Flex2 ANT Tasks, Flex-Ajax bridge, and lots more) and because the wiki pages associated with the various projects are very active.

flexcoders Yahoo Group
Why is Yahoo Groups being used? I don’t know, but this is probably the best place to post questions and look for answers. You’ll want to use Google to search the group though as my searches fail if I use more than 3 or 4 words (a fault of Yahoo).

flexcomponents Yahoo Group
Another good group - focused on component development.

The Livedocs
These can often be better (when they aren’t down) than the help system built into FlexBuilder due to the comment sections for each topic article and the fact that the Livedocs team can fix corrections immediately and keep them more up to date.

Cairngorm Documentation
Cairngorm is a solid framework for building substantial Flex2 applications. If you are working with Flex2 I highly recommend becoming familiar with it.

I have found all of the above to be quite helpful.

Comments

Flex2 External Interface Bug

Lest I lead people to believe I am a complete Flex fanatic with my previous posts, I thought it was time to post about a brickwall that we have not yet been able to conquer (and maybe someone will happen to read it and have an answer).

At work I use and love DWR. One component of our flagship product suite was initially a Javascript/Html interface that used DWR to communicate with our Java 1.5/Spring/Hibernate back-end. We decided to give Flex2 an honest, lengthy trial run as a potential replacement for the interface technology for this component.

When the decision was made to give this a shot, we had man-months of back-end functionality built and exposed via DWR. It simply did not make sense to rewrite this code. Further, there was no alternative to DWR that I liked as much. I refuse to believe FDS is worth its ridiculous price tag (I’d rather save 40k per server for additional engineers, additional disk arrays and more servers thank you) and all of the open source options I evaluated paled in comparison to DWR. We spent a man-week or so and built a communication layer that allowed to us to use DWR much as we were with Javascript inside of our Actionscript. This communication layer used the ExternalInterface API of Flex2 in some clever ways.

Now to the point of this whole post: we recently discovered what appears to be a pretty substantial bug in ExternalInterface. I’ll use JSON notation to demonstrate. It took us some time to realize what was going on.

Inside of the Javascript communication layer I can receive an object from DWR that looks exactly like this in Firebug:


mainObject = {
    id: “1234″,
    name: “mainObject Name”,
    mainControl: “mainObject Control”,
    subObject: {
        id: “abcd”,
        name: “subObject Name”,
        subControl: “subObject Control”
    }
}

Further, I can trace this with Firebug line for line right up through the point at which it is handed off as a parameter to a method exposed on ExternalInterface.

Now on the Actionscript3 side of things I have my breakpoint and debugger ready to stop immediately after reception of this object on the other side of the ExternalInterface. When it arrives it now looks like this:


mainObject = {
    id: “abcd”,
    name: “subObject Name”,
    mainControl: “mainObject Control”,
    subObject: {
        id: “abcd”,
        name: “subObject Name”,
        subControl: “subObject Control”
    }
}

Notice what happened:

  • All same named properties are set to the values of those properties on the subObject. The values on the mainObject were completely overwritten!
  • The differently named properties (mainControl and subControl) retained their correct values.

This a huge deal. To continue down this Flex2/DWR path we would have to refactor dozens and dozens of value objects to ensure no value objects that are nested inside of each other share property names and all future development would have to be done with this awareness as well.

This seems like a show stopping bug and that is why deep down I have to believe we are simply doing something odd with our implementation. Has anyone else encountered this issue or have any idea as to why we would be experiencing it? I don’t have any more code I can run a debugger on to try and figure this out.

I’ve got a post on the flexcoders list but our experience with that list has typically been that difficult questions are met largely with silence.

Comments (5)

Flex2 Custom Metadata

Flex2 has a pretty cool metadata construct that can be a huge feature for development - especially for certain types of tools and common objects. Mark at work put together a clever and very effective XMLNode-to-Typed-Class conversion tool using this technique and I thought I would share some of the bumps encountered to help anyone else with a similar task.

Here is a quick and to the point example. Using your own metadata requires Flex 2.0.1 as a required argument to mxmlc wasn’t added until that version. For this small example, lets say we wanted to use a [Transient] metadata tag to mark properties of a value object as client-side only to let some sort of imaginary persistence manager know that property should not be written to a database. You could use the same technique to specify types for objects in a collection (though this can be done with the specialized [ArrayElementType] tag), to specify types for both keys and values in a map structure, or any number of other scenarios.

With this example, you could have a value object that looks like this:


-----
public class ContactVO{
    [Transient]
    public var fullName: String = “John Doe”;

    // more properties and methods follow
}
—–

Now you can write code that can read this metadata information from these ContactVO objects at run time. Here is a drawn out and hardcoded example of doing this. You basically just use Flex2’s describeType method to obtain an XML description of an object and then use e4x to query that information.


-----
var contact:ContactVO = new ContactVO();

// get the E4X XML object description
var typeInfo:XML = describeType(contact);

// get the property we marked up with metadata
var fullNameInfo:XMLList = typeInfo..accessor.(@name == “fullName”);

// check for the [Transient] tag
if (fullNameInfo..metadata.(@name == “Transient”).length() > 0){
    // do something - maybe exclude it from a commit list
}
—–

Metadata tags can have properties too. Lets say you want to mark up a map structure so that you can determine the type of both the keys and values at run time. Flex2 doesn’t provide a convenient hashmap-ish class by default, so just assume you’ve made one.


-----
public class ContactVO{
    [HashMap(keyType="String", valueType="String")]
    public var phoneNumbers: MyHashMap = new MyHashMap();

    // more properties and methods follow
}
—–

You can access the metadata properties like this.


-----
var contact:ContactVO = new ContactVO();

// get the E4X XML object description
var typeInfo:XML = describeType(contact);

// get the phoneNumbers property we marked up with metadata
var phoneDesc:XMLList = typeInfo..accessor.(@name == “phoneNumbers”);

// grab the “HashMap” metadata description
var phoneMetaData:XMLList =
phoneDesc..metadata.(@name == “HashMap”);

// grab the “keyType” property from the “HashMap” metadata
var metaDataDesc:XMLList = phoneMetaData..arg.(@key == “keyType”);

// finally, grab the value of this “keyType” argument
var typeValue:String = metaDataDesc.@value;

Alert.show(”This HashMap has keys of type: ” + typeValue);
—–

To use your own metadata tag like this, you will need to use a custom argument when building your swfs. For the transient example above it would look like -keep-as3-metadata+=Transient. You can repeat this for as many custom metadata tags as you like. If you are building with Flex Builder, you can add this to the compiler arguments under Project -> Properties -> Flex Compiler. The problem we found was that Flex Builder ignores this entirely unless you are building a DEBUG swf. This was a enourmous headache and I still don’t know how to make Flex Builder use the additional arguments in both DEBUG and RUN mode. Only having DEBUG buildable through Flex Builder is fine for development but certainly not for production. The workaround available now is to use ANT (see my earlier post for an example of setting this up).

Hope someone else finds this helpful. The metadata construct available in Flex2 is quite powerful, you can use it to build tools and libraries that use annotations similar in some ways to those of Java 1.5.

Comments

Flex2 & ANT

I’ve had the opportunity to work with Adobe’s Flex2 and the Flex Builder IDE a substantial amount and have loads of tips and thoughts about the environment - both positive and negative. This first Flex2 post is about building a Flex2 project with Ant.

Flex Builder has some issues. While the debugger and design view can be nice, its weakness with ctrl-space completion and lack of refactoring support can really alienate a Java developer accustomed to the fantastic intelligence of Eclipse and IntelliJ IDEA (and not to mention fully capable Linux versions). I’ll post specific pointers and tips for working around Flex Builder’s weaknesses in the future. For now I’ll just describe how to use ANT to build your Flex2 projects.

My philosophy on projects is that if your team is larger than 1 person there should be absolutely no dependencies on an IDE or an environment to check out, modify, or build the code. If this doesn’t sound incredibly obvious, I highly recommend reading Practices of an Agile Developer or something similar.

That said, putting together an ANT build file that mimics what Flex Builder does under the hood is a great idea as it eliminates the IDE dependency and makes more advanced deployments easier as you can take advantage of a proven, professional build system in ANT. I’ll just do a basic configuration here and mention a few gotchas. If are hitting a brick wall with ANT and Flex2 just leave a comment and I will try to help.

If you just want links go to the Flex Ant Tasks page at Adobe Labs and optionally read this page for some documentation and tips.

Alternatively, here is a quick example. This assumes you have a project setup like this:

/myProject/Main.mxml - the main application mxml file
/myProject/etc - contains images, xml, or other project resources
/myProject/lib - contains any external libraries (.swcs)
/myProject/src - your .mxml and .as files
/myProject/build - the directory to place build artifacts
  • Make sure you have ANT properly installed
  • Extract the flexTasks.jar file from the zip on the Adobe Labs page and place it in your project’s lib directory
  • Create a build.xml file in your project’s root directory (see below)
  • Optionally configure Flex Builder to use your project’s build directory for compiler output and running/debugging (instead of the “bin” directory that is used by default). This simply make the build process and output more similar to that of developers using the ANT tasks

Sample build.xml

This covers compiling debug and non-debug versions of your swf as well as copying additional resources into your build directory. The build file can get complicated if you have lots of source directories, lots of external libraries, or if you need to pass the compiler special arguments (such as for preserving custom metadata).


<?xml version="1.0"?>
<!–
Required Environment Variables:
FLEX_HOME: must point to the root of your flex sdk (probably in flex builder directory)
–>
<project name=”myProject” default=”compile”>

    <!– Make the Flex Ant Tasks available –>
    <taskdef resource=”flexTasks.tasks” classpath=”lib/flexTasks.jar”/>

    <!– Module properties –>
    <property environment=”env”/>
    <property name=”build.dir” value=”build”/>
    <property name=”swf.name” value=”MyProjectSwf”/>
    <property name=”root.mxml” value=”Main.mxml”/>
    <property name=”locale” value=”en_US”/>
    <property name=”FLEX_HOME” value=”${env.FLEX_HOME}”/>

    <!– Clears out the build directory –>
    <target name=”clean”>
        <echo message=”Removing build directory contents…”/>
        <delete includeemptydirs=”true”>
         <fileset dir=”${build.dir}” includes=”**/*”/>
        </delete>
    </target>

    <!– standard compile w/o debug –>
    <target name=”compile”>
        <antcall target=”perform-compile”>
            <param name=”debugMode” value=”false”/>
        </antcall>
    </target>

    <!– compile with debug –>
    <target name=”compile-debug”>
        <antcall target=”perform-compile”>
            <param name=”debugMode” value=”true”/>
        </antcall>
    </target>

    <target name=”perform-compile”>
        <!– Make sure the build directory exists –>
        <mkdir dir=”${build.dir}”/>

        <mxmlc
            file=”${root.mxml}”
            output=”${build.dir}/${fullSwfName}”
            incremental=”true”
            debug=”${debugMode}”
        >
            <load-config filename=”${FLEX_HOME}/frameworks/flex-config.xml”/>

            <!– directories outside of your ‘main’ source directory –>
            <source-path path-element=”${FLEX_HOME}/frameworks”/>

            <!– list any swcs in your lib directory to include –>
            <library-path dir=”lib” append=”true”>
                <include name=”Cairngorm.swc”/>
                <include name=”SomeOtherLibrary.swc”/>
            </library-path>

            <!– flex sdk core libraries –>
            <library-path dir=”${FLEX_HOME}/frameworks” append=”true”>
                <include name=”libs”/>
                <include name=”../bundles/${locale}”/>
            </library-path>
        </mxmlc>

        <!– copy over the etc directory contents –>
        <antcall target=”copy-files”/>
    </target>

    <!– Copies contents of /etc to /build –>
    <target name=”copy-files”>
        <copy todir=”${build.dir}”>
            <fileset dir=”.” includes=”etc/**/*”/>
        </copy>
    </target>

</project>

This may not be the most barebones build file possible for an initial example, but it is a realistic one that takes into account external libraries and non-code items such as images and xml that you may want to use outside of the swf. With this file you can run any of the following commands from the root of your projects:

ant:
runs the default target, currently set to “compile”
ant compile:
builds a swf from your source code and places it in the build directory along with the contents of /etc
ant compile-debug:
same as compile except it generates a swf that can be debugged
ant clean:
deletes the contents of the build directory

ant copy-files:
copies the contents of /etc to /build. used as part of other targets but could be used individually

Some Notes

  • The FLEX_HOME property must be set in your build file - it is an undocumented requirement of the mxmlc task
  • Many mxmlc arguments available if you invoke the compiler directly are not available in the mxmlc ANT task. For these you will need to create a custom configuration file and use it in your ANT target. See this page for an example of setting up the config file. To use a config file in your build.xml simply add an additional <load-config> property to the mxmlc task.
  • If you are using Flex Builder it is best to use its preset builder and not change it to use your ANT targets. If you hook Flex Builder to ANT directly you will lose the benefits of inline error messages and warnings when building.
  • Note that the result of the copy-files target isn’t something automatically performed by the build process of Flex Builder. There may be a cleaner way, but you can configure an additional builder for your Flex Builder project that will run the copy-files target of your ANT build file after the regular build-related tasks have completed.
  • The above build.xml file assumes all developers have a FLEX_HOME environment variable set on their machine. This may seem like a hassle but it is a far more reasonable approach to obtaining consistency across platforms and environments that requiring a specific and expensive IDE.
  • This build file assumes you are running your swfs directly and not inside an html container. If you need to run your project inside of a container take a look at the html-wrapper ANT tasks described at that Adobe Labs page or put together your own wrappers and copy them over to the /build directory as is being done above for the contents of /etc.

I have a lot of Flex-related content that I would like to share, mostly brick walls that have been conquered. I hope to get them all posted eventually so that google can get them indexed and they can smooth adoption for other developers working with Flex.

Comments (8)