Sunday, October 16, 2016

Dynamic karma.conf.js for Jenkins

Karma-runner is a great way to run jasmine javascript test suites. One trick to make it easy to customize karma's runtime behavior is to take advantage of the fact that karma's config file is javascript - not just json, so it's easy to wire up karma.conf.js to change karma's behavior based on an environment variable.

For example, when run interactively karma can watch files, and rerun the test suite in Chrome when a file changes; but when Jenkins runs karma, karma should run through the test suite once in phantomJs, then exit. Here's one way to set that up.

First, if you generate your karma config.js file using karma init, and wire up the file to run your test suite in Chrome and watch files for changes, then you wind up with a karma.conf.js (or whatever.js) file structured like this:

module.exports = function(config) {
    config.set( { /* bunch of settings */ } );
}

To wire up the jenkins-phantomjs support - just pull the settings object out to its own variable, and wire up a block of code to change the settings when your favorite environment variable is set, and wire up Jenkins to set that environment variable ...

module.exports = function(config) {
    var settings = { /* bunch of settings */ },
        i, overrides = {};
    if ( process.env.KARMA_PHANTOMJS ) {  // jenkins is running karma ...
        overrides = {
            singleRun: true,
            reporters: ['dots', 'junit' ],
            junitReporter: {
                outputFile: 'test-result.xml'
            },
            browsers: [ 'PhantomJS', 'PhantomJS_custom'],
            customLaunchers: {
                PhantomJS_custom: {
                    flags: [ '--load-images=true'], 
                    options: {
                        windowName: 'my-window'
                    },
                    debug:true
                }
            }
         };
    }
    for ( i in overrides ) {
        settings[i] = overrides[i];
    }
    config.set( settings );
}

Jenkins can run Karma test suites with a shell script, and Jenkins' JUnit plugin harvests and publishes the test results; works great!

Saturday, September 24, 2016

JAAS vs AWS Security Policy vs RBAC vs FireBase vs ACL vs WTF ?

I've been thinking a bit about authentication and authorization lately, and while I may have my head semi-wrapped around authentication - authorization is kicking my ass a bit. Here's my understanding as of today.

First, there's something called "role based authorization" (RBAC) that pops up a lot, and is embodied in one way in the java EE specification. The basic idea behind RBAC is that a user (or a user group) is assigned a "role" that represents explicitly or implicitly a basket of permissions within an application. For example - an enterprise SAAS product that I work on at my day job defines "super user", "admin", and "standard" user roles under each customer's account. Each user is assigned a role that gives the user a certain basket of global permissions (this user can perform action X on any asset).

I was somewhat confused when I discovered that AWS defines an IAM role as a non-person user (or Principal in the JAAS world). It turns out that AWS implements something more powerful than the RBAC system I'm semi-familiar with - AWS builds up its security model around security policy specifications that can either be assigned to a user or group of users (like an RBAC role) or to an asset (like an ACL). When deciding whether a particular user may perform a particular action on a particular asset the AWS policy engine evaluates all applicable policies to come to a decision.

I have not worked at all with Google's Firebase, but it seems to implement a less powerful (compared to AWS IAM policies), but simpler access control mechanism that splits the difference between RBAC and ACL's via a policy specification that grants and restricts permissions on an application's tree of assets based on XPATH-like selection rules.

On thing I appreciate about Firebase's design is that it empowers the application developer with an access control mechanism, so if a team is building an application on Firebase, then the team's application can take advantage of Firebase's access control system to regulate access to the application's assets by the application's users.

On the other hand, The IAM policy tools in AWS provide a powerful mechanism for regulating what actions different team-members may take on AWS resources (S3 buckets, EC2 VM's, whatever) within a shared AWS account in which the team is deploying some application, but it's not clear to me how that team could leverage IAM's security policy engine within the team's own application to control how the application's (non-IAM) users may interact with the application's (non-AWS) assets. The AWS Cognito service seems to try to point in the direction of leveraging AWS policy within an application, but in an application space where business logic is implemented in the client, and the client interacts directly with the data-store. I'm still a member of the old school that thinks the client should implement UX, and access API's that implement the business logic and validation that precedes data manipulation.

Sunday, August 07, 2016

Custom JUnit4 TestRunner with Guice

It's easy to write your own JUnit test-runner. A developer on the java-platform often writes code in the context of some framework or container platform (Spring, Play, java-EE, OSGi, Guice, whatever) that provides facilities for application bootstrap and dependency injection. When at some point she wants to write a JUnit test suite for some subset of code the developer is faced with the problem of how to recreate the application framework's runtime context within JUnit's runtime context - which assumes a simple no-argument constructor for test-harness classes.

Fortunately - JUnit provides a serviceable solution for augmenting the test suite runtime context in the form of test runners. A developer uses an annotation on his test class to indicate that the tests require a custom runner. For example - Spring provides its SpringJUnit4ClassRunner which allows a test developer to use Spring annotations in her test suties - like this:

@RunWith(SpringJUnit4ClassRunner.class)
class WhateverTest {

     @Service(name="whatever")
     Whatever service;

     @Test
     public void testSomething() {
         assertTrue( ... );
     }
     ...
}



The Littleware project includes a system that extends Guice with a simple module and bootstrap mechanism that I was able to integrate with JUnit with a simple LittleTestRunner that follows the same trick that Spring's test runner uses of simply extending JUnit's default BlockJUnit4ClassRunner - overriding the createTest method - easy cheesey:

package littleware.test;

import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import littleware.bootstrap.LittleBootstrap;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.InitializationError;

/**
 * JUnit4 test runner enabled with littleware.bootstrap Guice bootrap and
 * injection. Imitation of SpringJunit4TestRunner:
 *
 */
public class LittleTestRunner extends BlockJUnit4ClassRunner {
    private static final Logger log = Logger.getLogger(LittleTestRunner.class.getName());

    /**
     * Disable BlockJUnit4ClassRunner test-class constructor rules
     */
    @Override
    protected void validateConstructor( List<Throwable> errors ) {}
    
    /**
     * Construct a new {@code LittleTestRunner} and initialize a
     * {@link LittleBootstrap} to provide littleware testing functionality to
     * standard JUnit tests.
     *
     * @param clazz the test class to be run
     * @see #createTestContextManager(Class)
     */
    public LittleTestRunner(Class<?> clazz) throws InitializationError {
        super(clazz);
        if (log.isLoggable(Level.FINE)) {
            log.log(Level.FINE, "constructor called with [{0}]", clazz);
        }
    }

    /**
     * This is where littleware hooks in
     * 
     * @return an instance of getClass constructed via the littleware managed Guice injector
     */
    @Override
    protected Object createTest() {
        try {
            return LittleBootstrap.factory.lookup(this.getTestClass().getJavaClass());
        } catch ( RuntimeException ex ) {
            log.log( Level.SEVERE, "Test class construction failed", ex );
            throw ex;
        }
    }
}

Now I can write tests in Guice's "inject dependencies into the constructor" style - like this:


/**
 * Just run UUIDFactory implementations through a simple test
 */
@RunWith(LittleTestRunner.class)
public class UUIDFactoryTester {

    private final Provider<UUID> uuidFactory;

    /**
     * Constructor stashes UUIDFactory to run test
     * against
     */
    @Inject()
    public UUIDFactoryTester(Provider<UUID> uuidFactory) {
        this.uuidFactory = uuidFactory;
    }

    /**
     * Just get a couple UUID's, then go back and forth to the string
     * representation
     */
    @Test
    public void testUUIDFactory() {

       //...
    }
}

Tuesday, July 19, 2016

Gradle with "Service Provider" Pattern

I've started dusting off my old littleware project over the last month or so on a "dev" branch in github. As part of the cleanup I'm moving littleware from its old ANT-IVY build system to Gradle. When I started with ant-ivy it was a great alternative to Maven, and allowed me to extend the build setup put in place by a Netbeans IDE's project with transitive dependencies, jenkins integration, scala support - all that great stuff. The problem is that the ant-ivy setup is idiosyncratic, and Gradle and SBT are well designed, widely used systems. I wound up going with Gradle, because I've read a lot of good things about it, and I want to become more familiar with it, and I had used SBT a little in the past, and found SBT's DSL annoying and its extension mechanisms inscrutable, and of course Google is using Gradle to build Android, ...

Anyway - like I said, I'm dusting off the code, and trying to remember what the hell I was thinking with some of it, but so far the Gradle integration has gone well. One of the issues I'm dealing with is the IVY-centric mechanism littleware had setup to pull in optional run-time dependencies. With Gradle the default behavior of the java plugin is to include all compile-time dependencies at run-time - which is what most people expect. Littleware's ant-ivy setup excluded optional dependencies (like mysql-connector versus postgresql - something like that), and included add-on IVY configurations like "with_mysql", "with_postgres", "with_aws", ... that kind of thing, so a client project would have a dependency specification something like this:

<dependency org="littleware" name="littelware" ... conf="compile->compile;runtime->runtime,with_mysql" />

Of course nobody else works like that, so in the interest of clarity and doing what people expect - I'm going to try to rework things, so that littleware's base jar asset includes service provider interfaces (SPI's), that add-on modules implement, so a client that wants to hook up littleware's ability to wire in a MySQL DataSource to a Guice injector would add a dependency to the 'littleware_mysql_module' to their build - something like that. The Jackson JSON parser implements a module system like this, and the SPI pattern is all over java-EE (JDBC, Servlets, whatever); it's a good and widely understood pattern. We'll see how it goes.



Sunday, June 12, 2016

Decouple UX and Services API's in Single Page Apps

An old-school PHP, Struts, JSF, Rails what have you webapp was deployed with a strong binding between the application's UX and backend services. A typical "3 tier" application would involve some kind of server-side MVC framework for generating HTML, and some business objects that would manage the storage and interpretation of data stored in a SQL database, and that was often the whole shebang.

UX in a modern single page application (SPA) is implemented in javascript that accesses backend microservices via AJAX calls to REST API's. Unfortunately there is still a tendency in many projects to combine the web UX with a backend service in a single project - maybe implement an old-school server-side login flow, then dynamically generate an HTML shell that defines the authenticated context in some javascript variables, and a call to the javascript app's bootstrap routine. I am not a fan of this way of structuring an application.

I like the "app shell" approach to structuring a SPA - where the web UX is its own separate project - a UI application that accesses backend micro-services. There are various advantages to this approach, but the immediate benefit is that it simplifies the UX team's life - they can use whatever tools they want (gulp, less, typescript, bower, jslint, ...), develop locally with a simple nodejs+express server and maybe some docker containers providing backend services. The developers can focus on UX and design, and do not have to deal with whatever backend technology might be in use (play, maven, tomcat, rails, whatever, ...). The app-shell development process closely resembles the dev process for a native Android or iOS app.


Sunday, May 15, 2016

Problems with Cookies (session management with multiple authentication providers)


The web project I work on has an old-school login with username and password, check the hash in the database, and set a cookie design that we would like to improve on to support third party API access and multiple external authentication providers (OpenID-Connect, OATH2, SAML).

First, just setting an auth cookies sucks. Suppose a visitor to a web site has two login accounts A and B. He logs into account A in one tab, then, in another tab, signs out of the first account, and signs into account B. Now if the first tab is running a "single page application" javascript app, and the javascript code assumes that it is either signed into a valid session or signed out, then that code in the first tab can wind up inadvertently issuing updates to account B that were intended for account A. An application should not rely on a cookie to track its session, since that cookie can be changed out from under the app by code running in another tab; the app should instead explicitly pass some X-auth header or some similar trick to explicitly specify which session it intends to communicate in.

Second, the old prompt the visitor for a username and password that are validated against an app-maintained (JAAS style) authentication database sucks. A modern app should support multiple identity providers, so that a visitor can authenticate against an identity provider of her choice to establish a login session. An app has a session management service to manage the authentication handshake with different identity providers. This kind of infrastructure works equally well for both native and web apps.

Anyway - that's my current thinking on the subject of authentication and session management. Design-time ideas like these rarely survive implementation of working code.

Monday, February 15, 2016

Docker and Application Versioning


When transitioning from a virtual machine based deployment model to a container based model there are at least two ways to version an application - versioning the container image or versioning packages that the container downloads at startup.

Incrementing the container image version has the advantage that a cluster administrator can easily determine which version of an application is deployed by querying which version of the image the container is running. Similarly the cluster admin can automate upgrades and rollbacks with scripts that spin up and bring down different versions of a container. The disadvantages of this approach include the need to integrate automated image builds into the continuous integration pipeline (Jenkins or whatever), and the proliferation of dev-only and broken image versions within the Docker registry.

As a first step toward deploying applications with containers the project I'm involved with is moving toward infrequently publishing a container image for each application which contains the application's envelope dependencies. We take advantage of the Chef scripts we already have to build virtual machines by running chef-client (in chef-zero local mode) during the image build, and wiring up the image with an entry-point that once again runs chef-client to update the container at startup time. The Chef scripts download the latest versions of the application and its dependencies into the container as RPM packages from YUM repositories that our continuous integration pipeline already maintains to support our virtual machine devops workflow.

Sunday, January 31, 2016

javascript decomposition with bower's shorthand resolver


We started using bower to manage third party dependencies in our projects, and realized that we could also use bower to help decompose our javascript applications into re-usable independently tested components. We started out with a lazy approach where we setup a jscommon/ folder under which we installed our different components (jscommon/A/, jscommon/B/, jscommon/C/, ...) where each component might have its own build and test scripts - whatever it needs.

We started out representing dependencies between components with file URL's in bower.json files, so if C depended on B and A, then it might have a bower.json file like this:

  {
     ...
     "dependencies" : {
            "A" : "../A",
            "B" : "../B"
     }
   }

Of course - that quickly falls apart when an application's bower.json file has a different relative path to the jscommon/ folder, but using a shorthand resolver solves the problem. An application (or test or whatever) registers a shorthand resolver in a .bowerrc file with the appropriate relative path like this:

{
    ...
    "shorthand-resolver" : "../../{{shorthand}}"
}

, and we specify local dependencies in bower.json with short-hands like this:

  {
     ...
     "dependencies" : {
            "A" : "jscommon/A",
            "B" : "jscommon/B"
     }
   }

Sunday, March 15, 2015

jQuery in custom namespace

A re-usable javascript component that depends on jQuery may need to repackage jQuery in a custom namespace to avoid overriding other versions of jQuery loaded onto a third party host page. Fortunately the jQuery project makes it easy to move the core API to a namespace. Just download the uncompressed developer version of jQuery, and replace the following block of code at the top of the file:


    if ( typeof module === "object" && typeof module.exports === "object" ) {
  // For CommonJS and CommonJS-like environments where a proper `window`
  // is present, execute the factory and get jQuery.
  // For environments that do not have a `window` with a `document`
  // (such as Node.js), expose a factory as module.exports.
  // This accentuates the need for the creation of a real `window`.
  // e.g. var jQuery = require("jquery")(window);
  // See ticket #14549 for more info.
  module.exports = global.document ?
   factory( global, true ) :
   function( w ) {
    if ( !w.document ) {
     throw new Error( "jQuery requires a window with a document" );
    }
    return factory( w );
   };
 } else {
  factory( global );
 }
with something like this:

        global.myNameSpace = global.myNameSpace || (global.myNameSpace = {});
        global.myNameSpace.jQuery = factory( global, true );

Saturday, January 11, 2014

Bumping up to jdk8 and JEE 7

I bumped up littleware's build to pull in JEE7 jars, and also went ahead and setup a jdk8 pre-release runtime, and so far everything works great.

I discovered a while ago when I bumped up to JEE6 that the Java EE dependencies are a little weird. The published JEE jar files are just intended for use at compile time, and only include the EE API definitions - no implementation. The idea is that a user builds his code against the API jars, and deploys to a JEE container that implements the APIs. If a developer instead deploys to an embedded environment like jetty or my fishRunner or just a standalone app that does something like using hybernate for JPA, then it's up to the developer to pull in the necessary implementation jars at runtime.

Tuesday, January 07, 2014

HTTP CORS Access-Control-Allow-Methods

I spent a few frustrating hours last week trying to figure out why PUT requests to a littleware web service I'm developing failed with an "unauthorized" HTTP 403 error code. I was thrown off for a while, because I assumed there was an error in the authentication and authorization filter protecting the web service, but that code worked fine for GET and POST requests, and the PUT requests didn't even appear in the server logs.

Anyway - long story short - I finally realized that the CORS filter protecting the web service was denying PUT requests by default. Fortunately, the open-source filter I'm using makes it easy to add PUT requests to the 'Access-Control-Allow-Methods' HTTP header in the pre-flight response.

So happy ending - the code works, and on to the next problem. This kind of bug is so frustrating though - when I do something a little out of the ordinary (HTTP PUT) that breaks some little part of a system that I've completely forgotten about, because it always works fine, and time is lost tracking down some configuration change rather than fixing logic bugs in code. C'est la vie!

Thursday, November 14, 2013

Enable gzip HTTP filter in embedded glassfish

I had to google around a little bit to figure out how to enable glassfish's built-in HTTP gzip compression in my little fishRunner tool that I described in an earlier post. The fishRunner runs embedded glassfish to launch webapps (.war files) on heroku.

Anyway - there may be a better way, but the trick I used just issues the embedded equivalent of the asadmin set command-line with this block of code:

{ // enable gzip compression while we're at it
    final CommandResult commandResult = commandRunner.run(
     "set",
     "configs.config.server-config.network-config.protocols.protocol.http-listener.http.compression=on"
     );
    if (commandResult.getExitStatus().equals(CommandResult.ExitStatus.FAILURE)) {
 throw new RuntimeException("Failed to configure gzip compression", commandResult.getFailureCause());
    }
}

I pushed that patch to github.

Thursday, November 07, 2013

openID for JAAS

I finally have my little ToDo app wired up with openID authentication. I'm running an openid4java based relying party, littleId, on heroku that provides the client with a signed web token that the client can present as credentials to other services (yet to be developed!) that share the signing secret.

LittleId includes a JAAS (java authentication and authorization service) login module (littleware.apps.littleId.client.controller.JaasLoginModule) that a web service can plug into its authentication stack to verify littleId web tokens. A service must share the secret littleId signed the token with to verify the token. Currently littleToDo uses the token to authenticate with a do-nothing littleware service that uses this JAAS login configuration (login.config):

> cat ..\littleApps\fishRunner\login.config
littleware.login {
    /*... Stacked LDAP login module ...
        com.sun.security.auth.module.LdapLoginModule SUFFICIENT
        userProvider="ldap://XXXXX/dc=XXXXXX,dc=XXX"
        authIdentity="{USERNAME}"
        userFilter="(cn:dn:={USERNAME})"
        useSSL=false
        debug=true;
        */
        com.sun.jmx.remote.security.FileLoginModule SUFFICIENT
        passwordFile="passwords.properties";

        littleware.apps.littleId.client.controller.JaasLoginModule SUFFICIENT;
};

The openID flow is handled in the browser by a few javascript and typescript modules (on github), and a popup, openIdPop.html. The whole thing relies on CORS (cross origin resource sharing) with XDR cookies enabled (littleId is able to stay stateless on the server side by stashing some data in a cookie) to allow the popup (hosted on the same apps.frickjack.com domain as little-ToDo's assets) to access littleId's REST service running on heroku under littleware.herokuapp.com.

Anyway, the tricky stuff is all handled in the popup. The openIdPop.html popup is actually loaded twice during the openId flow. First, when the parent browser window opens the popup, the user selects the openId provider to authenticate with (currently Google or Yahoo). The selection-handler retrieves the parameters to post to the openID provider (via an XHR request to the littleId service), and submits the data to the provider (Yahoo or Google). After the user authenticates with the openID provider, then the provider redirects the browser back to the littleId service (openID relying party). LittleID verifies that the openID authentication succeeded, and generates a web token that it delivers back to openIdPop.html in the query parameters attached to the browser redirect URL. The code in openIdPop.html looks like this:

    littleware.littleYUI.bootstrap( {/*  classNamePrefix: 'pure'  */ } ).use( 
        'transition', 'littleware-auth-littleId', 'littleware-littleUtil',
        function (Y) {
            var util = Y.littleware.littleUtil;
            var log = new util.Logger("openIdPop.html");
            var littleId = Y.littleware.auth.littleId;

            littleId.helperFactory.get().then(
                function (loginHelper) {

                    // handle provider-selection event
                    Y.one("div#app").delegate('click',
                            function (e) {
                                e.preventDefault();
                                var providerName = e.currentTarget.getAttribute('data-provider');
                                log.log("Authenticating with provider: " + providerName);
                                loginHelper.prepareAuthRequest(providerName).then(
                                    function (request) {
                                        log.log("Check 1");
                                        var formBlock = loginHelper.buildProviderForm(request);
                                        Y.one("body").appendChild(formBlock);
                                        //log.log("Added form to body: " + formBlock.getHTML());
                                        formBlock.one( "form" ).getDOMNode().submit();  // exit to openId provider!
                                    },
                                    function (err) {
                                        alert("Error collecting auth data");
                                        log.log("Auth prep error");
                                        console.dir(err);
                                    }
                                );
                            }, "a.little-idprovider"
                     );

                    // handle auth data present in the URL paremeter if any ...
                    if ( littleId.CallbackData.isCallbackURL( window.location.href ) ) {
                        var callbackData = littleId.CallbackData.buildFromURL(window.location.href);
                        var message = "";
                        if (callbackData.authSuccess) {
                            message = "Authenticated as " + callbackData.userCreds.email +
                                ", secret: " + callbackData.userCreds.secret;
                        } else {
                            message = "Not authenticated";
                        }

                        // little bit of feedback
                        Y.one("div#app").setHTML("<p>" + message + "</p>");
                        
                        // if running as a popup - than call out to parent window, and close this popup
                        if (window.opener) {
                            log.log("Notifying parent window of openId callback ..." );
                            window.opener.littleware.auth.littleId.providerCallback(callbackData).then(
                                function () {
                                    log.log("Parent window callback ok");
                                    window.close();
                                },
                                function (err) {
                                    alert("Failed parent window callback");
                                    log.log("Failed parent window callback");
                                    console.dir(err);
                                }
                              );
                        }
                    }
                }
            );
        }
    );

Fortunately - the host application (little-ToDo) doesn't need to worry about that. The app just launches the popup to initiate an authentication workflow (
window.open("/littleware_apps/auth/openIdPop.html", "openid_popup");
), and listens for the littleId credentials attribute to change:

  littleIdHelper.after("userCredsChange",
      (ev) => {
          var creds = littleIdHelper.get("userCreds");
          ...
       }
  );

Hopefully some of that makes sense. It doesn't look like much, but I'm pretty happy, because this little authentication service ties together a bunch of things I've been working on into one almost coherent mess ( running services on heroku, hosting content on S3, openID, YUI modules with typescript, YUI promises and handlebars templates, responsive mobile-webapp UX ), and hopefully gives me a good pattern to follow for building some other services.

Saturday, October 12, 2013

heroku glassfish runner

I wired up a little fishRunner tool (https://github.com/frickjack/littleware-fishRunner) that deploys a java web archive (.war file) to an embedded glassfish server. I setup the fishRunner to simplify launching web services on heroku - a cool polyglot PaaS offering that extends the IaaS from AWS with APIs that automate the load-based allocation of compute nodes for a linux web application, and also manages network load-balancing, logging, database provisioning, and an array of other services. A developer deploys an application to heroku by cloning the application's code to a heroku-hosted git repository. The code includes a configuration file that specifies a linux command line to launch the application. Each launched instance of the application runs in a container similar to a BSD jail that heroku calls a dyno.

Heroku's git-based deployment reflects its roots as a polyglot platform supporting dynamic languages like ruby and php that deploy a webapp by installing code behind a server. When heroku introduced java support in its blog, the company made a virtue out of its necessity to deploy by pushing code that is compiled and executed on the dyno - describing java's enterprise J2EE stack as ill-suited for software-as-a-service (SaaS) application deployment. Heroku encourages java web developers to package an application as an executable with an embedded http server like jetty rather than assemble the application into a web archive (.war file) suitable for submission to a J2EE server container.

I see two shortcomings in heroku's approach to java deployment. First, it requires the developer to manage an embedded server. Heroku's demo app (https://devcenter.heroku.com/articles/getting-started-with-java) shows how configuring jetty is easy for a simple web application, but the embedded container becomes more complicated to manage as the application's complexity increases with technologies like JPA, JDBC, IOC, JNDI, and others. I'm used to developing against a subset of the java EE API's, and delegating to a container (server) the responsibility to manage the environment required by those APIs. Deploying compiled code to a container is common in many java runtimes including android and plugins and extensions for OSGi based platforms like eclipse, netbeans, and glassfish.

My second complaint is that I don't like the idea of deploying java source code that is compiled on the production server. I'm used to a workflow where I build and test locally, and deploy a binary package. When working with a team I would introduce Jenkins or some similar continuous integration service into the mix, so that each patch submitted to a shared repository is automatically checked out, compiled, tested, and deployed to a shared test environment isolated from production. I can imagine a production-deployment setup where once the team is ready to release the code running in the test environment, then the code is deployed to a beta environment that shares the production database, but is not yet visible to the public. The code is finally released publicly by flipping a switch that moves the beta servers into production, and the old production servers stay online as a fallback if something goes wrong. Anyway - that's all just building imaginary castles - my personal configuration management needs are not complex, but back to the point - I don't like the idea of pushing code to the runtime server.

These are small complaints that have been commented on before other places (openShift's bLog, on java.net ). Heroku does now have an "enterprise for java" offering that supports war-file deployment to a tomcat container, and a sample application (https://github.com/heroku/java-sample) illustrates how to include tomcat's webapp-runner in the maven pom for a webapp project that compiles a war. There are also other PaaS clouds that cater to the java EE developer including RedHat's OpenShift, cloudbees, jelastic, Oracle's cloud, HP's cloud, AWS elastic beanstalk, and others.

In the end I'm still working with heroku - it's a great service whose benefits far outweigh its drawbacks: the price is right for development, it frees me from linux administration using IaaS like EC2 directly, my app comes up with a reasonable DNS name for an AJAX service (littleware.herokuapp.com) already network load balanced and with SSL (https), and heroku runs on AWS, so I can access AWS services ( dynamodb, S3, Simple queue, ...) without paying for off-AWS data-transfer. Finally, the fishRunner lets me deploy war-files to heroku in a nice way. The fishRunner takes a similar approach to tomcat's webapp-runner, but runs an embedded glassfish server supporting the java EE 7 web profile. Also - the fishRunner supports downloading a war file from an S3 bucket, so the workflow I envision deploys the fishRunner's code (something like 5 files) to a heroku app. At runtime the fishRunner downloads the war file and JAAS login config files defined in the heroku environment (via heroku config) from S3 (fishRunner can also just use local files for local testing), starts the glassfish server listening on heroku's environment-specified port, registers the postgres database connection pool defined by heroku's DATABASE_URL environment with glassfish's JNDI, configures JAAS, and deploys the WAR.

> java -cp 'target/*;.' littleware.apps.fishRunner.FishApp
...
SEVERE: Failed to launch webapp
littleware.apps.fishRunner.FishApp$ConfigException: Parameter must be specified in environment or on command line: DATASE_URL at littleware.apps.fishRunner.FishApp.main(FishApp.java:250)

Oct 12, 2013 3:52:38 PM littleware.apps.fishRunner.FishApp main
INFO:
fishRunner key value key value ...
Options pulled first from system environment,
then overriden by command line values:
S3_KEY
S3_SECRET
S3_CREDSFILE  - either both S3_KEY and S3_SECRET or S3_CREDSFILE must be defined
WAR_URI - required - either an s3:// URI otherwise treated as local file path
PORT - optional - defaults to 8080 if not otherwise specified
LOGIN_URI - optional - JAAS login.conf location either and s3:// URI otherwise treated as local file path
CONTEXT_ROOT - required - glassfish deploy context root for war
DATABASE_URL - required - ex: postgres://user:password@host:port/database
> heroku config
=== littleware Config Vars
CONTEXT_ROOT:               littleware_services
DATABASE_URL:               postgres://...
HEROKU_POSTGRESQL_NAVY_URL: postgres://...
JAVA_OPTS:                  -Xmx384m -Xss512k -XX:+UseCompressedOops
MAVEN_OPTS:                 -Xmx384m -Xss512k -XX:+UseCompressedOops
PATH:                       /app/.jdk/bin:/usr/local/bin:/usr/bin:/bin
S3_KEY:                     ...
S3_SECRET:                  ...
WAR_URI:                    s3://apps.frickjack.com/repo/littleware.apps/appsWeb/1.0-SNAPSHOT/appsWeb-1.0-SNAPSHOT.war

With heroku as a deploment platform, I'm back banging on littleware's simple JAAS authentication service. It's running now on my little dyno with CORS headers allowing AJAX access from pages hosted in my S3 bucket at http://apps.frickjack.com/. I'm working to implement the client-side javascript (or typescript) modules that will make the service easy to use on the client side, including support for OpenId via the littleId code I wrote a while ago, but currently the service just plugs in a null JAAS module that accepts any user password. You can get a feel for the service visiting these links in sequence:

Sunday, September 15, 2013

svg + img == awesome

I used an SVG file as the src attribute of an HTML img to solve two problems over the last couple weeks. First, I was working on an HTML5 replacement for the flash (.swf) banner at the top of http://mysticriver.org/. The slideshow worked fine, but we ran into a problem compositing the logo over the animating background images. The logo was created in illustrator. We were loading the logo as a gif with transparent background, and we ran into a problem where the logo would render with a white outline artifact. We found a trick to remove the artifact when compositing against a solid background, but that didn't work for an animating sequence of background images. We finaly tried referencing an svg version of the logo from the img tag, and that worked great - the browser (we tested IE10, Firefox, Chrome, iPhone Safari, Android Chrome) smoothly rendered the svg over the img background. We kept the gif around too as a fallback for older browsers.

I was so happy with how well svg worked with the banner that I decided to use inkscape to create an svg icon for little ToDo:

little ToDo icon

Another benefit of an svg icon is that it nicely scales to the different sizes that various devices prefer:

<link rel="shortcut icon" href="/littleware_apps/resources/img/appIcons/littleToDo/checkbox.16x16.ico" type="image/x-icon" />
<link rel="apple-touch-icon" href="/littleware_apps/resources/img/appIcons/littleToDo/checkbox.57x57.png" />
<link rel="apple-touch-icon" sizes="72x72" href="/littleware_apps/resources/img/appIcons/littleToDo/checkbox.72x72.png" />
<link rel="apple-touch-icon" sizes="114x114" href="/littleware_apps/resources/img/appIcons/littleToDo/checkbox.114x114.png" />
<link rel="apple-touch-icon" sizes="144x144" href="/littleware_apps/resources/img/appIcons/littleToDo/checkbox.144x144.png" />

I did run into a couple weird svg corner cases. First, internet explorer required that the svg file specify the viewBox and preserveAspectRatio properties in order to properly override the img dimensions via CSS or the width and height img attributes. Next, and related, inkscape does not set the viewBox and preserveAspectRatio attributes, so I set them by hand with inkscape's XML editor.

Saturday, September 14, 2013

little to do

I finally have an initial version of a little to do application running using the "tree control flow" stuff I described earlier.

LittleToDo is a simple app, but it took me a little while to wire it all up, and I think it has a couple interesting features. First - littleToDo implements nested to-do's, so each to-do item can itself have a list of child to-do's, and grand children, etc. The UI currently only allows four or five generations, but increasing that limit just requires changing a constant in the code.

The other interesting features are under the hood. Most of the code is implemented as typescript YUI modules. I had to tweak the YUI typescript definition file I'm using to make the typescript YUI module trick work with typescript's 0.9.1.1 release (which tightens up some syntax rules that were more relaxed in typescript 0.8), but I eventually wound up with code like this:

...

import importY = require("../../libts/yui");
importY; // workaround for typescript bug: https://typescript.codeplex.com/workitem/1531
import Y = importY.Y;
Y = exports;

import littleAsset = require("littleAsset");
littleAsset;
import ax = littleAsset.littleware.asset;


/**
 * @module littleware-asset-base
 * @namespace littleware.asset
 */
export module littleware.asset.manager {

...

    /**
     * API tool for interacting with the asset repo.
     * @class AssetManager
     */
    export interface AssetManager {
        /**
         * @method addListener
         * @param listener {function}
         * @return YUI subscription with "detach" method to unsubscribe
         */
        addListener(listener: (ev: RefEvent) => void ): Y.EventHandle;

        /**
         * @method saveAsset
         * @return {Y.Promise[AssetRef]}
         */
        saveAsset(value: ax.Asset, updateComment: string): Y.Promise<AssetRef>;
        /**
         * @method deleteAsset
         * @return {Y.Promise[void]}
         */
        deleteAsset(id: string, deleteComment: string): Y.Promise<void>;

        /**
         * Return a reference to the asset with the given id if any - otherwise null.
         * @method loadAsset
         * @return {Y.Promise[AssetRef]}
         */
        loadAsset(id: string): Y.Promise<AssetRef>;

        /**
         * Load the child of the given parent with the given name -
         * every child has a unique name within its set of siblings.
         * Usually implemented as a shortcut for loadAsset( listChildren( parentId )[name] ).
         * @method loadChild
         * @param parentId {string}
         * @param name {string}
         * @return {Y.Promise{AssetRef}}
         */
        loadChild(parentId: string, name: string): Y.Promise<AssetRef>;

        /**
         * Load the asset at the given path if any - same as loadSubpath( null, path )
         * @method loadPath
         * @param path {string}
         * @return {Y.Promise{AssetRef}}
         */
        loadPath(path: string): Y.Promise<AssetRef>;

        /**
         * Load the asset at the given path below the given root
         * @method loadSubPath
         * @param rootId {string} id of the asset to treat as the root, or null to use forest root
         * @param path {string}
         * @return {Y.Promise{AssetRef}}
         */
        loadSubpath(rootId: string, path: string): Y.Promise<AssetRef>;

        /**
         * Like loadSubpath, but allows specification of a builder function 
         * for each segment of the path to create a missing asset if it doesn't exist -
         * buildBranch sets the builder's fromId and name properties.
         * @method buildBranch
         * @param rootId {string} id of the asset to treat as the root, or null to use forest root
         * @param branch {{name:string,builder:(parent:Asset)=>AssetBuilder}[]}
         * @return {Y.Promise{AssetRef[]}}
         */
        buildBranch(rootId: string, branch: { name: string; builder: (parent: ax.Asset) => ax.AssetBuilder; }[]): Y.Promise<AssetRef[]>;


        /**
         * List the children if any under the given parent node
         * @method listChildren
         * @param parentId {string}
         * @return {Y.Promise{NameIdListRef}}
         */
        listChildren(parentId: string): Y.Promise<NameIdListRef>;

        /**
         * List the root (littleware.HOME-TYPE) nodes - shortcut for listChildren(null)
         * @method listRoots
         * @return {Y.Promise{NameIdListRef}}
         */
        listRoots(): Y.Promise<NameIdListRef>;
    }

...

The current implementation of the above AssetManager interface (which underlies the data-management for the app) just saves assets (nodes in a data tree) to the browser's local storage, but the API returns promises, so it will be easy in the future to swap in an implementation that saves data via AJAX to a remote service. The AssetManager also closely resembles littleware's asset manager and asset search API's (that javadoc is out of date, but that's about right), so I'm hoping to implement a littleware backend for the next release of "little to do" with littleId based authentication. We'll see how it goes ...

Thursday, September 05, 2013

promises, promises

YUI recently added a cool Promises module that I've been using the last couple weeks to implement client-side API's for a webapp. I like the way the code has come together, and I have a couple observations to record.

First, code that uses Promises is viral - it's natural for code that uses a promise-based API to itself use promises. Promises are a kind of monad like Haskell's IO() construct and Scala's Option class - which are also viral in my experience. For example, one of the first promise-based methods I wrote retrieved and compiled a handlebars template from the server:

        /**
         * Little helper loads and compiles a handlebar template via an AJAX call
         * 
         * @method loadHandlebar
         * @param url {string}
         * @return {Promise} Y.Promise delivers compiled template function or error info
         * @static
         */
        function loadHandlebar( url ) {            
            var promise = new Y.Promise( function(resolve,reject) {
                var ioconf = {
                  method:"GET",
                  on: {
                    success:function( id, resp, args ){
                        resolve( Y.Handlebars.compile( resp.responseText ) );
                    },
                    failure:function( id, resp, args ) {
                        log.log( "Failed to load " + url + ": " + resp.status + ", " + resp.statusText );
                        reject( resp );
                    }
                  },
                  timeout:60000
                };
                Y.io( url, ioconf );
            });
            return promise;
        }

That worked great, and the next thing I wanted was to dynamically load a template into a view with something like
littleUtil.loadHandlebar( url ).then( function(template){ view.template = template; } )
, but then the view couldn't use the template at render time until the promise was fulfilled. Rather than do something like
render: function() { this.templatePromise.then( function(template) { ... do render } ) ... }
, I setup a factory method for allocating views that itself returns a promise, and let the factory's client decide how to resolve the promise - which worked well in the little application I've been building that runs several asynchronous data-setup operations at start-up anyway:

    /**
     * Factory for PageView instances - maries YUI class inheritance with Typescript type system
     * @class PageViewFactory
     */
    export var PageViewFactory = {
        /**
         * Returns a Promise - the view loads various configuration 
         * information (templates, whatever) at the template, and 
         * fulfills the promise when the data is ready.
         * @method newView
         * @static
         * @return {Promise{PageView}}
         */
        newView: function (): Y.Promise {
            return templatePromise.then((template) => {
                var view = new SimplePageView();
                view.template = template;
                return view;
            });
        }
    };

This code is written in typescript - which extends javascript with support for static types. I'm only just in the process of migrating to typescript 0.9.1 which includes generic types. Hopefully after the upgrade I'll be able to modify the newView signature to something like:
newView: function(): Y.Promise<PageView> { ... }
.

Finally, it's interesting to compare yui's Promises implementation to the akka Futures implementation in scala. In javascript's single-threaded runtime promises usually coordinate callback execution between operations involving asynchronous IO. In scala's multithreaded runtime futures (promises by another name) are also used to simplify the coordination of concurrently running computations.

Tuesday, July 30, 2013

easy slideshow with YUI transitions

Using CSS transitions to animate the opacity of images is a great way to implement a web slideshow, but implementing an animation-based fallback for old browsers is a pain. Fortunately - yui includes a transition module that takes care of the fallback magic; which made it easy for me to code up an Android and iOS-friendly HTML replacement for a Flash .swf banner in a project I'm helping with.

The banner's markup leverages the absolute position in a relative position container trick:

div.banner {
    position:relative;
    height:230px;
}

div.banner img {
    position: absolute;
    opacity: 0;
}

div.banner img.logo { /* overlay logo on banner */
    opacity:1;
    bottom:25px;
    right:0;
}

<div id="banner" data-anim-period-secs="5" class="yui3-u-1 banner">
    <img src="/myrwa/resources/img/banner/lichen.jpg"/>
    <img src="/myrwa/resources/img/banner/Herringrun.jpg"/>
    <img src="/myrwa/resources/img/banner/TuftsSailingTeam.jpg"/>
    <img src="/myrwa/resources/img/banner/canoe.jpg"/>
    
    <img id="logo" class="logo" src="/myrwa/resources/img/myRWA_logo_2010.gif" />
</div>

The banner's javascript module implements a simple yui view that runs a setinterval loop (Y.later wraps setinterval) that applies an opacity transition to make the current image in the banner's slideshow opaque, and the last image transparent.

/*
 * Copyright 2013 http://mysticriver.org
 *
 * The contents of this file are freely available subject to the 
 * terms of the Apache 2.0 open source license.
 */


/**
 * see http://yuiblog.com/blog/2007/06/12/module-pattern/
 * YUI doc comments: http://developer.yahoo.com/yui/yuidoc/
 * YUI extension mechanism: http://developer.yahoo.com/yui/3/yui/#yuiadd
 *
 * @module myrwa-banner
 * @namespace myrwa
 */
YUI.add( 'myrwa-banner', function(Y) {
    Y.namespace('myrwa');

    function log( msg, level ) {
        level = level || 2;
        Y.log( msg, level, "myrwa/banner.js" );
    }
    
    /**
     * View abstraction for the header banner which animates with
     * transitions between a set of gallery images.  
     * Initialize with container
     * selector for markup initialized with banner images
     * ready for progressive enhancement.
     * 
     * @class BannerView
     */
    var BannerView = Y.Base.create( 'bannerView', Y.View, [], 
        {
            initializer:function(config) {
            },
                    
            render:function() {
                // do not setup the animation than once
                if ( this.rendered ) return;
                var container = this.get( "container" );
                var logoNode = container.one( "img.logo" );
                var imageNodes = container.all( "img" 
                         ).filter( function(img) { return ! Y.Node(img).hasClass( "logo" ); } );
                
                Y.assert( "Found image nodes", imageNodes.size() > 0 );
                
                // initialize the banner to show the first image
                imageNodes.each( function(n) { n.setStyle( "opacity", 0 ); } );
                imageNodes.item(0).setStyle( "opacity", 1 );
                
                if ( imageNodes.size() == 1 ) return;  // no need to animate
                
                var animPeriod = this.get( "animPeriodSecs" );
                if ( animPeriod < 2 ) {
                    // check for an attribute on the container if period not set in js code
                    var tmp = parseInt( container.getAttribute( "data-anim-period-secs" ) );
                    if ( tmp ) { animPeriod = tmp; }
                }
                if ( animPeriod < 2 ) animPeriod = 2;
                
                var currentImageIndex = 0;
                log( "Launching banner animation ..." );
                //
                // Every animPeriod seconds ease the current node out, and the new node in
                //
                Y.later( animPeriod * 1000, this, 
                    function(){
                        var nextImageIndex = (currentImageIndex + 1) % imageNodes.size();
                        var inNode = imageNodes.item( nextImageIndex );
                        var outNode = imageNodes.item( currentImageIndex );
                        //log( "banner transition from " + currentImageIndex + " to " + nextImageIndex );

                        inNode.setStyle( "opacity", 0 );
                        outNode.setStyle( "opacity", 1 );
                        inNode.show();
                        outNode.show();

                        outNode.transition( 
                                {
                                 easing: 'ease-out',
                                 duration: 0.75, // seconds
                                 opacity: 0
                                 }, 
                             function() { outNode.hide(); } 
                         );
                         inNode.transition(
                                 {
                                  easing: 'ease-in',
                                  duration: 0.75,
                                  opacity: 1
                                 }
                             );
                         currentImageIndex = nextImageIndex;
                    }, 
                    [], true
                );

                this.rendered = true;
            }
        }, 
        {
            ATTRS: {
                animPeriodSecs: {
                    value:0
                }
            }
        } 
    );

    //---------------------------------------

    Y.myrwa.banner = {
        BannerView:BannerView
    };
}, '0.1.1' /* module version */, {
    requires: [ 'node', 'test', 'transition', 'view']
});

A page bootstraps the banner animation with code like this:

YUI().use('myrwa-banner', 'test', function(Y){
 
    Y.log( "Hello, World!", 2, "myrwa/main.js" );
    var banner = new Y.myrwa.banner.BannerView( 
            {
                container:"#banner"
            }
        );
    banner.render();
});

Anyway - I was pretty happy with how that all came together. This little banner slideshow lacks the nice controls in bootstrap's slideshow, but it was good enough for what I needed, and I avoided pulling in bootstrap's dependencies (jquery, whatever). The code is available on github, and a page I used for testing is also online for now.

Thursday, July 11, 2013

testing YUI apps with phantomjs

It turns out that wiring up phantomjs to process a web page that runs a test suite based on yui's test module is pretty easy to do. I wish I did this sooner. Here's what I got working.

First, I'm already in the habit of setting up pages like this that run a javascript module through some tests. I just use yui's test framework, since I use yui for everything else, and yui test works basically the same way as junit - which I use when coding java. I wind up with the following code (on github here):

    littleware.littleYUI.bootstrap().use( 
 'littleware-littleUtil', 'test',
 function (Y) {
     // The modules are loaded and ready to use.
     // Your code goes here!
     var util = Y.littleware.littleUtil;
     var log = new util.Logger( "littleUtilTestSuite.html" );
     var suite = util.buildTestSuite();
     
     Y.Test.Runner.add( suite );
     
     if ( typeof( window.callPhantom ) != 'undefined' ) {
  // phantomjs environment!
  console.log( "Phantomjs detected!" );
  Y.Test.Runner.subscribe( Y.Test.Runner.COMPLETE_EVENT, window.callPhantom );
     }

     Y.Test.Runner.run();          
    });

The thing to notice is the window.callPhantom feature detection. Phantomjs integrates the V8 javascript engine with Webkit's DOM magic in a cool command-line javascript console. There's probably a better way to say that, but the important thing for me is that phantomjs offers a great way to run and evaluate the results of test web pages from the command line.

The phantomjs web site has a great list of references on its "headless testing" page, and I think my setup is pretty similar to what others do. My setup takes advantage of one trick that I wanted to mention - listening for YUI's test-run complete event with phantomjs' webpage.onCallback function to pass the web-page test results through to the phantomjs script (below) that runs through a series of test web pages. Ugh - that may not make much sense, but hopefully the "phantomTestRunner.js" script below (and in github) helps clarify what I mean.

/**
 * Little phantomjs script that opens a list of urls given
 * in the first argument as a comma-separated list.
 * Intended for running YUI test suites that callback to
 * phantom at test completion via:
 *    
 *    <pre>
 *       if ( typeof( window.callPhantom ) != 'undefined' ) {
 *           // phantomjs environment!
 *           console.log( "Phantomjs detected!" );
 *           Y.Test.Runner.subscribe( Y.Test.Runner.COMPLETE_EVENT, window.callPhantom );
 *       }
 *
 *    </pre>
 */

var system = require( 'system' );

if ( (system.args.length < 2) || system.args[1].match( /^-+/ ) ) {
    // use comma-separated list - easier to integrate with ant that way
    console.log( "script takes exactly one argument: a comma-separated list of test urls" );
    phantom.exit(1);
}

var urlList = system.args[1].split( /,+/ );
/*
for ( var i = 1; i < system.args.length; ++i ) {
    urlList.push( system.args[i] );
}
*/

var resultList = [];
var currentTest = 0;

var page = require( 'webpage' ).create();
page.onConsoleMessage = function(msg) { console.log( msg ); };

function runTest( url ) {
  page.open( url,
     function( status ) { 
         console.log( url + " status: " + status ); 
         if ( status != "success" ) {
             phantom.exit(1);
         } 
         page.evaluate( function() { console.log( "page location: " + window.location.href ); } );
     }
 );    
}

page.onCallback = function( testResult ) {
    resultList.push( testResult );
    currentTest += 1;
    if ( currentTest < urlList.length ) {
        runTest( urlList[currentTest] );
    } else {
        console.log( "-------------------------------------------------------------" );
        console.log( "-------------------------------------------------------------" );
        console.log( resultList.length + " Tests complete: " );
        console.log( JSON.stringify( resultList ) );
        phantom.exit( 0 );
    }
};


runTest( urlList[0] );

Anyway, long story short, running something like:
phantomjs phantomTestRunner.js http://apps.frickjack.com/littleware_apps/testsuite/littleUtilTestSuite.html
prints out the web-console messages from the page's test suite (the script can run multiple pages), and collects all the results at the end (I need to do more work with that). Pretty cool!

Wednesday, July 10, 2013

tree control-flow apps on a responsive grid

I wired up a proof of concept responsive browser UI that implements an application's views as a series of panels. A single panel holds the UI focus at any given time, and each panel is compact enough to render on a smart phone, so on a phone sized device the UI renders a single panel at a time, but on a larger device the UI may render 2, 3, or 4 adjoining panels depending on the device size. For example, the demo, application just navigates through a tree of panels (a decision-tree control flow is common in apps). On a typical laptop the UI renders up to 4 adjoining panels where the right-most panel is the "focus" of the app, and a panel's parent is to its left. If we make the browser window smaller, then the UI eventually removes the left-most panel from the screen, and continues removing the left panel as the window shrinks until only the focus-panel remains.

I started on the demo while thinking about how to design an application that from the beginning behaves in a reasonable way on a phone, tablet, or PC. I like the idea of using a responsive design to implement one web app that works well across devices. It's too much work to build a bunch of separate "apps" for iOS, Android, etc. with custom UI designs for phone and tablet. I recently started playing around with purecss (a CSS framework forked from yui) that includes an implementation of a "responsive grid". Responsive grids like those in Twitter bootstrap and purecss leverage CSS3 media queries to implement a web page layout that behaves differently on phone, tablet, and PC sized screens.

I implemented the demo with typescript and YUI - the code is on github. The UI manager follows patterns like those in YUI's app framework. The application registers views (panels) with the manager, and associates each panel with a route based on the panel's parent and a basename filter. The view manager then takes care of deciding which panels to render where when the application triggers a change in route. The view manager also registers a click-handler, so a.little-route links trigger a route change, but the application can also manage the YUI router directly.

Eventually we wind up with application code like this:

    // home page info
    var versionInfo = Y.Node.create("<div id='app-info'><span class='app-title'>LittleEvents</span><br>Version 0.0 2013/06/21</p></div>");
    var homePage = new Y.View();
    homePage.get("container").append(versionInfo);

    // router
    var router = new Y.Router(
 { root: "/littleware_apps/blog/gridDemo.html" }
 );

    // inject homepage and router into view manager
    var app = appModule.ViewManager.getFactory().create("app", "div#app", homePage, router);

    //
    // passed to registerPanel (below) - notifies panel of new path on view change.
    // could also just add a listener on the router ...
    //
    var viewListener = function (panelStatus) {
 if (panelStatus.state.name == "VISIBLE") {
     var oldPath = panelStatus.panel.view.get("pathParts").join("/");
     if (oldPath != panelStatus.path) {
  //log.log("Setting new path: " + oldPath + " != " + panelStatus.path);
  panelStatus.panel.view.set("pathParts", panelStatus.path.split("/"));
  app.markPanelDirty(panelStatus.panel.id);
     }
 }
    };

    // register panels according to position in control-flow tree
    app.registerRootPanel(panel1, panel1.id, viewListener);
    // panel2 is a "child" of panel1
    app.registerPanel(panel2, panel1.id, function () { return true; }, viewListener );
    app.registerPanel(panel3, panel2.id, function () { return true; }, viewListener );
    app.show();

..., and a library interface like this:

    export module ViewManager {
      ...
        /**
         * Little helper manages the display of panels based
         * on the routes triggered in router and size of display
         * (phone, tablet, whatever).
         * Assumes tree-based panel app.
         *
         * @class Manager
         */
        export interface Manager {
            name: string;

            /**
             * Root div under which to manage the panel UI
             */
            container: Y.Node;

            /**
             * Info panel embedded in home page at path "/" - splash screen,
             * version info, whatever placed above the "route index"
             * @property homePage {Y.View}
             */
            homePage: Y.View;
            router: Y.Router;

            /**
             * List of routes to sort and display in the "index" on the home page
             * along with the "root" panel paths.
             */
            routeIndex: string[];


            /**
             * Child panel of homePage - "config"
             * is reserved for internally managed configuration panels.
             */
            registerRootPanel(
                panel: LittlePanel,
                baseName: string,
                listener: (PanelStatus) => void
                );

            /**
             * Register panels along with id of its parent, and a baseFilter
             * that either accepts or rejects a route basename.
             * For example, given some route /path/to/parent/bla/foo/frick,
             * then for each element of the path [path,to,parent,bla,foo,frick],
             * test that element against the children of a parent panel
             * to determine which panel to associate with that route.
             *
             * @method registerPanel
             */
            registerPanel(
                    panel: LittlePanel,
                    parentId: string,
                    routeFilter: (string) => bool,
                    listener: (PanelStatus) => void
                );

            /**
             * By default the manager does not re-render a panel when it
             * becomes "visible" unless the panel is "dirty".
             * If the panel is already visible, then re-render panel once
             * call stack clears: Y.later( 0, () => render() ).
             *
             * @method markPanelDirty
             */
            markPanelDirty(panelId: string);

            /**
             * Triggers the manager to render its initial view (depends on the active route),
             * and begin responding to routing and dirty-panel notifications in the "Active" 
             * ManagerState.  NOOP if already active.
             *
             * @method show
             */
            show();

        }


        export interface Factory {
            /**
             * Create a new view manager that manipulates DOM in the given selector
             *
             * @param name alphanumeric to associate with this manager - used as a key
             *               in persistence store
             * @param selector CSS selector for div under which to build view
             */
            create(name: string, selector: string, homePage: Y.View, router: Y.Router): Manager;
            //create( name:string, selector: string): Manager;

            /**
             * Load manager state from persistent storage
             */
            load( name:string ): Manager;
        }

       ...
   }

Anyway - I'm pretty happy with how things fit together in the demo, but I won't really know how well this works until I use it with a couple apps. We'll see how it goes.