JS-Maven Roundup and Roadmap

To those of you who’ve been paying attention to the original project on GitHub, you’ve probably noticed that there’ve been a lot of changes there recently without any subsequent updates here. I’d like to take a moment to summarize what we’ve been up to, and keep everyone in the loop on where we’re going.

Inevitably as you start into a project you realize the full scope of what you’re trying to do, and as you do the finish line moves farther and farther towards the horizon. Most of the changes made were in respect to that.

js-maven-plugin is now js-maven

I renamed and moved the project because it was rapidly growing beyond the scope of a single maven plugin to include core libraries, tooling, archetypes and reports. Rather than try to hammer all these into one single java project I felt that switching over to a multi-module layout would give me the room I needed to grow. So far this seems to have been a good decision, as I now have the sense that I can (mentally) stretch my arms a little.

No more specific version commitments.

I’m making a concession to my own prioritization style here, which is very much a “Hey, this sounds like an interesting problem to solve” method. Needless to say, this doesn’t really work well with preplanned version releases, as there are some projects that are more fun and some that are less fun, and it resulted in me skipping around a lot. Instead, until I’ve got a few more contributors on the project, I’m simply going to outline a feature set for the various point versions and go after features as they strike me.

I understand that for an enterprise this is not the most optimal approach, so I will make this suggestion: If a particular feature is business critical enough for you to write your own solution, why not contribute it while you’re at it?

Documentation!

I’ve finally gotten around to setting up all the maven:site documentation for the project, and it’s now hosted on github’s pages for the project http://krotscheck.github.com/js-maven/ . Similarly, the README.md on github has been reduced to a quick start (since we know we all prefer copy/paste), as I don’t feel that much more was necessary.

Version Dependencies

We now support Maven 3 and Java 1.7 only. Annoying for some of you, yes, however in keeping with my philosophy of using the latest-and-greatest whenever possible we’re just going to put a line in the sand. That’s not to say we won’t ever support other versions, however those ports will have to come from the community (such as it is).

Licensing

I’m still debating licenses. Right now the project is under the MIT license, though I might move it over to New BSD. This project will never be licensed under the GPL or any similar viral/copyleft license, as those tend to greatly inhibit adoption.

Roadmap Commitments for v0.2.0

If you’ll take a look at the roadmap, you’ll see the list of features which I want to have ready for the next minor version release. Notable additions were the reporting plugins for JSLint and JSDoc, as it didn’t make much sense to me to include those without making them accessible either. Here’s a list of where we stand right now, with some details:

Plugin Documentation [branch- jsdoc-improvments]

Now that I’ve got the maven site reports running, they decided to get all unruly and hurt my feelings by insulting my coding style. The inevitable cleanup phase is in progress, which also includes a bit of judicious refactoring as the actual usability of the plugin gets a little focus.

Cleanup

As this is my first large maven plugin project, I’m still learning my way around. Refactoring and cleanup is necessary – for example, sourceEncoding should be handled via the global parameter and not individually per Mojo.

Maven Reports: JSLint and JSDoc

These are two new features I realized would be necessary as I got the site up and running. I’m not at all familiar how plugins like this get wired into maven as a whole, so they might go fast or they might not.

Common JS Libraries in a repository [branch- jslib-redeploy-plugin]

Having a javascript maven framework isn’t much use if you don’t have any libraries that you can depend on, so I’m writing a plugin that’ll take a URI-styled token string and will redeploy any artifacts found into a maven repository. This is proving tricky as I cannot resolve their own version dependencies easily (jqueryUI -> jquery for instance), however it’s coming along nicely. Once this is complete I’ll be able to provide a few more archetypes for other projects. jQuery, JavascriptMVC, you name it.

Unit Testing

I’m still evaluating the best javascript unit testing framework out there. There are many solutions for “How to test a javascript library in different javascript engines and/or browsers, but nobody seems to have figured out the holy grail yet. This feature might get punted into its own release, if it turns out that the scope is too big.

Tagged with: , , , ,
Posted in Blog

Building a JSDoc Runner for Maven

The next step in this plugin is to ensure that we can generate documentation. While it may seem like a stretch to do this so early in the process, it forces us to build a few other pieces that we’ll need later.

Our documentation generator will be JSDoc, chosen because it’s fairly mature, has a strong following, and its markup is broadly supported across IDE’s. In fact JSDoc seems to be one of the few broadly adopted standards out there, though to speculate why is beyond the scope of this post. What matters is that it’s a documentation writer written in Javascript that is run via Mozilla’s Rhino runtime. To properly run it we’ll have to resolve the dependency, unpack it into a temporary directory, and then spin up Rhino to execute the code.

I encountered a problem in the way JSDoc 2.4.0 interacts with Rhino 1.7RC3, a bug introduced by two inline function declarations without semicolons in TokenParser.js. While on one side one might argue that this is a bug in Rhino, declaring functions inside of other functions is a big no-no in the javascript world. As a result I’ve had to generate and deploy my own patched version of JSDoc.

Resolving JSDoc

All of the plugin artifacts for a given mojo are easily available as a parameter, thus locating the proper version of JSDoc is fairly simple.

Full source available on github

/**
 * @parameter default-value="${plugin.artifacts}"
 */
private List<Artifact> pluginArtifacts;

public Artifact locate(String groupId, String artifactId) {

	getLog().info("Resolving " + groupId + ":" + artifactId);

	for (Iterator<Artifact> iterator = pluginArtifacts.iterator(); iterator.hasNext();) {
		Artifact artifact = (Artifact) iterator.next();

		if (artifact.getGroupId().equals(groupId) && artifact.getArtifactId().equals(artifactId)) {
			return artifact;
		}
	}

	getLog().error(String.format("Failed to locate %s:%s", groupId, artifactId));
	getLog().error("This probably means that you didn't specify it as a dependency in the pom.xml file");

	throw new RuntimeException(String.format("Failed to locate %s:%s", groupId, artifactId));
}

Unpacking JSDoc

As all jar files are zip files, we can simply write a zip extraction routine to unpack it.

Full source available on github

ZipFile zipFile = new ZipFile(artifact.getFile(), ZipFile.OPEN_READ);
Enumeration zipFileEntries = zipFile.entries();

while (zipFileEntries.hasMoreElements()) {
	ZipEntry entry = (ZipEntry) zipFileEntries.nextElement();

	// Construct the new name
	String currentName = entry.getName();
	File destFile = new File(destinationDirectory, currentName);

	if (entry.isDirectory()) {
		// If we are a directory, create it.
		destFile.mkdirs();
	} else {
		// If we're a file, unzip it.
		BufferedInputStream is = new BufferedInputStream(zipFile.getInputStream(entry));
		int currentByte;

		// establish buffer for writing file
		byte data[] = new byte[BUFFER];

		// write the current file to disk
		FileOutputStream fos = new FileOutputStream(destFile);
		BufferedOutputStream dest = new BufferedOutputStream(fos, BUFFER);

		// read and write until last byte is encountered
		while ((currentByte = is.read(data, 0, BUFFER)) != -1) {
			dest.write(data, 0, currentByte);
		}
		dest.flush();
		dest.close();
		is.close();
	}
}

Configure JSDoc

At this point we’re back in the drudgery of maven configuration. Since we’d like to keep our configurations relatively separate (in a <jsdoc> sub tag in the mojo configuration), I’ve created a separate JSDocOptions class that can accept all of our options. I’m not going to bother copying it all here as there’s quite a bit of code, but you can see the full source code here.

Running JSDoc via Rhino

Lastly, we have to execute JSDoc, which is done via Rhino. This, again, is fairly straightforward.

Full source available on github

public void run(File inputDirectory, File outputDirectory, JSDocOptions options)
	throws JSDocRunnerException
{
	// Build the string arguments.
	String[] args = options.buildArguments(jsDocRoot, inputDirectory, outputDirectory);

	// Run the javascript engine.
	getLog().info("Building JSDoc...");

	// Create our logging streams
	MojoLoggerOutputStream errorStream = new MojoLoggerOutputStream(LogLevel.ERR);
	Main.setErr(new PrintStream(errorStream));

	MojoLoggerOutputStream outputStream = new MojoLoggerOutputStream(LogLevel.WARN);
	Main.setOut(new PrintStream(outputStream));

	Main.main(args);
	getLog().info("Complete");

}
Tagged with: , , , , ,
Posted in Blog

Building a Javascript Dependency Resolver in Maven

Dependency resolution, to most javascript developers, means adding another <script> include in the header of their html page, and in most cases this is fine. In fact in 95% of any javascript applications you don’t even have to host this file- just include a link to JQuery on Google Libraries and you won’t need to worry.

In the enterprise, however, different concerns start to take over. The project you’re working on might be dependent on an internal library built by another team, which is subsequently dependent on yet another library, and so on and so forth. As those libraries are incrementally updated, managing dependencies becomes a headache. Furthermore, the true power of javascript compilers like Google Closure are realized when all of the code is available at compile time. In fact, both the Simple and Advanced optimization modes require it, as it teaches Closure which code isn’t used and may thus be discarded.

Maven already has a very mature dependency resolution mechanism built into it, which is backed by a large public repository of common libraries. Javascript is still underrepresented in this arena, true, yet hopefully this plugin will eventually remedy that situation.

Choosing Scope

The maven dependency system requires a scope for each individual dependency to determine when it is needed in the build cycle. This is quite handy- you don’t for instance, want to have all the JUnit libraries be part of your final project compile, but you do want them available during the testing phase.

Similarly, Google’s closure compiler can process an external file (dependency) in two ways: Directly, immediately compiled into the final delivery source, and externally, files which are assumed to be self-contained and included in the final product via a different script tag. These two types of dependencies map rather nicely to Maven’s “compile” and “runtime” scope, however compiling a javascript file into a library causes complexity: If two libraries have the same compiled-in dependency, with different versions, perhaps even with different optimization levels, I run the risk of version and feature conflicts.

As a result, all of our dependencies will be treated as externals.

Resolving a dependency graph

The first step to building dependency resolution into my plugin requires constructing the dependency graph. Maven makes this (relatively) easy.

Full source available on github: DependencyResolver.java

private DependencyNode getDependencyGraph(Artifact rootArtifact)
        throws DependencyResolverException {
        try {
                Dependency rootDependency = new Dependency(rootArtifact, "compile");
                CollectRequest collectRequest = new CollectRequest(rootDependency,projectRepos);

                // Collect all the nodes
                CollectResult collectResult = repoSystem.collectDependencies(repoSession, collectRequest);
                return collectResult.getRoot();
        } catch (DependencyCollectionException e) {
                throw new DependencyResolverException("Cannot build project dependency graph", e);
        }
}

Filtering dependencies by scope and extension

Now that I have a graph of dependency relationships, I should now visit each node and only collect those nodes that match our required scopes. Since I’ll also use this extension for resolving jsunit, envjs or others, it behooves me to abstract it.

Full source available on github: DependencyResolver.java

public List<Artifact> resolve(String scope, String extension)
                throws DependencyResolverException {

        getLog().debug("Resolving dependencies: [scope:" + scope + "] [extension:"  + extension + "]");
        // Construct the dependency tree of our project.
        Artifact rootArtifact = new DefaultArtifact(project.getGroupId(),
                project.getArtifactId(), project.getPackaging(),
                project.getVersion());
        DependencyNode rootNode = this.getDependencyGraph(rootArtifact);

        // Gather dependency nodes in post order
        PostorderNodeListGenerator postOrderVisitor = new PostorderNodeListGenerator();

        // Filter out the root node.
        DependencyVisitor visitor = new FilteringDependencyVisitor(
                postOrderVisitor, new PatternExclusionsDependencyFilter(
                        rootArtifact.toString()));

        // If a scope is configured, add a scope filter.
        if (null != scope) {
                visitor = new FilteringDependencyVisitor(visitor,
                                new ScopeDependencyFilter(scope));
        }

        // If a type is configured, add a pattern exclusion filter
        if (null != extension) {
                String patternFilter = "*:*:" + extension + ":*";
                visitor = new FilteringDependencyVisitor(visitor,
                                new PatternInclusionsDependencyFilter(patternFilter));
        }

        // Run the collection.
        rootNode.accept(visitor);
        return postOrderVisitor.getArtifacts(true);
}

Resolving dependencies

As it turns out, maven dependencies may, or may not, be resolved. In practice this means that the system won’t bother to download the file until it’s actually needed, so we have to do it manually. The new Aether Dependency API in Maven3 means that artifacts are immutable, so we have to make a copy of each.

Full source available on github: DependencyResolver.java

        List<Artifact> unresolvedArtifacts = postOrderVisitor.getArtifacts(true);
        List<Artifact> resolvedArtifacts = new ArrayList<Artifact>();

        for (Iterator<Artifact> iterator = unresolvedArtifacts.iterator(); iterator.hasNext();) {
                Artifact artifact = iterator.next();
                ArtifactRequest artifactRequest = new ArtifactRequest(artifact,projectRepos, null);
                ArtifactResult artifactResult = repoSystem.resolveArtifact(repoSession, artifactRequest);
                getLog().debug(" + " + artifactResult.getArtifact().toString());
                resolvedArtifacts.add(artifactResult.getArtifact());
        }
        return resolvedArtifacts;

Including dependencies in our compiler

With a list of resolved dependency artifacts in hand, I can now include them in the compile method from the last post.

Full source available on github: JSLibraryCompilerMojo.java

        // Generate any externals that we need.
        List<JSSourceFile> externs = new ArrayList<JSSourceFile>();
        getLog().info("Resolving External Includes...");
        externs.addAll(resolveDependencies("compile","js"));

        // Generate the list of source files to include.
        List<JSSourceFile> source = new ArrayList<JSSourceFile>();
        getLog().info("Resolving Compile Includes...");
        source.addAll(generateJavascriptSourceList());

And there we are! We are now resolving dependencies from other javascript libraries that live in your, or someone else’s, maven repository.

Tagged with: , , , , , ,
Posted in Blog

Building a Javascript Compiler for Maven

This post is one in a series where I slowly assemble a Maven workflow for large javascript projects. If you came here via google, I recommend starting at the beginning: Designing a Javascript Maven Plugin. Alternatively, you can just go directly to the js-maven project on github.

Since there’s no sense in rebuilding something that already exists, I decided to start with google’s excellent closure compiler as a baseline, and simply write a maven wrapper around it. There’s nothing inherently wrong with the YUI Compressor; It’s quite a nice compressor. In the light of supporting more use cases, however, I feel that a compiler with more optimization options would be a better choice, especially since all of those options are individually configurable.

However before I wrote the plugin, there are a few things I had to take into consideration:

  1. Developers should be able to debug while developing. This means that the compiled code needs to be human readable.
  2. Optimization may impact code function, so the compiled code should be optimized.
  3. Once packaged, the code should be minified.

In practice, this means two compiler passes over the source code. The first is to perform code optimizations, yet provide a human readable source file for debugging (in jetty, for instance). The second is to minify, but not optimize, shortly before the asset is packaged. This suggests the following steps for my Javascript Library lifecycle.

compile
net.krotscheck:js-maven-plugin:optimize-js-lib
prepare-package
net.krotscheck:js-maven-plugin:minify-js-lib

Given that with Google Closure two steps are practically identical, most of the functionality will be abstracted. This means I’ll effectively be running the compiler twice, not a problem unless a browser decides to throw errors on whitespace usage.

The Compiler Plugin

There are three steps to compiling using Closure. First we need to gather all of our source files, then we need to configure the compiler, and then we’ll run the compiler and output our source. There is a fourth step- gathering dependencies, which I’ll cover in a subsequent post. None of these steps are especially difficult. Here’s step one, finding all of our source code.

Full source available on github: JSLibraryCompilerMojo.java

/**
 * Location of the source directory.
 * 
 * @parameter expression="${project.build.sourceDirectory}"
 * @required
 */
protected File sourceDirectory;

/**
 * This method scans the configured source directory of the Mojo and returns all
 * javascript documents, filtering by extension.
 */
protected List<JSSourceFile> generateJavascriptSourceList() {
    ArrayList<JSSourceFile> sourceList = new ArrayList<JSSourceFile>();

    String[] extensions = { "js" };
    getLog().debug("Discovering Source Files");

    Collection<File> sourceFiles = FileUtils.listFiles(sourceDirectory, extensions, true);

    for (Iterator<File> iterator = sourceFiles.iterator(); iterator.hasNext();) {
         File file = (File) iterator.next();

        sourceList.add(JSSourceFile.fromFile(file));

        getLog().debug(" + " + file.getAbsolutePath());
    }

    return sourceList;
}

Here’s step two, writing our compiler configuration. Note that I consciously made the choice to reduce the complexity of what my users can alter for the sake of illustration; This is primarily done

Tagged with: , , , , , , ,
Posted in Blog

Designing a Javascript Maven Plugin

As mentioned in my previous post, I believe that the tooling for enterprise grade javascript applications is simply not there yet. What is needed is a set of tools, preferably open source, that are framework agnostic and that fit into established development practice. In this and the subsequent posts, I will walk you through the process of building just this toolset using the Maven as a framework to glue it all together.

Overall Philosophy

First and foremost, the toolset must be independent of the code that is written. We simply cannot have a compiler that will only work for JQuery, or a testing harness that will only work if the test artifacts are composited via Rake.

Furthermore, I would like to adhere to Java conventions as much as possible, by which I mean JSDoc, JUnit-style unit tests and so forth. Java is syntactically very close to Javascript and Java’s strong prevalence in the enterprise space will reduce engineer friction.

The project artifacts will be packaged in as close to a native file type as possible. This becomes tricky: For something like a Javascript Library, a simple minified .js file will do. For a full web application with images, media, html and more this will be trickier as the nature of the server will define one packaging type over another: Jetty/Tomcat will prefer a .jar file, Apache will serve up an uncompressed archive, and so forth.

When possible, we’re going to take advantage of the latest and greatest: CSS compilers, for instance.

Why choose maven?

Maven is an obvious choice for various reasons. Firstly, it enjoys broad IDE integration. Secondly, it has an extremely powerful plugin framework that allows a user to use entire groups of plugins or focus on only the ones they really need. Thirdly, its dependency resolution mechanism is very powerful and very portable, which makes your builds extremely easy to bring to other platforms. Fourthly, it is open source and well supported by the community. Lastly, it enjoys broad support among continuous integration frameworks. All of these together make it a no-brainer to use as the core engine for our javascript toolset.

What workflow steps are needed?

Since maven defines a specific artifact lifecycle, we should start there and decide what we need our plugin to do. After starting to write this for a little bit however I realized that trying to declare it all up front is a significant undertaking, and should be split up by broader concepts:

  1. Compilation
  2. Optimization
  3. Unit Testing
  4. Functional Testing
  5. Packaging and Publishing
  6. Dependency Resolution
  7. Debug Runtime
  8. Headless Runtime
  9. PMD/Linting
  10. Documentation
  11. Reporting

Should we create an Archetype?

The short answer: Yes. The long answer: Archetypes are absolutely critical to drive adoption, examples and acceptance, and are a very useful speed-of-workflow tool. Given the many different potential framework projects out there however, we’re going to have to create many of these; One for JQuery, one for Mootools, one for Backbone… you get the idea. So how about we worry about that once we get more pieces of the compiler in place.

Where will the source live?

I’m glad you asked! I’ll be hosting the project on GitHub. Please feel free to contribute!

Where will the maven repository be?

This is as yet undecided. For now I recommend you clone the project and install it locally. Once there’s enough functionality on the project to warrant a full point release, we’ll revisit this.

Tagged with: , , ,
Posted in Blog

So long Actionscript, so long PHP.

To those of you not keeping up with my career as a technologist, I’ve long since abandoned my Actionscript roots (PHP is in serious decline) in favor of Javascript and Java. There are many reasons for this: Industry movement away from Flash, a shift in implementation practices, and a personal desire to keep my own skill set fresh and interesting. Wait a sec… Java? Fresh and interesting? What am I smoking, right? Well, read on:

Speed and Scalability

If you’re working on smaller projects with no notable load, “popular” languages such as Ruby, PHP and Python are great; After all, they are optimized for developer productivity rather than runtime performance, and in 99% of all cases this is fine. This is the main reason they’re so popular in the startup scene, as they enable extremely rapid feature development as a business evolves to find its place in the world.

At some point, however, a designed system must perform at scale, and as most detailed examinations such as this one show, not all languages are created equal. Delivering software at that level in a language that has a poor performance profile changes the discussion entirely: If your system chokes, you lose business transactions, customer confidence, and reporting data, all of which translates into money. In short, the argument stops being about the salary of one or two additional engineers, and more about the reliability and maintainability of your codebase.

Not only have years of optimization made it a very reliable and performant platform, the religious arguments about What Is The Best Tooling™ out there have been had and are mostly over, with configuration winning out over convention every time. The IDE’s support it, Continuous Integration Environments support it, Unit Testing frameworks exist and are very mature, and even though the code itself is extremely verbose, shitty Java will still run faster than shitty Ruby. Mind you, the study in the post linked above was done several years ago, and I’m quite certain that the various languages have improved since, however given the lack of community trumpeting about oh-look-we-finally-beat-Java, I’m going to take that post as mostly-still-accurate.

B2C vs. B2B

The second big shift that happens at scale is a shift from a consumer-based business to a service-based business. Don’t get me wrong: Consumers are important, however onboarding more consumers is a linear function, and usually not a very steep one. Onboarding businesses to your platform, however, is an entirely different beast, as each new partnership could bring orders of magnitude of extra load to your system. Is this a provisioning problem? Yes, yet with it come a whole new set of problems: Will you need to provide authentication integration with their corporate LDAP system? Will you have to Whitelabel your service? Will you need to provide API support? A dedicated and/or isolated data cluster?

To be fair, these are language-agnostic architectural problems, ones which encourage a clear separation of UI and API layer, forcing all business logic into the latter. Websites in this system are simply UI clients and serve no purpose other than making the API human-friendly (not to be discounted- UX is super important).

Now that the UI is simply a facade, does it really make sense to render it on a server before sending it back to a client? Let me illustrate with an example:

Consider a website written in PHP that consumes an API.

  1. I visit this website
  2. The Application Server makes an API call
  3. The API returns the data
  4. Application Server Renders the data.
  5. Browser renders the HTML
  6. I get my html

Now consider a website written mostly in Javascript that consumes an API:

  1. I visit this website
  2. Browser calls the API
  3. The API returns the data
  4. Browser renders the HTML

See the difference? In the former case, you’ve got two times as many network transactions (added points of failure), and the additional cost of maintaining an application server. In the latter case, you can host the files on a glorified hard drive (S3 for instance) and are talking to the API directly.

But Javascript Sucks!

It really doesn’t: Javascript is an amazing, powerful language that is hamstrung by browsers, an outdated standards body, and a lack of a unified toolset.

Thankfully, browsers are starting to come around (Chrome at the lead) and implementation difference are starting to be eroded. Most of the javascript libraries out there (such as JQuery) don’t seem to have caught up to this fact yet, and are still rooted in the philosophy that they can only work for what works everywhere – no worries, they’ll evolve soon enough if their communities will let them.

The current arguments between the WhatWG and the W3C are obvious to anyone in our industry, and different business philosophies between browser vendors make sites like caniuse.com an unfortunate necessity. Add to that the never-ending political maneuvering in the ECMAScript standardization process, and javascript will forever be a bastard child of 5 different parents each telling it to follow a different career. Having said that, Google has successfully used Chrome as a strategic tool that established a de-facto standard, forcing other vendors to catch up.

Javascript tooling is still in its infancy, and many tools out there have not yet grown beyond themselves. Take, for instance, a unit testing framework like QUnit that is sadly dependent on JQuery, or a functional testing framework like Lebowski which is inexorably tied to Ruby and Sproutcore. Furthermore, many frameworks are differentiated by philosophical differences (take QUnit vs Jasmine for instance) which muddies the water even more. Lastly, even with a plethora of tools, nobody has managed to wrap it all into one single, cohesive whole that combines Formatting, PMD/Linting, Unit Testing, Functional Testing, Documentation and Optimization.

However… the tools are (mostly) all there, individually. JSDoc has become a de-facto standard. Google’s Closure compiler is mostly platform agnostic, drawing most of its cues from JSDoc. Jasmine could be your one-size-fits-all Unit Testing framework (assuming you like behavioral testing), and JSTD is a powerful testing framework that can be used for either unit or functional testing (It’d be even more powerful if the various browser runtime engines could be run headlessly like Mozilla’s Rhino). All it takes is for some enterprising individual to put them all together.

Tagged with: , , , , ,
Posted in Blog

Database Upgrades and Migrations with Adobe AIR

This post will show you how to update your SQLite application when you release a new version of your AIR application (Android or otherwise). For those of you who are impatient, just copy the two classes provided and use them as demonstrated at the end of the post (or roll your own, your choice).

When releasing a new version of an AIR application that makes use of SQLite, it’s often necessary to upgrade the schema for that database as well. While taking a Hail Mary approach to the problem and just deleting the old database may work for some, it’s not an elegant option and will ensure that you can never persist data between versions. Instead, creating a way for your application to gracefully migrate its own database to the most recent version will ensure that your local data is not compromised, providing a smooth and seamless experience for the end user.

The Concept

We know that a database is the accumulation of all the CREATE, UPDATE and DELETE statements that have come before it. This means it may be easily represented and reconstructed by an ordered sequence of SQL files. It then becomes our task to manage which SQL files have already been executed in a given application version, and to execute (in order) those files which have not.

To accomplish this, we do the following:

  1. We create a migration table within your database in which we store which scripts have been executed.
  2. We check each script against this table before we run it.

There are other optimizations we can perform, however for this case we’re going to keep the idea simple.

Part 1: The Migration Class

The Migration class is a simple value container that contains your SQL Script as well as a Unique ID. Both of these are derivatives, since we want to give options on how to include the SQL file, and we don’t want to make our users have to think too much about how to set up a migration.

package com.fancybrandname.core.db
{
        import com.adobe.crypto.MD5;
        import com.fancybrandname.core.utils.LogUtil;

        import flash.data.SQLConnection;
        import flash.data.SQLStatement;
        import flash.errors.IllegalOperationError;

        import mx.logging.ILogger;

        /**
         * A specific database migration instance. It should contain both a version
         * and an embedded SQL source file that will be executed against the database.
         *
         * @author Michael Krotscheck
         */
        public class Migration
        {
                /**
                 * Logger
                 */
                private static const LOG : ILogger = LogUtil.getLogger( Migration );

                /**
                 * The SQL Source to use for this migration.
                 */
                private var _source : *;

                /**
                 * The SQL Statement extracted from the SQL Source object.
                 */
                private var _sqlStatement : String;

                /**
                 * This is the unique version key we use to identify this particular migration.
                 */
                private var _versionKey : String;

                /**
                 * The SQL Source to use for this migration.
                 */
                public function get source () : *
                {
                        return _source;
                }

                /**
                 * @private
                 */
                public function set source ( value : * ) : void
                {
                        _source = value;

                        // Here we attempt to extract the SQL statement from the passed object. If 
                        // the object is a Class (as with the @Embed directive), we instantiate and convert it.
                        // If it's anything else we try to invoke the toString method, and throw an error if we
                        // can't find it.
                        if ( _source is Class )
                        {
                                _sqlStatement = ( new _source() ).toString();
                        }
                        else
                        {
                                try
                                {
                                        _sqlStatement = _source.toString();
                                }
                                catch ( e : Error )
                                {
                                        throw new IllegalOperationError( 'Unrecognized type for SQL Migration. Please use Embeds or Strings' );
                                }
                        }

                        // Now we generate a unique ID from this string.
                        // You could request this key manually so that you don't have a dependency
                        // on the adobe crypto library, but I prefer doing it this way.
                        _versionKey = MD5.hash( sqlStatement );
                }

                /**
                 * This is the unique version key we use to identify this particular migration.
                 *
                 * @read-only
                 */
                public function get versionKey () : String
                {
                        return _versionKey;
                }

                /**
                 * This is the unique version key we use to identify this particular migration.
                 *
                 * @read-only
                 */
                public function get sqlStatement () : String
                {
                        return _sqlStatement;
                }

                /**
                 * Constructor.
                 */
                public function Migration ()
                {
                }
        }
}

Step 2: The Migration Manager

Our migration manager does the heavy lifting for this class, by checking the database for the last applied migration key and only applying those keys that come after it. It assumes several things. Firstly, that the migrations are in order of execution. Secondly, that the SqlConnection instance passed to it is a synchronous, not asynchronous connection. This method would definitely work for asynchronous connections, however you’d have to adjust the class to iterate using eventListeners. I leave this as an exercise to the reader.

package com.fancybrandname.core.db
{
	import flash.data.SQLConnection;
	import flash.data.SQLResult;
	import flash.data.SQLStatement;
	import flash.errors.IllegalOperationError;
	import flash.errors.SQLError;
	
	/**
	 * This metadata sets the default property for child nodes used in MXML. This
	 * makes MXML markup a lot easier to understand.
	 */
	[DefaultProperty( "migrations" )]
	/**
	 * A simple class that assists in managing multiple SQL files that may need
	 * to be run against a locally stored database. It makes the assumption
	 * that the migrations must be executed in the order in which they are provided,
	 * and that the database starts "clean" and untouched.
	 *
	 * @author Michael Krotscheck
	 */
	public class MigrationManager
	{
		
		/**
		 * The SQL Connection to use to connect to the database and check for migration.
		 */
		public var sqlConnection : SQLConnection;
		
		/**
		 * The migrations to run against the database. These must be in order of execution.
		 */
		public var migrations : Vector.<Migration> = new Vector.<Migration>();
		
		/**
		 * The name of the table within the database to store the migrations in.
		 */
		public var tableName : String = 'migration';
		
		/**
		 * Constructor
		 */
		public function MigrationManager ( sqlConnection : SQLConnection = null )
		{
			this.sqlConnection = sqlConnection;
		}
		
		/**
		 * This method runs the database migrations.
		 */
		public function migrate () : void
		{
			// Can we migrate?
			if ( !sqlConnection )
			{
				throw new IllegalOperationError( 'No SQLConnection provided' );
				return;
			}
			
			try
			{
				// Make sure the migration table exists.
				createMigrationTable();
				
				// Get the last version key applied to the database.
				var lastVersionKey : String = getLastVersionKey();
				
				// Iterate through our collection and execute every script AFTER the version key we found.
				var len : int = migrations.length;
				
				// Switch to let us know whether the last version key was found.
				// If the last version key is empty, apply all of them.
				var lastKeyFound : Boolean = lastVersionKey == '' ? true : false;
				
				for ( var i : int = 0; i < len; i++ )
				{
					var currentMigration : Migration = migrations[ i ];
					
					if ( lastKeyFound )
					{
						apply( currentMigration );
					}
					else if ( currentMigration.versionKey == lastVersionKey )
					{
						lastKeyFound = true;
					}
				}
			}
			catch ( e : SQLError )
			{
				throw( e );
			}
		}
		
		/**
		 * This method creates the migration table if it doesn't exist yet.
		 */
		private function createMigrationTable () : void
		{
			var sql : String = 'CREATE TABLE IF NOT EXISTS ' + tableName + ' (version varchar(32) NOT NULL PRIMARY KEY, applied_on timestamp NOT NULL);';
			
			var statement : SQLStatement = new SQLStatement();
			statement.sqlConnection = sqlConnection;
			statement.text = sql;
			statement.execute();
		}
		
		/**
		 * This method gets the last version key recorded in the database.
		 */
		private function getLastVersionKey () : String
		{
			var statement : SQLStatement = new SQLStatement();
			statement.text = 'SELECT version FROM ' + tableName + ' ORDER BY applied_on DESC LIMIT 0,1';
			statement.sqlConnection = sqlConnection;
			statement.execute();
			
			var sqlResult : SQLResult = statement.getResult();
			
			if ( sqlResult == null || !sqlResult.data || sqlResult.data.length == 0 )
			{
				// No migrations found.
				return '';
			}
			else
			{
				// We found one, return it.
				return sqlResult.data[ 0 ].version;
			}
		}
		
		/**
		 * This method applies the database migration to the database passed to this function.
		 * Please note that this version of the class only works with Synchronous SQLConnections.
		 */
		private function apply ( migration : Migration ) : void
		{
			// Apply the migration
			var updateStatement : SQLStatement = new SQLStatement();
			updateStatement.text = migration.sqlStatement;
			updateStatement.sqlConnection = sqlConnection;
			updateStatement.execute();
			
			// Update the version key
			var versionStatement : SQLStatement = new SQLStatement();
			versionStatement.text = 'INSERT INTO ' + tableName + ' (version, applied_on) VALUES ( :version , :timestamp )';
			versionStatement.parameters[ ':version' ] = migration.versionKey;
			versionStatement.parameters[ ':timestamp' ] = ( new Date() ).getTime();
			versionStatement.sqlConnection = sqlConnection;
			versionStatement.execute();
		}
	}
}

Step 3: Usage

Finally it’s time to use our class. As you can see the MXML markup is pretty straightforwar, and usage in ActionScript is similarly easy. Note that I’m using both @Embed directives and raw text, and pay particular attention to the embed mime-type. Without it, this doesn’t really work.

<s:Application 
	xmlns:fx="http://ns.adobe.com/mxml/2009"
	xmlns:parsley="http://www.spicefactory.org/parsley"
	xmlns:s="library://ns.adobe.com/flex/spark"
	xmlns:db="com.fancybrandname.core.db.*"
	>
	<fx:Script>
		<![CDATA[
			override protected function initializationComplete():void
			{
				super.initializationComplete();
				
				var dbFile :File = File.applicationStorageDirectory.resolvePath('myDb.db');
				var connection :SQLConnection = new SQLConnection();
				connection.open( dbFile );
				
				migrationManager.sqlConnection = connection;
				migrationManager.migrate();
			}
		]]>
	</fx:Script>

	<fx:Declarations>
		<db:MigrationManager id="migrationManager" tableName="migrations">
			<db:Migration source="@Embed(source='/sql/CreateSomeTable.sql',mimeType='application/octet-stream')"/>
			<db:Migration source="DROP TABLE IF EXISTS someTable;"/>
			<db:Migration source="@Embed(source='/sql/CreateSomeOtherTable.sql',mimeType='application/octet-stream')"/>
		</db:MigrationManager>
	</fx:Declarations>
</s:Application>
Tagged with: , , , , ,
Posted in Blog, Tutorials

Zend_Auth_Adapter_Facebook

This post is rather more nerdy than my other ones, and goes to describe how to create a Facebook authentication adapter for the Zend Framework, using the new Facebook Graph API. I do not intend to support this code explicitly, but I do work with it and will update it to suit my own needs. If you’re impatient, the full code for the adapter and a sample controller are the last two code samples at the bottom of this post.

A discussion about OAuth and OpenID

Strictly speaking, Facebook supports OAuth, not OpenID. OAuth is a way for your application to act on behalf of someone else on another side, while OpenId exists solely for you to use the other website as an authorization authority. The difference is subtle, however telling that Facebook has explicitly stated that it does not want to be an authorization authority. In practice, however, the two standards are interchangeable, and OAuth works pretty well as a way to authenticate a user.

How Facebook Auth Actually Works

Here’s the flow in four easy steps:

  1. The user clicks on a “Login to Facebook” link on your server, which redirects them to another page on your server that handles the authentication.
  2. Your Facebook Auth page puts all the parameters for your authentication, such as your app ID and scope, into a URL and sends the user to Facebook via that URL.
  3. Once Facebook returns the user (presumably authenticated) to your server, you can use the parameters sent via the URL to request an authentication token from Facebook. This is done via curl or some other server-to-server communication by calling the graph API.
  4. You use this authentication token to access the user’s information on Facebook.

Step 1: Get all the data you need

To properly authenticate with Facebook, you need four pieces of data. The first two are your application id and your application secret, both of which are available on your developer application page.

Screen shot 2010-08-21 at 1.50.04 PM.png

A sample Facebook application page

The second two pieces of data are specific to your implementation. These are the redirect URI, which is used to tell Facebook where to send the user once they are done authorizing your application, and the permission scope, which you use to tell Facebook what permissions you want to request from the user. This latter one actually warrants some more documentation, which Facebook has graciously provided here. Be careful: Users can get really suspicious if you ask for too much data.

Step 2: Redirect your user to Facebook.

With this data, you can redirect your user to Facebook. Facebook will then work some magic and ask the user whether they want to grant your application access to their profile. Don’t forget to urlencode that Redirect URI.

$loginUri = "https://graph.facebook.com/oauth/authorize?client_id={$appId}&redirect_uri={$redirectUri}&scope={$scope}";
header('Location: ' . $loginUri);

Step 3: Request an Auth Token

Assuming that the user authorizes your application, Facebook will return them to your redirect URI with the “code” parameter in the URL. You can use this parameter to request an authorization token from Facebook. In this case I’m using the Zend Request handler to simplify things a little.

$client = new Zend_Http_Client( "https://graph.facebook.com/oauth/access_token" );
$client->setParameterGet('client_id', $appId);
$client->setParameterGet('client_secret', $secret);
$client->setParameterGet('code', $code);
$client->setParameterGet('redirect_uri', $redirectUri);

$result = $client->request('GET');
$params = array();
parse_str($result->getBody(), $params);
$token = $params['access_token'];

Step 4: Read the user’s profile

At this point, you have an auth token that grants you access to read the user’s profile information in accordance to the permissions they have granted you. These permissions are completely blind- you don’t have to additionally specify what you want to access – the requests will simply return the information to which you have access.

$client = new Zend_Http_Client( "https://graph.facebook.com/me" );
$client->setParameterGet('client_id', $appId);
$client->setParameterGet('access_token', $token);
$result = $client->request('GET');
$user = json_decode($result->getBody());

Zend_Auth_Adapter_Facebook

For those of you who would rather not take the above code and wrap it into your own Auth Adapter, I’ve gone ahead and done all that work for you. Below you will find a sample Zend Controller, as well as a pre-baked Auth Adapter. If you come across any major security problems in it, I’d love to know, since I use this adapter myself :) .

file: Zend/Auth/Adapter/Facebook.php

class Zend_Auth_Adapter_Facebook implements Zend_Auth_Adapter_Interface
{
    /**
     * The Authentication URI, used to bounce the user to the facebook redirect uri.
     * 
     * @var string
     */
    const AUTH_URI = 'https://graph.facebook.com/oauth/authorize?client_id=%s&redirect_uri=%s';
    
    /**
     * The token URI, used to retrieve the OAuth Token.
     * 
     * @var string
     */
    const TOKEN_URI = 'https://graph.facebook.com/oauth/access_token';
    
    
    /**
     * The user URI, used to retrieve information about the user.
     * 
     * @var string
     */
    const USER_URI = 'https://graph.facebook.com/me';
    
    /**
     * The application ID
     *
     * @var string
     */
    private $_appId = null;

    /**
     * The application secret
     *
     * @var string
     */
    private $_secret = null;

    /**
     * The authentication scope (advanced options) requested
     *
     * @var string
     */
    private $_scope = null;

    /**
     * The redirect uri
     *
     * @var string
     */
    private $_redirectUri = null;

    /**
     * Constructor
     *
     * @param string $appId the application ID
     * @param string $secret the application secret
     * @param string $scope the application scope
     * @param string $redirectUri the URI to redirect the user to after successful authentication
     */
    public function __construct($appId, $secret, $redirectUri, $scope)
    {
        $this->_appId = $appId;
        $this->_secret = $secret;
        $this->_scope = $scope;
        $this->_redirectUri   = $redirectUri;
    }

    /**
     * Sets the value to be used as the application ID
     *
     * @param  string $appId The application ID
     * @return Zend_Auth_Adapter_Facebook Provides a fluent interface
     */
    public function setAppId($appId)
    {
        $this->_appId = $id;
        return $this;
    }

    /**
     * Sets the value to be used as the application secret
     *
     * @param  string $secret The application secret
     * @return Zend_Auth_Adapter_Facebook Provides a fluent interface
     */
    public function setSecret($secret)
    {
        $this->_secret = $secret;
        return $this;
    }

    /**
     * Sets the value to be used as the application scope (array())
     *
     * @param  string $scope The application scope
     * @return Zend_Auth_Adapter_Facebook Provides a fluent interface
     */
    public function setApplicationScope($scope)
    {
        $this->_scope = $scope;
        return $this;
    }

    /**
     * Sets the redirect uri after successful authentication
     *
     * @param  string $redirectUri The redirect URI
     * @return Zend_Auth_Adapter_Facebook Provides a fluent interface
     */
    public function setRedirectUri($redirectUri)
    {
        $this->_redirectUri = $redirectUri;
        return $this;
    }


    /**
     * Authenticates the user against facebook
     * Defined by Zend_Auth_Adapter_Interface.
     *
     * @throws Zend_Auth_Adapter_Exception If answering the authentication query is impossible
     * @return Zend_Auth_Result
     */
    public function authenticate()
    {
    	// Get the request object.
    	$frontController = Zend_Controller_Front::getInstance();
    	$request = $frontController->getRequest();
    	
    	// First check to see wether we're processing a redirect response.
    	$code = $request->getParam('code');
    	
    	if ( empty ($code ) )
    	{
	    	// Create the initial redirect
	    	$loginUri = sprintf(Zend_Auth_Adapter_Facebook::AUTH_URI , $this->_appId, $this->_redirectUri);
	 		
	    	if ( !empty($this->_scope) )
	    	{
	    		$loginUri .= "&scope=" . $this->_scope;
	    	}
	    	
	    	header('Location: ' . $loginUri );
    	}
    	else
    	{
    		// Looks like we have a code. Let's get ourselves an access token
	    	$client = new Zend_Http_Client( Zend_Auth_Adapter_Facebook::TOKEN_URI );
	    	$client->setParameterGet('client_id', $this->_appId);
	    	$client->setParameterGet('client_secret', $this->_secret);
	    	$client->setParameterGet('code', $code);
	    	$client->setParameterGet('redirect_uri', $this->_redirectUri);
	    	
	    	$result = $client->request('GET');
	    	$params = array();
	    	parse_str($result->getBody(), $params);
	    	
	    	// REtrieve the user info
	    	$client = new Zend_Http_Client(Zend_Auth_Adapter_Facebook::USER_URI );
	    	$client->setParameterGet('client_id', $this->_appId);
	    	$client->setParameterGet('access_token', $params['access_token']);
	    	$result = $client->request('GET');
	    	$user = json_decode($result->getBody());
	    	
            return new Zend_Auth_Result( Zend_Auth_Result::SUCCESS, $user->id, array('user'=>$user, 'token'=>$params['access_token']) );
    	}
    	
        return new Zend_Auth_Result( Zend_Auth_Result::FAILURE, null, 'Error while attempting to redirect.' );
    }
}

file: modules/default/controllers/FacebookauthController.php

/**
 * Sample Facebook Auth Controller.
 * 
 * @author Michael Krotscheck
 */
class FacebookauthController extends Zend_Controller_Action
{
	/**
	 * Application default action
	 */
	public function indexAction ()
	{
		$appId = 'YOUR_APP_ID';
		$secret = 'YOUR_APP_SECRET';
		$redirectUri = 'http://path-to-this-controller-and-action';
		$scope = 'YOUR_COMMA_SEPARATED_APPLICATION_SCOPE';
		
		// Create the authentication adapter.
		$adapter = new Zend_Auth_Adapter_Facebook( $appId, $secret, $redirectUri, $scope );
		
		// Get an authenticator instance
		$auth = Zend_Auth::getInstance();
		
		// This call will automatically redirect to facebook with the passed parameters.
		$result = $auth->authenticate($adapter);
		
		if ( $result->isValid() )
		{
			// Get the messages
			$messages = $result->getMessages();
			
			// Get the user object from the returned messages.
			$fbUser = $messages['user'];
			
			var_dump($fbUser);
		}
	}
}
Tagged with: , , ,
Posted in Blog

Breaking down a Client Application

With so little time to blog over the past few months, I took a few moments to really think about why I’d fallen out of the habit. I believe (personally) that much of this is because my writing time now goes into guiding and educating my own development team, and once I realized that I saw no reason to exclude all of you from any of these emails either. So yes, you should expect the content to start picking up again, but the topics will be focused around applied software development rather than the marketing and retail topics I spoke about while I was still at Resource.

This particular email was written to demystify the difference (as far as I’m concerned) of all the different parts of a client application. However rather than a heavy discussion on methods and algorithms, what I present here are actually the higher level concepts that govern the structure of an application. I tried to order them in a fairly logical fashion, starting at the “front” of the application (what you see) and moving towards the “back” (the bits that the user should never see).

But first, I feel it is important to understand the difference between business logic and view logic:

  • Business logic manages data and enforces business rules. It cleans, saves, reads, compares, and makes sure that the data models are properly updated. It is also important to make the distinction between server-side business logic and client business logic. That which lives on the server is usually focused more on business rules, while that which lives in the client usually cares more about server interaction and data manipulation.
  • View logic manages how data is displayed. It manages state, formatters, strings and view indexes, and doesn’t really care about how that data got to where it is, it only cares about how it should be displayed within this particular view.

Having said that, here are the major components of a client application. Caveat, this might not mean much to you Javascript guys.

Skin

A skin acts to describe the containers, colors and graphics that a control or view use. It can change its appearance based on a skin state (not to be confused with view state), and can refer to styles set by the component which it is skinning. It should not have any business or data logic whatsoever.

Control

A Control is the lowest level of functional components which comprise an application. These are our buttons, labels, data grids, view containers and so forth, and they are characterized by the fact that the data they display is not specialized, nor do they contain any real internal business logic at all. They know how to interact with a generic type and dispatch events when a particular action occurs.

View

A view is a collection of controls, containers, formatters, skins and subviews, all connected via a presentation model. It acts to collect and arrange elements rather than figuring out what each piece is supposed to do. As such, Views should contain little to no code at all, and should be managed by states and properties.

Item Renderer

An Item renderer is a very specialized kind of view, which deals with the display of a single piece of data in a collection. As a result, it is the only view which should be dependent on an external piece of data – one that is injected into a property of that ItemRenderer from a parent (the data property). Item renderers may contain Presentation MOdels to help them manage user gestures, but rarely make use of a data model as this is already provided.

Presentation Model

It is the job of the presentation model to act as a mediator between the view and the rest of the application. This does not mean that it should manage data, nor necessarily contain it. Instead, its job is to expose all the methods and data which are being used by the View, which includes models (bindable), states, actions which describe user methods and (in some cases) validators.

Data Model

The data model is a dumb data container. It should contain little-to-no logic on its own, and instead allow the tasks to clean, sort and manipulate anything that is assigned to the model. Thinking of it differently: The Data Model is the authoritative source of any data used in the application. All data starts here and ends here. Views get their data from the Data Model via binding. Tasks retrieve model instances. Presentation models make data available by exposing a public instance of a data model.

Task

Tasks are where our “business” logic resides, in the most generic and granular way possible. As an example, the act of loading a user might require reading the user object, retrieving their preferences, and initializing the application locale. Rather than try to handle this all in one task, it is better to use a SequenceTask or ParalellTask to collect the three different actions. Why? Because your preferences screen might need to reload user preferences after they’ve been saved, but you don’t want to refresh the entire user.

Tasks, much like presentation models, are able to collect any data and utilities they need to achieve what they need to achieve, however when they are done they need to let go of those items and dispose of themselves properly. The important thing to note here is that any business logic that can be put into the service layer should live in the service layer; We don’t want to expose the guts of our business rules to the world, and servers are much easier to protect from questionable data manipulation.

Delegate

A delegate is our service proxy, and acts as a channel back and forth from the database. It allows us access to the business logic that lives on the server in a reliable and consistent way. In most cases, each delegate method will have one task that wraps it and makes sure that the data is properly cleaned and presented.

Utility

Utilities are classes that provide common data manipulation routines that are global to all aspects of the application. A good utility, for instance, would be a method that handles date conversions from GMT to Local and back. A less desirable utility method would be one that formats a piece of data for a specific view, as this should be handled within the view itself.

Libraries

Libraries are the catchall for everything that was left out, and it includes pieces of highly specialized logic that don’t really fit neatly elsewhere. Good examples of this are video encoding libraries, classes that manage encryption, filesystem manipulation and other tasks like that.

VO

A VO, or Value Object (also known as DO/Data Object) is the raw data which we manipulate. It describes a particular Domain Object and acts as a package that we pass from method to model to view. They only contain data related to that same object, and should not contain any formatting logic and only limited validation logic (is-not-zero checks and so forth). Formatting should be handled by the view, validation should be handled by tasks or utilities.

Tagged with: , , , ,
Posted in Blog

Hi, my name is Mike, and I’m a Flash Developer

Let me ask you this: Am I less of a person because I write code for a living?

How about this: Am I less of a person because I work in Flash?

I haven’t really (well, sortof) weighted in on the Flash vs. HTML5 debate yet, mostly because the main posters pro and con are both more technically qualified, have a stronger social following, and/or have more backing from marketers. It is the futility of the debate itself that interests me, both from a very personal perspective and from the perspective of an internet veteran.

The nature of the debate that I observe from the comfort of my Laptop is the following:

  • Camp A: Here’s a bunch of reasons why Flash sucks!
  • Camp B: Nuh-uh! Here’s a list of reasons why that’s not the case!

As you probably realize yourself, this argument structure is essentially religious, and can be replayed whenever two people have different opinions and are not willing to reconcile. It’s certainly not restricted to the technology space (Choice vs. Life anyone?), and the futility of entering into such an argument is well known to anyone who’s ever been involved in a flame war.

Now take this Adobe/Apple debate and pose it to our society (full of technology-advocates, evangelists and gurus), and you’ll soon come to realize that Adobe cannot win this argument, not given its current strategy. This is for two reasons:

  1. You cannot win a flamewar from a defensive position.
  2. You cannot win a flamewar with reason.

Adobe’s Flash Team are doing an admirable job at defending against everything that’s coming out of Apple, the Javascript community, and the standards purists. Unfortunately by the time they get to debunking these statements the damage has been done, and no matter how quickly they respond they will never get the same media penetration that Apple will. Why? Because people don’t like to be told they’re wrong, and anyone who listens to a flamewar long enough will start to tune out. Just ask Fox News- they make a living on this – and there’s no way you can reach an unreceptive audience no matter how good your arguments are.

Secondly, any aggressor can very easily cherry-pick its arguments to attack the defender on weak points, and thus invalidate their entire argument simply because one point wasn’t well researched. While the overall flash platform is a strong contender on the web, you’ll notice that the argument is focused on a few key shortcomings – performance, accessibility, ‘open-ness’ and the like – in which HTML5 has strength. By defending specifically against these weak points, Adobe is then empowering critics to further choose sub-points where they have strength, and through this cycle the argument is diminished and diminished until at the end all we remember is that Adobe was trying to defend itself and their critics never ran out of anything to point to and say: “Hey you suck”.

Lastly, as I have seen in several cases, arguments have started go get personal, and as soon as you enter that arena you can invalidate someone’s argument simply by pointing out their own shortcomings. To use myself as an example, you can invalidate anything I have to say about Flash and HTML5 by pointing out that I’m not a real CS major, that I haven’t remained at a job for more than 4 years, or that I go so far as to claim that there are people more qualified to talk about this than I in the very first paragraph in this post. My experience and job title doesn’t matter – as soon as you say “Oh, you don’t know what you’re talking about, you’re not a CS Major” you’ve got me completely discredited.

But what bothers me the most is the first concession which the Adobe Community had to make, and that is that their pool of developers draws from the design community and are subsequently not the best. Now, every technology has a wide range of developer competence, yet with Flash it’s becoming a banner cry: “Flash Developers suck”. Your technology choice does not define how skilled you are, and yet it’s becoming pretty clear that those of us who choose Actionscript and Flash are seen as incompetent, less than human, and not worth listening to because we’re blind and ignorant fanboys who don’t understand what they’re missing with [Insert Language Here].

This last point is perhaps the most damaging, because it’s eroding Adobe’s hard-won community. Do you like feeling like a shitty developer? Do you want to be the pariah of the web? Do you like being told that you are the scum of the earth? No, and I don’t either, and if like myself you’re simply trying to make a living, chances are you’re seriously considering learning something else to make sure you remain employable.

So the real question is: Will rational minds prevail? I doubt it.

Tagged with: , , ,
Posted in Blog