Tuesday, November 15, 2011

More news from Adobe on Flex

The Adobe Flex team has finally come out from hiding long enough to post an edit to their original blog post that sent the community screaming. While this does not address all of my concerns, it makes me feel a bit better about the direction of Flex, as long as Adobe is still committing resources. Some key pieces of information are quoted below.

This is about all I needed to hear, although I do think it might turn into the CFML Advisory board debacle. This would just mean that Adobe brings it back in-house I guess:
Adobe will also have a team of Flex SDK engineers contributing to those new Apache projects as their full-time responsibility.

FINALLY, the statement that they don't hate Flex right now, just that it is a stopgap until HTML5 gets cool:
[W]e are incredibly proud of what we’ve achieved with Flex and know that it will continue to provide significant value for many years to come. We expect active and on-going contributions from the Apache community. To be clear, Adobe plans on steadily contributing to the projects and we are working with the Flex community to make them contributors as well.

Ok, I think this could work, but again, inklings of the last committee stuff they tried before:
We expect project management to include both Adobe engineers as well as key community leaders.

Awesome:
Adobe will continue to support applications built with Flex, as well as all future versions of the SDK

I don't buy this one. The first time there is a divergence in the committee, this will spell trouble for this initiative:
Adobe will undertake the required work to ensure Flash Builder is compatible with future releases of Flex SDK.

Well, while I still continue to learn HTML5 (the not-even-a-spec-or-supported-by-my-browser language), I will still happily create Flex applications for mobile. The complete post can be seen here, just scroll down past the stuff you read last time.

Monday, November 14, 2011

Making Sense of the Madness (Adobe)

So, I'm trying to write a quick and non FUD post about the recent Adobe news on Flex and Flash. I'm going to say a few things here, and everyone knows I'm an Adobe fanboy, but this stuff hit pretty hard and Adobe is not taking steps behind the scenes to clear up the confusion so the rumor mill is flying. Many peers and friends are now stuck in the lurch trying to figure out what is happening to their careers and the platform they spent years building. Primarily, what do we tell our clients that we worked hard selling these technologies as recently as last week. Here is what Adobe needs to address:
  1. Adobe did a terrible job working with and through the community that serves them well. They have abandoned the roadmap and have made us look inept. This needs to be addressed. A new roadmap needs to be released ASAP. Let everyone know you have a plan for the next few years and GET THE COMMUNITY BEHIND IT.
  2. Adobe needs to address our concerns with HTML5. They abandoned a great technology (Flex) for a technology that doesn't even have a spec defined yet. They may not have abandoned Flex at all, but man are they doing a shitty job saying that. Just let us know the plan for transition and what timeframe it happens in.
  3. What is happening with ColdFusion? We need to hear that the budget and plan has not been changed. SHOW THE ROADMAP!
  4. What's going on with AIR and mobile? Flex was the 'go to' for us to build cross platform apps. HTML5 makes great web sites. How about native apps. Don't tell me I HAVE to use PhoneGap. My hope is that I will still be using ActionScript to write AIR apps, but who knows! Adobe needs to let us know.
Mainly, Adobe needs to stand up and work on some succinct messaging and we need to see the 1, 2, and 5 year plans. Adobe can do whatever they want. That's fine, they need to do what they need to do to make $$. What is unacceptable is the poor messaging, divergence from their community focus and lack of response to the current mess they have made with their poor release of information. Hopefully when the dust settles, we will see this:
  1. Spoon takes care of Flex, and it doesn't turn into the mess that was the CFML Advisory Board debacle.
  2. Clients embrace Flex post Adobe, just like they embrace jQuery, Drupal and other OSS projects.
  3. Adobe continues to support AIR mobile apps written in ActionScript.
  4. Adobe continues to support Flash Builder with Flex, or opensources what exists.
  5. ColdFusion doesn't disappear into the darkness next week ;)
  6. LiveCycle goes through a 4th retooling and minimizes it reach and focus to core concepts. Adobe also retools the pricing structure to reflect this change.
  7. DRM and DAM becomes a focus with easy tools (outside of LiveCycle) that allow us to work them right into our production workflow.

Thursday, September 1, 2011

Accessing the Twitter Streaming API from Adobe Flex/AIR

Over the weekend I discovered an interesting new hobby, so being the geek that I am I wrote a mobile application to support it. I found that I really like watching the pictures people post on Twitter in real-time. This was based around hurricane #irene so seeing the incoming photos was awesome, although I suppose it could also be applied to even more interesting endeavors like #friskyfriday (NSFW) ;) In the application I wrote I wanted to connect to the Twitter live stream instead of using search. Although it doesn't send you the full firehose of tweets, I wanted everything in real-time for geek-ability.

I won't post all the gory details of getting this application up and running, but there were two things that anyone developing for this API will need to do, and here they are:

1. Connect to the Streaming API


I had tried to do this in the past and had much more luck using curl or Java to coax the tweets out of the Streaming API, but this time I wanted to do it all in ActionScript as I didn't want to keep track of anything in a database. I wanted real-time streaming of data as it came in, and nothing historical. To do this I used the URLStream class. URLStream opens a HTTP request and then downloads the received data as it comes. URLStream is especially suited to this type of request as the download does not have to finish before you can access the downloaded bytes. This means two things: 1) You can read the bytes as they come and maintain the connection to keep receiving bytes, and 2) you must be super careful, as the Streaming API sends chunks of data. This means that the last chunk you received from Twitter might not be a complete object. You can imagine what this does to the parser (more on this later).

The Streaming API current supports both basic and OAuth authentication. Twitter says that basic authentication is deprecated, so use at your own risk. This article will use basic auth as OAuth and Twitter has been done to death on
other blogs.

While setting up the stream controller, we will create a property that will hold our URLStream. This must exist in the scope of the controller as all of the events will interact with this stream as the URLRequest is processed.

private var stream:URLStream;
After instantiating the class we need to set up our URLStream. This is accomplished by defining a new request, setting the URI we want to hit, setting the basic auth parameters and finally adding event handlers to react to the data streaming in or any errors that might occur.

public function startStream():void {
  stream = new URLStream();
  var request:URLRequest;
      
  request = new URLRequest("http://stream.twitter.com/1/statuses/filter.json?track=&include_entities=1");
  
  // set up basic auth
  var encoder:Base64Encoder = new Base64Encoder();
  encoder.encode(appController.username + ":" + appController.password);
  var credentials:String = encoder.toString();
  var authHeader:URLRequestHeader = new URLRequestHeader("Authorization","Basic " + credentials);
  //add the header to request
  request.requestHeaders.push(authHeader);
  request.method = URLRequestMethod.POST;
  
  // in a real app I would also recommend handlers for SecurityErrorEvent.SECURITY_ERROR, IOErrorEvent.IO_ERROR at a minimum
  stream.addEventListener(ProgressEvent.PROGRESS, progressHandler);
  
  try {
    stream.load(request);
  } catch (error:Error) {
    trace("Bad URL.");
  }
}
A few notes about the code above... First, there is a track parameter being passed but it is currently empty. This allows you to filter your request. For my example I let the user enter this in on a prior view and passed it in via an application controller. Second, we are only listening for a single event, ProgressEvent.PROGRESS. In the real application I suggest you listen for at least security errors and input errors. You can also listen for HTTP status codes. This is important and Twitter would like for you to watch those codes and throttle your requests accordingly. Please make sure to do this or they will terminate your stream. Lastly, please note that we are requesting the response to be JSON. The streaming API only allows you to request JSON. Next, we need to define our handler. The one I am concerned with for this tutorial is the progress handler (it handles the ProgressEvent.PROGRESS event). When a progress event is received you can check the Stream object to see if enough bytes have been loaded to process the stream's data. In this application I didn't care how much data had been sent as I was more concerned with if the data available had any complete tweets (JSON objects) in it. To do this I fire off an event to another controller which processes the data.

private function progressHandler(event:ProgressEvent):void {
  // read the bytes
  var x:ByteArray = new ByteArray();
  stream.readBytes(x, 0, stream.bytesAvailable);
  
  // create an event to create the picTweets from the JSON
  var te:TweetEvent = new TweetEvent( TweetEvent.NEW_TWEET_RECEIVED );
  te.json = x.toString();
  // I'm using Swiz to dispatch my events - perhaps you should be also ;)
  dispatcher.dispatchEvent(te);
  
  trace("Progress Event Handled.");
}
We read the bytes from the stream as a ByteArray. The ByteArray data is then sent across to the other controller as a string using a custom event. The custom event looks like this:
package events
{
  import flash.events.Event;
  
  public class TweetEvent extends Event
  {
    public static const NEW_TWEET_RECEIVED:String = "events.TweetEvent.NEW_TWEET_RECEIVED";
    
    public var json:String;
    
    public function TweetEvent(type:String, bubbles:Boolean=true, cancelable:Boolean=false)
    {
      super(type, bubbles, cancelable);
    }
  }
}

2. Parse the resulting data


The handler for this event takes the JSON response and adds it to any other text left over in the buffer from prior requests. We then need to parse the JSON text to look for complete objects to deserialize. Twitter made this easy. They send along a newline character at the end of each JSON object. Simply split() the JSON text on newline and you'll have an array of objects. By trying to deserialize any of these objects using the JSON class (available from the
as3corelib package) we can create tweet objects. Any text that can't be deserialized will be put back into the String variable that is holding the JSON data to be parsed with the next data stream response.

public function handleNewTweet(json:String):void {
  // add the new json to the text storage - this is a private var for this controller
  jsonText += json;
  
  // now parse through the text for newline chars, which denote the end of a JSON object
  var aJSON:Array = jsonText.split(/\n/);
  
  
  if ( aJSON.length ) {
    // clear the jsonText for appending any partial objects
    jsonText = '';
    
    for ( var i:String in aJSON ) {
      // try to deserialize, if we error, we dont have a complete object in that array, add it back to the queue (it will always be the last item in the array that fails, if any)
      // deserialize the JSON
      if ( aJSON[i].length ) {
        try {
          var tweet:Object = JSON.decode(aJSON[i]);
          
          var j:String;
          
          // HERE IS WHERE YOU CAN ACTUALLY PROCESS THE TWEET
          
        } catch ( e:Error ) {
          // here is where we add the partial object back into the jsonText variable - will be appended to the next response
          if ( aJSON[i].length && aJSON[i].indexOf('\r') != 0 ) { jsonText += aJSON[i]; }
        }
      }
    }
  }
}
For my purposes I just parsed the entities for media and displayed it within my application. You have the full status object to play with for your application. Have fun, and if you use this drop me a comment so I can check it out!

Wednesday, August 31, 2011

ColdSpring Setter Injection versus Constructor Arguments Snafu

I was troubleshooting an issue a colleague of mine was having the other day, where to his credit he was following best practices but it shot him in the foot. Wanted to mention it here quickly.

Most times when you are injecting a bean into another bean in ColdSpring you will use setter injections. They look like:

<bean id="bean1" class="com.nictunney.Bean1"/>

<bean id="bean2" class="com.nictunney.Bean2">
  <property name="Bean1">
    <ref bean="Bean1"/>
  </property>
</bean>

Bean2 defines a mutator (setter) method, setBean1(). ColdSpring will inject the Bean1 into Bean2 when Bean2 is created by explicitly calling Bean2.setBean1(bean1). This is a best practice when performing dependency injection to prevent circular dependencies.

So the problem we were encountering in code was that the Bean2 constructor was calling a method that relied on the Bean1 property, which was of course undefined during the instantiation. It took a little while to track this down as this was a Model-Glue subapplication with lots of other stuff that could have been creating the error.

So, the lesson learned is to remember how ColdSpring and DI works. ColdSpring is going to create an instance of a bean, call the bean's constructor and then perform any necessary setter injections. It all makes perfect sense, except when you least expect it ;)

The answer in this case was to determine if a circular dependency would occur, which in our case it wouldn't, and instead inject the dependent bean via a constructor argument like this:

<bean id="bean1" class="com.nictunney.Bean1"/>

<bean id="bean2" class="com.nictunney.Bean2">
  <constructor-arg name="bean1">
    <ref bean="Bean1" />
  </constructor-arg>
</bean>

In the constructor call setBean1(arguments.bean1).

Wednesday, August 24, 2011

Are you ColdFusion Curious?

There is always lots of buzz about Adobe ColdFusion across social networking channels from developers. Most of it is positive, but ColdFusion also takes some abuse from programmers who have not used it. This criticism then gets met with some pushback from well-meaning CF developers, which in turn makes the bulk of us look like rabid loyalists.

Unfortunately for those scouring for information on language pros or cons, many of the negative statements are misinformed, heresy, and some of it is just good-natured ribbing (which I encourage as I do the same). I'd like to challenge all of you who use a web development language like Ruby, PHP, C# and ASP to take a single hour and attend one of the Adobe ColdFusion Developer Week sessions and challenge yourself to check out CF and see just how productive it can be. Don't worry, I won't tell any of your friends you were there ;)

Sunday, August 14, 2011

Android Mobile Application Connection Issues with Flash Remoting and ColdFusion

I was debugging an application tonight that uses Flex to connect an Android mobile application to ColdFusion via Flash Remoting. Every time I tried to call the remote method via the RemoteObject I got the message:

Channel.Connect.Failed error NetConnection.Call.Failed: HTTP: Failed

So I checked my LAN settings and then played around with my Mac firewall control panel. It was disabled so I was baffled. I then came across this post that hinted that it could be my usage of the CheckPoint VPN-1 SecureClient. Disabling and closing the client had no effect, so eventually I figured out I had to just disable the security policy (Tools > Disable security policy). I hope this helps someone out there from ruining an otherwise perfectly good evening of coding!

Wednesday, August 10, 2011

Dear ColdFusion Team: Please fix this stuff in Zeus

I love ColdFusion. I've built a 13 year career out of it (so far). I want to see CF continue to be a great language. Here are a few things I see that need to be addressed:

1. Shorthand for creating structures
This is one of those things that I'm not sure how it got put into the language, but lets fix it now before it becomes one of those embedded issues we just have to accept for version after version like 1 based array indices (more on that later). Fix it now!

// current
myStruct = {key="value"};

// suggested
myStruct = {key:"value"};

2. Arrays should be zero-indexed
This is one I've mentioned on for years. The standard across every other language is to use zero based indices. Why CF didn't do this from the get-go is an enigma to me. It is a PITA and requires a different sort of looping criteria as well as a different sort of index allocation during creation (unless you are using arrayAppend()). I would like to suggest making this backwards compatible by offering an application level variable that will permit you to enable the old 1 based indices while you are converting apps or maintaining legacy apps.

3. Looping over Arrays (item versus index)
This is a biggie that was discussed on other blogs recently. When I loop over an array in tags I do this:
<cfloop array="#myArray#" index="item">

This makes no sense. An index is just that, the current array index you are looking at. In CF this actually returns the item specified by the current index. I cannot get the current index without searching my array. The correct way to implement this would be to allow the user to specify either attribute. This would also prevent the user from having to track the index via an incrementer. I have had to do this frequently when trying to match up the current array with another array with related data.

<cfloop aray="#myArray#" index="i" item="item">

In this loop index would refer to the current index, and item would refer to the current item - makes sense right?

4. Looping over queries in CFScript
The way to do this currently is to loop from 1 to qry.recordCount. This isn't terrible, however I'd like to see this updated as such:
for ( row in myQuery ) {
}

The same could be said for an array loop.
for ( item in myArray ) {
}

5. Clean up object functions
Let's catch up with other languages on this one. len(), arrayLen() etc. Instead, I should be able to use a variable.length function regardless of the type (string or array). Also arrayAppend() (myArray.push()), arrayGetAt() (myArray.getAt(), myArray.pop()), listAppend() (myList.addItem()), etc. There are tons of them that should be addressed. You don't need to do anything but deprecate these existing functions for a few versions.

6. Query() object
Well, this one is tricky, as I've been trying to come up with a better way of handling this myself. Going back to the early days of ColdFusion (back when we called it Cold Fusion ;) ) the initial selling point of the language was the ability to interact easily with databases. This was so much the case that IIRC the tag was actually prefaced db and not cf! is a very simple way to access queries, and I continue to use it as opposed to its script counterpart. In fact, this is so much the case that I have to write my DAOs in tag based syntax instead of using script, which would be preferred.

I'm not going to go into all the reasons I dislike the current script based query syntax, but my biggest issue currently is with how query params are assigned. I'd recommend this approach:

var myQuery = new Query({
sql: "SELECT col from table where id = new queryParam({value: arguments.id, cfsqltype: 'CF_SQL_VARCHAR'})",
datasource: application.dsn
});

Having to use a Query object is also a bit frustrating although I think I can just tack execute().getResult() onto the statement above, but I'd have to check. It would be much easier to wrap the query object and have it call a query function, which in turn would function just like cfquery:

var myQuery = query(sql="SELECT col from table where id = new queryParam({value: arguments.id, cfsqltype: 'CF_SQL_VARCHAR'})", datasource: application.dsn);

Those are some initial thoughts for ColdFusion syntax changes. Please feel free to post your own in the comments below.

Sunday, August 7, 2011

Wednesday, April 27, 2011

CF Builder Express Edition - Yeah, It's free!

Some big news from Adobe was released today: ColdFusion Builder will now have an express edition. What this means is that after your CFB trial expires, you will be able to continue using Builder save a few features that will be disabled. It is up to you to determine if those features are worth the $300 price tag.

Here are some of the features that will not be available in the free edition:

  • Code Assist for Extensions
  • Code Insight
  • Extension Callbacks
  • Connections to remote CF Servers
  • Quick Fix
  • Remote Project Debugging
  • Refactoring
  • ColdFusion Search
  • Code Formatting
  • FTP Support
  • Log Viewer
  • Local File Browser
  • Code Hyperlinks
  • Hover Help

For me, I use some of those features and the time savings is worth the $109 upgrade price. You'll have to make that decision for yourself, but regardless, CFB Express is still full featured.

Here is a link to all ColdFusion Builder 2 Features for comparison.

Friday, April 8, 2011

CFServant: Instant Service Tier - Episodes 1 and 2

This post is meant to show how quickly you can deploy a service tier with CFServant.  My goal with writing CFServant was to make deploying CF as a service tier simple.  Well, I don't think it gets easier than this!

I'm new to Jing, so these videos are large (in resolution). They are linked below.

Episode I:
Basic Service Object Creation

Episode II:
Basic DAO Usage (also shows load() method)


One thing I should've done in the last video is call the load() method with a new ID instead of no ID. What this would do is return a new object with the specified ID set.

My next video will show the Remote Proxy, which is where I really think people will get excited about CFServant. Enjoy!

Thursday, April 7, 2011

New ColdFusion OSS Project Launched

In my last position I had some interactions with Ruby on Rails.  I was impressed by Rails and how quickly I could get a service tier up and running.  There were some things with setting up the Active Records that kinda irritated me though, and I could honestly care less about scaffolding.  Ever since that project I've been wishing I had something similar in ColdFusion.

Most of the applications I write anymore have a detached service tier and generally have a few different clients that hit those services.  More often than not I'm seeing the same methods over and over, and my mind returns to the stuff I saw in Ruby on Rails.  I've always been a big proponent of a RESTful interface, but at the same time I didn't always write my clients to utilize a RESTful interface.  I always created a bunch of methods that called similar a similar API though, like get(), put(), post() and delete().

Enter ColdFusion 9.  CF 9 provides CF ORM, a native implementation of Hibernate.  This allows us to do all sorts of cool stuff.  It permits us to provide common beans for each of our application objects.  Before, this would have to be completed with some mishmash of other techniques.

To that end I've created CFServant.  It serves two purposes.  The first is to create a bunch of generic service methods that allow you to stand up services for each of your CF ORM beans quickly.  By quickly I mean with two attributes added to your service CFC.  Instantly you'll have methods like get(), put() post() and delete().  The second purpose is to provide a remote proxy.  This proxy provides a remoting API that can be used from clients built in HTML and JavaScript (Ajax).  Any ORM bean you have a service defined for can now be accessed automatically by calling the getObjectName() method on the service proxy.  If I wanted to get all Users, I'd call getUsers().  Want a single user with the ID of 1?  Call getUser(1).  Everything is extensible so you aren't tied to the native implementation.

I plan on recording a video series on how this all works, but go check out the CFServant wiki for lots of information.

Wednesday, March 30, 2011

Article on jQuery and ColdFusion

RIARockstars just published my article on connecting jQuery clients to an Adobe ColdFusion service tier. Go check it out! It was really hard to fit so much into a short article, but I think it lays a lot of groundwork so I can delve into individual pieces in more detail. From the article I can see talking about
  • Client side validation with the jQuery validation plugin
  • Server side validation techniques and error handling strategies with disparate clients
  • Packaging data for return to JavaScript or Flex
  • Creating assemblers for multiple client technologies
  • Using ColdFusion ORM with a remote client 
Amongst others.  If after reading the article there is anything you'd like to read more about, drop me a line!

Monday, February 21, 2011

ColdFusion ORM: Computed Property Gotcha

I was fighting an ORM issue today (well, several) and I came across an issue that is more of a gotcha than a real problem.  ColdFusion ORM supports the idea of computed properties.  I was using a computed property to give me the sum of all approved amounts in a child collection like so:

<cfcomponent displayname="Parent" persistent="true">
...
  <cfproperty name="totalFundingApproved" formula="select sum(ch.approved_amount) from child ch where ch.parent_id = id" />
...
</cfcomponent>

I'm using Ajax to persist records, and then I am returning the parent object to my callback function so I can then parse the object and update the UI with the new values.  The super-simplified service method would like like this:

var parent = entityLoadByPK('Parent', arguments.id);

child = parent.getChild();
child.setTotalFundingApproved(arguments.approvedAmount);
entitySave(parent);

return parent;

Now, it would be silly for ColdFusion to reload the values on the calculated property every time we ask for it.  The problem is, I need it to be updated when the child is updated!  There are a few things we could do to get the functionality we want.  First, we can move the computed property out into a method call:

<cffunction name="getTotalFundingApproved" access="public" returntype="query" output="false">
  <cfset var q = '' />
  
  <cfquery name="q" datasource="#dsn#">
  select sum(ch.approved_amount) from child ch where ch.parent_id = #getID()#
  </cfquery>
  
  <cfreturn q />
</cffunction>

The problem with that in my specific case is that I need to be able to sort on that property, and no matter how hard you try, you just can't sort on a method ;)  Another option is to create a method like getTotalFundingApproved().  This seems kludgy to me.  With proper documentation it could work, but you could have two possible values for the same property at any given time depending on the child collection's state.

I ended up reloading the object and returning that copy.  To ensure a fresh read I'm using the ORMClearSession() method:

ormclearsession();
var parent = entityLoadByPK('Parent', id);
return parent;

This works, but I'd love to see a way to trigger a reload of the computed property that I could use in the preUpdate() or preInsert() event handlers.

Update (2/22/2011): I was looking through the new functions in CF 9 today and found entityReload(). This could be used instead of ormClearSession(). ormClearSession() worked for me here since no other objects were in the session at the time. In other cases, entityReload() just makes more sense.

Thursday, February 17, 2011

ColdFusion ORM: Answering Some Event Handling Quandaries

I'm working with ColdFusion ORM event handlers and had a few questions I couldn't find answers to so I figured I'd write some tests.  I wanted to determine:
  1. Does preUpdate fire if no properties have changed?
  2. Does postUpdate fire if no properties have changed?
  3. What is the order of operations when using both a CFC event handler and object event handler method?
I figured the best way to test this would be to persist a bunch of tick counts during each event handlers process.  The persistent CFC is as follows:

<cfcomponent displayname="EventRecorder" persistent="true">
  
  <!--- properties --->
  <cfproperty name="id" fieldtype="id" type="numeric" unsavedvalue="-1" generator="increment" />
  <cfproperty name="preInsertTick" type="int" />
  <cfproperty name="postInsertTick" type="int" />
  <cfproperty name="preUpdateTick" type="int" />
  <cfproperty name="postUpdateTick" type="int" />
  <cfproperty name="sysPreInsertTick" type="int" />
  <cfproperty name="sysPostInsertTick" type="int" />
  <cfproperty name="sysPreUpdateTick" type="int" />
  <cfproperty name="sysPostUpdateTick" type="int" />
  
  <cffunction name="init" returntype="EventRecorder" output="false">
    <cfset setID(-1) />
    <cfreturn this />
  </cffunction>
  
  <cffunction name="preInsert" returntype="void" output="false">
    <cfset setPreInsertTick(getTickCount()) />
  </cffunction>
  
  <cffunction name="postInsert" returntype="void" output="false">
    <cfset setPostInsertTick(getTickCount()) />
  </cffunction>
  
  <cffunction name="preUpdate" returntype="void" output="false">
    <cfset setPreUpdateTick(getTickCount()) />
  </cffunction>
  
  <cffunction name="postUpdate" returntype="void" output="false">
    <cfset setPostUpdateTick(getTickCount()) />
  </cffunction>
  
</cfcomponent>

You'll notice that right before and right after each insert and update the current timer is pushed into the EventRecorder object. The same is done in our application event handler CFC:

component implements="cfide.orm.IEventHandler" {
  
  public void function preLoad(any entity) {}
  public void function postLoad(any entity) {}
  
  public void function preInsert (any entity) {
    entity.setSysPreInsertTick(getTickCount());
  }
  
  public void function postInsert (any entity) {
    entity.setSysPostInsertTick(getTickCount());
  }
  
  public void function preUpdate (any entity, struct oldData) {
    entity.setSysPreUpdateTick(getTickCount());
  }
  
  public void function postUpdate (any entity) {
    entity.setSysPostUpdateTick(getTickCount());
  }
  
  public void function preDelete(any entity) {}
  public void function postDelete(any entity) {}
  
}

The only requirement when creating an event handler CFC is that it must implement cfide.orm.IEventHandler.  While we only need 4 methods, remember that we need to implement each method with the appropriate signatures defined in the interface.  Don't forget that we need to tell ColdFusion that we want to enable event handler as well as define the event handler CFC:

this.ormSettings.eventHandling = true;
this.ormSettings.eventHandler = 'blogexamples.hql.cfc.EventHandler';

The cfm file that kicks it all off looks like this:

<cfscript>
  o = entityNew('EventRecorder');
  entitySave(o);
  ormFlush();
  
  entitySave(o);
  ormFlush();
</cfscript>

When the above was executed we see the following results:

  1. Does preUpdate fire if no properties have changed? Yes
  2. Does postUpdate fire if no properties have changed? Yes
  3. What is the order of operations when using both a CFC event handler and object event handler method? They appear to fire at the exact same time
I'm not happy with the results for #3.  Let's rerun the test with a delay set into both preUpdate methods by adding:

sleep(1000);


Eureka!  The class's event handler method fires before the application event handler CFC.

Wednesday, February 16, 2011

jQuery: remove() Performance Issues Tested

I was reading the comments on the jquery .remove() function, and came across this:
If you've got a container with a lot of child elements you had better empty the container element before removing it. Due to the way jQuery handles remove vs empty, empty is thousands of times faster, at least in this situation.

So do this:
$('#container').empty().remove();

…instead of this:
$('#container').remove();
So for each time I've used remove() since then, I've been doing empty().remove(), as it is "thousands of times faster".  Well, I've been meaning to test this so here we go.  I created a simple template for testing.  The idea was to created a table with 5000 rows with elements nested 3 levels deep.  As expected, jQuery drops these things from the DOM pretty quickly.  Here is the template (I'm looping using ColdFusion, but you could recreate this in any other language:

<script type="text/javascript" src="jquery-1.4.2.min.js"></script>
<script type="text/javascript">
  function go (){
    var start = new Date();
    $('#tbltest').empty().remove();
    var end = new Date();
    alert(end - start);
  }
</script>
<button onclick="go();">Remove</button>
<table id="tbltest">
  <tbody>
    <cfloop from="1" to="5000" index="i">
      <tr>
        <td><a href="javascript:void(0);"><span>Text</span></a></td>
        <td><a href="javascript:void(0);"><span>Text</span></a></td>
        <td><a href="javascript:void(0);"><span>Text</span></a></td>
        <td><a href="javascript:void(0);"><span>Text</span></a></td>
        <td><a href="javascript:void(0);"><span>Text</span></a></td>
        <td><a href="javascript:void(0);"><span>Text</span></a></td>
        <td><a href="javascript:void(0);"><span>Text</span></a></td>
        <td><a href="javascript:void(0);"><span>Text</span></a></td>
      </tr>
    </cfloop>
  </tbody>
</table>

Note that I'm using jQuery 1.4.2.  The plan was to allow the page to load fully and then trigger the remove() or empty().remove().

First, when running this 10 times using remove():
  1. 869ms
  2. 906ms
  3. 907ms
  4. 919ms
  5. 889ms
  6. 912ms
  7. 948ms
  8. 1055ms
  9. 933ms
  10. 900ms
Then, 10 times using empty().remove():
  1. 941ms
  2. 897ms
  3. 881ms
  4. 923ms
  5. 954ms
  6. 951ms
  7. 1015ms
  8. 968ms
  9. 939ms
  10. 962ms
When averaged the remove() method took 924ms and empty().remove() averaged 943ms.  Not the results I was expecting to see, but I'm not ready to call this busted just yet. I had a hunch that the issue was with deeply nested objects and the way that jQuery traverses the DOM.  I changed the code to have several layers of nested tables as such:

<script type="text/javascript" src="jquery-1.4.2.min.js"></script>
<script type="text/javascript">
  function go (){
    var start = new Date();
    $('#tbltest').empty().remove();
    var end = new Date();
    alert(end - start);
  }
</script>
<button onclick="go();">Remove</button>
<table id="tbltest">
  <tbody>
    <cfloop from="1" to="2000" index="i">
      <tr>
        <td>
          <table>
          <tbody>
            <tr>
              <td>
                <table>
                <tbody>
                  <tr>
                    <td>
                      <table>
                      <tbody>
                        <tr>
                          <td>
                          <span><span><span><span><span><span><a href="javascript:void(0);"><span>Text</span></a></span></span></span></span></span></span>
                          </td>
                          <td>
                          <span><span><span><span><span><span><a href="javascript:void(0);"><span>Text</span></a></span></span></span></span></span></span>
                          </td>
                          <td>
                          <span><span><span><span><span><span><a href="javascript:void(0);"><span>Text</span></a></span></span></span></span></span></span>
                          </td>
                          <td>
                          <span><span><span><span><span><span><a href="javascript:void(0);"><span>Text</span></a></span></span></span></span></span></span>
                          </td>
                          <td>
                          <span><span><span><span><span><span><a href="javascript:void(0);"><span>Text</span></a></span></span></span></span></span></span>
                          </td>
                        </tr>
                      </tbody>
                      </table>
                    </td>
                  </tr>
                </tbody>
                </table>
              </td>
            </tr>
          </tbody>
          </table>
        </td>
      </tr>
      </tr>
    </cfloop>
  </tbody>
</table>

remove():
  1. 769ms
  2. 812ms
  3. 767ms
  4. 744ms
  5. 822ms
  6. 778ms
  7. 766ms
  8. 822ms
  9. 1018ms
  10. 778ms
empty().remove():
  1. 997ms
  2. 782ms
  3. 792ms
  4. 978ms
  5. 1062ms
  6. 765ms
  7. 728ms
  8. 759ms
  9. 1003ms
  10. 834ms
Averaged, remove() took 808ms and empty().remove() took 772ms.  This result made sense and was more the expectation I expected to see.  Perhaps over a large set of elements it would actually matter.  I'd say it isn't worth worrying about, but at least my curiosity has been quelled.

A few things to call out here are that I am using jQuery 1.4.2 on Firefox 3.6.13.  This matters as different results could be given in IE with it's JS engine, as well as the comment on the jQuery remove() page may have been referring to another version, although I did choose 1.4.2 to mimic the date of the post.

    Tuesday, February 15, 2011

    ColdFusion ORM: Filtering on Composite Objects

    I've had the need a few times lately to return a subset of ColdFusion ORM objects. In this case EntityLoad() might not always be the way to go. This post will hopefully help those new to ORM determine the appropriate action.

    First, lets chat about entityLoad(). EntityLoad() looks like the perfect way to query for a collection of objects.  Given an ORM object of type Pig, consider the following definition:
    <cfcomponent displayname="Pig" persistent="true">
        
        <!--- properties --->
        <cfproperty name="id" fieldtype="id" type="numeric" unsavedvalue="-1" />
        <cfproperty name="color" type="string" />
        <cfproperty name="age" type="numeric" />
        
        <cfproperty name="breed"
              fieldtype="many-to-one" cfc="Breed"
              fkcolumn="breedid" lazy="true" />
        
    </cfcomponent>
    

    It's easy to use entityLoad() to load up all pigs that are pink:
    entityLoad('Pig', {color='Pink'});
    

    Now notice that each Pig has a breed.  Let's say we want to get all pigs of breed 'Yorkshire'.  Here's where entityLoad() falls on its face.  If we tried to use entityLoad() it would look like this:
    entityLoad('Pig', {breedid=1})
    

    We get the following error:
    Property BREEDID not present in the entity. 
    

    This is because entityLoad() is a convenience function that only works against defined properties.  To entityLoad(), breedid is not a property.  Instead, Breed is the property, being of type Breed.  To get around this we can use HQL instead.  HQL is very easy to understand if you know SQL, and very easy to execute.  Please refer to my last blog entry ColdFusion ORM: Using HQL for a primer on HQL with ColdFusion ORM. For the examples below we will be using ORMExecuteQuery() as opposed to <cfquery>

    Filtering the objects to get all Pigs which are Yorkshires, we can do this:
    ormExecuteQuery("from Pig where breedid = 1");
    

    ormExecuteQuery() returns an array of matching objects.  This is the case unless we are using aggregate functions like sum() or count().  In that case you get an array of arrays.  See the last section of ColdFusion ORM: Using HQL for a primer on HQL for more details on how aggregate function results are returned.

    Instead of querying on a property of Pig itself, we can even query against properties of the composite object Breed:
    ormExecuteQuery("from Pig pig where pig.breed.name = 'Yorkshire'");
    

    One more thing, HQL also allows us to use alternate operators.  We can then use ORMExecuteQuery() to do things like:
    ormExecuteQuery("from Pig where age < 2");
    

    ColdFusion ORM: Using HQL

    It is important to note in ColdFusion ORM that functions like entityLoadByPK() and entityLoad() are merely convenience functions. Each (so far as I know) simply hides the underlying HQL (Hibernate Query Language). HQL is much like SQL, aside from the fact that not all SQL aggregates and functions are supported as well as we can reference object relational mappings in our HQL as Hibernate understands how to translate them into the appropriate SQL based on our datasource type.

    There are two simple ways to execute HQL in a ColdFusion application. The first is with ORMExecuteQuery(). The second is to use a cfquery block with dbtype="hql". For this set of examples we will use ORMExecuteQuery().

    We can do a few cool things with HQL. For one, since HQL knows how our objects are mapped we can use the shortcut 'id' to reference a property. Given an ORM object with the cfproperty id definition:
    <cfproperty name="chicken_id" fieldtype="id" type="numeric" unsavedvalue="-1" />
    

    The following HQL statements are considered synonymous:
    from Chicken where id = 1;
    from Chicken where chicken_id = 1;
    

    Please note that the object name 'Chicken' is case sensitive. If you mistype the case here you will receive the error 'org.hibernate.hql.ast.QuerySyntaxException: Chicken is not mapped [ from chicken where chicken_id = 1 ]'.

    Another cool thing, we can query on composite objects as well. Given Rooster in a one-to-many relationship with Hens, we can get all roosters who are associated with a red hen like this:
    from Rooster rooster where rooster.hen.color = 'red';
    

    Note that the query above uses an inner join by default. Other join types are supported by explicitly specifying the join just like in standard SQL.

    Just like in SQL, we can also use 'order by' to sort the resulting collection of ORM objects. To return a collection ordered alphabetically by the Rooster's name:
    from Rooster order by name asc;
    

    Lastly (well, certainly not last, but the end of this primer), HQL supports aggregate functions. This works a bit differently in that the result is an array of arrays. This is technically how a query object works in ColdFusion, but without the niceties of struct-type column names. This confuses me as HQL supports 'as' (like select sum(column) as mysum from Object). Maybe that's something coming in CF X? For now expect an array for each object found with an array of properties.

    Friday, February 11, 2011

    jQuery: Partial Loads

    I had an issue in jQuery the other day where a load() command wasn't working properly.  There could have been an error in my target HTML, but I sure couldn't find it. What I was doing was using Ajax to load table rows into a form.  The HTML being loaded just wasn't maintaining structure (<TD>s nested into a <TR>.  To fix this I read about jQuery partial load.  I don't know how I never saw this before as I can see a few cool uses.

    A partial load works like this: add a selector after the url in a load() method call and jQuery will load all matching elements into your specified element.
    // load all td elements from the specified url into myElement
    
    $('#myElement').load('http://url/template.html td');
    

    This is shorthand for the following:
    $.get(''http://url/template.html', function (html) {
    
        $(html).find('td').each(function (){
    
            $('#myElement').append(this);
    
        });
    
    }
    

    The documentation can be seen here.

    My Take on the ColdFusion Team Changes

    So, if you haven't already read the news, head on over to Adam Lehman's blog and read about what he dubs "The Modern Age of ColdFusion", aka some major shifts in the direction of the ColdFusion platform.  I figure I'd throw out my interpretation...

    When I first heard about this, I grilled Adam pretty hard and I only had one concern: Will the new product champion(s) have at much at stake in ColdFusion's success as he and Alison did?  Time will tell, but Adam has assured me that the person leading this up not only understands what he and Alison have laid out on the ColdFusion roadmap, but also came to many of the same conclusions.  Another good thing (my understanding) is that the stellar team who has been creating ColdFusion is still there.

    What this translates to is that moving ColdFusion under a new business unit gives its developers (not end users, the people who write the ColdFusion engine) a unique opportunity to take the time and money necessary to have the next version of ColdFusion fulfill Adam and Alison's vision, which, coincidentally is what the community at large has been asking for.

    On that note, I'd like to express my gratitude to both Adam and Alison for not only the awesome roadmap they have laid out for the next version of CF, but for the dedication they have demonstrated and the flak they have taken on behalf of (and sometimes at the hands of) the ColdFusion community.  Look at where we've gone in the last two versions of ColdFusion including my love/hate relationship with ORM, extensibility features with Microsoft Office, ColdFusion as a Service (CFAAS), enhanced CF script including MUCH better ECMAScript compliance, image manipulation, core engine improvements and all of ColdFusion Builder!  Heck, that only scratches the surface.  So I raise my glass to Alison and Adam and say 'Viva la ColdFusion!'

    Wednesday, January 12, 2011

    Issues Installing ColdFusion Builder and Flash Builder as Plugins on Mac

    Here's a quick post on issues I was having while installing ColdFusion Builder and Flash Builder, in case anyone else runs into the same.  I installed Eclipse 3.6 (Helios) and then added Flash Builder and ColdFusion Builder as plugins, which is a fine configuration.  The problems I was encountering were:
    1. Flash Builder reports - An internal error occurred during: "Creating Adobe Flash Player trust file...". com/adobe/flexbuilder/utils/osnative/NativeControl
    2. ColdFusion Builder does not color any code.  While that was the most obvious identifier, upon further inspection CF Builder didn't work period.
    3. Could not register CF Builder and was never even asked!  All registration links under the CF Builder Help menu were disabled (grayed out).
    After reinstalling I started poking around on the web and it turns out that I had installed the wrong version of Eclipse.  I was used to installing Eclispe for JEE Developers.  The download link includes 2 MacOS versions, one for 32bit and one for 64bit.  Both Eclipse versiosn ran on Cocoa the framework instead of Carbon, which CF Builder does not support (and seemingly neither does Flash Builder).

    When downloading Eclipse make sure you choose a Carbon build.  It is available under Eclipse Classic 3.6.1.  Just select Mac OS X(Carbon) from the download links list.

    If you care about the framework differences in Carbon and Cocoa, check out Cocoa Vs. Carbon.