Subscribe to feed
Blog | About

Archive for DWR

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)

Generating DWR Javascript Files

I’ve talked about DWR before. It is a mature project that is well constructed and very useful. DWR generates Javascript files at runtime that you can include in your client-side code and use to call exposed Java methods. A few days ago I wanted to generate these files as part of our normal build process so that we could open and read them directly. More importantly (for me at least) I wanted to have them in my project directory so that IntelliJ could index them. This may seem trivial but I am obsessive about letting my IDE aware of as much as possible and I found this to be a huge help when writing client-side Javascript. Now I can ctrl-space complete, ctrl-click, and everything else IntelliJ can do with Javascript on these DWR Javascript files.

I posted my plan to the dwr mailing list and as usual the creator of DWR himself showed me exactly how to accomplish this when using a dwr.xml file for configuration and when not using Spring. If this is your environment I encourage you to search that list. There is a great community there and I have found it extremely helpful and responsive many times.

That said, I wanted to do this in our Spring project using DWR 2.0rc2 with an annotations based DWR configuration. It was pretty easy to take Joe’s example and adapt it to this case. Here is the core of the Java code to get started.

First the imports I am using:

import org.directwebremoting.util.FakeServletContext;
import org.directwebremoting.util.FakeServletConfig;
import org.directwebremoting.impl.ContainerUtil;
import org.directwebremoting.impl.DefaultContainer;
import org.directwebremoting.extend.Remoter;
import org.directwebremoting.spring.SpringContainer;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.servlet.ServletContext;
import javax.servlet.ServletConfig;

Now the code that sets up a DWR Remoter object which we can request Javascript content from:

ClassPathXmlApplicationContext appCtx =
  new ClassPathXmlApplicationContext(”testContext.spring.xml”);

Map<String, String> initParameters = new HashMap<String, String>();
// comma-delimited list of annotated classes
initParameters.put(”classes”, “FooAjaxController”);
// telling DWR to use a different Container subclass
initParameters.put(”org.directwebremoting.Container”, 
  ”org.directwebremoting.spring.SpringContainer”);

ServletContext servletContext = new FakeServletContext();
ServletConfig servletConfig = new FakeServletConfig(”dwr-test”, 
  servletContext, initParameters);

DefaultContainer container = 
  ContainerUtil.createDefaultContainer(servletConfig);
((SpringContainer)container).setBeanFactory(appCtx);
ContainerUtil.setupDefaultContainer(container, servletConfig);
ContainerUtil.configureFromAnnotations(container);

Remoter remoter = (Remoter)container.getBean(Remoter.class.getName());

// do what you want with the Javascript content

appCtx.close();

In the above code, before the appCtx.close() line you can pull Javascript content out of the Remoter object like this:

remoter.generateInterfaceScript("fooAjaxController", "/fake/path");

The second parameter is the path you want the Javascript files to use for accessing corresponding Java servlets and in this case they didn’t matter. fooAjaxController is the Javascript name for the DWR Java class. My preference for naming of DWR classes is that all Java class names end with AjaxController and that the bean name and DWR Javascript name are the same and the camelcase version of the class name.

From here it is pretty straight forward to do whatever you want with this Javascript content from the remoter. In my case I pulled the bean definitions out of the Spring context, separated out all beans with a name ending in AjaxController and then built lists of bean names and class names at run-time to use for the classes parameter and for reading the content back and writing it out to files. This way nothing had to be hard-coded and changes made to the bean configurations in our Spring context were automatically accounted for.

Hopefully someone else finds this useful.

Comments (2)

DWR Annotations & Spring

We recently upgraded DWR in our primary product from version 1.1.4 to version 2.0rc2. The newest version offers a pile of great improvements on an already excellent piece of code. One of my largest motivations for the upgrade was the new annotation-based configuration option and the resulting elimination of our dwr.xml file that had grown to be large and confusing. The signatures block can be completely dropped with Java 1.5 and the create and convert elements can now be represented 100% in annotations.

Our codebase was built with Spring in mind from the start and I hit a small block when trying to get the annotations to work. This page provides a start but doesn’t specifically mention Spring. Here is what an annotated RemoteProxy class looks like with Spring factored in:


import org.directwebremoting.annotations.RemoteProxy;
import org.directwebremoting.annotations.Param;
import org.directwebremoting.annotations.RemoteMethod;
import org.directwebremoting.spring.SpringCreator;

/**
* A Java controller exposed via DWR
*/
@RemoteProxy(
    creator = SpringCreator.class,
    creatorParams = @Param(name = “beanName”, value = “someAjaxController”),
    name = “someAjaxController”
)
public class SomeAjaxController{

    @RemoteMethod
    public int trivialExample(int param1, int param2){
        return param1 + param2;
    }
}

That’s it. As someone who lived mostly in the client-side of DWR I wasn’t familiar with the creator concept and it took me more than a single google run to find the creator/creatorParams being used clearly in the RemoteProxy annotation. In our project we standardized on making the beanName and Javascript name the same but they could be different in the above example.

Hopefully this saves someone else a few additional minutes of searching. I hope to post about Spring and DWR more in the future - they are both excellent projects with experienced, helpful communities.

Comments (5)

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)