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.

Friday, June 28, 2013

CDI and Guice with jsr-300 javax.inject In Same Webapp

Things can get weird when java SE code developed with IOC is deployed into a java EE container that implements CDI. For example - the littleware code I work with implements simple modules and life cycle management with guice, so when I deploy that code in a webapp to a glassfish server I want guice's runtime to manage littleware's dependency injection, but I might also want CDI to inject a glassfish-managed JPA EntityManager into a servlet for me, and both guice and CDI are scanning for the same javax.inject runtime annotations. Fortunately - this kind of thing worked fine for me until recently, since CDI 1 as introduced in java EE 6 ignored jars that did not include a META-INF/beans.xml file.

Java Enterprise Edition 6 introduced JSR-330 (javax.inject annotations and provider interfaces) and JSR-299 (CDI - contexts and dependency injection) to the java-ee platform. The javax.inject package standardized the annotations and interfaces used by java's various inversion of control and dependency-injection systems (guice, spring, dagger, pico container, ...), so that code written with one IOC system in mind could be more easily used in an application that deployed to another system.

CDI introduced an IOC implementation for java enterprise edition (EE) technologies (servlets, JSF, EJB, JPA, ...) deployed in EE containers like glassfish, jboss, weblogic, and websphere.

I discovered CDI's behavior changed slightly in the new java EE 7 runtime when a webapp that ran fine in glassfish version 3 (a java EE 6 container) failed to deploy under glassfish 4 (the EE-7 reference implementation). The errors in the glassfish-4 logs lead me to this ticket in guava's bug database. It turns out that CDI's new version 1.1 runtime automatically scans all jars deployed with an enterprise application, and guava-14+ includes a couple classes with javax.inject annotations that CDI processed in an unintended way (littleware still uses guice's com.google.inject package annotations and interfaces, so CDI ignored those).

I eventually found one easy solution to this javax.inject - CDI 1.1 mix-up is to add a META-INF/beans.xml CDI config file to jars that include javax.inject annotations that require special or no handling by CDI. I added the following META-INF/beans.xml with a bean-discover-mode attribute set to "none" to the guava jar in my webapp, and that solved the problem. Hopefully guava's maintainers will add a similar beans.xml file to guava's binary distribution on maven central.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
       bean-discovery-mode="none">
</beans>

Friday, June 07, 2013

javase Hibernate: goodbye Ejb3Configuration, hello JNDI Context mock

I've been cleaning up littleware's asset database code this week, and was dismayed to discover upon updating the hibernate dependencies that the latest hibernate releases have deprecated and removed the Ejb3Configuration mechanism for bootstrapping a JPA environment and allocating an EntityManagerFactory in a javase (no javaee container) application. Fortunately - I came up with a hacky way to trick hibernate into using the DataSource instance I want it to use.

Somehow when I started working with JPA I already had code in littleware's guice-based IOC setup to initialize and inject DataSource instances in traditional JDBC code either directly or via JNDI lookup. When I started using JPA in littleware I wanted to wire things up so that JPA used the same DataSource as the rest of the code. When running in a web container like Tomcat or Glassfish the app can let the container manage the DataSource, and both JPA and the guice-runtime access the same DataSource via a directory (JNDI) lookup, but when running a standalone application, I had things coded up to use hibernate, and used Ejb3Configuration to tell hibernate to use littleware's DataSource.

Anyway, with Ejb3Configuration going away I had to specify the database connection parameters in a JPA persistence.xml file in one of two ways. The first option was to specify properties for a JDBC Driver that JPA (hibernate) would wrap with its own connection pool manager, and the first hack I tried implemented a JDBC Driver (it's just a one method interface) that pulled connections from littleware's DataSource. That actually worked fine - I'm always amazed when these things work. I setup persistence.xml like this:

<?xml version="1.0" encoding="UTF-8"?>

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
             version="2.0">

  <persistence-unit name="littlewarePU" transaction-type="RESOURCE_LOCAL">
        <description>This JPA persistence unit tracks littleware assets
        </description>

        <!-- jndi datasource lookup        
        <non-jta-data-source>jdbc/littleDB</non-jta-data-source>
        -->
        
        <class>littleware.asset.server.db.jpa.AssetTypeEntity</class>
        <class>littleware.asset.server.db.jpa.TransactionEntity</class>
        <class>littleware.asset.server.db.jpa.AssetAttribute</class>
        <class>littleware.asset.server.db.jpa.AssetDate</class>
        <class>littleware.asset.server.db.jpa.AssetLink</class>
        <class>littleware.asset.server.db.jpa.AssetEntity</class>

    <properties>

      <property name="eclipselink.target-database" value="DERBY"/>
      <property name="eclipselink.ddl-generation" value="create-tables" />
      <property name="hibernate.hbm2ddl.auto" value="create"/>
      <property name="hibernate.dialect" value="org.hibernate.dialect.DerbyDialect" />
            
      <property name="javax.persistence.jdbc.driver" value="littleware.asset.server.db.jpa.LittleDriver"/>
      <property name="javax.persistence.jdbc.url" value="jdbc:littleware://ignore/this/stuff"/>
      <property name="javax.persistence.jdbc.user" value="APP"/>
      <property name="javax.persistence.jdbc.password" value="APP"/>

    </properties> 
    
  </persistence-unit>
</persistence>

And I wrote a bogus littleware.asset.server.db.jpa.LittleDriver that some initialization code configured via its setDataSource() method:


/**
 * JDBC driver that just delegates to the active DataSource defined in the
 * active littleware runtime.
 * Register this driver with JPA persistence.xml to plug into the
 * littleware managed DataSource.
 */
public class LittleDriver implements Driver {
    private static  DataSource dataSource = null;
    /**
     * HibernateProvider injects littleware data source at startup time as needed
     */
    public static void setDataSource( DataSource value ) {
        dataSource = value;
    }
    
    
    @Override
    public Connection connect(String string, Properties prprts) throws SQLException {
        Whatever.get().check( "LittleDriver requires data source injection",  null != dataSource );
        return dataSource.getConnection();
    }

    @Override
    public boolean acceptsURL(String string) throws SQLException {
        return true;
    }

    private static final DriverPropertyInfo[] empty = new DriverPropertyInfo[0];
    @Override
    public DriverPropertyInfo[] getPropertyInfo(String string, Properties prprts) throws SQLException {
        return empty;
    }

    @Override
    public int getMajorVersion() {
        return 0;
    }

    @Override
    public int getMinorVersion() {
        return 0;
    }

    @Override
    public boolean jdbcCompliant() {
        return false;
    }

    private static final Logger log = Logger.getLogger( LittleDriver.class.getName() );
    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        return log;
    }
    
}

Anyway - that worked great, and it's actually good enough for my current needs (mostly just junit tests), but it made me itch thinking about hibernate wrapping a connection pool around a mock driver that pulls connections from another connection pool. Ugh. So I started thinking about setting up an in-memory JNDI directory where initialization code could stuff the DataSource before allocating the JPA (hibernate or whatever) EntityManagerFactory. I found this cool little SimpleJNDI JNDI implementation, but it wasn't registered with Maven central, and it was a little bigger than I would like to copy into my code base, and anyway - I didn't need a whole JNDI implementation - I just needed to trick hibernate into using my DataSource, so I tried just wiring up a mock JNDI Context, and it worked! It took a few tries to figure out which methods hibernate calls to do its directory lookup, but in the end I wound up with a persistence.xml file like this:

<?xml version="1.0" encoding="UTF-8"?>

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
             version="2.0">

  <persistence-unit name="littlewarePU" transaction-type="RESOURCE_LOCAL">
        <description>This JPA persistence unit tracks littleware assets
        </description>

        <non-jta-data-source>jdbc/littleDB</non-jta-data-source>
        
        <class>littleware.asset.server.db.jpa.AssetTypeEntity</class>
        <class>littleware.asset.server.db.jpa.TransactionEntity</class>
        <class>littleware.asset.server.db.jpa.AssetAttribute</class>
        <class>littleware.asset.server.db.jpa.AssetDate</class>
        <class>littleware.asset.server.db.jpa.AssetLink</class>
        <class>littleware.asset.server.db.jpa.AssetEntity</class>

    <properties>
    </properties> 
    
  </persistence-unit>
</persistence>

The mock JNDI Context looks like this:

/**
 * Mock JNDI context that is just NOOPs except
 * lookup always returns the DataSource injected via
 * the setDataSource static method.
 * Similar to LittleDriver - just a hack to try to get
 * hibernate to use our DataSource
 */
public class LittleContext implements javax.naming.Context {
    private static  DataSource dataSource = null;
    /**
     * HibernateProvider injects littleware data source at startup time as needed
     */
    public static void setDataSource( DataSource value ) {
        dataSource = value;
    }

    @Override
    public Object lookup(Name name) throws NamingException {
        return dataSource;
    }

    @Override
    public Object lookup(String string) throws NamingException {
        return dataSource;
    }

    @Override
    public void bind(Name name, Object o) throws NamingException {
        throw new UnsupportedOperationException("Not supported yet."); 
    }

    @Override
    public void bind(String string, Object o) throws NamingException {
        throw new UnsupportedOperationException("Not supported yet."); 
    }

    @Override
    public void rebind(Name name, Object o) throws NamingException {
        throw new UnsupportedOperationException("Not supported yet."); 
    }

    @Override
    public void rebind(String string, Object o) throws NamingException {
        throw new UnsupportedOperationException("Not supported yet."); 
    }

    @Override
    public void unbind(Name name) throws NamingException {
        throw new UnsupportedOperationException("Not supported yet."); 
    }

    @Override
    public void unbind(String string) throws NamingException {
        throw new UnsupportedOperationException("Not supported yet."); 
    }

    @Override
    public void rename(Name name, Name name1) throws NamingException {
        throw new UnsupportedOperationException("Not supported yet."); 
    }

    @Override
    public void rename(String string, String string1) throws NamingException {
        throw new UnsupportedOperationException("Not supported yet."); 
    }

    @Override
    public NamingEnumeration<NameClassPair> list(Name name) throws NamingException {
        throw new UnsupportedOperationException("Not supported yet."); 
    }

    @Override
    public NamingEnumeration<NameClassPair> list(String string) throws NamingException {
        throw new UnsupportedOperationException("Not supported yet."); 
    }

    @Override
    public NamingEnumeration<Binding> listBindings(Name name) throws NamingException {
        throw new UnsupportedOperationException("Not supported yet."); 
    }

    @Override
    public NamingEnumeration<Binding> listBindings(String string) throws NamingException {
        throw new UnsupportedOperationException("Not supported yet."); 
    }

    @Override
    public void destroySubcontext(Name name) throws NamingException {
        throw new UnsupportedOperationException("Not supported yet."); 
    }

    @Override
    public void destroySubcontext(String string) throws NamingException {
        throw new UnsupportedOperationException("Not supported yet."); 
    }

    @Override
    public Context createSubcontext(Name name) throws NamingException {
        throw new UnsupportedOperationException("Not supported yet."); 
    }

    @Override
    public Context createSubcontext(String string) throws NamingException {
        throw new UnsupportedOperationException("Not supported yet."); 
    }

    @Override
    public Object lookupLink(Name name) throws NamingException {
        throw new UnsupportedOperationException("Not supported yet."); 
    }

    @Override
    public Object lookupLink(String string) throws NamingException {
        throw new UnsupportedOperationException("Not supported yet."); 
    }

    @Override
    public NameParser getNameParser(Name name) throws NamingException {
        return getNameParser("");
    }

    @Override
    public NameParser getNameParser( String string) throws NamingException {
        return new NameParser(){

            @Override
            public Name parse( final String string) throws NamingException {
                return new Name(){
                    @Override
                    public Object clone() { return this; }
                    
                    @Override
                    public int compareTo(Object o) {
                        throw new UnsupportedOperationException("Not supported yet."); 
                    }

                    @Override
                    public int size() {
                        return 1;
                    }

                    @Override
                    public boolean isEmpty() {
                        return false;
                    }

                    @Override
                    public Enumeration<String> getAll() {
                        throw new UnsupportedOperationException("Not supported yet."); 
                    }

                    @Override
                    public String get(int i) {
                        return string;
                    }

                    @Override
                    public Name getPrefix(int i) {
                        throw new UnsupportedOperationException("Not supported yet."); 
                    }

                    @Override
                    public Name getSuffix(int i) {
                        throw new UnsupportedOperationException("Not supported yet."); 
                    }

                    @Override
                    public boolean startsWith(Name name) {
                        throw new UnsupportedOperationException("Not supported yet."); 
                    }

                    @Override
                    public boolean endsWith(Name name) {
                        throw new UnsupportedOperationException("Not supported yet."); 
                    }

                    @Override
                    public Name addAll(Name name) throws InvalidNameException {
                        throw new UnsupportedOperationException("Not supported yet."); 
                    }

                    @Override
                    public Name addAll(int i, Name name) throws InvalidNameException {
                        throw new UnsupportedOperationException("Not supported yet."); 
                    }

                    @Override
                    public Name add(String string) throws InvalidNameException {
                        throw new UnsupportedOperationException("Not supported yet."); 
                    }

                    @Override
                    public Name add(int i, String string) throws InvalidNameException {
                        throw new UnsupportedOperationException("Not supported yet."); 
                    }

                    @Override
                    public Object remove(int i) throws InvalidNameException {
                        throw new UnsupportedOperationException("Not supported yet."); 
                    }
                };

            }
        };
    }

    @Override
    public Name composeName(Name name, Name name1) throws NamingException {
        throw new UnsupportedOperationException("Not supported yet."); 
    }

    @Override
    public String composeName(String string, String string1) throws NamingException {
        throw new UnsupportedOperationException("Not supported yet."); 
    }

    @Override
    public Object addToEnvironment(String string, Object o) throws NamingException {
        throw new UnsupportedOperationException("Not supported yet."); 
    }

    @Override
    public Object removeFromEnvironment(String string) throws NamingException {
        throw new UnsupportedOperationException("Not supported yet."); 
    }

    @Override
    public Hashtable<?, ?> getEnvironment() throws NamingException {
        throw new UnsupportedOperationException("Not supported yet."); 
    }

    @Override
    public void close() throws NamingException {}

    @Override
    public String getNameInNamespace() throws NamingException {
        throw new UnsupportedOperationException("Not supported yet."); 
    }

    
    //---------------------------
    
    /**
     * Assign this to the "java.naming.factory.initial" system property
     * to make the LittleContext mock the initial context.
     */
    public static class Factory implements javax.naming.spi.InitialContextFactory {

        @Override
        public Context getInitialContext(Hashtable<?, ?> hshtbl) throws NamingException {
            return new LittleContext();
        }
        
    }
}

This is the JPA EntityManager guice Provider used in a standalone application:

@Singleton
public class HibernateProvider implements Provider<EntityManagerFactory> {

    private final DataSource dataSource;
    private final String dataSourceURL;
    private EntityManagerFactory emFactory = null;


    @Inject
    public HibernateProvider(@Named("datasource.littleware") DataSource dsource,
            @Named("datasource.littleware") String sDatasourceUrl) {
        dataSource = dsource;
        dataSourceURL = sDatasourceUrl;
    }


    @Override
    public EntityManagerFactory get() {
        if (null == emFactory) {
            LittleDriver.setDataSource( dataSource );
            LittleContext.setDataSource( dataSource );
            if ( null == System.getProperty(  "java.naming.factory.initial" ) ) {
                System.setProperty( "java.naming.factory.initial", LittleContext.Factory.class.getName() );
            }
            
            emFactory = Persistence.createEntityManagerFactory( "littlewarePU" );
        }
        return emFactory;
    }
}

Anyway - I barely know how to use JPA, so duplicate these hacks at your own risk. The code is online here for now (I'm always moving things around in that repo).

Tuesday, June 04, 2013

typescript syntax highlighting in jedit and netbeans

Typescript syntax-highlighting is available in a few editors (vim, emacs, sublime) thanks to editor plugins published by Microsoft, and deeper type-aware language support with code-completion and refactoring is available for Visual Studio. Of course the code editors I usually use (jedit and netbeans) don't include typescript support, so I took a little time to see if I could get something working.

Adding basic typescipt support to jedit was easy. JEdit's syntax highlighting and indentation logic for a particular file is driven by the "mode" xml file associated with the file's extension (.js, .ts, .java, whatever). JEdit extracts lexical rules for syntax-coloring from the lists of keywords, operators, and regular expressions in the mode file. For typescript I associated .ts files with jedit's javascript mode, and added a few items to the keyword lists in javascript's mode xml file (declare, export, ...), and I was pretty happy with the result. This thread on stackoverflow describes where to find mode xml files, and the mode "catalog" file. I also disabled the typoscript (European CMS - who knew?) entry in the catalog, because typoscript also uses the ".ts" file extension:

<MODE NAME="javascript"  FILE="javascript.xml"
    FILE_NAME_GLOB="{*.{js,ts},Buildsub}" />

...
<MODE NAME="typoscript"  FILE="typoscript.xml"
    FILE_NAME_GLOB="*.tsXXX"/<


I tried a similar trick with netbeans, and followed these instructions to configure netbeans to treat ".ts" files as javascript. Unfortunately the resulting behavior in a netbeans editor is not that great. Netbeans builds its language support around a parser of the full language grammar - it doesn't just highlight keywords. The various syntax structures in typescript that are not legal in javascript are highlighted as errors in netbeans. Ugh! Anyway - it's better than nothing. This page has instructions for coding in java a netbeans module for a new language. It would be a fun project to code up a typescript extension for netbeans using the typescript tsc compiler via the java-based rhino javascript engine. This entry describes running tsc with rhino on the command line. I'll save that project for another day ...

Monday, June 03, 2013

compiling typescript with rhino javascript engine

I spent way too long the last couple days patching the typescript compiler (tsc) to run with java's rhino javascript engine. I mentioned in an earlier post how my dell laptop died on me. I wound up buying an hp envy m4 laptop on clearance at BestBuy to replace it, but before that I did that I was goofing around for a couple days getting by with my Android phone and the old PowerBook G4 I pulled off the shelf that amazingly boot up for me. Anyway - I thought I'd try to get tsc running from the command-line. The compiler is javascript compiled from typescript code, so I first tried to build node from source, but node's build setup requires newer versions of python, make, and gcc than what the powerbook had, so then I got the idea to try to run tsc with rhino, since the powerbook did have java 1.5 installed (java 1.6+ ships with rhino and a jrunscript command line tool). Of course it didn't "just work", and I let myself get sucked into banging my head on it - even after I got the new laptop. Ugh.

Anyway - I eventually got tsc running with rhino (the patch listing is further below). The easy part of the project was implementing a rhino version of tsc's IIO interface for file IO. The rhino implementation just calls through to java.io classes, and tsc runs a few feature tests to figure out which javascript engine it's running under:

    if (typeof ActiveXObject === "function")
        return getWindowsScriptHostIO();
    else if (typeof require === "function")
        return getNodeIO();
    else if ( typeof java != "undefined" )
        return getRhinoIO();
    else
        return null; // Unsupported host

The IO code was straight forward; the painful part was working around rhino's quirks. The first quirk I ran into was that rhino appears to treat the names of java's primitive types as reserved words, so things like:
var byte = 0;
or option.short = "v";
are illegal. Fortunately - that only popped up a couple places in the tsc code, but it's an unfortunate "feature" for rhino to have.

Another problem I ran into was invoking "delete" on an instance of java.io.File. Javascript includes delete in its collection of reserved words, but it should be legal to include a "delete()" method on some class. Rhino's javascript grammar probably just needs some love. The workaround was to access the method via f["delete"]() instead of f.delete().

C:\Users\Reuben\Documents\Code\typescript\src\compiler
> node
> var Foo = function() { return this; }
undefined
> Foo.prototype.delete = function() { return "bla"; }
[Function]
> (new Foo()).delete();
'bla'

...


> C:\Users\Reuben\Documents\Code\typescript\src\compiler
> jrunscript
js> var Foo = function() { return this; }
js> Foo.prototype.delete = function() { return "bla"; }
script error: sun.org.mozilla.javascript.internal.EvaluatorException: missing name after . operator (<STN> at line number 1
js>

Another problematic feature of rhino is that it does not hide the distinction between javascript's string type (which tsc expects), and java's java.lang.String. I discovered that Rhino has methods for converting between java types and javascript types - including the String() method mentioned here:

    resolvePath: function (path) {
        return <string> String( (new java.io.File(path)).getCanonicalPath() );
    },

There were one or two other small rhino quirks to work out, but the big one that surprised me was that rhino's regular expression objects apparently don't respect javascript's normal scoping rules. The tsc compiler was failing under rhino with various undefined types that weren't properly pulled in via the file-reference comments (see section 9.1.1 in the typescript spec). I eventually had a test setup, and found rhino would load every other referenced file, so a tsc run with rhino had this output:

(1)Reading code from C:/Users/Reuben/Documents/Code/typescript/src/compiler/typescript.ts
Found code at C:/Users/Reuben/Documents/Code/typescript/src/compiler/typescript.ts
 file reference: diagnostics.ts
 file reference: nodeTypes.ts
 file reference: ast.ts
 file reference: astWalkerCallback.ts
 file reference: astLogger.ts
 file reference: base64.ts
 file reference: emitter.ts
 file reference: parser.ts
 file reference: scanner.ts
 file reference: scopeWalk.ts
 file reference: symbols.ts
 file reference: tokens.ts
 file reference: typeCollection.ts
 file reference: types.ts
 file reference: referenceResolution.ts
 file reference: incrementalParser.ts

The output with nodejs was:

   (1)Reading code from C:/Users/Reuben/Documents/Code/typescript/src/compiler/typescript.ts
   Found code at C:/Users/Reuben/Documents/Code/typescript/src/compiler/typescript.ts
    file reference: diagnostics.ts
    file reference: flags.ts
    file reference: nodeTypes.ts
    file reference: hashTable.ts
    file reference: ast.ts
    file reference: astWalker.ts
    file reference: astWalkerCallback.ts
    file reference: astPath.ts
    file reference: astLogger.ts
    file reference: binder.ts
    file reference: base64.ts
    file reference: sourceMapping.ts
    file reference: emitter.ts
    file reference: errorReporter.ts
    file reference: parser.ts
    file reference: printContext.ts
    file reference: scanner.ts
    file reference: scopeAssignment.ts
    file reference: scopeWalk.ts
    file reference: signatures.ts
    file reference: symbols.ts
    file reference: symbolScope.ts
    file reference: tokens.ts
    file reference: typeChecker.ts
    file reference: typeCollection.ts
    file reference: typeFlow.ts
    file reference: types.ts
    file reference: pathUtils.ts
    file reference: referenceResolution.ts
    file reference: precompile.ts
    file reference: incrementalParser.ts
    file reference: declarationEmitter.ts

Anyway, long story short, it turned out that the reference strings were each processed by a function with a regular expression, and rhino had this crazy behavior where regular expression objects appear to be global.

with rhino:

js> function doTest( s ) { var rx =  /^\s*(\/\/\/\s*/gim;
return (rx.exec(s) == null); }

js> doTest(comment);
false
js> doTest(comment);
true
js> doTest(comment);
false
js> doTest(comment);
true

with node:
> function doTest(s) {
... var rx =  /^\s*(\/\/\/\s*/gim;
... return (rx.exec(s) == null);
... }
undefined
> comment
'///<reference path=\'sourceMapping.ts\' />'
> doTest(comment);
false
> doTest(comment);
false
> doTest(comment);
false
> doTest(comment);
false
> doTest(comment);
false

Unbelievable. Anyway - the work around is to reset the rx.lastIndex before each run, so:

    function getFileReferenceFromReferencePath(comment: string): IFileReference {
        var referencesRegEx = /^(\/\/\/\s*<reference\s+path=)('|")(.+?)\2\s*(static=('|")(.+?)\2\s*)*\/>/gim;
        referencesRegEx.lastIndex = 0;  // work around ridiculous bug in rhino ...
        var match = referencesRegEx.exec(comment);
        ...

Finally - I just tested this stuff by running the compiler on itself. The typescript repo on codeplex includes a bunch of test cases and an nmake based Makefile, but I was too lazy to download visual studio and get that working. In the end - rhino compiled tsc in 2 minutes, and node did it in 3 seconds. Ugh!

C:\Users\Reuben\Documents\Code\typescript\src\compiler
> date; jrunscript tsc.js --out tsc2.js tsc.ts; date

Monday, June 3, 2013 12:49:41 PM
Monday, June 3, 2013 12:51:39 PM


C:\Users\Reuben\Documents\Code\typescript\src\compiler
> date; node tsc2.js --out tsc3.js tsc.ts; date

Monday, June 3, 2013 12:58:40 PM
Monday, June 3, 2013 12:58:43 PM

Update 2013/06/29: I tried java 8's new nashorn javascript engine (in a jdk8 pre-release) to see how it did. Nashorn currently runs the tsc compile about 10% faster than rhino - still a lot slower than node. Doh!

> date; & 'C:\Program Files\Java\jdk1.8.0\bin\jrunscript.exe' .\tsc2.js --out tsc2.js tsc.ts; date;

Saturday, June 29, 2013 5:29:11 PM
Saturday, June 29, 2013 5:30:57 PM

Anyway - I'll check to see if the typescript maintainers will accept this patch, but I'll be surprised if they want anything to do with rhino after reading this sad tale ...


diff --git a/src/compiler/base64.ts b/src/compiler/base64.ts
index ee2d3c5..b4fc315 100644
--- a/src/compiler/base64.ts
+++ b/src/compiler/base64.ts
@@ -67,20 +67,21 @@ module TypeScript {
 
             var shift = 0;
             for (var i = 0; i < inString.length; i++) {
-                var byte = Base64Format.decodeChar(inString[i]);
+                // note: "byte" is reserved in java Rhino javascript environment - ugh
+                var bite = Base64Format.decodeChar(inString[i]);
                 if (i === 0) {
                     // Sign bit appears in the LSBit of the first value
-                    if ((byte & 1) === 1) {
+                    if ((bite & 1) === 1) {
                         negative = true;
                     }
-                    result = (byte >> 1) & 15; // 1111x
+                    result = (bite >> 1) & 15; // 1111x
                 } else {
-                    result = result | ((byte & 31) << shift); // 11111
+                    result = result | ((bite & 31) << shift); // 11111
                 }
 
                 shift += (i == 0) ? 4 : 5;
 
-                if ((byte & 32) === 32) {
+                if ((bite & 32) === 32) {
                     // Continue
                 } else {
                     return { value: negative ? -(result) : result, rest: inString.substr(i + 1) };
diff --git a/src/compiler/io.ts b/src/compiler/io.ts
index a5eb1ad..6e75bf2 100644
--- a/src/compiler/io.ts
+++ b/src/compiler/io.ts
@@ -13,6 +13,9 @@
 // limitations under the License.
 //
 
+declare var arguments:any;
+var javaArgs:any = arguments; // Rhino sets global arguments ... bla
+
 interface IResolvedFile {
     content: string;
     path: string;
@@ -105,7 +108,10 @@ declare class Enumerator {
     constructor (o: any);
 }
 declare function setTimeout(callback: () =>void , ms?: number);
+
 declare var require: any;
+declare var java: any;
+
 declare module process {
     export var argv: string[];
     export var platform: string;
@@ -123,7 +129,7 @@ declare module process {
 }
 
 var IO = (function() {
-
+    
     // Create an IO object for use inside WindowsScriptHost hosts
     // Depends on WSCript and FileSystemObject
     function getWindowsScriptHostIO(): IIO {
@@ -533,12 +539,251 @@ var IO = (function() {
             },
             quit: process.exit
         }
-    };
+    }
+    ;
+        
+    
+    function getRhinoIO():IIO {
+        var utf8 = java.nio.charset.Charset.forName( "UTF-8" );
+        var jscriptArgs = [];
+   
+        for( var i=0; i < javaArgs.length; ++i ) {
+            //
+            // convert java string to javascript string (so javascript string methods work - ugh!)
+            // see https://groups.google.com/forum/?fromgroups#!topic/mozilla.dev.tech.js-engine.rhino/FV15_KJVLGM
+            //
+            jscriptArgs.push( String( javaArgs[i] ) );
+        }
+        
+        
+        /**
+         * Byte-order-mark detector - ugh.
+         * @param streamInn java.io.InputStream
+         * @return java.io.Reader
+         * @see http://blog.publicobject.com/2010/08/handling-byte-order-mark-in-java.html
+         */
+       function inputStreamToReader(streamIn) {
+         // buffered stream supports mark and reset
+         var stream = new java.io.BufferedInputStream( streamIn );
+         stream.mark(3);
+         var byte1 = stream.read();
+         var byte2 = stream.read();
+         if (byte1 == 0xFF && byte2 == 0xFE) {
+           return new java.io.InputStreamReader(stream, "UTF-16LE");
+         } else if (byte1 == 0xFF && byte2 == 0xFF) {
+           return new java.io.InputStreamReader(stream, "UTF-16BE");
+         } else {
+           var byte3 = stream.read();
+           if (byte1 == 0xEF && byte2 == 0xBB && byte3 == 0xBF) {
+             return new java.io.InputStreamReader(stream, "UTF-8");
+           } else {
+             stream.reset();
+             return new java.io.InputStreamReader(stream);
+           }
+         }
+       };
+
+          return {
+            readFile: function (file):string {
+                try  {
+                    var f = new java.io.File( file );
+                    if( (! f.exists()) || (! f.isFile()) ) { return ""; }
+                    var buffer = java.lang.reflect.Array.newInstance( java.lang.Character.TYPE, f.length() + 128 ); // 128 fudge
+                    var reader = new java.io.BufferedReader( 
+                       inputStreamToReader(
+                              new java.io.FileInputStream( f ) 
+                         ) 
+                    );
+                    try {
+                     var offset = 0;
+                        for( var step = reader.read( buffer, offset, buffer.length - offset ); 
+                             step >= 0; step = reader.read( buffer, offset, buffer.length - offset ) ) {
+                             offset += step;
+                             //java.lang.System.out.println( "Just read num bytes: " + step );
+                        }
+                        var javaString = new java.lang.String( buffer, 0, offset )
+                        //java.lang.System.out.println( "Read: " + javaString );
+                        // convert java string to javascript string ... ugh
+                        return <string> String( new java.lang.String( buffer, 0, offset ) );
+                    } catch (ex) { 
+                        reader.close();
+                        ex.printStackTrace( java.lang.System.err );
+                        throw ex; 
+                    }
+                } catch (e) {
+                    IOUtils.throwIOError("Error reading file \"" + file + "\" - " + e.toString(), e );
+                }
+            },
+            writeFile: function( path, content ) {
+               var f = new java.io.File( path );
+               if( f.exists() && f.isFile() ) {
+                   var writer = new java.io.OutputStreamWriter(
+                         new java.io.FileOutputStream( f ), utf8
+                    );
+                    writer.write( content );
+                    writer.close();
+               }
+            },
+            deleteFile: function (path) {
+               var f = new java.io.File( path );
+               if( f.exists() && f.isFile() ) {
+                   // delete is reserved in javascript - confused Rhino parser - ugh
+                   f["delete"]();
+                }
+            },
+            fileExists: function (path) {
+                var result:bool = (new java.io.File( path )).exists();
+                return result;
+            },
+            createFile: function (path, useUTF8?) {
+                var f = new java.io.File( path );
+                if ( f.exists() && (! f.isFile()) ) {
+                    IOUtils.throwIOError("Error creating file \"" + path + "\".", null ); 
+                } else if ( ! f.exists() ) {
+                    var dir = f.getParentFile();
+                    dir.mkdirs();
+                }
+                try  {
+                    var writer = new java.io.OutputStreamWriter(
+                             new java.io.FileOutputStream( f ), utf8
+                            );
+                } catch (e) {
+                    IOUtils.throwIOError("Couldn't write to file '" + path + "'.", e);
+                }
+                return new IOUtils.BufferedTextWriter( {
+                    Write: function (str:string ) {
+                        writer.write( str );
+                    },
+                    WriteLine: function (str:string) {
+                        writer.write( str + "\n" );
+                    },
+                    Close: function () {
+                        writer.close();
+                        writer = null;
+                    }
+                } );
+            },
+            dir: function dir(path, spec?, options?):string[] {
+                options = options || {
+                };
+                function filesInFolder(folder:any):string[] {
+                    var paths = [];
+                    var files = folder.listFiles();
+                    for(var i = 0; i < files.length; i++) {
+                        var f = files[i];
+                        if(options.recursive && f.isDirectory()) {
+                            paths = paths.concat(filesInFolder(f));
+                        } else if(f.isFile() && (!spec || f.getName().match(spec))) {
+                            paths.push( String( f.getPath() ) ); // convert to javascript String
+                        }
+                    }
+                    return paths;
+                }
+                return filesInFolder( new java.io.File( path ) );
+            },
+            createDirectory: function (path) {
+                try  {
+                    if(!this.directoryExists(path)) {
+                       (new java.io.File( path )).mkdirs();
+                    }
+                } catch (e) {
+                    IOUtils.throwIOError("Couldn't create directory '" + path + "'.", e);
+                }
+            },
+            directoryExists: function (path) {
+                var f = new java.io.File( path );
+                var result:bool = f.exists() && f.isDirectory();
+                return result;
+            },
+            resolvePath: function (path) {
+                return <string> String( (new java.io.File(path)).getCanonicalPath() );
+            },
+            dirName: function (path) {
+                return <string> String( (new java.io.File( path )).getCanonicalFile().getParent() );
+            },
+            findFile: function (rootPath, partialFilePath) {
+                var scan = new java.io.File( rootPath + "/" + partialFilePath ).getCanonicalFile();
+                while(true) {
+                    if( scan.exists() ) {
+                        try  {
+                            var content = this.readFile( scan.getPath() );
+                            return {
+                                content: <string> content,
+                                path: <string> String( scan.getPath() )
+                            };
+                        } catch (err) {
+                        }
+                    } else {
+                        // climb up the file system ... ?
+                        var parent = (new java.io.File( rootPath )).getParent();
+                        if( parent == null ) {
+                            return null;
+                        } else {
+                            scan = new java.io.File( parent, partialFilePath );
+                        }
+                    }
+                }
+            },
+            print: function (str) {
+                java.lang.System.out.print( str );
+            },
+            printLine: function (str) {
+                java.lang.System.out.println( str );
+            },
+            arguments: <string[]> jscriptArgs, 
+            stderr: {
+                Write: function (str) {
+                    java.lang.System.err.print(str);
+                },
+                WriteLine: function (str) {
+                    java.lang.System.err.println(str );
+                },
+                Close: function () {
+                }
+            },
+            stdout: {
+                Write: function (str) {
+                    java.lang.System.out.print(str);
+                },
+                WriteLine: function (str) {
+                    java.lang.System.out.println(str );
+                },
+                Close: function () {
+                }
+            },
+            
+            /**
+             * Could implement watchFile() with java-7 nio2 code, but too lazy to bother,
+             * since WindowsScriptHost skips this method too ... :)
+             * @see http://docs.oracle.com/javase/tutorial/essential/io/notification.html 
+             */
+            watchFile: null,
+            run: function(source, filename) {
+                try {
+                    eval(source);
+                } catch (e) {
+                    IOUtils.throwIOError("Error while executing file '" + filename + "'.", e);
+                }
+            },
+            getExecutingFilePath: function () {
+                return this.arguments[0];
+            },
+            quit: function (exitCode? : number = 0) {
+                try {
+                    java.lang.System.lang.exit(exitCode);
+                } catch (e) {
+                }
+            }
+        };
+    }
+    ;
 
     if (typeof ActiveXObject === "function")
         return getWindowsScriptHostIO();
     else if (typeof require === "function")
         return getNodeIO();
+    else if ( typeof java != "undefined" )
+        return getRhinoIO();
     else
         return null; // Unsupported host
 })();
diff --git a/src/compiler/optionsParser.ts b/src/compiler/optionsParser.ts
index a10fb8f..7a7eb73 100644
--- a/src/compiler/optionsParser.ts
+++ b/src/compiler/optionsParser.ts
@@ -18,7 +18,7 @@
 interface IOptions {
     name?: string;
     flag?: bool;
-    short?: string;
+    shorty?: string;  // note: "short" is reserved in java
     usage?: string;
     set?: (s: string) => void;
     type?: string;
@@ -34,7 +34,7 @@ class OptionsParser {
 
         for (var i = 0; i < this.options.length; i++) {
 
-            if (arg === this.options[i].short || arg === this.options[i].name) {
+            if (arg === this.options[i].shorty || arg === this.options[i].name) {
                 return this.options[i];
             }
         }
@@ -89,8 +89,8 @@ class OptionsParser {
             var usageString = "  ";
             var type = option.type ? " " + option.type.toUpperCase() : "";
 
-            if (option.short) {
-                usageString += this.DEFAULT_SHORT_FLAG + option.short + type + ", ";
+            if (option.shorty) {
+                usageString += this.DEFAULT_SHORT_FLAG + option.shorty + type + ", ";
             }
 
             usageString += this.DEFAULT_LONG_FLAG + option.name + type;
@@ -110,27 +110,27 @@ class OptionsParser {
         }
     }
 
-    public option(name: string, config: IOptions, short?: string) {
+    public option(name: string, config: IOptions, shorty?: string) {
         if (!config) {
-            config = <any>short;
-            short = null;
+            config = <any>shorty;
+            shorty = null;
         }
 
         config.name = name;
-        config.short = short;
+        config.shorty = shorty;
         config.flag = false;
 
         this.options.push(config);
     }
 
-    public flag(name: string, config: IOptions, short?: string) {
+    public flag(name: string, config: IOptions, shorty?: string) {
         if (!config) {
-            config = <any>short;
-            short = null;
+            config = <any>shorty;
+            shorty = null;
         }
 
         config.name = name;
-        config.short = short;
+        config.shorty = shorty;
         config.flag = true
 
         this.options.push(config);
diff --git a/src/compiler/precompile.ts b/src/compiler/precompile.ts
index 88adf32..c66f937 100644
--- a/src/compiler/precompile.ts
+++ b/src/compiler/precompile.ts
@@ -131,6 +131,7 @@ module TypeScript {
 
     function getFileReferenceFromReferencePath(comment: string): IFileReference {
         var referencesRegEx = /^(\/\/\/\s*<reference\s+path=)('|")(.+?)\2\s*(static=('|")(.+?)\2\s*)*\/>/gim;
+        referencesRegEx.lastIndex = 0;  // work around ridiculous bug in rhino ...
         var match = referencesRegEx.exec(comment);
 
         if (match) {
@@ -294,6 +295,7 @@ module TypeScript {
             
             if (!comment.isBlock) {
                 var referencedCode = getFileReferenceFromReferencePath(comment.getText());
+                //CompilerDiagnostics.debugPrint( "Considering comment as possible reference (" + (referencedCode ? "ok" : "no") + "): " + comment.getText() );
                 if (referencedCode) {
                     referencedCode.minChar = comment.startPos;
                     referencedCode.limChar = referencedCode.minChar + comment.value.length;
diff --git a/src/compiler/referenceResolution.ts b/src/compiler/referenceResolution.ts
index 442d8ec..52ae47c 100644
--- a/src/compiler/referenceResolution.ts
+++ b/src/compiler/referenceResolution.ts
@@ -102,7 +102,7 @@ module TypeScript {
                 // if the path is relative, or came from a reference tag, we don't perform a search
                 if (isRelativePath || isRootedPath || !performSearch) {
                     try {
-                        CompilerDiagnostics.debugPrint("   Reading code from " + normalizedPath);
+                        CompilerDiagnostics.debugPrint("   (1)Reading code from " + normalizedPath);
                             
                         // Look for the .ts file first - if not present, use the .ts, the .d.str and the .d.ts
                         try {
@@ -116,19 +116,19 @@ module TypeScript {
                                 else if (isTSFile(normalizedPath)) {
                                     normalizedPath = changePathToSTR(normalizedPath);
                                 }
-                                CompilerDiagnostics.debugPrint("   Reading code from " + normalizedPath);
+                                CompilerDiagnostics.debugPrint("   (2)Reading code from " + normalizedPath);
                                 resolvedFile.content = ioHost.readFile(normalizedPath);
                             }
                             catch (err) {
                                 normalizedPath = changePathToDSTR(normalizedPath);
-                                CompilerDiagnostics.debugPrint("   Reading code from " + normalizedPath);
+                                CompilerDiagnostics.debugPrint("   (3)Reading code from " + normalizedPath);
 
                                 try {
                                     resolvedFile.content = ioHost.readFile(normalizedPath);
                                 }
                                 catch (err) {
                                     normalizedPath = changePathToDTS(normalizedPath);
-                                    CompilerDiagnostics.debugPrint("   Reading code from " + normalizedPath);
+                                    CompilerDiagnostics.debugPrint("   (4)Reading code from " + normalizedPath);
                                     resolvedFile.content = ioHost.readFile(normalizedPath);
                                 }
                             }
@@ -148,7 +148,10 @@ module TypeScript {
 
                     // if the path is non-relative, we should attempt to search on the relative path
                     resolvedFile = ioHost.findFile(parentPath, normalizedPath);
-
+                    CompilerDiagnostics.debugPrint("   Attempting to resolve (" + parentPath + ", " + normalizedPath + ") got: " + 
+                         (resolvedFile == null) ? "null" : resolvedFile.path 
+                    );
+                    
                     if (!resolvedFile) {
                         if (isSTRFile(normalizedPath)) {
                             normalizedPath = changePathToTS(normalizedPath);
@@ -187,6 +190,10 @@ module TypeScript {
                     var resolvedFilePath = ioHost.resolvePath(resolvedFile.path);
                     sourceUnit.referencedFiles = preProcessedFileInfo.referencedFiles;
 
+                    for (var i = 0; i < preProcessedFileInfo.referencedFiles.length; i++) {
+                        var fileReference = preProcessedFileInfo.referencedFiles[i];
+                        CompilerDiagnostics.debugPrint("    file reference: " + fileReference.path);
+                    }
                     // resolve explicit references
                     for (var i = 0; i < preProcessedFileInfo.referencedFiles.length; i++) {
                         var fileReference = preProcessedFileInfo.referencedFiles[i];