HFCD for Flash Builder: Build Your Flex App 2-3x Faster

HFCD is an extension for Flash/Flex Builder that delegates compilation of your Flex application to a special "compiler daemon" which can run locally or on a remote machine.  The goal of the project is simple: faster builds!  HFCD is the brainchild of Clement Wong, the former compiler engineering lead on the Flex SDK team.  Here are a few useful things to understand about HFCD:

  • HF installs as a Flex Builder plugin which will delegate compilation to a separate OS-level process running either locally or on a remote machine.
  • The compiler daemon process is persistent, meaning it continues to run across multiple builds.  This allows the Java virtual machine to optimize compilation execution each time that a build is run.  The JVM is *very* good at this.
  • The Flex Builder plugin watches for file modifications and immediately pushes these changes to the compiler daemon process.  The daemon has an internal representation of the project file system and will launch internal incremental builds automatically when files change.

So, how fast is it really?  I benchmarked HFCD on two different machines.  I used the Flex 3.4.1 SDK for compilation and ran clean builds of my application each time.  My test project was a real-world Flex app currently in development consisting of about 15 modules and 350 MXML files and ActionScript classes.

2006 Intel Macbook Pro, Core Duo 2.16 GHz, 2 GB RAM, 7200 RPM HD, Leopard 10.5, 32-bit Java 5

  • Stock: 135 seconds average
  • HFCD (1st run): 155 seconds
  • HFCD (successive): 75 seconds average

Intel Core i7 920 @ 3.2 GHz, HT off, 12 GB RAM, dual 7200 RPM HD's in RAID 0, Vista 64-bit, 32-bit Java 6

  • Stock: 65 seconds average
  • HFCD (1st run): 52 seconds
  • HCFD (successive): 21 seconds average (!!!)

As indicated in the documentation, the performance of HFCD increases dramatically after the first build due to the numerous optimizations in HellFire and the JVM itself.  The Macbook was nearly 2x faster while the Windows box was just over 3x faster.  Very impressive and a real time saver!

I hope to post some new benchmarks soon.  I need to do some more research to get HFCD running on a 64-bit JVM as that isn't supported out of the box.  Also I'd like to configure my Macbook to delegate the compilation to my Windows box, especially to ascertain what kind impact the network topology has on build performance.

 

 

ActionScript Annoyances (Part 1): Limitations on Default Parameter Values

Every time I encounter something in AS3 that annoys me, I'm going to compel myself to blog about it, with the hope that it will filter through the Internet ethos to the desk of some of the Flash Player engineers in San Francisco.

Can't use static constants as default values for function parameters

Example:

public class PropertyType {
public static const LISTING_RESIDENTIAL : String = "ListingResidential";
...
}

public function createSearch(propertyType : String = PropertyType.LISTING_RESIDENTIAL) : void {
...
}

Results in this compiler error:

1047: Parameter initializer unknown or is not a compile-time constant.

Of course the bigger complaint here might be that there are no enumerated types in ActionScript... but that's probably asking too much. :)

Integrating Blaze Data Services and Spring Security

One thing to keep in mind with the out-of-the-box security support in Blaze DS is the approach to integration is container-specific: there is support for Tomcat (and therefore JBoss), WebSphere, Weblogic and Oracle through various implementations of the LoginCommand interface. Unfortunately if you have custom security requirements for authentication that means you're dealing with a lot of cumbersome, container-specific security configuration and/or writing and configuring JAAS plugins. The authorization support in Blaze DS is limited to specifying which roles have access to a particular destination which isn't nearly flexible enough.

Fortunately Spring Security provides solutions to many common problems in Java EE space, including features like container portability, a flexible authentication provider model, authorization of service method invocation via AOP and even some very cool ACL support to enforce granular security at the domain object level. Integrating Spring Security with Blaze DS isn't as hard as you think either: I was able to bang out a quick proof of concept over a weekend.

The config for Spring Security 2 is quite straightforward with the new XML namespace support in your Spring config files:

<security:http>
<security:form-login>
</security:form-login>

This is basically a very stripped-down configuration since things like RememberMeServices (using cookies) don't usually apply in a Flex-based RIA. You can also throw in a very simple AuthenticationProvider like this one from the SS2 docs:

<security:authentication-provider>
<security:user-service>
<security:user name="jimi" password="jimispassword" authorities="ROLE_USER, ROLE_ADMIN"/>
<security:user name="bob" password="bobspassword" authorities="ROLE_USER"/>
</security:user-service>
</security:authentication-provider>

There are two items you need to add to your web.xml to bootstrap SS2 in a Servlet container:

<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

<filter>
<filter-name>securityContextAwareFilter</filter-name>
<filter-class>org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter</filter-class>
</filter>

<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</filter-mapping>
</filter-mapping>

<filter-mapping>
<filter-name>securityContextAwareFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

The first filter is standard part of any Spring Security configuration. The "SecurityContextHolderAwareRequestFilter" adapts SS2 to the Servlet environment so that calls like getPrincipal() and isUserInRole() behave as expected. This really comes in handy when you have other code in your projects that assumes a "standard" Java security setup.

Now we need a little config in the Blaze services-config.xml file:

<security>
<login-command class="net.histos.util.spring.SpringSecurityLoginCommand" server="Tomcat"/>
<security-constraint id="valid-user">
<auth-method>Custom</auth-method>
<roles>
<role>ROLE_USER</role>
</roles>
</security-constraint>
</security>

Blaze DS seems to require that a "server" attribute be specified for any LoginCommand even though this isn't really used in our implementation. The security-constraint isn't necessary if you are going to use SS2's service method invocation authorization support. However if your security requirements are more straightforward you can do role/destination based restrictions here. Then simply add this element to the appropriate destinations:

<security>
<security-constraint ref="valid-user"/>
</security>

The last part is some Java code. If you extend AppServerCommand you get a default impl for this method:

protected boolean doAuthorization(Principal principal, List roles, HttpServletRequest request) throws SecurityException

This method makes use of isUserInRole(), so by adding the servlet filter referred to above, this logic can work without any modification required. This leaves only two methods in your LoginCommand impl:

public Principal doAuthentication(String username, Object credentials) {
log.debug("doAuthentication");
// get the ProviderManager from app context
Map<string, providermanager=""> map = getContext().getBeansOfType(ProviderManager.class);
if (map.size() != 1)
throw new RuntimeException("Spring ApplicationContext must contain exactly one ProviderManager bean");
ProviderManager provider = map.get( map.keySet().iterator().next() );
// authenticate
String password = extractPassword(credentials);
Authentication auth = provider.authenticate( new UsernamePasswordAuthenticationToken(username, password) );
SecurityContextHolder.getContext().setAuthentication(auth);
return auth;
}

public boolean logout(Principal principal) {
log.debug("logout");
SecurityContextHolder.getContext().setAuthentication(null);
return true;
}

Those are the basics! As I said, this a proof of concept that I haven't had time to test extensively yet but it should get you started! One other note: I noticed while testing the Flex side that calling login() or logout() on a RemoteObject without calling a "regular" service method would result an error; apparently the ChannelSet hadn't been defined yet. I did a little digging and found that apparently you're supposed to call login() or logout() on the underlying ChannelSet itself. I wrote a very simple ChannelSet implementation that can be easily instantiated in MXML and bound as the channelSet property for your RemoteObjects:

package net.histos.flex.util
{
import mx.messaging.ChannelSet;
import mx.messaging.channels.AMFChannel;

public class SimpleChannelSet extends ChannelSet
{
public function set url(channelUrl : String) : void {
addChannel(new AMFChannel("defaultChannel", channelUrl));
}
}
}

<util:SimpleChannelSet id="channelSet" url="http://localhost:8080/testapp/messagebroker/amf"/>

Then simply invoke login() and logout() on the ChannelSet itself and you should have no problems.

FlashPlayer 10 "Astro" now in beta

FlashPlayer 10 (codename: Astro) is now available on Adobe Labs.  3D support and hardware acceleration is the big news here.  Also check out the release notes for two new significant improvements:

  • Vectors - part of ECMAScript 4, these are basically like Arrays except you can specify the type stored inside of it.  Finally a taste of generics from Java!  Better type safety and improved performance.

  • File Reference runtime access - pull local files into the RIA as a ByteArray or String, work with the data and even save changes back to the original file!

It's a great time to be a RIA developer. :)

Swiz IoC / MVC Framework for Adobe Flex

Chris Scott (the creator of ColdSpring) has released a new IoC / MVC framework for Flex called Swiz.  This is really going to shake up the Flex MVC space and hopefully make people realize that Cairngorm is not the best option out there.  The moment I saw code generation tools springing up to help make life with Cairngorm easier it confirmed my belief (and those of my colleagues) that CG makes things more complicated than they need to be.

Chris and I have been talking about the design of Swiz for the past few months during its evolution.  As time goes on I hope to contribute in as many ways possible, including docs, sample apps and hopefully some framework code too.

Swiz on!