<?xml version="1.0" encoding='utf-8'?>
<?xml-stylesheet type="text/xsl" href="https://raibledesigns.com/roller-ui/styles/atom.xsl" media="screen"?><feed xmlns="http://www.w3.org/2005/Atom" 
      xmlns:app="http://www.w3.org/2007/app"
      xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/">

    <title type="html">Search for [jQuery] in weblog rd</title>
    <subtitle type="html">Search results for [jQuery] within weblog Raible Designs</subtitle>
    <id>https://raibledesigns.com/rd/feed/entries/atom?q=jQuery</id>

    <link rel="self" type="application/atom+xml" 
        href="https://raibledesigns.com/rd/feed/entries/atom?q=jQuery" />

    <link rel="alternate" type="text/html" 
        href="https://raibledesigns.com/rd/search?q=jQuery" />

    <link rel="search" type="application/opensearchdescription+xml" 
        href="https://raibledesigns.com/roller-services/opensearch/rd" />
    <opensearch:Query role="request" searchTerms="jQuery" startPage="1" />

    <link rel="first" type="application/atom+xml" href="https://raibledesigns.com/rd/feed/entries/atom?q=jQuery" />
    <link rel="next" type="application/atom+xml" href="https://raibledesigns.com/rd/feed/entries/atom?q=jQuery&amp;page=1" />
    <updated>2026-03-30T03:31:45-06:00</updated>
    <generator uri="http://roller.apache.org" version="5.0.3 (1388864191739:dave)">Apache Roller</generator>

        <entry>
        <id>https://raibledesigns.com/rd/entry/best_practices_for_using_foundation1</id>
        <title type="html">Best Practices for using Foundation with AngularJS Revisited</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/best_practices_for_using_foundation1"/>
        <published>2015-02-19T09:38:42-07:00</published>
        <updated>2015-02-19T15:49:23-07:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="foundation" scheme="http://roller.apache.org/ns/tags/" />
        <category term="css" scheme="http://roller.apache.org/ns/tags/" />
        <category term="javascript" scheme="http://roller.apache.org/ns/tags/" />
        <category term="foundationforapps" scheme="http://roller.apache.org/ns/tags/" />
        <category term="angularjs" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">&lt;p&gt;
&lt;a href=&quot;http://foundation.zurb.com&quot;&gt;&lt;img src=&quot;//raibledesigns.com/repository/images/angular-foundation.png&quot; width=&quot;200&quot; class=&quot;picture&quot; alt=&quot;Angular Foundation&quot; style=&quot;margin-top: -12px&quot;&gt;&lt;/a&gt;
A couple weeks ago I wrote about &lt;a href=&quot;//raibledesigns.com/rd/entry/best_practices_for_using_foundation&quot;&gt;using Foundation with AngularJS&lt;/a&gt;. Based on research I&apos;d done, I concluded that it was best to use &lt;a href=&quot;http://foundation.zurb.com/apps/&quot;&gt;Foundation for Apps&lt;/a&gt; for any webapps my client created and &lt;a href=&quot;http://foundation.zurb.com/&quot;&gt;Foundation for Sites&lt;/a&gt; for any websites (e.g. a WordPress-based intranet).&lt;/p&gt;
&lt;p&gt;After doing my initial research, I did some prototyping with Foundation for Apps (F4A). What I discovered is that F4A does &lt;em&gt;not&lt;/em&gt; include all the same components as Foundation for Sites (F5). For example, the top-bar and dropdown functionality are missing. I &lt;a href=&quot;http://foundation.zurb.com/forum/posts/22587-dropdowns-in-foundation-for-apps&quot;&gt;posted my issues to the Foundation Forums&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The response I received:&lt;/p&gt;
&lt;blockquote class=&quot;quote&quot;&gt;
It should work. You would need to copy over all the Scss and global mixins that you used in top-bar or at least all the output CSS from it. Otherwise there is no reason the components won&apos;t fit into the grid.&lt;/blockquote&gt;
&lt;p&gt;I was able to import Foundation for Sites into my project by adding it to bower.json:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;  &quot;dependencies&quot;: {
    &quot;foundation-apps&quot;: &quot;~1.0.2&quot;,
    &quot;foundation&quot;: &quot;~5.5.1&quot;
  }
&lt;/pre&gt;
&lt;p&gt;After doing this, I added the new path to Gulpfile.js:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;var sassPaths = [
  &apos;client/assets/scss&apos;,
  &apos;bower_components/foundation/scss&apos;,
  &apos;bower_components/foundation-apps/scss&apos;
];
&lt;/pre&gt;
&lt;p&gt;
After making this change, the top-bar rendered and my dropdowns worked. Since there was no jQuery in the page, I thought this might be a viable option. However, &lt;a href=&quot;http://foundation.zurb.com/forum/12220-jason-demitri&quot;&gt;Jason Demitri&lt;/a&gt; quickly pointed out it probably wouldn&apos;t work with mobile. He was right.&lt;/p&gt;
&lt;p&gt;While using F4A, I noticed that its components, and much of its look-n-feel, was different than F5. If you look at its &lt;a href=&quot;http://foundation.zurb.com/apps/app-templates/email/#!/&quot;&gt;Email App template&lt;/a&gt;, you&apos;ll see it looks kinda like a mobile app, even in a desktop browser. After trying F4A myself, I decided that F4A wasn&apos;t for us.  First of all, it doesn&apos;t seem to provide a consistent look and feel with a website that&apos;s written using F5. Furthermore, F4A only supports IE10+. In the healthcare industry, there&apos;s a lot of older browsers out there, so my client needs to support IE9 as a minimum.&lt;/p&gt;
&lt;p&gt;For these reasons, I decided to try &lt;a href=&quot;http://pineconellc.github.io/angular-foundation/&quot;&gt;Angular directives for Foundation&lt;/a&gt;. I took a prototype I&apos;d written with F5, removed its JavaScript, added Angular Foundation + Foundation dependencies to bower.json, added references to the respective scripts in index.html and added &apos;mm.foundation&apos; as a dependency in app.js. The experiment worked beautifully and I was quite happy with the results. I shared my findings with the team and we decided &lt;strong&gt;Angular Foundation is the best way to integrate Foundation and AngularJS&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;F4A is pretty new and I imagine it&apos;ll add more of F5&apos;s features as it evolves. However, I don&apos;t know if the two will ever be so similar that they can live side-by-side and allow a seamless experience for users. If you&apos;re interested in mixing F4A and F5, you might want to watch Jason Demitri&apos;s &lt;a href=&quot;https://github.com/RelutionDev/foundationUltra&quot;&gt;foundationUltra&lt;/a&gt;. This project combines Angular Foundation, Foundation for Sites, Foundation for Apps and Font Awesome. You can see a demo at &lt;a href=&quot;http://relutiondev.github.io/foundationUltra/&quot;&gt;http://relutiondev.github.io/foundationUltra/&lt;/a&gt;.</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/getting_started_with_angularjs</id>
        <title type="html">Getting Started with AngularJS</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/getting_started_with_angularjs"/>
        <published>2015-01-29T11:12:38-07:00</published>
        <updated>2015-09-23T06:44:03-06:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="npm" scheme="http://roller.apache.org/ns/tags/" />
        <category term="javascript" scheme="http://roller.apache.org/ns/tags/" />
        <category term="git" scheme="http://roller.apache.org/ns/tags/" />
        <category term="angularjs" scheme="http://roller.apache.org/ns/tags/" />
        <category term="node" scheme="http://roller.apache.org/ns/tags/" />
        <summary type="html">&lt;p&gt;I was hired by my current client in November to help them choose a technology stack for developing modern web applications.
   In our first sprint, we decided to look at JavaScript MVC frameworks. I suggested &lt;a href=&quot;https://angularjs.org/&quot;&gt;AngularJS&lt;/a&gt;, &lt;a href=&quot;http://emberjs.com/&quot;&gt;Ember.js&lt;/a&gt; and &lt;a href=&quot;http://facebook.github.io/react/&quot;&gt;React&lt;/a&gt;. Since
    most of the team was new to JavaScript MVC, I decided to create a tutorial for them. I tried to make it easy so they
    could learn how to write a simple web application with AngularJS. I thought others could benefit from this article as well,
so I asked (and received) permission from my client to publish it here.&lt;/p&gt;
&lt;h3&gt;What you&apos;ll build&lt;/h3&gt;
&lt;p&gt;You&apos;ll build a simple web application with AngularJS. You&apos;ll also add search and edit features with mock data.&lt;/p&gt;
&lt;h3&gt;What you&apos;ll need&lt;/h3&gt;
&lt;ul&gt;
    &lt;li&gt;About 15-30 minutes&lt;/li&gt;
    &lt;li&gt;A favorite text editor or IDE. I recommend &lt;a href=&quot;https://www.jetbrains.com/idea/&quot;&gt;IntelliJ IDEA&lt;/a&gt;.&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;http://git-scm.com/&quot;&gt;Git&lt;/a&gt; installed.&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;http://nodejs.org/&quot;&gt;Node.js&lt;/a&gt; and NPM installed.&lt;/li&gt;
&lt;/ul&gt;</summary>
        <content type="html">&lt;p&gt;I was hired by my current client in November to help them choose a technology stack for developing modern web applications.
   In our first sprint, we decided to look at JavaScript MVC frameworks. I suggested &lt;a href=&quot;https://angularjs.org/&quot;&gt;AngularJS&lt;/a&gt;, &lt;a href=&quot;http://emberjs.com/&quot;&gt;Ember.js&lt;/a&gt; and &lt;a href=&quot;http://facebook.github.io/react/&quot;&gt;React&lt;/a&gt;. Since
    most of the team was new to JavaScript MVC, I decided to create a tutorial for them. I tried to make it easy so they
    could learn how to write a simple web application with AngularJS. I thought others could benefit from this article as well,
so I asked (and received) permission from my client to publish it here.&lt;/p&gt;
&lt;h3&gt;What you&apos;ll build&lt;/h3&gt;
&lt;p&gt;You&apos;ll build a simple web application with AngularJS. You&apos;ll also add search and edit features with mock data.&lt;/p&gt;
&lt;h3&gt;What you&apos;ll need&lt;/h3&gt;
&lt;ul&gt;
    &lt;li&gt;About 15-30 minutes&lt;/li&gt;
    &lt;li&gt;A favorite text editor or IDE. I recommend &lt;a href=&quot;https://www.jetbrains.com/idea/&quot;&gt;IntelliJ IDEA&lt;/a&gt;.&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;http://git-scm.com/&quot;&gt;Git&lt;/a&gt; installed.&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;http://nodejs.org/&quot;&gt;Node.js&lt;/a&gt; and NPM installed.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Create a simple web application&lt;/h3&gt;
&lt;ol&gt;
    &lt;li&gt;
        &lt;p&gt;Clone the angular-seed repository using &lt;a href=&quot;http://git-scm.com/&quot;&gt;git&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;
git clone https://github.com/angular/angular-seed.git angular-tutorial
cd angular-tutorial&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;There are two kinds of dependencies in this project: tools and angular framework code. The tools help manage
            and test the application.&lt;/p&gt;
        &lt;ul&gt;
            &lt;li&gt;To get the tools that depend upon via &lt;code&gt;npm&lt;/code&gt;, the &lt;a href=&quot;https://www.npmjs.org/&quot;&gt;node
                package manager&lt;/a&gt;.
            &lt;/li&gt;
            &lt;li&gt;To get the angular code via &lt;code&gt;bower&lt;/code&gt;, a &lt;a href=&quot;http://bower.io/&quot;&gt;client-side code package
                manager&lt;/a&gt;.
            &lt;/li&gt;
        &lt;/ul&gt;
        &lt;p&gt;The project has preconfigured &lt;code&gt;npm&lt;/code&gt;&amp;nbsp;to automatically run &lt;code&gt;bower&lt;/code&gt;&amp;nbsp;so you can
            simply do:&lt;/p&gt;
        &lt;pre&gt;npm install&lt;/pre&gt;
    &lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Run the application&lt;/h3&gt;
&lt;p&gt;The project is configured with a simple development web server. The simplest way to start this server is:&lt;/p&gt;
&lt;pre&gt;npm start&lt;/pre&gt;
&lt;p&gt;Now browse to the app at &lt;code&gt;&lt;a href=&quot;http://localhost:8000/app/index.html&quot;&gt;http://localhost:8000/app/&lt;/a&gt;&lt;/code&gt;.
&lt;/p&gt;
&lt;h3&gt;Add a search feature&lt;/h3&gt;
&lt;p&gt;To add a search feature, open the project in an IDE or your favorite text editor. For IntelliJ IDEA, use File &amp;gt;
    New Project &amp;gt; Static Web and point to the directory you cloned angular-seed to.&lt;/p&gt;
&lt;h3&gt;The Basics&lt;/h3&gt;
&lt;ol&gt;
    &lt;li&gt;
        &lt;p&gt;Create an &lt;code&gt;app/search/index.html&lt;/code&gt; file with the following HTML:&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;form ng-submit=&quot;search()&quot;&amp;gt;
    &amp;lt;input type=&quot;search&quot; name=&quot;search&quot; ng-model=&quot;term&quot;&amp;gt;
    &amp;lt;button&amp;gt;Search&amp;lt;/button&amp;gt;
&amp;lt;/form&amp;gt;
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Create &lt;code&gt;app/search/search.js&lt;/code&gt; and define the routes (URLs) and controller for the search feature.
        &lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
angular.module(&apos;myApp.search&apos;, [&apos;ngRoute&apos;])

    .config([&apos;$routeProvider&apos;, function ($routeProvider) {
        $routeProvider
            .when(&apos;/search&apos;, {
                templateUrl: &apos;search/index.html&apos;,
                controller: &apos;SearchController&apos;
            })
    }])

    .controller(&apos;SearchController&apos;, function () {
        console.log(&quot;In Search Controller...&quot;);
    });
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Modify &lt;code&gt;app/app.js&lt;/code&gt; and add the &amp;quot;myApp.search&amp;quot; module you created above.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;angular.module(&apos;myApp&apos;, [
  &apos;ngRoute&apos;,
  &apos;myApp.view1&apos;,
  &apos;myApp.view2&apos;,
  &apos;myApp.version&apos;,
  &apos;myApp.search&apos;
])&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Modify &lt;code&gt;app/index.html&lt;/code&gt; and add a link to the search.js file.&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;script src=&quot;view2/view2.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;search/search.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;components/version/version.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Refresh your browser and navigate to &lt;a href=&quot;http://localhost:8000/app/#/search&quot;&gt;http://localhost:8000/app/#/search.&lt;/a&gt;
            You should see an input field and search button. You should also see a log message printed in your browser&apos;s
            console.
            In Chrome, you can view the console using View &amp;gt; Developer &amp;gt; JavaScript Console. You can make it
            easier to navigate to this page by adding a menu item in &lt;code&gt;app/index.html&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;ul class=&quot;menu&quot;&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&quot;#/view1&quot;&amp;gt;view1&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&quot;#/view2&quot;&amp;gt;view2&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&quot;#/search&quot;&amp;gt;search&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&lt;/pre&gt;
    &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This section has shown you how to add a new controller and view to a basic AngularJS application. This was fairly simple to create.
    The next section shows you how to create a fake backend API. &lt;/p&gt;
&lt;h3&gt;The Backend&lt;/h3&gt;
&lt;p&gt;To get search results, you&apos;re going to create a SearchService
    that makes HTTP requests. These HTTP requests will be handled by a mock backend using some of Angular&apos;s built-in
    mocking tools. The backend implementation was created using &lt;a
        href=&quot;http://www.jeremyzerr.com/angularjs-backend-less-development-using-httpbackend-mock&quot;&gt;AngularJS Backend-less Development Using a $httpBackend Mock&lt;/a&gt;.&lt;/p&gt;
&lt;ol&gt;
    &lt;li&gt;
        &lt;p&gt;Add a &lt;code&gt;SearchService&lt;/code&gt; to &lt;code&gt;app/search/search.js&lt;/code&gt;. This is done in Angular using its &lt;a
            href=&quot;https://docs.angularjs.org/guide/providers#factory-recipe&quot;&gt;Factory Recipe&lt;/a&gt;. Make sure to remove the
            semicolon from the &lt;code&gt;.controller&lt;/code&gt; code block.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;.controller(&apos;SearchController&apos;, function () {
    console.log(&quot;In Search Controller...&quot;);
})

.factory(&apos;SearchService&apos;, function ($http) {
    var service = {
        query: function (term) {
            return $http.get(&apos;/search/&apos; + term);
        }
    };
    return service;
});
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Inject the &lt;code&gt;SearchService&lt;/code&gt; into the &lt;code&gt;SearchController&lt;/code&gt; and use it to get results from
            the backend. The form in &lt;code&gt;app/search/index.html&lt;/code&gt; calls the &lt;code&gt;search()&lt;/code&gt; function. The
            &amp;quot;term&amp;quot; is defined by the input field in this page with
            &lt;span style=&quot;color: rgb(0,0,255);&quot;&gt;ng-model=&lt;/span&gt;&lt;span
                style=&quot;color: rgb(0,128,0);&quot;&gt;&amp;quot;term&amp;quot;.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;.controller(&apos;SearchController&apos;, function ($scope, SearchService) {
    $scope.search = function () {
        console.log(&quot;Search term is: &quot; + $scope.term);
        SearchService.query($scope.term).then(function (response) {
            $scope.searchResults = response.data;
        });
    };
})&lt;/pre&gt;
        &lt;p&gt;If you try to search for a &quot;foo&quot; term now, you&apos;ll see the following error in your console.&lt;/p&gt;
        &lt;pre&gt;Search term is: foo&lt;br/&gt;GET &lt;a href=&quot;http://localhost:8000/search/foo&quot;&gt;http://localhost:8000/search/foo&lt;/a&gt; 404 (Not Found)&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Create &lt;code&gt;app/search/mock-api.js&lt;/code&gt; for the fake backend. Populate it with the following JavaScript.
        &lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;// We will be using backend-less development
// $http uses $httpBackend to make its calls to the server
// $resource uses $http, so it uses $httpBackend too
// We will mock $httpBackend, capturing routes and returning data
angular.module(&apos;myApp&apos;)
    .service(&apos;ServerDataModel&apos;, function ServerDataModel() {
        this.data = [
            {
                id: 1,
                name: &quot;Peyton Manning&quot;,
                phone: &quot;(303) 567-8910&quot;,
                address: {
                    street: &quot;1234 Main Street&quot;,
                    city: &quot;Greenwood Village&quot;,
                    state: &quot;CO&quot;,
                    zip: &quot;80111&quot;
                }
            },
            {
                id: 2,
                name: &quot;Demaryius Thomas&quot;,
                phone: &quot;(720) 213-9876&quot;,
                address: {
                    street: &quot;5555 Marion Street&quot;,
                    city: &quot;Denver&quot;,
                    state: &quot;CO&quot;,
                    zip: &quot;80202&quot;
                }
            },
            {
                id: 3,
                name: &quot;Von Miller&quot;,
                phone: &quot;(917) 323-2333&quot;,
                address: {
                    street: &quot;14 Mountain Way&quot;,
                    city: &quot;Vail&quot;,
                    state: &quot;CO&quot;,
                    zip: &quot;81657&quot;
                }
            }
        ];

        this.getData = function () {
            return this.data;
        };

        this.search = function (term) {
            if (term == &quot;&quot; || term == &quot;*&quot;) {
                return this.getData();
            }
            // find the name that matches the term
            var list = $.grep(this.getData(), function (element, index) {
                term = term.toLowerCase();
                return (element.name.toLowerCase().match(term));
            });

            if (list.length === 0) {
                return [];
            } else {
                return list;
            }
        };
    })
    .run(function ($httpBackend, ServerDataModel) {

        $httpBackend.whenGET(/\/search\/\w+/).respond(function (method, url, data) {
            // parse the matching URL to pull out the term (/search/:term)
            var term = url.split(&apos;/&apos;)[2];

            var results = ServerDataModel.search(term);

            return [200, results, {Location: &apos;/search/&apos; + term}];
        });

        $httpBackend.whenGET(/search\/index.html/).passThrough();
        $httpBackend.whenGET(/view/).passThrough();
    });&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;This file uses &lt;a href=&quot;http://api.jquery.com/jquery.grep/&quot;&gt;jQuery.grep()&lt;/a&gt;, so you&apos;ll need to install
            jQuery. Modify &lt;code&gt;bower.json&lt;/code&gt; and add jQuery to the list of dependencies. &lt;pre
        class=&quot;brush: js&quot;&gt;&quot;dependencies&quot;: {
  ...
  &quot;html5-boilerplate&quot;: &quot;~4.3.0&quot;,
  &quot;jquery&quot;: &quot;~1.10.x&quot;
}&lt;/pre&gt;
        &lt;p&gt;Stop the npm process, run
            &amp;quot;npm install&amp;quot; to download and install jQuery, then &amp;quot;npm start&amp;quot; again.&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Modify &lt;code&gt;app/app.js&lt;/code&gt;&amp;nbsp;and add a dependency on &lt;a
            href=&quot;https://docs.angularjs.org/api/ngMockE2E&quot;&gt;ngMockE2E&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;angular.module(&apos;myApp&apos;, [&apos;ngMockE2E&apos;,
  &apos;ngRoute&apos;,
  ...&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Modify &lt;code&gt;app/index.html&lt;/code&gt; to include references to &lt;code&gt;jquery.js&lt;/code&gt;,
            &lt;code&gt;angular-mocks.js&lt;/code&gt; and &lt;code&gt;mock-api.js&lt;/code&gt;.&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
&amp;lt;script src=&quot;bower_components/angular-route/angular-route.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;bower_components/angular-mocks/angular-mocks.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;bower_components/jquery/jquery.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
...
&amp;lt;script src=&quot;search/mock-api.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;components/version/version.js&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Add the following HTML to &lt;code&gt;app/search/index.html&lt;/code&gt; to display the search results.&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;div&amp;gt;
    &amp;lt;pre&amp;gt;{{ searchResults | json}}&amp;lt;/pre&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/pre&gt;
        &lt;p&gt;After making this change, you should be able to search for &amp;quot;p&amp;quot;, &amp;quot;d&amp;quot; or &amp;quot;v&amp;quot; and
            see results as JSON.&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;To make the results more readable, change the above HTML to use a &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt; and Angular&apos;s &lt;a
            href=&quot;https://docs.angularjs.org/api/ng/directive/ngRepeat&quot;&gt;ng-repeat&lt;/a&gt; directive.&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;table ng-show=&quot;searchResults.length&quot; style=&quot;width: 100%&quot;&amp;gt;
    &amp;lt;thead&amp;gt;
    &amp;lt;tr&amp;gt;
        &amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;Phone&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;Address&amp;lt;/th&amp;gt;
    &amp;lt;/tr&amp;gt;
    &amp;lt;/thead&amp;gt;
    &amp;lt;tbody&amp;gt;
    &amp;lt;tr ng-repeat=&quot;person in searchResults&quot;&amp;gt;
        &amp;lt;td&amp;gt;{{person.name}}&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;{{person.phone}}&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;{{person.address.street}}&amp;lt;br/&amp;gt;
            {{person.address.city}}, {{person.address.state}} {{person.address.zip}}
        &amp;lt;/td&amp;gt;
    &amp;lt;/tr&amp;gt;
    &amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&lt;/pre&gt;
    &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This section has shown you how to fetch and display search results. The next section builds on this and shows how to edit and save a record.&lt;/p&gt;
&lt;h3&gt;Add an Edit Feature&lt;/h3&gt;
&lt;ol&gt;
    &lt;li&gt;
        &lt;p&gt;Modify &lt;code&gt;app/search/index.html&lt;/code&gt;&amp;nbsp;to add a link for editing a person.&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;table ng-show=&quot;searchResults.length&quot; style=&quot;width: 100%&quot;&amp;gt;
    ...
    &amp;lt;tr ng-repeat=&quot;person in searchResults&quot;&amp;gt;
        &amp;lt;td&amp;gt;&amp;lt;a href=&quot;&quot; ng-click=&quot;edit(person)&quot;&amp;gt;{{person.name}}&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
        ...
    &amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Add an &lt;code&gt;edit()&lt;/code&gt; function to &lt;code&gt;SearchController&lt;/code&gt;.
           Notice that the &lt;a href=&quot;https://docs.angularjs.org/api/ng/service/$location&quot;&gt;$location service&lt;/a&gt;
            dependency has been added to the controller&apos;s initialization function. &lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
.controller(&apos;SearchController&apos;, function ($scope, $location, SearchService) {
    ...

    $scope.edit = function (person) {
        $location.path(&quot;/edit/&quot; + person.id);
    }
})&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Create a route to handle the edit URL in &lt;code&gt;app/search/search.js&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
.config([&apos;$routeProvider&apos;, function ($routeProvider) {
    $routeProvider
        .when(&apos;/search&apos;, {
            templateUrl: &apos;search/index.html&apos;,
            controller: &apos;SearchController&apos;
        })
        .when(&apos;/edit/:id&apos;, {
            templateUrl: &apos;search/edit.html&apos;,
            controller: &apos;EditController&apos;
        });
}])&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Create &lt;code&gt;app/search/edit.html&lt;/code&gt; to display an editable form. The HTML below shows how you can use &lt;a
            href=&quot;https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Using_data_attributes&quot;&gt;HTML5&apos;s data
            attributes&lt;/a&gt; to have valid attributes instead of ng-*.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
&amp;lt;form ng-submit=&quot;save()&quot;&amp;gt;
    &amp;lt;div&amp;gt;
        &amp;lt;label for=&quot;name&quot;&amp;gt;Name:&amp;lt;/label&amp;gt;
        &amp;lt;input type=&quot;text&quot; data-ng-model=&quot;person.name&quot; id=&quot;name&quot;&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;div&amp;gt;
        &amp;lt;label for=&quot;phone&quot;&amp;gt;Phone:&amp;lt;/label&amp;gt;
        &amp;lt;input type=&quot;text&quot; data-ng-model=&quot;person.phone&quot; id=&quot;phone&quot;&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;fieldset&amp;gt;
        &amp;lt;legend&amp;gt;Address:&amp;lt;/legend&amp;gt;
        &amp;lt;address style=&quot;margin-left: 50px&quot;&amp;gt;
            &amp;lt;input type=&quot;text&quot; data-ng-model=&quot;person.address.street&quot;&amp;gt;&amp;lt;br/&amp;gt;
            &amp;lt;input type=&quot;text&quot; data-ng-model=&quot;person.address.city&quot;&amp;gt;,
            &amp;lt;input type=&quot;text&quot; data-ng-model=&quot;person.address.state&quot; size=&quot;2&quot;&amp;gt;
            &amp;lt;input type=&quot;text&quot; data-ng-model=&quot;person.address.zip&quot; size=&quot;5&quot;&amp;gt;
        &amp;lt;/address&amp;gt;
    &amp;lt;/fieldset&amp;gt;
    &amp;lt;div&amp;gt;
        &amp;lt;button type=&quot;submit&quot;&amp;gt;Save&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/form&amp;gt;
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Modify &lt;code&gt;SearchService&lt;/code&gt;&amp;nbsp;to contain functions for finding a person by their id, and saving
            them.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
.factory(&apos;SearchService&apos;, function ($http) {
    var service = {
        query: function (term) {
            return $http.get(&apos;/search/&apos; + term);
        },
        fetch: function (id) {
            return $http.get(&apos;/edit/&apos; + id);
        },
        save: function(data) {
            return $http.post(&apos;/edit/&apos; + data.id, data);
        }
    };
    return service;
});&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Create &lt;code&gt;EditController&lt;/code&gt; in &lt;code&gt;app/search/search.js&lt;/code&gt;. &lt;a
            href=&quot;https://docs.angularjs.org/api/ngRoute/service/$routeParams&quot;&gt;$routeParams&lt;/a&gt; is an Angular service
            that allows you to grab parameters from a URL.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
.controller(&apos;EditController&apos;, function ($scope, $location, $routeParams, SearchService) {
    SearchService.fetch($routeParams.id).then(function (response) {
        $scope.person = response.data;
    });

    $scope.save = function() {
        SearchService.save($scope.person).then(function(response) {
            $location.path(&quot;/search/&quot; + $scope.person.name);
        });
    }
})&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;At the bottom of &lt;code&gt;app/search/mock-api.js&lt;/code&gt;, in its &lt;code&gt;run()&lt;/code&gt; function, add the following:
        &lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;$httpBackend.whenGET(/\/search/).respond(function (method, url, data) {
    var results = ServerDataModel.search(&quot;&quot;);

    return [200, results];
});

$httpBackend.whenGET(/\/edit\/\d+/).respond(function (method, url, data) {
    // parse the matching URL to pull out the id (/edit/:id)
    var id = url.split(&apos;/&apos;)[2];

    var results = ServerDataModel.find(id);

    return [200, results, {Location: &apos;/edit/&apos; + id}];
});

$httpBackend.whenPOST(/\/edit\/\d+/).respond(function(method, url, data) {
    var params = angular.fromJson(data);

    // parse the matching URL to pull out the id (/edit/:id)
    var id = url.split(&apos;/&apos;)[2];

    var person = ServerDataModel.update(id, params);

    return [201, person, { Location: &apos;/edit/&apos; + id }];
});

$httpBackend.whenGET(/search\/edit.html/).passThrough();&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Further up in the same file, add &lt;code&gt;find()&lt;/code&gt; and &lt;code&gt;update()&lt;/code&gt; methods to &lt;code&gt;ServerDataModel&lt;/code&gt;.
        &lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;this.getData = function () {
    return this.data;
};

this.search = function (term) {
    ...
};

this.find = function (id) {
    // find the game that matches that id
    var list = $.grep(this.getData(), function (element, index) {
        return (element.id == id);
    });
    if (list.length === 0) {
        return {};
    }
    // even if list contains multiple items, just return first one
    return list[0];
};

this.update = function (id, dataItem) {
    // find the game that matches that id
    var people = this.getData();
    var match = null;
    for (var i = 0; i &lt; people.length; i++) {
        if (people[i].id == id) {
            match = people[i];
            break;
        }
    }
    if (!angular.isObject(match)) {
        return {};
    }
    angular.extend(match, dataItem);
    return match;
};&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;The &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; in &lt;code&gt;app/search/edit.html&lt;/code&gt;&amp;nbsp;calls a &lt;code&gt;save()&lt;/code&gt; function to update a
            person&apos;s data. You already implemented this above. The function executes the logic below.&amp;nbsp;&lt;/p&gt;
        &lt;pre class=&quot;brush: js&quot;&gt;$location.path(&quot;/search/&quot; + $scope.person.name);&lt;/pre&gt;
        &lt;p&gt;Since the SearchController doesn&apos;t execute a search automatically when you execute this URL, add the
            following logic to do so in &lt;code&gt;app/search/search.js&lt;/code&gt;. Note that
            &lt;code&gt;$routeParams&lt;/code&gt; is added to the list of injected dependencies.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
.controller(&apos;SearchController&apos;, function ($scope, $location, $routeParams, SearchService) {
    if ($routeParams.term) {
        SearchService.query($routeParams.term).then(function (response) {
            $scope.term = $routeParams.term;
            $scope.searchResults = response.data;
        });
    }
    ...
})&lt;/pre&gt;
        &lt;p&gt;You&apos;ll also need to add a new route so search/term is recognized.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;.config([&apos;$routeProvider&apos;, function ($routeProvider) {
    $routeProvider
        .when(&apos;/search&apos;, {
            templateUrl: &apos;search/index.html&apos;,
            controller: &apos;SearchController&apos;
        })
        .when(&apos;/search/:term&apos;, {
            templateUrl: &apos;search/index.html&apos;,
            controller: &apos;SearchController&apos;
        })
        ...
}])&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;After making all these changes, you should be able to refresh your browser and search/edit/update a person&apos;s
        information. If it works - nice job!
    &lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Source Code&lt;/h3&gt;
&lt;p&gt;A completed project with this code in it is available on GitHub at &lt;a
    href=&quot;https://github.com/mraible/angular-tutorial&quot;&gt;https://github.com/mraible/angular-tutorial&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;There are three commits that make the changes for the three main steps in this tutorial:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;a
        href=&quot;https://github.com/mraible/angular-tutorial/commit/9792a0fbf1c2a5f1171498de7666e6f13cdd0537&quot;&gt;The
        Basics&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a
        href=&quot;https://github.com/mraible/angular-tutorial/commit/e9c277a50606d7ebaf9bcefa46f5942e2edf7ecf&quot;&gt;The
        Backend&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a
        href=&quot;https://github.com/mraible/angular-tutorial/commit/56be9decd0242dd60f06ef7232db723d6595ed0c&quot;&gt;Add
        an Edit Feature&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Extra Credit&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;http://linqed.eu/2014/10/07/deploying-angular-seed-to-heroku/&quot;&gt;Deploy your completed app to Heroku&lt;/a&gt;. See
    running version of this tutorial at &lt;a
        href=&quot;https://angular-123.herokuapp.com&quot;&gt;https://angular-123.herokuapp.com&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;Summary&lt;/h3&gt;
&lt;p&gt;I hope you&apos;ve enjoyed this quick-and-easy tutorial on how to get started with AngularJS. In a future tutorial, I&apos;ll show
you &lt;a href=&quot;//raibledesigns.com/rd/entry/testing_angularjs_applications&quot;&gt;how to write unit tests and integration tests for this application&lt;/a&gt;. If you&apos;re in Denver next Tuesday, I&apos;ll be &lt;a href=&quot;http://www.meetup.com/DOSUG1/events/219099019/&quot;&gt;speaking about AngularJS at Denver&apos;s Open Source Users Group&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you&apos;re a Java developer that&apos;s interested in developing with AngularJS and Spring Boot, you might want to checkout
the &lt;a href=&quot;http://www.infoq.com/news/2015/01/jhipster-2.0&quot;&gt;recently released JHipster 2.0&lt;/a&gt;.&lt;/p&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/appfuse_reduced</id>
        <title type="html">AppFuse, Reduced</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/appfuse_reduced"/>
        <published>2014-12-16T06:03:31-07:00</published>
        <updated>2014-12-17T16:39:14-07:00</updated> 
        <category term="/Java" label="Java" />
        <category term="java" scheme="http://roller.apache.org/ns/tags/" />
        <category term="maven" scheme="http://roller.apache.org/ns/tags/" />
        <category term="maintenance" scheme="http://roller.apache.org/ns/tags/" />
        <category term="lessxml" scheme="http://roller.apache.org/ns/tags/" />
        <category term="appfuse" scheme="http://roller.apache.org/ns/tags/" />
        <category term="spring" scheme="http://roller.apache.org/ns/tags/" />
        <summary type="html">&lt;p&gt;
In November, I had some time off between clients. To occupy my time, I exercised my body and brain a bit. I spent a couple hours a day exercising and a few hours a day working on
&lt;a href=&quot;http://appfuse.org&quot;&gt;AppFuse&lt;/a&gt;. AppFuse isn&apos;t used to start projects nearly as much as it once was. This makes sense since there&apos;s been a ton of innovation on the JVM and there&apos;s lots of
&lt;em&gt;get-started-quickly&lt;/em&gt; frameworks now. Among my favorites are Spring Boot, JHipster, Grails and Play.
&lt;p&gt;
    You can see that AppFuse&apos;s community activity has decreased quite a bit over the years by looking at its mailing list
    traffic.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;http://appfuse.markmail.org/&quot;
       title=&quot;AppFuse Mailing List Traffic, December 2014 by Matt Raible, on Flickr&quot;&gt;&lt;img
        src=&quot;https://farm8.staticflickr.com/7525/15825430580_0531875e59.jpg&quot; width=&quot;500&quot; 
        alt=&quot;AppFuse Mailing List Traffic, December 2014&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
    Even though there&apos;s not a lot of users talking on the mailing list, it still seems to get quite a few downloads from
    Maven Central.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;https://www.flickr.com/photos/mraible/16011987392&quot;
       title=&quot;AppFuse Maven Central Stats, November 2014 by Matt Raible, on Flickr&quot;&gt;&lt;img
        src=&quot;https://farm8.staticflickr.com/7534/16011987392_442236433b.jpg&quot; width=&quot;500&quot; 
        alt=&quot;AppFuse Maven Central Stats, November 2014&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
    I think the biggest value that AppFuse provides now is a learning tool for those who work on it. Also, it&apos;s a good place to
    show other developers how they can evolve with open source frameworks (e.g. Spring, Hibernate, JSF, Tapestry, Struts) over several years. Showing how
    we migrated to Spring MVC Test, for example, might be useful. The upcoming move to Spring Data instead of our
    Generic DAO solution might be interesting as well. 
&lt;/p&gt;
&lt;p&gt;
    Regardless of whether AppFuse is used a lot or not, it should be easy to maintain. Over the several weeks, I made some
    opinionated changes and achieved some pretty good progress on simplifying things and making the project easier to
    maintain. The previous structure has a lot of duplicate versions, properties and plugin configurations between
    different projects. I was able to leverage Maven&apos;s inheritance model to make a number of improvements:
&lt;/p&gt;</summary>
        <content type="html">&lt;p&gt;
In November, I had some time off between clients. To occupy my time, I exercised my body and brain a bit. I spent a couple hours a day exercising and a few hours a day working on
&lt;a href=&quot;http://appfuse.org&quot;&gt;AppFuse&lt;/a&gt;. AppFuse isn&apos;t used to start projects nearly as much as it once was. This makes sense since there&apos;s been a ton of innovation on the JVM and there&apos;s lots of
&lt;em&gt;get-started-quickly&lt;/em&gt; frameworks now. Among my favorites are Spring Boot, JHipster, Grails and Play.
&lt;p&gt;
    You can see that AppFuse&apos;s community activity has decreased quite a bit over the years by looking at its mailing list
    traffic.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;http://appfuse.markmail.org/&quot;
       title=&quot;AppFuse Mailing List Traffic, December 2014 by Matt Raible, on Flickr&quot;&gt;&lt;img
        src=&quot;https://farm8.staticflickr.com/7525/15825430580_0531875e59.jpg&quot; width=&quot;500&quot; 
        alt=&quot;AppFuse Mailing List Traffic, December 2014&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
    Even though there&apos;s not a lot of users talking on the mailing list, it still seems to get quite a few downloads from
    Maven Central.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;https://www.flickr.com/photos/mraible/16011987392&quot;
       title=&quot;AppFuse Maven Central Stats, November 2014 by Matt Raible, on Flickr&quot;&gt;&lt;img
        src=&quot;https://farm8.staticflickr.com/7534/16011987392_442236433b.jpg&quot; width=&quot;500&quot; 
        alt=&quot;AppFuse Maven Central Stats, November 2014&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
    I think the biggest value that AppFuse provides now is a learning tool for those who work on it. Also, it&apos;s a good place to
    show other developers how they can evolve with open source frameworks (e.g. Spring, Hibernate, JSF, Tapestry, Struts) over several years. Showing how
    we migrated to Spring MVC Test, for example, might be useful. The upcoming move to Spring Data instead of our
    Generic DAO solution might be interesting as well. 
&lt;/p&gt;
&lt;p&gt;
    Regardless of whether AppFuse is used a lot or not, it should be easy to maintain. Over the several weeks, I made some
    opinionated changes and achieved some pretty good progress on simplifying things and making the project easier to
    maintain. The previous structure has a lot of duplicate versions, properties and plugin configurations between
    different projects. I was able to leverage Maven&apos;s inheritance model to make a number of improvements:
&lt;/p&gt;
&lt;ol&gt;
    &lt;li&gt;Changed AppFuse&apos;s parent to be based on the &lt;a
        href=&quot;http://www.infoq.com/news/2014/07/springio-platform&quot;&gt;Spring IO Platform&lt;/a&gt;. This project
        is a dependency manager that defines version numbers for open source projects that work well with Spring.
    &lt;/li&gt;
    &lt;li&gt;Defined plugins, their versions and configurations in &lt;code&gt;&amp;lt;pluginManagement&gt;&lt;/code&gt;.&lt;/li&gt;
    &lt;li&gt;Defined dependencies, their versions and exclusions in &lt;code&gt;&amp;lt;dependencyManagement&gt;&lt;/code&gt;.&lt;/li&gt;
    &lt;li&gt;Simplified archetypes so new projects have minimal dependencies. For example, here&apos;s a basic project&apos;s &lt;code&gt;pom.xml&lt;/code&gt;:
    &lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;project xmlns=&quot;http://maven.apache.org/POM/4.0.0&quot; xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
         xsi:schemaLocation=&quot;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd&quot;&amp;gt;

    &amp;lt;modelVersion&amp;gt;4.0.0&amp;lt;/modelVersion&amp;gt;
    &amp;lt;groupId&amp;gt;com.company&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;springmvc-project&amp;lt;/artifactId&amp;gt;
    &amp;lt;packaging&amp;gt;war&amp;lt;/packaging&amp;gt;
    &amp;lt;version&amp;gt;1.0-SNAPSHOT&amp;lt;/version&amp;gt;
    &amp;lt;name&amp;gt;AppFuse Spring MVC Application&amp;lt;/name&amp;gt;

    &amp;lt;parent&amp;gt;
        &amp;lt;groupId&amp;gt;org.appfuse&amp;lt;/groupId&amp;gt;
        &amp;lt;artifactId&amp;gt;appfuse-web&amp;lt;/artifactId&amp;gt;
        &amp;lt;version&amp;gt;3.5.0-SNAPSHOT&amp;lt;/version&amp;gt;
    &amp;lt;/parent&amp;gt;

    &amp;lt;build&amp;gt;
        &amp;lt;plugins&amp;gt;
            &amp;lt;plugin&amp;gt;
                &amp;lt;groupId&amp;gt;de.juplo&amp;lt;/groupId&amp;gt;
                &amp;lt;artifactId&amp;gt;hibernate4-maven-plugin&amp;lt;/artifactId&amp;gt;
            &amp;lt;/plugin&amp;gt;
            &amp;lt;plugin&amp;gt;
                &amp;lt;groupId&amp;gt;org.codehaus.mojo&amp;lt;/groupId&amp;gt;
                &amp;lt;artifactId&amp;gt;dbunit-maven-plugin&amp;lt;/artifactId&amp;gt;
            &amp;lt;/plugin&amp;gt;
            &amp;lt;plugin&amp;gt;
                &amp;lt;groupId&amp;gt;org.codehaus.mojo&amp;lt;/groupId&amp;gt;
                &amp;lt;artifactId&amp;gt;build-helper-maven-plugin&amp;lt;/artifactId&amp;gt;
            &amp;lt;/plugin&amp;gt;
        &amp;lt;/plugins&amp;gt;
    &amp;lt;/build&amp;gt;

    &amp;lt;dependencies&amp;gt;
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.appfuse&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;appfuse-${web.framework}&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;${appfuse.version}&amp;lt;/version&amp;gt;
            &amp;lt;type&amp;gt;pom&amp;lt;/type&amp;gt;
        &amp;lt;/dependency&amp;gt;
    &amp;lt;/dependencies&amp;gt;

    &amp;lt;properties&amp;gt;
        &amp;lt;amp.genericCore&amp;gt;true&amp;lt;/amp.genericCore&amp;gt;
        &amp;lt;amp.fullSource&amp;gt;false&amp;lt;/amp.fullSource&amp;gt;
        &amp;lt;dao.framework&amp;gt;hibernate&amp;lt;/dao.framework&amp;gt;
        &amp;lt;db.name&amp;gt;mydatabase&amp;lt;/db.name&amp;gt;
        &amp;lt;web.framework&amp;gt;spring&amp;lt;/web.framework&amp;gt;

        &amp;lt;!-- Framework/Plugin versions --&amp;gt;
        &amp;lt;appfuse.version&amp;gt;3.5.0-SNAPSHOT&amp;lt;/appfuse.version&amp;gt;
        &amp;lt;java.version&amp;gt;1.7&amp;lt;/java.version&amp;gt;
    &amp;lt;/properties&amp;gt;

    &amp;lt;profiles&amp;gt;
        &amp;lt;profile&amp;gt;
            &amp;lt;id&amp;gt;itest&amp;lt;/id&amp;gt;
            &amp;lt;build&amp;gt;
                &amp;lt;plugins&amp;gt;
                    &amp;lt;plugin&amp;gt;
                        &amp;lt;groupId&amp;gt;org.codehaus.cargo&amp;lt;/groupId&amp;gt;
                        &amp;lt;artifactId&amp;gt;cargo-maven2-plugin&amp;lt;/artifactId&amp;gt;
                    &amp;lt;/plugin&amp;gt;
                    &amp;lt;plugin&amp;gt;
                        &amp;lt;groupId&amp;gt;org.codehaus.mojo&amp;lt;/groupId&amp;gt;
                        &amp;lt;artifactId&amp;gt;webtest-maven-plugin&amp;lt;/artifactId&amp;gt;
                    &amp;lt;/plugin&amp;gt;
                &amp;lt;/plugins&amp;gt;
            &amp;lt;/build&amp;gt;
        &amp;lt;/profile&amp;gt;
    &amp;lt;/profiles&amp;gt;

    &amp;lt;reporting&amp;gt;
        &amp;lt;plugins&amp;gt;
            &amp;lt;plugin&amp;gt;
                &amp;lt;groupId&amp;gt;org.codehaus.mojo&amp;lt;/groupId&amp;gt;
                &amp;lt;artifactId&amp;gt;webtest-maven-plugin&amp;lt;/artifactId&amp;gt;
            &amp;lt;/plugin&amp;gt;
        &amp;lt;/plugins&amp;gt;
    &amp;lt;/reporting&amp;gt;
&amp;lt;/project&amp;gt;
&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;
    span.diffstat {
        white-space: nowrap;
        text-align: right;
        font-family: Helvetica, arial, freesans, clean, sans-serif, &quot;Segoe UI Emoji&quot;, &quot;Segoe UI Symbol&quot;;
        color: #666;
        font-weight: bold;
        font-size: 12px;
        cursor: default;
    }

    span.diffstat .lines-added, span.diffstat .lines-deleted {
        display: inline-block;
        margin-left: 3px;
        font-weight: bold;
    }
&lt;/style&gt;
&lt;p&gt;
    The pull request for these changes says it all:
&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;a href=&quot;https://github.com/appfuse/appfuse/pull/20&quot;&gt;AppFuse&lt;/a&gt; &lt;span class=&quot;diffstat&quot;&gt;
          &lt;span class=&quot;lines-added&quot; style=&quot;color: #55a532&quot;&gt;
            &lt;span class=&quot;diffstat-icon&quot;&gt;+&lt;/span&gt;4,822
          &lt;/span&gt;
          &lt;span class=&quot;lines-deleted&quot; style=&quot;color: #bd2c00&quot;&gt;
            &lt;span class=&quot;diffstat-icon&quot;&gt;-&lt;/span&gt;14,369
          &lt;/span&gt;
        &lt;/span&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;https://github.com/appfuse/appfuse-light/pull/1&quot;&gt;AppFuse Light&lt;/a&gt; &lt;span class=&quot;diffstat&quot;&gt;
              &lt;span class=&quot;lines-added&quot; style=&quot;color: #55a532&quot;&gt;
                &lt;span class=&quot;diffstat-icon&quot;&gt;+&lt;/span&gt;776
              &lt;/span&gt;
              &lt;span class=&quot;lines-deleted&quot; style=&quot;color: #bd2c00&quot;&gt;
                &lt;span class=&quot;diffstat-icon&quot;&gt;-&lt;/span&gt;4,687
              &lt;/span&gt;

            &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That&apos;s right, I was able to eliminate a good chunk of code without affecting any of AppFuse&apos;s functionality&lt;sup&gt;&lt;a
    href=&quot;http://raibledesigns.com/rd/entry/appfuse_reduced#footnote1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.
    I think we can all agree that less code == easier maintenance. This theme will continue as we work on future
    releases.
&lt;/p&gt;

&lt;p&gt;Other improvements include migrating all tests to use JUnit4, integrating Spring MVC Test, and configuring the
    surefire plugin to run tests in parallel. I also The &lt;a href=&quot;http://mojo.codehaus.org/build-helper-maven-plugin/&quot;&gt;build-helper-maven-plugin&lt;/a&gt;
    is now used to find open ports for Cargo to run and a lot of testing was done to ensure you can build/test multiple
    AppFuse-derived projects at the same time. Finally, I migrated to the &lt;a href=&quot;http://juplo.de/hibernate4-maven-plugin/&quot;&gt;hibernate4-maven-plugin&lt;/a&gt; and upgraded to Tapestry 5.4.&lt;/p&gt;

&lt;p&gt;In the next version of AppFuse, I plan to remove as
    much XML as possible - moving all of the configuration to Spring&apos;s JavaConfig. We&apos;ll also be moving to Java 8 as a
    minimum. I&apos;m even considering getting rid of all the pom.xml files in favor of another build language that requires
    less code. &lt;/p&gt;
&lt;p&gt;In other words, the upcoming 3.5 release will be the last release that supports Java 7 and uses Spring&apos;s XML for configuration. AppFuse 4.0 will strive for #NoXML.
   &lt;a href=&quot;http://appfuse.org/display/APF/Roadmap&quot;&gt;The project&apos;s roadmap&lt;/a&gt; has more details on additional
    hopes and dreams.&lt;/p&gt;

&lt;p&gt;We&apos;d love to hear your feedback on these change. Do you like the simplification theme? Are you OK with having AppFuse
    as a parent in your projects?
&lt;/p&gt;
&lt;p class=&quot;footnotes&quot; style=&quot;border-top: 1px dotted silver; padding-top: 5px; font-size: .9em&quot;&gt;
    &lt;a name=&quot;footnote1&quot;&gt;&lt;/a&gt;1. For project
    and code stats, see &lt;a href=&quot;https://www.openhub.net/p/appfuse/analyses/latest/languages_summary&quot;&gt;AppFuse on Open
    Hub&lt;/a&gt;.
&lt;/p&gt;
</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/developing_an_ios_native_app</id>
        <title type="html">Developing an iOS Native App with Ionic</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/developing_an_ios_native_app"/>
        <published>2014-03-27T16:38:55-06:00</published>
        <updated>2014-05-08T19:47:26-06:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="ionic" scheme="http://roller.apache.org/ns/tags/" />
        <category term="ios" scheme="http://roller.apache.org/ns/tags/" />
        <category term="mobile" scheme="http://roller.apache.org/ns/tags/" />
        <category term="html5" scheme="http://roller.apache.org/ns/tags/" />
        <category term="angularjs" scheme="http://roller.apache.org/ns/tags/" />
        <category term="ionicframework" scheme="http://roller.apache.org/ns/tags/" />
        <summary type="html">&lt;p&gt;
    In my current project, I&apos;ve been helping a client develop a native iOS app for their customers. It&apos;s
    written mostly in Objective-C and talks to a REST API. I talked about &lt;a
        href=&quot;http://raibledesigns.com/rd/entry/documenting_your_spring_api_with&quot;&gt;how we documented our REST API&lt;/a&gt;
    a couple days ago. We developed a prototype for this application back in December, using
    &lt;a href=&quot;http://angularjs.org/&quot;&gt;AngularJS&lt;/a&gt; and &lt;a href=&quot;http://getbootstrap.com/&quot;&gt;Bootstrap&lt;/a&gt;. Rather than
    using &lt;a href=&quot;http://phonegap.com/&quot;&gt;PhoneGap&lt;/a&gt;, we loaded our app in a UIWebView.
&lt;/p&gt;

&lt;p&gt;
    It all seemed to work well until we needed to read an activation code with the device&apos;s camera. Since
    we didn&apos;t know how to do &lt;a href=&quot;http://en.wikipedia.org/wiki/Optical_character_recognition&quot;&gt;OCR&lt;/a&gt; in JavaScript,
    we figured a mostly-native app was the way to go. We hired an outside company to do iOS development in January and
    they&apos;ve been developing the app since the beginning of February. In the last couple weeks, we encountered some
    screens that seemed fitting for HTML5, so we turned back to our AngularJS prototype.
&lt;/p&gt;

&lt;p&gt;The prototype used Bootstrap heavily, but we quickly learned it didn&apos;t look like an iOS 7 app, which is what our
    UX Designer requested. A co-worker pointed out &lt;a href=&quot;http://ionicframework.com/&quot;&gt;Ionic&lt;/a&gt;, developed by
    &lt;a href=&quot;http://drifty.com/&quot;&gt;Drifty&lt;/a&gt;. It&apos;s basically &lt;em&gt;Bootstrap for Native&lt;/em&gt;, so the apps you develop
    look and behave like a mobile application.
&lt;/p&gt;

&lt;blockquote class=&quot;quote&quot;&gt;
    &lt;strong&gt;What is Ionic?&lt;/strong&gt;&lt;br/&gt;
    Free and open source, Ionic offers a library of mobile-optimized HTML, CSS and JS components for building highly
    interactive apps. Built with Sass and optimized for AngularJS.
&lt;/blockquote&gt;

&lt;p&gt;
    I started developing with Ionic a few weeks ago. Using its CSS classes and AngularJS directives,
    I was able to create several new screens in a matter of days. Most of the time, I was learning new things:
    how to &lt;a
        href=&quot;http://forum.ionicframework.com/t/is-it-possible-to-override-the-back-button-behavior-on-certain-screens/1867/2&quot;&gt;
    override its back button&lt;/a&gt; behavior (to launch back into the native app), how to
    &lt;a href=&quot;http://ionicframework.com/docs/angularjs/controllers/view-state/&quot;&gt;configure routes&lt;/a&gt; with
    &lt;a href=&quot;https://github.com/angular-ui/ui-router&quot;&gt;ui-router&lt;/a&gt;, and how to make the
    &lt;a href=&quot;http://ionicframework.com/docs/angularjs/views/loading/&quot;&gt;$ionicLoading service&lt;/a&gt; look native. Now that
    I know a lot of the basics, I feel like I can really crank out some code.
&lt;/p&gt;

&lt;p style=&quot;border: 1px solid #91c89c;
color: #333;
padding: 10px 10px 10px 10px;
background: #f3f9f4;&quot;&gt;
    &lt;strong&gt;Tip:&lt;/strong&gt;
    I learned how subviews work with ui-router thanks to a YouTube video of &lt;a
        href=&quot;http://www.youtube.com/watch?v=dqJRoh8MnBo&quot;&gt;Tim Kindberg on Angular UI-Router&lt;/a&gt;. However, subviews
    never fully made sense until I saw
    &lt;a href=&quot;http://forum.ionicframework.com/t/using-state-go-to-navigate-between-views-doesnt-work-location-path-does/1846/11&quot;&gt;
        Jared Bell&apos;s diagram&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
    To demonstrate how easy it is to use Ionic, I whipped up a quick example application. You can get the source
    on GitHub at &lt;a href=&quot;https://github.com/mraible/boot-ionic&quot;&gt;https://github.com/mraible/boot-ionic&lt;/a&gt;. The app is a
    refactored version of Josh Long&apos;s &lt;a href=&quot;https://github.com/mraible/boot-examples/tree/master/x-auth-security&quot;&gt;x-auth-security&lt;/a&gt; that uses Ionic instead
    of raw AngularJS and Bootstrap. To keep things simple, I did not develop the native app that wraps the HTML.
&lt;/p&gt;</summary>
        <content type="html">&lt;p&gt;
    In my current project, I&apos;ve been helping a client develop a native iOS app for their customers. It&apos;s
    written mostly in Objective-C and talks to a REST API. I talked about &lt;a
        href=&quot;http://raibledesigns.com/rd/entry/documenting_your_spring_api_with&quot;&gt;how we documented our REST API&lt;/a&gt;
    a couple days ago. We developed a prototype for this application back in December, using
    &lt;a href=&quot;http://angularjs.org/&quot;&gt;AngularJS&lt;/a&gt; and &lt;a href=&quot;http://getbootstrap.com/&quot;&gt;Bootstrap&lt;/a&gt;. Rather than
    using &lt;a href=&quot;http://phonegap.com/&quot;&gt;PhoneGap&lt;/a&gt;, we loaded our app in a UIWebView.
&lt;/p&gt;

&lt;p&gt;
    It all seemed to work well until we needed to read an activation code with the device&apos;s camera. Since
    we didn&apos;t know how to do &lt;a href=&quot;http://en.wikipedia.org/wiki/Optical_character_recognition&quot;&gt;OCR&lt;/a&gt; in JavaScript,
    we figured a mostly-native app was the way to go. We hired an outside company to do iOS development in January and
    they&apos;ve been developing the app since the beginning of February. In the last couple weeks, we encountered some
    screens that seemed fitting for HTML5, so we turned back to our AngularJS prototype.
&lt;/p&gt;

&lt;p&gt;The prototype used Bootstrap heavily, but we quickly learned it didn&apos;t look like an iOS 7 app, which is what our
    UX Designer requested. A co-worker pointed out &lt;a href=&quot;http://ionicframework.com/&quot;&gt;Ionic&lt;/a&gt;, developed by
    &lt;a href=&quot;http://drifty.com/&quot;&gt;Drifty&lt;/a&gt;. It&apos;s basically &lt;em&gt;Bootstrap for Native&lt;/em&gt;, so the apps you develop
    look and behave like a mobile application.
&lt;/p&gt;

&lt;blockquote class=&quot;quote&quot;&gt;
    &lt;strong&gt;What is Ionic?&lt;/strong&gt;&lt;br/&gt;
    Free and open source, Ionic offers a library of mobile-optimized HTML, CSS and JS components for building highly
    interactive apps. Built with Sass and optimized for AngularJS.
&lt;/blockquote&gt;

&lt;p&gt;
    I started developing with Ionic a few weeks ago. Using its CSS classes and AngularJS directives,
    I was able to create several new screens in a matter of days. Most of the time, I was learning new things:
    how to &lt;a
        href=&quot;http://forum.ionicframework.com/t/is-it-possible-to-override-the-back-button-behavior-on-certain-screens/1867/2&quot;&gt;
    override its back button&lt;/a&gt; behavior (to launch back into the native app), how to
    &lt;a href=&quot;http://ionicframework.com/docs/angularjs/controllers/view-state/&quot;&gt;configure routes&lt;/a&gt; with
    &lt;a href=&quot;https://github.com/angular-ui/ui-router&quot;&gt;ui-router&lt;/a&gt;, and how to make the
    &lt;a href=&quot;http://ionicframework.com/docs/angularjs/views/loading/&quot;&gt;$ionicLoading service&lt;/a&gt; look native. Now that
    I know a lot of the basics, I feel like I can really crank out some code.
&lt;/p&gt;

&lt;p style=&quot;border: 1px solid #91c89c;
color: #333;
padding: 10px 10px 10px 10px;
background: #f3f9f4;&quot;&gt;
    &lt;strong&gt;Tip:&lt;/strong&gt;
    I learned how subviews work with ui-router thanks to a YouTube video of &lt;a
        href=&quot;http://www.youtube.com/watch?v=dqJRoh8MnBo&quot;&gt;Tim Kindberg on Angular UI-Router&lt;/a&gt;. However, subviews
    never fully made sense until I saw
    &lt;a href=&quot;http://forum.ionicframework.com/t/using-state-go-to-navigate-between-views-doesnt-work-location-path-does/1846/11&quot;&gt;
        Jared Bell&apos;s diagram&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
    To demonstrate how easy it is to use Ionic, I whipped up a quick example application. You can get the source
    on GitHub at &lt;a href=&quot;https://github.com/mraible/boot-ionic&quot;&gt;https://github.com/mraible/boot-ionic&lt;/a&gt;. The app is a
    refactored version of Josh Long&apos;s &lt;a href=&quot;https://github.com/mraible/boot-examples/tree/master/x-auth-security&quot;&gt;x-auth-security&lt;/a&gt; that uses Ionic instead
    of raw AngularJS and Bootstrap. To keep things simple, I did not develop the native app that wraps the HTML.
&lt;/p&gt;

&lt;p&gt;Below are the steps I used to convert from AngularJS + Bootstrap to Ionic. If you want to convert a simple AngularJS
    app to use Ionic, hopefully this will help.&lt;/p&gt;

&lt;p&gt;
    &lt;strong&gt;1. Download Ionic and add it to your project.&lt;/strong&gt;
&lt;/p&gt;

&lt;p&gt;Ionic 1.0 Beta was released earlier this week. You can download it from &lt;a
        href=&quot;http://code.ionicframework.com/1.0.0-beta.1/ionic-v1.0.0-beta.1.zip&quot;&gt;here&lt;/a&gt;. Add its files
    to your project. In this example, I &lt;a
            href=&quot;https://github.com/mraible/boot-ionic/commit/d564bb8ecb26c8519f88d6797db53f6ae327d9ad&quot;&gt;added them&lt;/a&gt;
    to &lt;em&gt;src/main/resources/public&lt;/em&gt;. In my index.html, I removed Bootstrap&apos;s CSS and replaced it with Ionic&apos;s.
&lt;/p&gt;
&lt;pre class=&quot;brush: diff&quot;&gt;
-    &amp;lt;link href=&quot;webjars/bootstrap/3.1.1/css/bootstrap.min.css&quot; rel=&quot;stylesheet&quot;&amp;gt;
+    &amp;lt;link rel=&quot;stylesheet&quot; href=&quot;css/ionic.css&quot;/&amp;gt;
  &amp;lt;/head&amp;gt;
-&amp;lt;body style=&quot;padding-top: 60px&quot;&amp;gt;
+&amp;lt;body&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Next, I replaced Angular, Bootstrap and jQuery&apos;s JavaScript references.&lt;/p&gt;
&lt;pre class=&quot;brush: diff&quot;&gt;
-    &amp;lt;script src=&quot;webjars/jquery/2.0.3/jquery.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
-    &amp;lt;script src=&quot;webjars/bootstrap/3.1.1/js/bootstrap.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
-    &amp;lt;script src=&quot;webjars/angularjs/1.2.13/angular.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
+    &amp;lt;script src=&quot;js/ionic.bundle.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
     &amp;lt;script src=&quot;webjars/angularjs/1.2.13/angular-resource.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
-    &amp;lt;script src=&quot;webjars/angularjs/1.2.13/angular-route.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
     &amp;lt;script src=&quot;webjars/angularjs/1.2.13/angular-cookies.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&lt;/pre&gt;
&lt;blockquote class=&quot;quote&quot; style=&quot;margin-left: 0; margin-right: 0&quot;&gt;
    &lt;strong&gt;What about WebJars?&lt;/strong&gt;&lt;br/&gt;
    You might ask - why not use &lt;a href=&quot;http://www.webjars.org/&quot;&gt;WebJars&lt;/a&gt;? You can, once
    &lt;a href=&quot;https://github.com/webjars/ionic/pull/2&quot;&gt;this pull request&lt;/a&gt; is accepted and an updated version is
deployed to Maven central. &lt;a href=&quot;https://github.com/mraible/boot-ionic/commit/1bffd792683b2428fc16a73f3e7e3e6d43f327c2&quot;&gt;Here&apos;s how&lt;/a&gt;
the application would change.&lt;/blockquote&gt;
&lt;p&gt;
    &lt;strong&gt;2. Change from Angular&apos;s Router to ui-router.&lt;/strong&gt;
&lt;/p&gt;

&lt;p&gt;Ionic uses ui-router for matching URLs and loading particular pages.
    The raw Angular routing looks pretty similar to how it does with ui-router, except it uses a
    &lt;code&gt;$stateProvider&lt;/code&gt;
    service instead of &lt;code&gt;$routeProvider&lt;/code&gt;. You&apos;ll notice I also added &apos;ionic&apos; as a dependency.
&lt;/p&gt;
&lt;pre class=&quot;brush: diff&quot;&gt;
-angular.module(&apos;exampleApp&apos;, [&apos;ngRoute&apos;, &apos;ngCookies&apos;, &apos;exampleApp.services&apos;])
+angular.module(&apos;exampleApp&apos;, [&apos;ionic&apos;, &apos;ngCookies&apos;, &apos;exampleApp.services&apos;])
 	.config(
-		[ &apos;$routeProvider&apos;, &apos;$locationProvider&apos;, &apos;$httpProvider&apos;, function($routeProvider, $locationProvider, $httpProvider) {
+		[ &apos;$stateProvider&apos;, &apos;$urlRouterProvider&apos;, &apos;$httpProvider&apos;, function($stateProvider, $urlRouterProvider, $httpProvider) {

-	    $routeProvider.when(&apos;/create&apos;, { templateUrl: &apos;partials/create.html&apos;, controller: CreateController});
+           $stateProvider.state(&apos;create&apos;, {url: &apos;/create&apos;, templateUrl: &apos;partials/create.html&apos;, controller: CreateController})
+               .state(&apos;edit&apos;, {url: &apos;/edit/:id&apos;, templateUrl: &apos;partials/edit.html&apos;, controller: EditController})
+               .state(&apos;login&apos;, {url: &apos;/login&apos;, templateUrl: &apos;partials/login.html&apos;, controller: LoginController})
+               .state(&apos;index&apos;, {url: &apos;/index&apos;, templateUrl: &apos;partials/index.html&apos;, controller: IndexController});

-	    $routeProvider.when(&apos;/edit/:id&apos;, { templateUrl: &apos;partials/edit.html&apos;, controller: EditController});
-	    $routeProvider.when(&apos;/login&apos;, { templateUrl: &apos;partials/login.html&apos;, controller: LoginController});
-	    $routeProvider.otherwise({templateUrl: &apos;partials/index.html&apos;, controller: IndexController});
-
-	    $locationProvider.hashPrefix(&apos;!&apos;);
+	    $urlRouterProvider.otherwise(&apos;/index&apos;);
&lt;/pre&gt;
&lt;p&gt;
    &lt;strong&gt;3. Add Ionic elements to your index.html.&lt;/strong&gt;
&lt;/p&gt;

&lt;p&gt;
    In contrast to Bootstrap&apos;s navbar, Ionic has header and footer elements. Rather than using a &lt;a
        href=&quot;http://docs.angularjs.org/api/ngRoute/directive/ngView&quot;&gt;ng-view&lt;/a&gt;
    directive, you use an &amp;lt;ion-nav-view&amp;gt;. It&apos;s a pretty slick setup once you understand it, especially since they
    allow you to easily override &lt;a href=&quot;http://ionicframework.com/docs/api/directive/ionNavBackButton/&quot;&gt;back-button
    behavior&lt;/a&gt; and
    &lt;a href=&quot;http://ionicframework.com/docs/api/directive/ionNavButtons/&quot;&gt;nav buttons&lt;/a&gt;.
&lt;/p&gt;
&lt;pre class=&quot;brush: diff&quot;&gt;
-    &amp;lt;nav class=&quot;navbar navbar-fixed-top navbar-default&quot; role=&quot;navigation&quot;&amp;gt;
-        &amp;lt;!-- lots of HTML here --&gt;
-    &amp;lt;/nav&amp;gt;
-
-    &amp;lt;div class=&quot;container&quot;&amp;gt;
-        &amp;lt;div class=&quot;alert alert-danger&quot; ng-show=&quot;error&quot;&amp;gt;{{error}}&amp;lt;/div&amp;gt;
-        &amp;lt;div ng-view&amp;gt;&amp;lt;/div&amp;gt;
-    &amp;lt;/div&amp;gt;
+    &amp;lt;ion-nav-bar class=&quot;bar-positive nav-title-slide-ios7&quot;&amp;gt;&amp;lt;/ion-nav-bar&amp;gt;
+    &amp;lt;ion-nav-view animation=&quot;slide-left-right&quot;&amp;gt;
+        &amp;lt;div class=&quot;alert alert-danger&quot; ng-show=&quot;error&quot;&amp;gt;{{error}}&amp;lt;/div&amp;gt;
+    &amp;lt;/ion-nav-view&amp;gt;
+    &amp;lt;ion-footer-bar class=&quot;bar-dark&quot; ng-show=&quot;user&quot;&amp;gt;
+        &amp;lt;button class=&quot;button button-assertive&quot; ng-click=&quot;logout()&quot;&amp;gt;
+            Logout
+        &amp;lt;/button&amp;gt;
+    &amp;lt;/ion-footer-bar&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
    &lt;strong&gt;4. Change your templates to use &amp;lt;ion-view&gt; and &amp;lt;ion-content&gt;.&lt;/strong&gt;
&lt;/p&gt;
&lt;p&gt;After routes are migrated and basic navigation is working, you&apos;ll need to modify your templates to use &amp;lt;ion-view&amp;gt;
    and &amp;lt;ion-content&amp;gt;. Here&apos;s a diff from the most complicated page in the app.
&lt;/p&gt;
&lt;pre class=&quot;brush: diff&quot;&gt;
-&amp;lt;div style=&quot;float: right&quot;&amp;gt;
-	&amp;lt;a href=&quot;#!/create&quot; class=&quot;btn btn-default&quot; ng-show=&quot;hasRole(&apos;ROLE_ADMIN&apos;)&quot;&amp;gt;Create&amp;lt;/a&amp;gt;
-&amp;lt;/div&amp;gt;
-&amp;lt;div class=&quot;page-header&quot;&amp;gt;
-	&amp;lt;h3&amp;gt;News&amp;lt;/h3&amp;gt;
-&amp;lt;/div&amp;gt;
+&amp;lt;ion-view title=&quot;News&quot;&amp;gt;
+    &amp;lt;ion-content&amp;gt;
+        &amp;lt;ion-nav-buttons side=&quot;left&quot;&amp;gt;
+            &amp;lt;div class=&quot;buttons&quot; ng-show=&quot;hasRole(&apos;ROLE_ADMIN&apos;)&quot;&amp;gt;
+                &amp;lt;button class=&quot;button button-icon icon ion-ios7-minus-outline&quot;
+                        ng-click=&quot;data.showDelete = !data.showDelete&quot;&amp;gt;&amp;lt;/button&amp;gt;
+            &amp;lt;/div&amp;gt;
+        &amp;lt;/ion-nav-buttons&amp;gt;
+        &amp;lt;ion-nav-buttons side=&quot;right&quot;&amp;gt;
+            &amp;lt;a href=&quot;#/create&quot; class=&quot;button button-icon icon ion-ios7-plus-outline&quot;
+               ng-show=&quot;hasRole(&apos;ROLE_ADMIN&apos;)&quot;&amp;gt;&amp;lt;/a&amp;gt;
+        &amp;lt;/ion-nav-buttons&amp;gt;

-&amp;lt;div ng-repeat=&quot;newsEntry in newsEntries&quot;&amp;gt;
-	&amp;lt;hr /&amp;gt;
-	&amp;lt;div class=&quot;pull-right&quot;&amp;gt;
-		&amp;lt;a ng-click=&quot;deleteEntry(newsEntry)&quot; class=&quot;btn btn-xs btn-default&quot; ng-show=&quot;hasRole(&apos;ROLE_ADMIN&apos;)&quot;&amp;gt;Remove&amp;lt;/a&amp;gt;
-		&amp;lt;a href=&quot;#!/edit/{{newsEntry.id}}&quot; class=&quot;btn btn-xs btn-default&quot; ng-show=&quot;hasRole(&apos;ROLE_ADMIN&apos;)&quot;&amp;gt;Edit&amp;lt;/a&amp;gt;
-	&amp;lt;/div&amp;gt;
-	&amp;lt;h4&amp;gt;{{newsEntry.date | date}}&amp;lt;/h4&amp;gt;
-	&amp;lt;p&amp;gt;{{newsEntry.content}}&amp;lt;/p&amp;gt;
-&amp;lt;/div&amp;gt;
-&amp;lt;hr /&amp;gt;
+        &amp;lt;ion-list show-delete=&quot;data.showDelete&quot; on-delete=&quot;deleteEntry(item)&quot;
+                  option-buttons=&quot;itemButtons&quot; can-swipe=&quot;hasRole(&apos;ROLE_ADMIN&apos;)&quot;&amp;gt;
+            &amp;lt;ion-item ng-repeat=&quot;newsEntry in newsEntries&quot; item=&quot;newsEntry&quot;&amp;gt;
+                &amp;lt;h4&amp;gt;{{newsEntry.date | date}}&amp;lt;/h4&amp;gt;
+                &amp;lt;p&amp;gt;{{newsEntry.content}}&amp;lt;/p&amp;gt;
+            &amp;lt;/ion-item&amp;gt;
+        &amp;lt;/ion-list&amp;gt;
+    &amp;lt;/ion-content&amp;gt;
+&amp;lt;/ion-view&amp;gt;
&lt;/pre&gt;
&lt;p&gt;

    I did migrate
    to use an &lt;a href=&quot;http://codepen.io/ionic/pen/JsHjf&quot;&gt;&amp;lt;ion-list&amp;gt; with delete/options buttons&lt;/a&gt;, so
some additional JavaScript changes were needed.
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
-function IndexController($scope, NewsService) {
+function IndexController($scope, $state, NewsService) {

    $scope.newsEntries = NewsService.query();

+     $scope.data = {
+         showDelete: false
+     };
+
    $scope.deleteEntry = function(newsEntry) {
         newsEntry.$remove(function() {
              $scope.newsEntries = NewsService.query();
         });
    };
+
+     $scope.itemButtons = [{
+         text: &apos;Edit&apos;,
+         type: &apos;button-assertive&apos;,
+         onTap: function (item) {
+              $state.go(&apos;edit&apos;, {id: item.id});
+         }
+     }];
}
&lt;/pre&gt;
&lt;h3&gt;Screenshots&lt;/h3&gt;
&lt;p&gt;After making all these changes, the app looks pretty good in Chrome.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;https://farm4.staticflickr.com/3733/13454844134_3bec9e6d75_b.jpg&quot;
   data-href=&quot;https://www.flickr.com/photos/mraible/13454844134&quot; title=&quot;Ionic Login&quot; rel=&quot;lightbox[ionic]&quot;&gt;
    &lt;img src=&quot;//farm4.staticflickr.com/3733/13454844134_3bec9e6d75_n.jpg&quot; width=&quot;244&quot; height=&quot;320&quot; alt=&quot;Ionic Login&quot;&gt;&lt;/a&gt;
&lt;a href=&quot;https://farm6.staticflickr.com/5545/13454844054_fec4b44a35_b.jpg&quot;
   data-href=&quot;https://www.flickr.com/photos/mraible/13454844054&quot; title=&quot;Ionic News&quot; rel=&quot;lightbox[ionic]&quot;&gt;
    &lt;img src=&quot;//farm6.staticflickr.com/5545/13454844054_fec4b44a35_n.jpg&quot; width=&quot;244&quot; height=&quot;320&quot; alt=&quot;Ionic News&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;https://farm4.staticflickr.com/3808/13454594373_3092b81b25_b.jpg&quot;
       data-href=&quot;https://www.flickr.com/photos/mraible/13454594373&quot; title=&quot;Ionic Swipe&quot; rel=&quot;lightbox[ionic]&quot;&gt;
        &lt;img src=&quot;//farm4.staticflickr.com/3808/13454594373_3092b81b25_n.jpg&quot; width=&quot;244&quot; height=&quot;320&quot; alt=&quot;Ionic Swipe&quot;&gt;&lt;/a&gt;
    &lt;a href=&quot;https://farm4.staticflickr.com/3769/13454594503_c5609d4000_b.jpg&quot;
       data-href=&quot;https://www.flickr.com/photos/mraible/13454594503&quot; title=&quot;Ionic Edit&quot; rel=&quot;lightbox[ionic]&quot;&gt;
        &lt;img src=&quot;//farm4.staticflickr.com/3769/13454594503_c5609d4000_n.jpg&quot; width=&quot;244&quot; height=&quot;320&quot; alt=&quot;Ionic Edit&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;h3&gt;Tips and Tricks&lt;/h3&gt;
&lt;p&gt;
    In additional to figuring out how to use Ionic, I discovered a few other tidbits along the way. First of all,
    we had a different default color for the header. Since Ionic uses generic color names (e.g. light, stable, positive, calm),
    I found it easy to change the default value for &quot;positive&quot; and then continue to use their class names.
&lt;/p&gt;
&lt;p&gt;
    &lt;strong&gt;Modifying CSS variable colors&lt;/strong&gt;&lt;br/&gt;
To modify the base color for &quot;positive&quot;, I &lt;a href=&quot;https://github.com/driftyco/ionic&quot;&gt;cloned the source&lt;/a&gt;, and
modified &lt;em&gt;scss/_variables.scss&lt;/em&gt;.
&lt;/p&gt;
&lt;pre class=&quot;brush: diff&quot;&gt;
$light: #fff !default;
$stable: #f8f8f8 !default;
-$positive: #4a87ee !default;
+$positive: #589199 !default;
$calm: #43cee6 !default;
$balanced: #66cc33 !default;
$energized: #f0b840 !default;
&lt;/pre&gt;
&lt;p&gt;After making this change, I ran &quot;grunt&quot; and copied &lt;em&gt;dist/css/ionic.css&lt;/em&gt; into our project.&lt;/p&gt; 
&lt;p&gt;
    &lt;strong&gt;iOS Native Integration&lt;/strong&gt;&lt;br/&gt;
    Our app uses a similar token-based authentication mechanism as x-auth-security, except its backed by Crowd.
    However, since users won&apos;t be logging directly into the Ionic app, we added
    the &quot;else&quot; clause in &lt;em&gt;app.js&lt;/em&gt; to allow a token to be passed in via URL. We also allowed the backend API
    path to be overridden.
&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
/* Try getting valid user from cookie or go to login page */
var originalPath = $location.path();
$location.path(&quot;/login&quot;);
var user = $cookieStore.get(&apos;user&apos;);

if (user !== undefined) {
    $rootScope.user = user;
    $http.defaults.headers.common[xAuthTokenHeaderName] = user.token;
    $location.path(originalPath);
} else {
    // token passed in from native app
    var authToken = $location.search().token;
    if (authToken) {
        $http.defaults.headers.common[&apos;X-Auth-Token&apos;] = authToken;
    }
}

// allow overriding the base API path
$rootScope.apiPath = &apos;/api/v1.0&apos;;
if ($location.search().apiPath) {
    $rootScope.apiPath = $location.search().apiPath;
}
&lt;/pre&gt;
&lt;p&gt;By adding this logic, the iOS app can pull up any particular page in a webview and let the Ionic app talk to the API. Here&apos;s
    what the Objective-C code looks like:
    &lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
NSString *versionNumber = @&quot;v1.0&quot;;
NSString *apiPath = @&quot;https://server.com/api/&quot;;
NSString *authToken = [TemporaryDataStore sharedInstance].authToken;
// webapp is a symbolic link to the Ionic app, created with Angular Seed
NSString *htmlFilePath = [[NSBundle mainBundle] pathForResource:@&quot;index&quot; ofType:@&quot;html&quot; inDirectory:@&quot;webapp/app&quot;];

// Note: We need to do it this way because &apos;fileURLWithPath:&apos; would encode the &apos;#&apos; to &apos;%23&quot; which breaks the html page
NSURL *htmlFileURL = [NSURL fileURLWithPath:htmlFilePath];

NSString *webappURLPath = [NSString stringWithFormat:@&quot;%@#/news?apiPath=%@%@&amp;token=%@&quot;,
                           htmlFileURL.absoluteString, apiPath, versionNumber, authToken];

// Now convert the string to a URL (doesn&apos;t seem to encode the &apos;#&apos; this way)
NSURL *webappURL = [NSURL URLWithString:webappURLPath];
[super updateWithURL:webappURL];
&lt;/pre&gt;
&lt;p&gt;We also had to write some logic to navigate back to the native app. We used a &lt;a href=&quot;http://www.idev101.com/code/Objective-C/custom_url_schemes.html&quot;&gt;
    custom URL scheme&lt;/a&gt; to do this, and the Ionic app simply called it. To override the default back button, I added
    an &quot;ng-controller&quot; attribute to &amp;lt;ion-nav-bar&amp;gt; and added a custom back button.

&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;ion-nav-bar class=&quot;bar-positive nav-title-slide-ios7&quot; ng-controller=&quot;NavController&quot;&amp;gt;
    &amp;lt;ion-nav-back-button class=&quot;button-icon&quot; ng-click=&quot;goBack()&quot;&amp;gt;
        &amp;lt;i class=&quot;ion-arrow-left-c&quot;&amp;gt;&amp;lt;/i&amp;gt;
    &amp;lt;/ion-nav-back-button&amp;gt;
&amp;lt;/ion-nav-bar&amp;gt;
&lt;/pre&gt;
&lt;p&gt;To detect if the app was loaded by iOS (vs. a browser, which we tested in), we used the following logic:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
// set native app indicator
if (document.location.toString().indexOf(&apos;appName.app&apos;) &gt; -1) {
    $rootScope.isNative = true;
}
&lt;/pre&gt;
&lt;p&gt;Our Ionic app has three entry points, defined by &quot;stateName1&quot;, &quot;stateName2&quot; and &quot;stateName3&quot; in this example. The code for our &lt;code&gt;NavController&lt;/code&gt; handles navigating back normally (when in a browser) or back to the native app. The &quot;appName&quot; reference below is a 3-letter acronym we used for our app.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
.controller(&apos;NavController&apos;, function($scope, $ionicNavBarDelegate, $state) {
    $scope.goBack = function() {
        if ($scope.isNative &amp;amp;&amp;amp; backToNative($state)) {
            location.href=&apos;appName-ios://back&apos;;
        } else {
            $ionicNavBarDelegate.back();
        }
    };

    function backToNative($state) {
        var entryPoints = &amp;#91;&apos;stateName1&apos;, &apos;stateName2&apos;, &apos;stateName3&apos;&amp;#93;;
        return entryPoints.some(function (entry) {
            return $state.current === $state.get(entry);
        });
    }
})
&lt;/pre&gt;
&lt;h3&gt;Summary&lt;/h3&gt;
&lt;p&gt;

    I&apos;ve enjoyed working with Ionic over the last month. The biggest change I&apos;ve had to make to our AngularJS app has been
    to integrate &lt;a href=&quot;https://github.com/angular-ui/ui-router&quot;&gt;ui-router&lt;/a&gt;. Apart from this, the JavaScript didn&apos;t
    change much. However, the HTML had to change quite a bit. As far as CSS is concerned, I found myself tweaking things
    to fit our designs, but less so than I did with Bootstrap.     When I&apos;ve run into issues with Ionic, the community has been very helpful on their
    &lt;a href=&quot;http://forum.ionicframework.com/&quot;&gt;forum&lt;/a&gt;. It&apos;s the first forum I&apos;ve used that&apos;s powered by
    &lt;a href=&quot;http://www.discourse.org/&quot;&gt;Discourse&lt;/a&gt;, and I dig it.&lt;/p&gt;
&lt;p&gt;You can find the source from this article in my &lt;a href=&quot;https://github.com/mraible/boot-ionic&quot;&gt;boot-ionic project&lt;/a&gt;. Clone it and run &quot;mvn spring-boot:run&quot;, then open &lt;a href=&quot;http://localhost:8080&quot;&gt;http://localhost:8080&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;If you&apos;re looking to create a native app using HTML5 technologies, I highly recommend you take a look at Ionic.
    We&apos;re glad we did.
    &lt;a href=&quot;http://www.infoq.com/news/2014/03/angular-2-0&quot;&gt;Angular 2.0 will target mobile apps&lt;/a&gt; and Ionic is already
    making them look pretty damn good.
&lt;/p&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/using_grunt_with_angularjs_for</id>
        <title type="html">Using Grunt with AngularJS for Front End Optimization</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/using_grunt_with_angularjs_for"/>
        <published>2014-01-15T12:15:52-07:00</published>
        <updated>2014-01-15T22:11:46-07:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="angularjs" scheme="http://roller.apache.org/ns/tags/" />
        <category term="grunt" scheme="http://roller.apache.org/ns/tags/" />
        <category term="pagespeed" scheme="http://roller.apache.org/ns/tags/" />
        <category term="yslow" scheme="http://roller.apache.org/ns/tags/" />
        <summary type="html">&lt;p&gt;I&apos;m passionate about front end optimization and have been for years. My original inspiration was Steve Souders and his &lt;a href=&quot;http://raibledesigns.com/rd/entry/oscon_2008_even_faster_web&quot;&gt;Even Faster Web Sites talk at OSCON 2008&lt;/a&gt;. Since then, I&apos;ve &lt;a href=&quot;http://raibledesigns.com/rd/entry/javascript_and_css_concatenation&quot;&gt;optimized this blog&lt;/a&gt;, made it even faster &lt;a href=&quot;http://raibledesigns.com/rd/entry/new_look_and_feel_designed&quot;&gt;with a new design&lt;/a&gt;, doubled the speed of several apps for clients and showed how to &lt;a href=&quot;http://raibledesigns.com/rd/entry/improving_appfuse_s_pagespeed_with&quot;&gt;make AppFuse faster&lt;/a&gt;. As part of my &lt;a href=&quot;http://raibledesigns.com/rd/entry/devoxx_2013_a_nordic_countries&quot;&gt;Devoxx 2013 presentation&lt;/a&gt;, I showed &lt;a href=&quot;https://vimeo.com/mraible/page-speed-demo&quot;&gt;how to do page speed optimization in a Java webapp&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I developed a couple AngularJS apps last year. To concat and minify their stylesheets and scripts, I used mechanisms that already existed in the projects. On one project, it was Ant and its &lt;a href=&quot;https://ant.apache.org/manual/Tasks/concat.html&quot;&gt;concat task&lt;/a&gt;. On the other, it was part of a Grails application, so I used the &lt;a href=&quot;http://grails.org/plugin/resources&quot;&gt;resources&lt;/a&gt; and &lt;a href=&quot;http://grails.org/plugin/yui-minify-resources&quot;&gt;yui-minify-resources&lt;/a&gt; plugins.
&lt;/p&gt;
&lt;p&gt;The Angular project I&apos;m working on now will be published on a web server, as well as bundled in an iOS native app. Therefore, I turned to &lt;a href=&quot;http://gruntjs.com/&quot;&gt;Grunt&lt;/a&gt; to do the optimization this time. I found it to be quite simple, once I figured out &lt;a href=&quot;http://stackoverflow.com/questions/21056767/angular-and-grunt&quot;&gt;how to make it work with Angular&lt;/a&gt;. Based on my findings, I submitted a &lt;a href=&quot;https://github.com/angular/angular-seed/pull/131&quot;&gt;pull request to add Grunt to angular-seed&lt;/a&gt;. 
&lt;/p&gt;
&lt;p&gt;Below are the steps I used to add Grunt to my Angular project.&lt;/p&gt;</summary>
        <content type="html">&lt;p&gt;I&apos;m passionate about front end optimization and have been for years. My original inspiration was Steve Souders and his &lt;a href=&quot;http://raibledesigns.com/rd/entry/oscon_2008_even_faster_web&quot;&gt;Even Faster Web Sites talk at OSCON 2008&lt;/a&gt;. Since then, I&apos;ve &lt;a href=&quot;http://raibledesigns.com/rd/entry/javascript_and_css_concatenation&quot;&gt;optimized this blog&lt;/a&gt;, made it even faster &lt;a href=&quot;http://raibledesigns.com/rd/entry/new_look_and_feel_designed&quot;&gt;with a new design&lt;/a&gt;, doubled the speed of several apps for clients and showed how to &lt;a href=&quot;http://raibledesigns.com/rd/entry/improving_appfuse_s_pagespeed_with&quot;&gt;make AppFuse faster&lt;/a&gt;. As part of my &lt;a href=&quot;http://raibledesigns.com/rd/entry/devoxx_2013_a_nordic_countries&quot;&gt;Devoxx 2013 presentation&lt;/a&gt;, I showed &lt;a href=&quot;https://vimeo.com/mraible/page-speed-demo&quot;&gt;how to do page speed optimization in a Java webapp&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I developed a couple AngularJS apps last year. To concat and minify their stylesheets and scripts, I used mechanisms that already existed in the projects. On one project, it was Ant and its &lt;a href=&quot;https://ant.apache.org/manual/Tasks/concat.html&quot;&gt;concat task&lt;/a&gt;. On the other, it was part of a Grails application, so I used the &lt;a href=&quot;http://grails.org/plugin/resources&quot;&gt;resources&lt;/a&gt; and &lt;a href=&quot;http://grails.org/plugin/yui-minify-resources&quot;&gt;yui-minify-resources&lt;/a&gt; plugins.
&lt;/p&gt;
&lt;p&gt;The Angular project I&apos;m working on now will be published on a web server, as well as bundled in an iOS native app. Therefore, I turned to &lt;a href=&quot;http://gruntjs.com/&quot;&gt;Grunt&lt;/a&gt; to do the optimization this time. I found it to be quite simple, once I figured out &lt;a href=&quot;http://stackoverflow.com/questions/21056767/angular-and-grunt&quot;&gt;how to make it work with Angular&lt;/a&gt;. Based on my findings, I submitted a &lt;a href=&quot;https://github.com/angular/angular-seed/pull/131&quot;&gt;pull request to add Grunt to angular-seed&lt;/a&gt;. 
&lt;/p&gt;
&lt;p&gt;Below are the steps I used to add Grunt to my Angular project.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Install Grunt&apos;s command line interface with &quot;sudo npm install -g grunt-cli&quot;.&lt;/li&gt;
&lt;li&gt;Edit package.json to include a version number (e.g. &quot;version&quot;: &quot;1.0.0&quot;).&lt;/li&gt;
&lt;li&gt;Add Grunt plugins in package.json to do concat/minify/asset versioning:
&lt;pre class=&quot;brush: js&quot;&gt;
    &quot;grunt&quot;: &quot;~0.4.1&quot;,
    &quot;grunt-contrib-concat&quot;: &quot;~0.3.0&quot;,
    &quot;grunt-contrib-uglify&quot;: &quot;~0.2.7&quot;,
    &quot;grunt-contrib-cssmin&quot;: &quot;~0.7.0&quot;,
    &quot;grunt-usemin&quot;: &quot;~2.0.2&quot;,
    &quot;grunt-contrib-copy&quot;: &quot;~0.5.0&quot;,
    &quot;grunt-rev&quot;: &quot;~0.1.0&quot;,
    &quot;grunt-contrib-clean&quot;: &quot;~0.5.0&quot;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Run &quot;sudo npm install&quot; to install the project&apos;s dependencies.&lt;/li&gt;
&lt;li&gt;Create a &lt;code&gt;Gruntfile.js&lt;/code&gt; that runs all the plugins.
&lt;pre class=&quot;brush: js&quot;&gt;
module.exports = function (grunt) {

    grunt.initConfig({
        pkg: grunt.file.readJSON(&apos;package.json&apos;),

        clean: [&quot;dist&quot;, &apos;.tmp&apos;],

        copy: {
            main: {
                expand: true,
                cwd: &apos;app/&apos;,
                src: [&apos;**&apos;, &apos;!js/**&apos;, &apos;!lib/**&apos;, &apos;!**/*.css&apos;],
                dest: &apos;dist/&apos;
            },
            shims: {
                expand: true,
                cwd: &apos;app/lib/webshim/shims&apos;,
                src: [&apos;**&apos;],
                dest: &apos;dist/js/shims&apos;
            }
        },

        rev: {
            files: {
                src: [&apos;dist/**/*.{js,css}&apos;, &apos;!dist/js/shims/**&apos;]
            }
        },

        useminPrepare: {
            html: &apos;app/index.html&apos;
        },

        usemin: {
            html: [&apos;dist/index.html&apos;]
        },

        uglify: {
            options: {
                report: &apos;min&apos;,
                mangle: false
            }
        }
    });

    grunt.loadNpmTasks(&apos;grunt-contrib-clean&apos;);
    grunt.loadNpmTasks(&apos;grunt-contrib-copy&apos;);
    grunt.loadNpmTasks(&apos;grunt-contrib-concat&apos;);
    grunt.loadNpmTasks(&apos;grunt-contrib-cssmin&apos;);
    grunt.loadNpmTasks(&apos;grunt-contrib-uglify&apos;);
    grunt.loadNpmTasks(&apos;grunt-rev&apos;);
    grunt.loadNpmTasks(&apos;grunt-usemin&apos;);

    // Tell Grunt what to do when we type &quot;grunt&quot; into the terminal
    grunt.registerTask(&apos;default&apos;, [
        &apos;copy&apos;, &apos;useminPrepare&apos;, &apos;concat&apos;, &apos;uglify&apos;, &apos;cssmin&apos;, &apos;rev&apos;, &apos;usemin&apos;
    ]);
};
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Add comments to app/index.html so usemin knows what files to process. The comments are the important part, your files will likely be different.
&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;!-- build:css css/app-name.min.css --&amp;gt;
&amp;lt;link rel=&quot;stylesheet&quot; href=&quot;lib/bootstrap/bootstrap.min.css&quot;/&amp;gt;
&amp;lt;link rel=&quot;stylesheet&quot; href=&quot;lib/font-awesome/font-awesome.min.css&quot;/&amp;gt;
&amp;lt;link rel=&quot;stylesheet&quot; href=&quot;lib/toaster/toaster.css&quot;/&amp;gt;
&amp;lt;link rel=&quot;stylesheet&quot; href=&quot;css/app.css&quot;/&amp;gt;
&amp;lt;link rel=&quot;stylesheet&quot; href=&quot;css/custom.css&quot;/&amp;gt;
&amp;lt;link rel=&quot;stylesheet&quot; href=&quot;css/responsive.css&quot;/&amp;gt;
&amp;lt;!-- endbuild --&amp;gt;
...

&amp;lt;!-- build:js js/app-name.min.js --&amp;gt;
&amp;lt;script src=&quot;lib/jquery/jquery-1.10.2.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;lib/bootstrap/bootstrap.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;lib/angular/angular.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;lib/angular/angular-animate.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;lib/angular/angular-cookies.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;lib/angular/angular-resource.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;lib/angular/angular-route.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;lib/fastclick.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;lib/toaster/toaster.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;lib/webshim/modernizr.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;lib/webshim/polyfiller.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;js/app.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;js/services.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;js/controllers.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;js/filters.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;js/directives.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;!-- endbuild --&amp;gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;A couple of things to note: 1) the &lt;em&gt;copy&lt;/em&gt; task copies the &quot;shims&quot; directory from &lt;a href=&quot;http://afarkas.github.io/webshim/demos/&quot;&gt;Webshims lib&lt;/a&gt; because it loads files dynamically and 2) setting &quot;mangle: false&quot; on the &lt;em&gt;uglify&lt;/em&gt; task is necessary for Angular&apos;s dependency injection to work. I tried to use &lt;a href=&quot;https://npmjs.org/package/grunt-ngmin&quot;&gt;grunt-ngmin&lt;/a&gt; with uglify and had no luck.&lt;/p&gt;
&lt;p&gt;After making these changes, I&apos;m able to run &quot;grunt&quot; and get an optimized version of my app in the &quot;dist&quot; folder of my project. For development, I continue to run the app from my &quot;app&quot; folder, so I don&apos;t currently have a need for watching and processing assets on-the-fly. That could change if I start using LESS or CoffeeScript.
&lt;/p&gt;
&lt;p&gt;The results speak for themselves: from 27 requests to 5 on initial load, and only 3 requests for less than 2K after that.&lt;/p&gt;
&lt;table class=&quot;comparison&quot; style=&quot;max-width: 600px&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;YSlow&lt;/th&gt;
&lt;th&gt;Page Speed&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;No optimization&lt;/td&gt;
&lt;td&gt;75 &lt;div style=&quot;float: right&quot;&gt;27 HTTP requests / 464K&lt;/div&gt;&lt;/td&gt;
&lt;td&gt;55/100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apache optimization (gzip and expires headers)&lt;/td&gt;
&lt;td&gt;89
&lt;div style=&quot;float: right&quot;&gt;
initial load: 26 requests / 166K&lt;br/&gt;
primed cache: 4 requests / 40K 
&lt;/div&gt;
&lt;/td&gt;
&lt;td&gt;88/100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apache + concat/minified/versioned files&lt;/td&gt;
&lt;td&gt;98
&lt;div style=&quot;float: right&quot;&gt;
initial load: 5 requests / 136K&lt;br/&gt;
primed cache: 3 requests / 1.4K
&lt;/div&gt;
&lt;/td&gt;
&lt;td&gt;93/100&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; Andreas Andreou has a &lt;a href=&quot;https://twitter.com/andyhot/status/423571136538877952&quot;&gt;nice tip&lt;/a&gt; on how to reduce the LOC in this example.
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Add &quot;matchdep&quot; as a dependency in package.json (or run &quot;sudo npm install matchdep --save-dev&quot;).
&lt;pre class=&quot;brush: js; gutter: false&quot;&gt;
&quot;matchdep&quot;: &quot;~0.3.0&quot;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Replace all the &lt;code&gt;grunt.loadNpmTasks(...)&lt;/code&gt; calls with the following:
&lt;pre class=&quot;brush: js; gutter: false&quot;&gt;
require(&apos;matchdep&apos;).filterDev(&apos;grunt-*&apos;).forEach(grunt.loadNpmTasks);
&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Thanks Andreas!&lt;/p&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/developing_with_angularjs_part_iv</id>
        <title type="html">Developing with AngularJS - Part IV: Making it Pop</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/developing_with_angularjs_part_iv"/>
        <published>2013-09-12T10:54:29-06:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="design" scheme="http://roller.apache.org/ns/tags/" />
        <category term="css3" scheme="http://roller.apache.org/ns/tags/" />
        <category term="responsivedesign" scheme="http://roller.apache.org/ns/tags/" />
        <category term="javascript" scheme="http://roller.apache.org/ns/tags/" />
        <category term="taleo" scheme="http://roller.apache.org/ns/tags/" />
        <category term="angularjs" scheme="http://roller.apache.org/ns/tags/" />
        <summary type="html">&lt;p&gt;
Welcome to the final article in a series on my experience developing with &lt;a href=&quot;http://angularjs.org/&quot;&gt;AngularJS&lt;/a&gt;. I learned its concepts, beat my head against-the-wall with and finally tamed it enough to create a &quot;My Dashboard&quot; feature for a client. For previous articles, please see the following:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/developing_with_angularjs_part_i&quot;&gt;Part I: The Basics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/developing_with_angularjs_part_ii&quot;&gt;Part II: Dialogs and Data&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/developing_with_angularjs_part_iii&quot;&gt;Part III: Services&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The last mile of development for the My Dashboard feature was to spice things up a bit and make it look better. We hired a design company to come up a new look and feel and they went to work. Within a week, we had a meeting with them and they presented a few different options. We picked the one we liked the best and went to work. Below are screenshots that I used to implement the new design.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;

&lt;a data-href=&quot;http://www.flickr.com/photos/mraible/8904971086/&quot; href=&quot;http://farm6.staticflickr.com/5447/8904971086_e24b89fa7e_c.jpg&quot; title=&quot;My Dashboard - New Design&quot; rel=&quot;lightbox[makingitpop]&quot;&gt;&lt;img src=&quot;http://farm6.staticflickr.com/5447/8904971086_e24b89fa7e_m.jpg&quot; width=&quot;240&quot; height=&quot;229&quot; alt=&quot;My Dashboard - New Design&quot; style=&quot;border: 1px solid silver&quot;&gt;&lt;/a&gt;

&lt;a data-href=&quot;http://www.flickr.com/photos/mraible/8904971332/&quot; href=&quot;http://farm3.staticflickr.com/2866/8904971332_9e919549b8_c.jpg&quot; title=&quot;My Dashboard with Show More&quot; rel=&quot;lightbox[makingitpop]&quot;&gt;&lt;img src=&quot;http://farm3.staticflickr.com/2866/8904971332_9e919549b8_m.jpg&quot; width=&quot;240&quot; height=&quot;229&quot; alt=&quot;My Dashboard with Show More&quot; style=&quot;border: 1px solid silver; margin-left: 20px&quot;&gt;&lt;/a&gt;

&lt;/p&gt;</summary>
        <content type="html">&lt;p&gt;
Welcome to the final article in a series on my experience developing with &lt;a href=&quot;http://angularjs.org/&quot;&gt;AngularJS&lt;/a&gt;. I learned its concepts, beat my head against-the-wall with and finally tamed it enough to create a &quot;My Dashboard&quot; feature for a client. For previous articles, please see the following:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/developing_with_angularjs_part_i&quot;&gt;Part I: The Basics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/developing_with_angularjs_part_ii&quot;&gt;Part II: Dialogs and Data&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/developing_with_angularjs_part_iii&quot;&gt;Part III: Services&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The last mile of development for the My Dashboard feature was to spice things up a bit and make it look better. We hired a design company to come up a new look and feel and they went to work. Within a week, we had a meeting with them and they presented a few different options. We picked the one we liked the best and went to work. Below are screenshots that I used to implement the new design.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;

&lt;a data-href=&quot;http://www.flickr.com/photos/mraible/8904971086/&quot; href=&quot;http://farm6.staticflickr.com/5447/8904971086_e24b89fa7e_c.jpg&quot; title=&quot;My Dashboard - New Design&quot; rel=&quot;lightbox[makingitpop]&quot;&gt;&lt;img src=&quot;//farm6.staticflickr.com/5447/8904971086_e24b89fa7e_m.jpg&quot; width=&quot;240&quot; height=&quot;229&quot; alt=&quot;My Dashboard - New Design&quot; style=&quot;border: 1px solid silver&quot;&gt;&lt;/a&gt;

&lt;a data-href=&quot;http://www.flickr.com/photos/mraible/8904971332/&quot; href=&quot;http://farm3.staticflickr.com/2866/8904971332_9e919549b8_c.jpg&quot; title=&quot;My Dashboard with Show More&quot; rel=&quot;lightbox[makingitpop]&quot;&gt;&lt;img src=&quot;//farm3.staticflickr.com/2866/8904971332_9e919549b8_m.jpg&quot; width=&quot;240&quot; height=&quot;229&quot; alt=&quot;My Dashboard with Show More&quot; style=&quot;border: 1px solid silver; margin-left: 20px&quot;&gt;&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;At first, I thought implementing this design might take quite a bit of effort, since it looked like it used custom fonts. It&apos;s true we could use CSS3&apos;s &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face&quot;&gt;@font-face&lt;/a&gt;, but I knew it might take awhile to find the right fonts with the appropriate licenses. When I received the screenshot below, I was pleased to see that all fonts were web-safe.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a data-href=&quot;http://www.flickr.com/photos/mraible/8904971378/&quot; href=&quot;http://farm3.staticflickr.com/2839/8904971378_a0ac267cc0_c.jpg&quot; title=&quot;My Dashboard Fonts&quot; rel=&quot;lightbox[makingitpop]&quot;&gt;&lt;img src=&quot;//farm3.staticflickr.com/2839/8904971378_a0ac267cc0.jpg&quot; width=&quot;500&quot; height=&quot;430&quot; alt=&quot;My Dashboard Fonts&quot; style=&quot;border: 1px solid silver&quot;&gt;&lt;/a&gt;

&lt;/p&gt;
&lt;h3 id=&quot;design-elements&quot;&gt;Design Elements&lt;/h3&gt;
&lt;p&gt;There are a number of elements in this new design that I had to create. For example, if numbers were only 1 digit, we had to add a leading zero to them in the summary band. Other design elements we needed to implement are listed below:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;A background image that filled the page&lt;/li&gt;&lt;li&gt;Fade to white on summary widget titles&lt;/li&gt;&lt;li&gt;A responsive grid for summary widgets&lt;/li&gt;&lt;li&gt;Provide a colored background for odd rows in the summary grid&lt;/li&gt;&lt;li&gt;Add a &quot;Show More&quot; band at the bottom of Tasks, Summary and Reports when there&apos;s more items to display&lt;/li&gt;&lt;/ul&gt;
&lt;p class=&quot;nolink&quot;&gt;In addition to these elements, there was quite a bit of work to conform to the new colors, fonts and drop-shadows. I implemented all of these using CSS3 (border-radius, box-shadow, box-sizing, linear-gradient), and lots of trial-and-error. To use the best fonts across various devices, I used CSS-Trick&apos;s &lt;a href=&quot;http://css-tricks.com/snippets/css/font-stacks/&quot;&gt;Font Stacks&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;new-background&quot;&gt;New Background&lt;/h3&gt;
&lt;p&gt;The new background shown in the screenshots above has a light source in the middle of it. Therefore, it&apos;s impossible to tile/repeat it across the page since it&apos;s not uniform. To make it work, I used a 1024 x 768 image and CSS3&apos;s &lt;code&gt;background-size: cover&lt;/code&gt;. For more information on background-size, see SitePoint&apos;s &lt;a href=&quot;http://www.sitepoint.com/css3-background-size-property/&quot;&gt;How to Resize Background Images with CSS3&lt;/a&gt;. This worked great on smaller screens, but we noticed some issues on 30&quot; monitors. Therefore, we ended up getting a new repeatable background and stopped using background-size.&lt;/p&gt;
&lt;h3 id=&quot;leadingzero-filter&quot;&gt;LeadingZero filter&lt;/h3&gt;
&lt;p&gt;For the first leading zero feature, I wrote an Angular filter. I put the code for this in &lt;em&gt;filters.js&lt;/em&gt;:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
filter(&apos;leadingZero&apos;, function() {
    return function(input) {
        if (input.length === 1) {
            return &quot;0&quot; + input;
        } else if (input.length &amp;gt; 2) {
            return &quot;+99&quot;;
        } else {
            return input;
        }
    }
});
&lt;/pre&gt;
&lt;p&gt;This filter is used in the HTML template as follows:&lt;/p&gt;
&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;div class=&quot;summary-value&quot;&amp;gt;{{widget.value | leadingZero}}&amp;lt;/div&amp;gt;
&lt;/pre&gt;
&lt;h3 id=&quot;text-fadeout&quot;&gt;Text Fade Out&lt;/h3&gt;
&lt;p&gt;To implement the fade-to-white text in summary titles, I started with &lt;a href=&quot;http://css-tricks.com/text-fade-read-more/&quot;&gt;this tutorial&lt;/a&gt;. I quickly discovered that it worked best for vertical text blocks and not for horizontal text. Then I found &lt;a href=&quot;http://xion.org.pl/2011/12/26/text-ellipsis-with-gradient-fade-in-pure-css/&quot;&gt;Text Ellipsis with Gradient Fade in Pure CSS&lt;/a&gt;, which uses :after to position a block over the text that fades to white. Since the title is not the right-most element (the numbers are), I had to figure out best positioning that worked cross-browser. Below is the CSS I used to implement this feature.&lt;/p&gt;
&lt;pre class=&quot;brush: css&quot;&gt;
.dashboard .summary-title:after {
    display: block;
    position: absolute;
    right: 66px;
    top: 5px;
    bottom: 5px;
    width: 30px;
    background: -moz-linear-gradient(left,  rgba(255,255,255,0) 0%, #fff 20px); /* FF3.6+ */
    background: -webkit-linear-gradient(left,  rgba(255,255,255,0) 0%, #fff 20px); /* Chrome10+,Safari5.1+ */
    background: -o-linear-gradient(left,  rgba(255,255,255,0) 0%, #fff 20px); /* Opera 11.10+ */
    background: -ms-linear-gradient(left,  rgba(255,255,255,0) 0%, #fff 20px); /* IE10+ */
    background: linear-gradient(to right,  rgba(255,255,255,0) 0%, #fff 20px); /* W3C */
    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr=&apos;#00ffffff&apos;, endColorstr=&apos;#ffffff&apos;,GradientType=1 ); /* IE6-9 */
    content: &quot;&quot;;
}
&lt;/pre&gt;
&lt;h3 id=&quot;responsive-grid&quot;&gt;Responsive Grid&lt;/h3&gt;
&lt;p&gt;To implement the responsive grid of summary widgets, I started with Codrops&apos; &lt;a href=&quot;http://tympanus.net/codrops/2013/04/17/responsive-full-width-grid/&quot;&gt;Responsive Full Width Grid Tutorial&lt;/a&gt;. This proved to be a great model and I used the following CSS to position all the &amp;lt;li&amp;gt;&apos;s appropriately. In the code below, &lt;code&gt;.summary-item&lt;/code&gt; is the class on the &amp;lt;li&amp;gt; elements.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
.dashboard .summary-item {
    border-right: 1px solid #d1d1d1;
    border-bottom: 1px solid #d1d1d1;
    /* put the border on the inside of the box */
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    -ms-box-sizing: border-box;
    box-sizing: border-box;
    font-family: Constantia, &quot;Lucida Bright&quot;, Lucidabright, &quot;Lucida Serif&quot;, Lucida, &quot;DejaVu Serif&quot;, &quot;Bitstream Vera Serif&quot;, &quot;Liberation Serif&quot;, Georgia, serif;
    font-size: 14px;
    color: #666;
    height: 50px;
    box-shadow: inset 0 0 6px rgba(0,0,0, 0.25);
    /* responsive grid */
    position: relative;
    float: left;
    overflow: hidden;
    width: 25% /* Fallback */
    width: -webkit-calc(100% / 4);
    width: calc(100% / 4);
}

@media screen and (max-width: 1400px) {
    .dashboard .summary-item {
        width: 33.33333333333333%; /* Fallback */
        width: -webkit-calc(100% / 3);
        width: calc(100% / 3);
    }
}

@media screen and (max-width: 1000px) {
    .dashboard .summary-item {
        width: 50%; /* Fallback */
        width: -webkit-calc(100% / 2);
        width: calc(100% / 2);
    }
}
&lt;/pre&gt;
&lt;p&gt;This worked great in most browsers, but we did find an issue with IE9. When squishing/expanding the browser window, sometimes there would be a blank column on the right side. To fix this, I changed the width on the default &lt;code&gt;.summary-item&lt;/code&gt; to be 25%, and removed the lines with &lt;code&gt;calc&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;brush: css&quot;&gt;
.dashboard .summary-item {
    ...
    width: 25%
}
&lt;/pre&gt;
&lt;h3 id=&quot;coloring-oddrows&quot;&gt;Coloring Odd Rows&lt;/h3&gt;
&lt;p&gt;Coloring odd rows in a table is easy, but when the rows are in a responsive grid, that&apos;s a whole different story. For tables, the CSS rules are extremely simple:&lt;/p&gt;
&lt;pre class=&quot;brush: css&quot;&gt;
tr:nth-child(even) {background: #CCC}
tr:nth-child(odd) {background: #FFF}
&lt;/pre&gt;
&lt;p&gt;Via Twitter, &lt;a href=&quot;http://twitter.com/tomaslin&quot;&gt;@tomaslin&lt;/a&gt; advised me that the nth-child selector could probably be used for this, but it&apos;d likely require some JavaScript to make it responsive. I found the excellent &lt;a href=&quot;http://nthmaster.com/&quot;&gt;Master of the :nth-child&lt;/a&gt; and began trying to figure it out. The following function is what we now use to color odd rows in the Summary Bar.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
function colorRows() {
    var lisInRow = 0;
    var items = $(&apos;.summary-items li&apos;);
    items.each(function() {
        if($(this).prev().length &amp;gt; 0) {
            if($(this).position().top != $(this).prev().position().top) return false;
            lisInRow++;
        }
        else {
            lisInRow++;
        }
    });
    var rows = items.length / lisInRow;
    for (var i = 0; i &amp;lt; rows; i++) {
        var selector = &quot;nth-child(n+{x}):nth-child(-n+{y})&quot;;
        var x = (lisInRow * i) + 1;
        var y = x + (lisInRow - 1);
        selector = selector.replace(&apos;{x}&apos;, &apos;&apos; + x);
        selector = selector.replace(&apos;{y}&apos;, &apos;&apos; + y);
        if (i % 2) {
            $(&apos;.summary-items li:&apos; + selector).addClass(&apos;odd&apos;);
        } else {
            $(&apos;.summary-items li:&apos; + selector).removeClass(&apos;odd&apos;);
        }
    }
}
&lt;/pre&gt;
&lt;p&gt;The above code is in &lt;em&gt;dashboard.js&lt;/em&gt; and is called anytime the browser window is resized (to adapt to the responsive grid).&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
$(window).resize(colorRows);
&lt;/pre&gt;
&lt;p&gt;It&apos;s also called when summary widgets are re-ordered, in the &lt;code&gt;updateOrder()&lt;/code&gt; function of &lt;code&gt;WidgetController&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
$scope.updateOrder = function(event, ui) {
    ...
    Preferences.saveWidgetOrder(type, {items: items});
    if (type === &apos;summary&apos;) {
        colorRows();
    }
};
&lt;/pre&gt;
&lt;p&gt;I&apos;d like to figure out how to make this more Angular-esque, but all the &quot;how to hook into window.resize&quot; articles I found make it seem harder than this.&lt;/p&gt;
&lt;h3 id=&quot;show-more&quot;&gt;Show More&lt;/h3&gt;
&lt;p&gt;The last feature I had to implement was the &quot;Show More&quot; bar that appears when widgets are hidden. This was the most difficult thing to implement and I tried many different things before arriving at a solution that works. First of all, the widgets bars that can be expanded are put into their original (collapsed) state using &lt;code&gt;max-height&lt;/code&gt; and &lt;code&gt;overflow: hidden&lt;/code&gt;. From there, I look at the list inside the bar and compare the height&apos;s of the two elements. If the list is taller than the bar, the Show More bar is added.&lt;/p&gt;
&lt;div class=&quot;alert alert-info&quot;&gt;
I originally looked at the list&apos;s &lt;code&gt;:last-child&lt;/code&gt; to see if it was visible, but jQuery&apos;s &lt;a href=&quot;http://api.jquery.com/hidden-selector/&quot;&gt;:hidden selector&lt;/a&gt; only works on items that are hidden by &lt;code&gt;display: none&lt;/code&gt; rather than ones that are hidden by &lt;code&gt;overflow&lt;/code&gt;.
&lt;/div&gt;
&lt;p&gt;As you can see from the code below, there&apos;s special logic needed to expand the min-height of the Summary Bar since it doesn&apos;t have enough room at the bottom to add the bar in its collapsed state.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
function showMore(element) {
    var bar = element.parent().parent();
    var list = element.parent();
    var barId = bar.attr(&apos;id&apos;);
    var listHeight = list.height();
    var barHeight = bar.height();
    var isSummaryBar = (barId.indexOf(&apos;summary&apos;) &amp;gt; -1);
    var summaryBarMinHeight = 260;
    var showMoreShouldBeVisible = (isSummaryBar &amp;amp;&amp;amp; element.position().top &amp;gt;= 200) ? true : listHeight &amp;gt; barHeight;
    if (showMoreShouldBeVisible) {
        var messages = {};
        // the variables below are defined in the host page, before this file is loaded
        messages.more = showMoreText;
        messages.less = showLessText;

        var showMore = $(&apos;&amp;lt;div class=&quot;show-more&quot;/&amp;gt;&apos;).html(messages.more + &quot; &amp;lt;b class=&apos;caret&apos;&amp;gt;&amp;lt;/b&amp;gt;&quot;);
        showMore.appendTo(bar);
        // summary bar doesn&apos;t have enough room for the Show More bar in its collapsed state,
        // so change it from 242 to 260
        if (isSummaryBar) {
            bar.css({&apos;min-height&apos;: summaryBarMinHeight + &apos;px&apos;, &apos;max-height&apos;: &apos;&apos;});
        }

        showMore.bind(&apos;click&apos;, function (e) {
            var element = $(this);
            var parent = element.parent();

            if (element.hasClass(&apos;less&apos;)) {
                parent.css({&quot;max-height&quot;: &apos;&apos;});
                if (isSummaryBar) {
                    parent.css({&quot;min-height&quot;: summaryBarMinHeight + &apos;px&apos;}).animate(200);
                } else {
                    parent.css({&quot;min-height&quot;: &apos;&apos;}).animate(200);
                }
                element.removeClass(&apos;less&apos;);
                element.html(messages.more + &apos; &amp;lt;b class=&quot;caret&quot;&amp;gt;&amp;lt;/b&amp;gt;&apos;);
            } else {
                parent.css({
                    &quot;max-height&quot;: 9999,
                    &quot;min-height&quot;: &apos;auto&apos;
                }).animate({
                        &quot;min-height&quot;: parent.height() + 19
                    }, 200);

                element.addClass(&apos;less&apos;);
                element.html(messages.less + &apos; &amp;lt;b class=&quot;caret caret-up&quot;&amp;gt;&amp;lt;/b&amp;gt;&apos;);
            }

            // prevent jump-down
            return false;
        });
    } else {
        // Remove show-more in case it was previously added
        if (bar.find(&apos;.show-more&apos;).length &amp;gt; 0) {
            if (isSummaryBar) {
                bar.css(&apos;min-height&apos;, summaryBarMinHeight - 18)
            } else {
                bar.attr(&apos;style&apos;, &apos;&apos;);
            }

            bar.find(&apos;.show-more&apos;).remove();
        }
    }
}

function showMoreOnResize() {
    var dataItems = $(&apos;.task-items,.summary-items,.report-items&apos;);
    dataItems.each(function() {
        var lastItem = $(this).find(&apos;li:last-child&apos;);
        if (lastItem.length &amp;gt; 0) {
            showMore(lastItem);
        }
    });
}
&lt;/pre&gt;
&lt;p&gt;At first, I wrote this logic as a directive, but when I needed it for responsiveness, I moved it into &lt;em&gt;dashboard.js&lt;/em&gt;. The &lt;code&gt;showMoreOnResize()&lt;/code&gt; function is called on window resize.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
$(window).resize(showMoreOnResize);
&lt;/pre&gt;
&lt;p&gt;I also found that I had to add it to the Preferences service after widgets were saved (since the number displayed could change).&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
factory(&apos;Preferences&apos;, function ($filter) {
    return {
        ...
        // Save hidden and visible (and order) widgets from config dialog
        saveWidgetPreferences: function (type, widgets) {
            ...
            DWRFacade.saveDashboardWidgetPreference(type, preferences, {
                callback: function() {
                    // recalculate show more bar
                    showMoreOnResize();
                },
                errorHandler: function (errorString) {
                    alert(errorString);
                }
            });
        }
    }
});
&lt;/pre&gt;
&lt;p&gt;To implement the .caret-up (the .caret class is from Bootstrap), I found &lt;a href=&quot;https://github.com/twitter/bootstrap/issues/2902&quot;&gt;a caret-right howto&lt;/a&gt; and used it to create &lt;code&gt;.caret-up&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;brush: css&quot;&gt;
.caret-up {
    border-left: 4px solid transparent;
    border-right: 4px solid transparent;
    border-top: 4px solid transparent;
    border-bottom: 4px solid black;
}
&lt;/pre&gt;
&lt;h3 id=&quot;summary&quot;&gt;Summary&lt;/h3&gt;
&lt;p&gt;
The final My Dashboard feature is something that I&apos;m quite proud of. A fellow developer, Vlad, did an excellent job of implementing the backend and admin portions. The Product Team&apos;s vision and desire to make it &lt;em&gt;Pop!&lt;/em&gt; created something great. The fact that we didn&apos;t have to support IE8 helped a lot in the implementation. Below is a screenshot of how My Dashboard looked when we completed the project.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a data-href=&quot;http://www.flickr.com/photos/mraible/8904354347/&quot; href=&quot;http://farm8.staticflickr.com/7380/8904354347_0b38a0bf2d_c.jpg&quot; title=&quot;My Dashboard&quot; rel=&quot;lightbox[makingitpop]&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7380/8904354347_0b38a0bf2d.jpg&quot; width=&quot;500&quot; height=&quot;338&quot; alt=&quot;My Dashboard&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;Angular isn&apos;t mentioned much in this article. That&apos;s because we didn&apos;t have to do much to the existing Angular code to implement the new design. It was just a matter of writing/modifying some CSS as well as introducing some JavaScript for colored rows and show more. If you know how these features could be written in a more Angular Way, I&apos;d love to hear about it.&lt;/p&gt;
&lt;p&gt;If you&apos;d still like to learn more about Angular and why it&apos;s good to integrate it little by little, I encourage you to read &lt;a href=&quot;http://oscarvillarreal.com/2013/05/07/5-reasons-to-use-angularjs-in-the-corporate-app-world/&quot;&gt;5 reasons to use AngularJS in the corporate app world&lt;/a&gt;.&lt;/p&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/developing_with_angularjs_part_ii</id>
        <title type="html">Developing with AngularJS - Part II: Dialogs and Data</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/developing_with_angularjs_part_ii"/>
        <published>2013-06-20T08:45:13-06:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="angularui" scheme="http://roller.apache.org/ns/tags/" />
        <category term="jqueryui" scheme="http://roller.apache.org/ns/tags/" />
        <category term="jquery" scheme="http://roller.apache.org/ns/tags/" />
        <category term="angularjs" scheme="http://roller.apache.org/ns/tags/" />
        <category term="javascript" scheme="http://roller.apache.org/ns/tags/" />
        <category term="taleo" scheme="http://roller.apache.org/ns/tags/" />
        <summary type="html">&lt;p&gt;A couple of days ago, I wrote an article on &lt;a href=&quot;http://raibledesigns.com/rd/entry/developing_with_angularjs_part_i&quot;&gt;how I started developing with AngularJS&lt;/a&gt;. I used AngularJS for several months to develop a &quot;My Dashboard&quot; feature for a client&apos;s product and learned a whole bunch of stuff along the way. &lt;/p&gt;
&lt;p&gt;This article provides an overview of how I changed some of My Dashboard&apos;s features to use Angular instead of jQuery. After finishing the prototype work in January, we started moving bits and pieces into the main application. We kept the same file names for our Angular-related files and copied them into the project.
&lt;/p&gt;
&lt;p&gt;
&lt;a href=&quot;http://www.flickr.com/photos/mraible/8904352343/&quot; title=&quot;Directory Structure&quot;&gt;&lt;img src=&quot;http://farm3.staticflickr.com/2856/8904352343_20beb87da8_o.png&quot; width=&quot;280&quot; height=&quot;268&quot; alt=&quot;Directory Structure&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;All these files are packaged up into a &lt;code&gt;dashboard.js&lt;/code&gt; file that&apos;s included at the bottom of our Dashboard page. While our prototype used jQuery 1.9 and jQuery UI 1.10, the application&apos;s codebase used jQuery 1.7.1 and jQuery UI 1.8.3. Luckily, this didn&apos;t present a problem as everything continued to work as expected.&lt;/p&gt;
&lt;p&gt;Around this time, we also had many discussions with the Product Team about charts. Since Highcharts required we purchase a license, we took at look at &lt;a href=&quot;http://www.anychart.com/&quot;&gt;AnyChart&lt;/a&gt;, which we were already using. We were able to get AnyChart to work with our existing &lt;em&gt;chart&lt;/em&gt; directive with minimal changes. Most changes were in the JSON itself. &lt;/p&gt;
&lt;p&gt;We committed the first pass (with sample data still hard-coded) in mid-February.&lt;/p&gt;</summary>
        <content type="html">&lt;p&gt;A couple of days ago, I wrote an article on &lt;a href=&quot;http://raibledesigns.com/rd/entry/developing_with_angularjs_part_i&quot;&gt;how I started developing with AngularJS&lt;/a&gt;. I used AngularJS for several months to develop a &quot;My Dashboard&quot; feature for a client&apos;s product and learned a whole bunch of stuff along the way. &lt;/p&gt;
&lt;p&gt;This article provides an overview of how I changed some of My Dashboard&apos;s features to use Angular instead of jQuery. After finishing the prototype work in January, we started moving bits and pieces into the main application. We kept the same file names for our Angular-related files and copied them into the project.
&lt;/p&gt;
&lt;p&gt;
&lt;a href=&quot;http://www.flickr.com/photos/mraible/8904352343/&quot; title=&quot;Directory Structure&quot;&gt;&lt;img src=&quot;//farm3.staticflickr.com/2856/8904352343_20beb87da8_o.png&quot; width=&quot;280&quot; height=&quot;268&quot; alt=&quot;Directory Structure&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;All these files are packaged up into a &lt;code&gt;dashboard.js&lt;/code&gt; file that&apos;s included at the bottom of our Dashboard page. While our prototype used jQuery 1.9 and jQuery UI 1.10, the application&apos;s codebase used jQuery 1.7.1 and jQuery UI 1.8.3. Luckily, this didn&apos;t present a problem as everything continued to work as expected.&lt;/p&gt;
&lt;p&gt;Around this time, we also had many discussions with the Product Team about charts. Since Highcharts required we purchase a license, we took at look at &lt;a href=&quot;http://www.anychart.com/&quot;&gt;AnyChart&lt;/a&gt;, which we were already using. We were able to get AnyChart to work with our existing &lt;em&gt;chart&lt;/em&gt; directive with minimal changes. Most changes were in the JSON itself. &lt;/p&gt;
&lt;p&gt;We committed the first pass (with sample data still hard-coded) in mid-February.&lt;/p&gt;
&lt;h3 id=&quot;angular-ui-carousel&quot;&gt;Angular UI&apos;s Carousel&lt;/h3&gt;
&lt;p&gt;While finishing our initial prototype, I learned about Angular UI&apos;s &lt;a href=&quot;https://github.com/angular-ui/bootstrap/tree/master/src/carousel&quot;&gt;Carousel&lt;/a&gt;, an implementation of Bootstrap&apos;s Carousel. It required a 70% less HTML and is quite a bit easier to read. Below is the refactored carousel section.&lt;/p&gt;
&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;carousel interval=&quot;-1&quot; class=&quot;oneup&quot;&amp;gt;
    &amp;lt;slide ng-repeat=&quot;widget in widgets | filter: {type: &apos;chart&apos;} | orderBy: &apos;order&apos;&quot; active=&quot;slide.active&quot; class=&quot;chart&quot;&amp;gt;
        &amp;lt;chart class=&quot;widget&quot; value=&quot;{{widget}}&quot; type=&quot;{{widget.chartType}}&quot;&amp;gt;&amp;lt;/chart&amp;gt;
    &amp;lt;/slide&amp;gt;
&amp;lt;/carousel&amp;gt;
&amp;lt;carousel interval=&quot;-1&quot; class=&quot;twoup&quot;&amp;gt;
    &amp;lt;slide ng-repeat=&quot;widget in widgets | filter: {type: &apos;chart&apos;} | chunk: 2 | orderBy: &apos;order&apos;&quot; active=&quot;slide.active&quot; class=&quot;chart&quot;&amp;gt;
        &amp;lt;chart class=&quot;widget&quot; value=&quot;{{widget&amp;#91;0&amp;#93;}}&quot; type=&quot;{{widget&amp;#91;0&amp;#93;.chartType}}&quot;&amp;gt;&amp;lt;/chart&amp;gt;
        &amp;lt;chart class=&quot;widget&quot; value=&quot;{{widget&amp;#91;1&amp;#93;}}&quot; type=&quot;{{widget&amp;#91;1&amp;#93;.chartType}}&quot;&amp;gt;&amp;lt;/chart&amp;gt;
    &amp;lt;/slide&amp;gt;
&amp;lt;/carousel&amp;gt;
&lt;/pre&gt;
&lt;p&gt;I was also able to remove the JavaScript that once initialized the carousel.&lt;/p&gt;
&lt;pre class=&quot;brush: diff&quot;&gt;
-    var carousel = $(&apos;.carousel&apos;);
-    $(carousel).carousel({
-        interval: 0
-    });
&lt;/pre&gt;
&lt;p&gt;While integrating this directive, I found a way to improve it by &lt;a href=&quot;https://github.com/angular-ui/bootstrap/commit/aedc05654b19342d96eabc4fc08d6a090765a48b&quot;&gt;hiding navigation indicators if there&apos;s only one slide&lt;/a&gt;. And thus my first contribution to Angular UI was born.&lt;/p&gt;
&lt;h3 id=&quot;angular-ui-sortable&quot;&gt;Angular UI&apos;s Sortable&lt;/h3&gt;
&lt;p&gt;Next, I switched from using JavaScript to Angular UI&apos;s &lt;a href=&quot;https://github.com/angular-ui/ui-sortable&quot;&gt;&lt;em&gt;sortable&lt;/em&gt;&lt;/a&gt;&lt;em&gt; &lt;/em&gt;to initialize drag-and-drop functionality. This was as simple as removing 5 lines of JavaScript and adding &quot;ui-sortable&quot; as an attribute to HTML tags.&lt;/p&gt;
&lt;pre class=&quot;brush: diff&quot;&gt;
&amp;lt;div class=&quot;container-widgets&quot; ng-controller=&quot;WidgetController&quot; ng-cloak&amp;gt;
     &amp;lt;div class=&quot;row-fluid&quot;&amp;gt;
         &amp;lt;div class=&quot;span9&quot;&amp;gt;
-                &amp;lt;ul class=&quot;widgets&quot;&amp;gt;
+                &amp;lt;ul class=&quot;widgets&quot; ui-sortable=&quot;{handle:&apos;.heading&apos;}&quot;&amp;gt;
                 &amp;lt;li id=&quot;summary-bar&quot;&amp;gt;
                     &amp;lt;div class=&quot;heading&quot;&amp;gt;Summary &amp;lt;a href=&apos;#&apos; class=&apos;configure&apos;&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;
-                        &amp;lt;ul class=&quot;tiles&quot;&amp;gt;
+                        &amp;lt;ul class=&quot;tiles&quot; ui-sortable&amp;gt;
                         &amp;lt;li class=&quot;span3&quot; ng-repeat=&quot;widget in widgets | filter:{type: &apos;summary&apos;} | orderBy: &apos;order&apos;&quot;&amp;gt;
                             &amp;lt;h3&amp;gt;{{widget.value}}&amp;lt;/h3&amp;gt;
@@ -36,7 +36,7 @@
                 &amp;lt;/li&amp;gt;
                 &amp;lt;li id=&quot;task-bar&quot;&amp;gt;
                     &amp;lt;div class=&quot;heading&quot;&amp;gt;My Tasks &amp;lt;a href=&apos;#&apos; class=&apos;configure&apos;&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;
-                        &amp;lt;ul class=&quot;tasks&quot;&amp;gt;
+                        &amp;lt;ul class=&quot;tasks&quot; ui-sortable&amp;gt;
@@ -151,55 +131,44 @@
-    $(&apos;.widgets&apos;).sortable({
-        cursor: &quot;move&quot;,
-        handle: &quot;.heading&quot;
-    }).disableSelection();
-    $(&apos;.tiles,.tasks&apos;).sortable();
&lt;/pre&gt;
&lt;p&gt;This directive uses jQuery UI under the covers, so it accepts all the same arguments you&apos;d normally use.&lt;/p&gt;
&lt;h3 id=&quot;dialogs-with-jquery&quot;&gt;Dialogs with jQuery&lt;/h3&gt;
&lt;p&gt;The next feature I implemented was a dialog that allowed end users to add/remove widgets from their dashboard. Since I knew how to do this with jQuery, that&apos;s the path I started down. I figured it&apos;d be easier to get something working quickly and then refactor to Angular later. I put the HTML for the dialog at the bottom of the page:
&lt;/p&gt;
&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;script type=&quot;text/javascript&quot; src=&quot;js/move.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script type=&quot;text/javascript&quot; src=&quot;js/upDown.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;div id=&quot;configure-widgets&quot; class=&quot;hidden&quot; data-title=&quot;Bar Configuration&quot; data-close=&quot;Close&quot;&amp;gt;
    &amp;lt;div class=&quot;row-fluid center&quot;&amp;gt;
        &lt;p&gt;Decide which items you would like to display and set the display order.&lt;/p&gt;
        &amp;lt;div class=&quot;span5&quot; style=&quot;margin-left: 25px&quot;&amp;gt;
            &amp;lt;label for=&quot;available-widgets&quot; class=&quot;bold&quot;&amp;gt;Available Widgets&amp;lt;/label&amp;gt;
            &amp;lt;select size=10 id=&quot;available-widgets&quot; multiple class=&quot;width100pr&quot;&amp;gt;&amp;lt;/select&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div class=&quot;span1 arrows&quot; style=&quot;padding-top: 50px&quot;&amp;gt;
            &amp;lt;img src=&quot;images/arrow_up.png&quot; class=&quot;arrow arrow-up&quot; alt=&quot;Move Up&quot; onclick=&quot;return moveUp($(&apos;#assigned-widgets&apos;)&amp;#91;0&amp;#93;);&quot;/&amp;gt;
            &amp;lt;img src=&quot;images/arrow_right.png&quot; class=&quot;arrow arrow-right&quot; alt=&quot;Move Right&quot; onclick=&quot;return move($(&apos;#available-widgets&apos;)&amp;#91;0&amp;#93;, $(&apos;#assigned-widgets&apos;)&amp;#91;0&amp;#93;);&quot;/&amp;gt;
            &amp;lt;img src=&quot;images/arrow_left.png&quot; class=&quot;arrow arrow-left&quot; alt=&quot;Move Left&quot; onclick=&quot;return move($(&apos;#assigned-widgets&apos;)&amp;#91;0&amp;#93;, $(&apos;#available-widgets&apos;)&amp;#91;0&amp;#93;);&quot;/&amp;gt;
            &amp;lt;img src=&quot;images/arrow_down.png&quot; class=&quot;arrow arrow-down&quot; alt=&quot;Move Down&quot; onclick=&quot;return moveDown($(&apos;#assigned-widgets&apos;)&amp;#91;0&amp;#93;);&quot;/&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div class=&quot;span5&quot;&amp;gt;
            &amp;lt;label for=&quot;assigned-widgets&quot; class=&quot;bold&quot;&amp;gt;Assigned Widgets&amp;lt;/label&amp;gt;
            &amp;lt;select size=10 id=&quot;assigned-widgets&quot; multiple class=&quot;width100pr&quot;&amp;gt;&amp;lt;/select&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Then I changed the &lt;code&gt;WidgetController&lt;/code&gt; to split the &lt;code&gt;widgets&lt;/code&gt; variable into variables for each widget type.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
+    var filter = $filter(&apos;filter&apos;);
+    var orderBy = $filter(&apos;orderBy&apos;);
+
+    $scope.summaryWidgets = filterData($scope.widgets, {type: &apos;summary&apos;}, filter, orderBy);
+    $scope.taskWidgets = filterData($scope.widgets, {type: &apos;task&apos;}, filter, orderBy);
+    $scope.chartWidgets = filterData($scope.widgets, {type: &apos;chart&apos;}, filter, orderBy);
+}
+
+function filterData(array, query, filter, orderBy) {
+    var data = filter(array, query);
+    return orderBy(data, &apos;order&apos;);
+}
&lt;/pre&gt;
&lt;p&gt;Finally, I added a new &lt;em&gt;config&lt;/em&gt; directive and hooked it into the page by adding &lt;em&gt;config=&quot;type&quot; &lt;/em&gt;to each heading (e.g. &lt;code&gt;&amp;lt;div class=&quot;heading&quot; config=&quot;task&quot;&amp;gt;My Tasks&amp;lt;/div&amp;gt;&lt;/code&gt;).&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;

.directive(&apos;config&apos;, function ($sanitize, $filter) {
    return {
        restrict: &apos;A&apos;,
        link: function (scope, element, attrs) {
            var configBtn = $(&apos;&amp;lt;a href=&quot;#&quot;/&amp;gt;&apos;).addClass(&apos;configure&apos;);
            configBtn.appendTo(element);
            var widgets = scope&amp;#91;attrs.config + &apos;Widgets&apos;&amp;#93;;
             
            // availableWidgets is defined as a global variable embedded in the page
            var allWidgets = availableWidgets; 
            $(configBtn).on(&apos;click&apos;, function (e) {
                e.preventDefault();
                var configDialog = $(&apos;#configure-widgets&apos;);
                var availableWidgets = $(&apos;#available-widgets&apos;);
                availableWidgets.empty();
                var filter = $filter(&apos;filter&apos;);
                var orderBy = $filter(&apos;orderBy&apos;);
                allWidgets = filter(allWidgets, {type: attrs.config});
                allWidgets = orderBy(allWidgets, &apos;order&apos;);
                var unselectedWidgets = jQuery.grep(allWidgets, function(item) {
                    return jQuery.inArray(item, widgets) &amp;lt; 0;
                });
                $.each(unselectedWidgets, function(index, item) {
                    var title = (item.title) ? item.title.replace(/&amp;amp;quot;/g, &apos;&quot;&apos;) : &apos;No Title&apos;;
                    availableWidgets.append(new Option(title, item.id));
                });
                var assignedWidgets = $(&apos;#assigned-widgets&apos;);
                assignedWidgets.empty();
                $.each(widgets, function(index, item) {
                    var title = (item.title) ? item.title.replace(/&amp;amp;quot;/g, &apos;&quot;&apos;) : &apos;No Title&apos;;
                    assignedWidgets.append(new Option(title, item.id));
                });
                configDialog.dialog({
                    title: $(element).text() + &apos; &apos; + configDialog.attr(&apos;data-title&apos;),
                    width: 600,
                    modal: true,
                    buttons: &amp;#91;{
                            text: configDialog.attr(&apos;data-close&apos;),
                            &apos;class&apos;: &apos;btn&apos;,
                            click: function() {
                                $(this).dialog(&quot;close&quot;);
                            }
                        }&amp;#93;
                });
            });
        }
    }
})
&lt;/pre&gt;
&lt;p&gt;As you can tell, this is quite a bit of code, and it doesn&apos;t even show you the JavaScript in move.js and upDown.js (included at the top of the dialog HTML). While writing this code, I could tell that I was not doing things the &lt;em&gt;Angular Way&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;To refactor, I did some research, found the &lt;a href=&quot;https://github.com/angular-ui/bootstrap/tree/master/src/dialog&quot;&gt;$dialogProvider&lt;/a&gt; service and went to work.&lt;/p&gt;
&lt;h3 id=&quot;dialogs-with-angular&quot;&gt;Dialogs with Angular&lt;/h3&gt;
&lt;p&gt;I started by refactoring the &lt;em&gt;config&lt;/em&gt; directive to be much shorter.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
.directive(&apos;config&apos;, function() {
    return {
        restrict: &apos;A&apos;,
        link: function (scope, element, attrs) {
            var configBtn = $(&apos;&amp;lt;a href=&quot;&quot;/&amp;gt;&apos;).addClass(&apos;configure&apos;);
            configBtn.appendTo(element);
            configBtn.bind(&apos;click&apos;, function(e) {
                e.preventDefault();
                e.stopPropagation();
                scope.$apply(scope.configureDialog(attrs.config, $(element).text()))
            });
        }
    }
})
&lt;/pre&gt;
&lt;p&gt;Line #10 that starts with &lt;code&gt;scope.$apply&lt;/code&gt; is what makes the magic happens.This calls the &lt;code&gt;configureDialog()&lt;/code&gt; function in &lt;code&gt;WidgetController&lt;/code&gt;. The &lt;code&gt;$dialog&lt;/code&gt; service you see below is injected by adding it as a parameter to the &lt;code&gt;WidgetController&lt;/code&gt; function.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;

$scope.configureDialog = function (type, title) {
    var dialog = $dialog.dialog({
        dialogClass: &apos;modal draggable&apos;,
        modalFade: false,
        backdropClick: true,
        controller: &apos;ConfigController&apos;,
        template: $(&apos;#configure-widgets&apos;).html(),
        resolve: {
            title: function() {
                return title;
            },
            hiddenItems: function () {
                return Widget.getHiddenWidgets(type);
            },
            items: function () {
                return angular.copy($scope&amp;#91;type + &apos;Widgets&apos;&amp;#93;);
            }
        }
    });
    dialog.open().then(function(results) {
        if (!angular.isUndefined(results)) {
            $scope&amp;#91;type + &apos;Widgets&apos;&amp;#93; = results.items;
            Preferences.saveWidgetPreferences(type, results);
        }
    });
};
&lt;/pre&gt;
&lt;div class=&quot;alert alert-info&quot;&gt;
&lt;p style=&quot;margin-top: 0&quot;&gt;&lt;strong&gt;draggable directive&lt;/strong&gt;&lt;br/&gt;
If you look closely, you&apos;ll see this dialog is initialized with a &apos;draggable&apos; class. I created a &lt;em&gt;draggable &lt;/em&gt;directive that gives draggability using jQuery UI to any elements with this class.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
.directive(&apos;draggable&apos;, function() {
    return {
        restrict: &apos;C&apos;,
        link: function(scope, elem, attr, ctrl) {
            elem.draggable({handle: &apos;.modal-header&apos;});
        }
    };
});
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The interesting parts of this file are the &lt;strong&gt;controller&lt;/strong&gt;, &lt;strong&gt;template&lt;/strong&gt;, and &lt;strong&gt;resolve&lt;/strong&gt; parameters. The &lt;strong&gt;controller&lt;/strong&gt; is just another function in &lt;em&gt;controllers.js&lt;/em&gt;, the &lt;strong&gt;template&lt;/strong&gt; is HTML in the page (so text could be i18n-ized) and &lt;strong&gt;resolve&lt;/strong&gt; has the variables to pass to the controller. &lt;code&gt;Widget&lt;/code&gt; and &lt;code&gt;Preferences&lt;/code&gt; are services I&apos;ll talk about in the Part 3. The reason angular.copy() is used is so the widget arrays aren&apos;t modified while the dialog is displayed (we want to wait until the user clicks &quot;Save&quot;).&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;ConfigController&lt;/code&gt; function started quite simply:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
function ConfigController($scope, $sanitize, dialog, title, hiddenItems, items) {
    $scope.title = title;
    $scope.hiddenItems = hiddenItems;
    $scope.items = items;
}
&lt;/pre&gt;
&lt;p&gt;The HTML (defined by the aforementioned &lt;strong&gt;template&lt;/strong&gt; parameter) is as follows. You can see that &lt;em&gt;title&lt;/em&gt;, &lt;em&gt;hiddenItems&lt;/em&gt; and &lt;em&gt;items&lt;/em&gt; are the variables defined in $scope and used to render the data.&lt;/p&gt;
&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;div id=&quot;configure-widgets&quot; class=&quot;modal hidden&quot;&amp;gt;
    &amp;lt;div class=&quot;modal-header&quot; style=&quot;border: 0&quot;&amp;gt;
        &amp;lt;button type=&quot;button&quot; class=&quot;close&quot; ng-click=&quot;close(false)&quot; aria-hidden=&quot;true&quot; style=&quot;margin-top: -3px&quot;&amp;gt;&amp;amp;times;&amp;lt;/button&amp;gt;
        &amp;lt;h4&amp;gt;{{title}} Bar Configuration&amp;lt;/h4&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;div class=&quot;modal-body&quot;&amp;gt;
        &lt;p&gt;Decide which items you would like to display and set the display order.&lt;/p&gt;
        &amp;lt;div class=&quot;row-fluid center&quot;&amp;gt;
            &amp;lt;div class=&quot;span4&quot; style=&quot;width: 320px; margin-left: 25px&quot;&amp;gt;
                &amp;lt;label for=&quot;available-widgets&quot; class=&quot;bold&quot;&amp;gt;Available Widgets&amp;lt;/label&amp;gt;
                &amp;lt;select size=10 id=&quot;available-widgets&quot; ng-model=&quot;items.available&quot; ng-options=&quot;i.title for i in hiddenItems&quot;
                        class=&quot;width100pr&quot; multiple=&quot;multiple&quot;&amp;gt;&amp;lt;/select&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;div class=&quot;span1 arrows&quot; style=&quot;padding-top: 50px&quot;&amp;gt;
                &amp;lt;img src=&quot;images/arrow_up.png&quot; class=&quot;arrow arrow-up&quot; alt=&quot;Move Up&quot; ng-click=&quot;moveUp(items.selected, items)&quot;/&amp;gt;
                &amp;lt;img src=&quot;images/arrow_right.png&quot; class=&quot;arrow arrow-right&quot; alt=&quot;Move Right&quot; ng-click=&quot;moveItem(items.available, hiddenItems, items)&quot;/&amp;gt;
                &amp;lt;img src=&quot;images/arrow_left.png&quot; class=&quot;arrow arrow-left&quot; alt=&quot;Move Left&quot; ng-click=&quot;moveItem(items.selected, items, hiddenItems)&quot;/&amp;gt;
                &amp;lt;img src=&quot;images/arrow_down.png&quot; class=&quot;arrow arrow-down&quot; alt=&quot;Move Down&quot; ng-click=&quot;moveDown(items.selected, items)&quot;/&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;div class=&quot;span4&quot; style=&quot;width: 320px&quot;&amp;gt;
                &amp;lt;label for=&quot;assigned-widgets&quot; class=&quot;bold&quot;&amp;gt;Assigned Widgets&amp;lt;/label&amp;gt;
                &amp;lt;select size=10 id=&quot;assigned-widgets&quot; ng-model=&quot;items.selected&quot; ng-options=&quot;i.title for i in items&quot;
                        class=&quot;width100pr&quot; multiple=&quot;multiple&quot;&amp;gt;&amp;lt;/select&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;div class=&quot;modal-footer&quot;&amp;gt;
        &amp;lt;button ng-click=&quot;close(true)&quot; class=&quot;btn btn-primary&quot;&amp;gt;Save and Close&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/pre&gt;
&lt;p&gt;There&apos;s a couple of new directives introduced by this code: &lt;em&gt;ngOptions&lt;/em&gt;&amp;nbsp;and &lt;em&gt;ngClick&lt;/em&gt;. The former is used to display options in a &amp;lt;select&amp;gt;, the latter to call functions in the controller. These functions are defined in &lt;code&gt;ConfigController&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
$scope.moveUp = function(items, list) {
    angular.forEach(items, function(item) {
        var idx = list.indexOf(item);
        if (idx != -1) {
            list.splice(idx - 1, 0, list.splice(idx, 1)[0]);
        }
    });
};

$scope.moveDown = function(items, list) {
    angular.forEach(items, function(item) {
        var idx = list.indexOf(item);
        if (idx != -1) {
            list.splice(idx + 1, 0, list.splice(idx, 1)[0]);
        }
    });
};

$scope.moveItem = function(items, from, to) {
    angular.forEach(items, function(item) {
        var idx = from.indexOf(item);
        if (idx != -1) {
            from.splice(idx, 1);
            to.push(item);
        }
    });
};

$scope.close = function(save) {
    if (save) {
        dialog.close({
            hidden: $scope.hiddenItems,
            items: $scope.items
        });
    } else {
        dialog.close();
    }
};
&lt;/pre&gt;
&lt;p&gt;As you can see, Angular allows you to easily access and manipulate the data. Its two-way binding feature is great because when you modify the object in JavaScript, it auto-updates the displayed HTML.The only thing you need to do to the HTML is to add the &lt;a href=&quot;http://docs.angularjs.org/api/ng.directive:ngModel&quot;&gt;&lt;em&gt;ngModel&lt;/em&gt;&lt;/a&gt; directive.&lt;/p&gt;
&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;ul class=&quot;tasks&quot; ng-model=&quot;taskWidgets&quot; ui-sortable&amp;gt;
&lt;/pre&gt;
&lt;p&gt;The dialog&apos;s &lt;code&gt;close()&lt;/code&gt; method is called in the header (where it passes false) and in the footer (where it passes true). The &lt;code&gt;configureDialog()&lt;/code&gt; function handles saving if the user indicates they wanted to do so. The 3rd line (starts with $scope) is all that&apos;s needed to update the UI. The Preferences service is covered in the next article.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
dialog.open().then(function(results) {
    if (!angular.isUndefined(results)) {
        $scope[type + &apos;Widgets&apos;] = results.items;
        Preferences.saveWidgetPreferences(type, results);
    }
});
&lt;/pre&gt;
&lt;h3 id=&quot;modals-with-data&quot;&gt;Modals with Data&lt;/h3&gt;
&lt;p&gt;Displaying the widgets in a consolidated dashboard is a nice product feature, but we wanted to take it a step further and allow users to &quot;click through&quot; to see the data. To do this, I added an &quot;event&quot; directive that could read from our JSON data and act upon it accordingly. We decided on 2 types of events: &lt;strong&gt;function&lt;/strong&gt; and &lt;strong&gt;href&lt;/strong&gt;. The &lt;strong&gt;href&lt;/strong&gt; event type is for report widgets, because we want to allow users to click on the widget and it takes them directly to the report. For &lt;strong&gt;function&lt;/strong&gt;, we simply &lt;em&gt;eval()&lt;/em&gt; what&apos;s passed in. The function is expected to have a &lt;em&gt;container&lt;/em&gt; argument that it can use to render data in a modal window.&lt;/p&gt;
&lt;p&gt;Using the &lt;em&gt;event&lt;/em&gt; directive, you can attach this behavior to a widget simply by adding a class.&lt;/p&gt;
&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;h3 class=&quot;events&quot;&amp;gt;{{widget.value}}&amp;lt;/h3&amp;gt;
&lt;/pre&gt;
&lt;p&gt;The &lt;em&gt;event&lt;/em&gt; directive that attaches click behavior is below:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
directive(&apos;events&apos;, function () {
    // This is necessary because widgets may be chunked when charts are displayed 2-up
    function getWidget(element, widget, scope) {
        if (element.hasClass(&apos;chart-title&apos;)) {
            if (element.parent().hasClass(&apos;first&apos;)) {
                widget = scope.widget[0];
            } else if (element.parent().hasClass(&apos;second&apos;)) {
                widget = scope.widget[1];
            } else {
                widget = scope.widget;
            }
        } else {
            widget = scope.widget;
        }
        return widget;
    }
    return {
        restrict: &apos;C&apos;,
        link: function (scope, element, attrs) {
            var widget = getWidget(element, widget, scope);
            if (angular.isUndefined(widget)) {
                return;
            }
            var events = widget.events;
            if (!angular.isUndefined(events)) {
                for (var e in events) {
                    if (e === &apos;function&apos;) {
                        if ($(&apos;#dialog-frame&apos;).length === 0) {
                            $(&apos;&amp;lt;div id=&quot;dialog-frame&quot; class=&quot;modal hide&quot;/&amp;gt;&apos;).appendTo(&apos;body&apos;);
                            var header = $(&apos;&amp;lt;div class=&quot;modal-header&quot;&amp;gt;&apos;);
                            header.append($(&apos;&amp;lt;button type=&quot;button&quot; class=&quot;close&quot; data-dismiss=&quot;modal&quot;&amp;gt;&amp;times;&amp;lt;/button&amp;gt;&apos;));
                            header.append($(&apos;&amp;lt;h4/&amp;gt;&apos;).append(scope.widget.title));
                            header.appendTo(&apos;#dialog-frame&apos;);
                            $(&apos;&amp;lt;div class=&quot;modal-body&quot;/&amp;gt;&apos;).appendTo(&apos;#dialog-frame&apos;);
                        }
                        element.bind(&apos;click&apos;, function(event) {
                            event.preventDefault();
                            event.stopPropagation();
                            var dialog = $(&apos;#dialog-frame&apos;);
                            var title = widget.title;
                            dialog.find(&apos;h4&apos;).html(title);
                            var dialogBody = dialog.find(&apos;.modal-body&apos;);
                            dialogBody.empty();
 
                            // display a checking for new data message when widget&apos;s value is 0
                            var checkingMessage = $(&apos;#wait-checking&apos;).html();
                            if (scope.widget.value === &quot;0&quot;) {
                                dialogBody.html(checkingMessage);
                            // otherwise, display a loading message
                            } else {
                                dialogBody.html($(&apos;#wait-loading&apos;).html());
                            }
 
                            // center the dialog on the page
                            dialog.css({
                                width: &apos;560px&apos;,
                                &apos;margin-left&apos;: function() {
                                    return -($(this).width() / 2);
                                }
                            });
 
                            dialog.modal(&apos;show&apos;);
                            var container = dialogBody;
                            eval(events[e]);
                        });
                    } else if (e === &quot;href&quot;) {
                        element.bind(&apos;click&apos;, function() {
                            location.href = events[e];
                        })
                    } else {
                        console.log(&apos;Event type &quot;&apos; + e + &apos;&quot; not supported.&apos;);
                    }
                }
            }
        }
    }
})
&lt;/pre&gt;
&lt;h3 id=&quot;empty-widgets-message&quot;&gt;Empty Widgets Message&lt;/h3&gt;
&lt;p&gt;When there are no widgets for a particular type, we wanted to display a message telling the end user. To do this, I used the &lt;a href=&quot;http://docs.angularjs.org/api/ng.directive:ngHide&quot;&gt;&lt;em&gt;ngHide&lt;/em&gt;&lt;/a&gt; directive and passed in the array&apos;s length as an expression. I originally had this on a &amp;lt;li&amp;gt; in the respective widget list, but noticed it causes issues when dragging and dropping.&lt;/p&gt;
&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;div ng-hide=&quot;summaryWidgets.length&quot; class=&quot;widgets-empty&quot;&amp;gt;
    No Summary Bar Widgets currently visible
&amp;lt;/div&amp;gt;
&lt;/pre&gt;
&lt;h3 id=&quot;summary&quot;&gt;Summary&lt;/h3&gt;
&lt;p&gt;This article has provided an overview of how I changed some of My Dashboard&apos;s features to use Angular instead of jQuery. I hope it&apos;s helped to show how powerful directives can be and how MVC works in Angular. I particularly enjoyed learning how to use the $dialog service. As a word of warning, its usage might change in future releases since it is currently being &lt;a href=&quot;https://github.com/angular-ui/bootstrap/issues/441&quot;&gt;rewritten to be more maintainable&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&quot;http://raibledesigns.com/rd/entry/developing_with_angularjs_part_iii&quot;&gt;next article&lt;/a&gt;, I&apos;ll talk about how I developed Services and integrated DWR. If you see any code that can be improved upon, or issues with the code/architecture in this article, please leave a comment.&lt;/p&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/developing_with_angularjs_part_i</id>
        <title type="html">Developing with AngularJS - Part I: The Basics</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/developing_with_angularjs_part_i"/>
        <published>2013-06-18T09:06:52-06:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="jquery" scheme="http://roller.apache.org/ns/tags/" />
        <category term="highcharts" scheme="http://roller.apache.org/ns/tags/" />
        <category term="javascript" scheme="http://roller.apache.org/ns/tags/" />
        <category term="angularjs" scheme="http://roller.apache.org/ns/tags/" />
        <category term="bootstrap" scheme="http://roller.apache.org/ns/tags/" />
        <category term="taleo" scheme="http://roller.apache.org/ns/tags/" />
        <summary type="html">&lt;p&gt;There&apos;s &lt;a href=&quot;http://hop.ie/blog/angularjs-introduction/&quot;&gt;many&lt;/a&gt;, &lt;a href=&quot;http://www.raweng.com/blog/2013/01/30/introduction-to-angularjs-part-1/&quot;&gt;many&lt;/a&gt; different &lt;a href=&quot;http://www.webdesignerdepot.com/2013/04/an-introduction-to-angularjs/&quot;&gt;introductions&lt;/a&gt; to &lt;a href=&quot;http://angularjs.org/&quot;&gt;AngularJS&lt;/a&gt; available on the internet. This article is not another introduction, but rather a story about my learning experience. It all started way back in January of this year. I was working as a UI Architecture Consultant at Taleo/Oracle, my client for the last 21 months. My gig there ended last month, but they agreed to let me publish a series of articles about the knowledge I gained.&lt;/p&gt;

&lt;h3 id=&quot;background&quot;&gt;Project Background&lt;/h3&gt;
&lt;p&gt;The Director of Product Management had been working on the concepts for a new project - codenamed &quot;Visual MyView&quot;. Below is a mockup he created for our kickoff meeting on January 4th.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a data-href=&quot;http://www.flickr.com/photos/mraible/8904352595/&quot; href=&quot;http://farm3.staticflickr.com/2872/8904352595_1678cfd1ab_c.jpg&quot; title=&quot;My Dashboard - Original Mockup&quot; rel=&quot;lightbox[angular-dashboard1]&quot;&gt;&lt;img src=&quot;http://farm3.staticflickr.com/2872/8904352595_1678cfd1ab.jpg&quot; width=&quot;500&quot; height=&quot;296&quot; alt=&quot;My Dashboard - Original Mockup&quot; style=&quot;border: 1px solid silver; box-shadow: 5px 5px 10px #888&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
From his original email about the above mockup:
&lt;/p&gt;
&lt;div class=&quot;quote&quot;&gt;
    &lt;p&gt;The intent here is that one of the columns has rows that have a similar width. The rows could be dragged and dropped into a different order &#8211; or potentially the two columns could also be reordered. The rows will basically be comprised of similar widgets. You can see in the mockup how the first two rows might look &#8211; and sample widgets. The widgets shown can be configured by the end user, as well as the order in which they are displayed. Other requirements given to us were the following.
    &lt;/p&gt;
    &lt;ul&gt;&lt;li&gt;Row 1 is comprised of &apos;summary&apos; widgets that are &apos;todo&apos; items. Reviews needing done &#8211; approvals required &#8211; etc.&lt;/li&gt;
        &lt;li&gt;Row 2 will be a graph row &#8211; having graphs and charts to display information &#8211; larger squares will build this row.&lt;/li&gt;
        &lt;li&gt;Row 3&apos;s content was not determined yet.&lt;/li&gt;&lt;/ul&gt;
&lt;/div&gt;
&lt;p&gt;I started the initial layout with static HTML and CSS and had a wireframe to show by mid January.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a data-href=&quot;http://www.flickr.com/photos/mraible/8904969226/&quot; href=&quot;http://farm3.staticflickr.com/2885/8904969226_c33d020e07_c.jpg&quot; title=&quot;Wireframe&quot; rel=&quot;lightbox[angular-dashboard1]&quot;&gt;&lt;img src=&quot;http://farm3.staticflickr.com/2885/8904969226_c33d020e07_n.jpg&quot; width=&quot;320&quot; height=&quot;268&quot; alt=&quot;Wireframe&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;By the end of January, we&apos;d renamed the project to My Dashboard and had a working prototype using &lt;a href=&quot;http://randomibis.com/coolclock/&quot;&gt;CoolClock&lt;/a&gt; and &lt;a href=&quot;http://momentjs.com/&quot;&gt;moment.js&lt;/a&gt; for the clock in the top right, &lt;a href=&quot;http://angularjs.org/&quot;&gt;AngularJS&lt;/a&gt; to display widget data, &lt;a href=&quot;http://jqueryui.com/&quot;&gt;jQuery UI&lt;/a&gt; for drag-n-drop of rows and widgets, Bootstrap&apos;s &lt;a href=&quot;http://twitter.github.com/bootstrap/javascript.html#carousel&quot;&gt;Carousel&lt;/a&gt; for holding charts and &lt;a href=&quot;http://www.highcharts.com/&quot;&gt;Highcharts&lt;/a&gt; for rendering charts.&lt;/p&gt;
</summary>
        <content type="html">&lt;p&gt;There&apos;s &lt;a href=&quot;http://hop.ie/blog/angularjs-introduction/&quot;&gt;many&lt;/a&gt;, &lt;a href=&quot;http://www.raweng.com/blog/2013/01/30/introduction-to-angularjs-part-1/&quot;&gt;many&lt;/a&gt; &lt;a href=&quot;http://www.codeproject.com/Articles/607873/Extending-HTML-with-AngularJS-Directives&quot;&gt;different&lt;/a&gt; &lt;a href=&quot;http://www.webdesignerdepot.com/2013/04/an-introduction-to-angularjs/&quot;&gt;introductions&lt;/a&gt; to &lt;a href=&quot;http://angularjs.org/&quot;&gt;AngularJS&lt;/a&gt; available on the internet. This article is not another introduction, but rather a story about my learning experience. It all started way back in January of this year. I was working as a UI Architecture Consultant at Taleo/Oracle, my client for the last 21 months. My gig there ended last month, but they agreed to let me publish a series of articles about the knowledge I gained.&lt;/p&gt;

&lt;h3 id=&quot;background&quot;&gt;Project Background&lt;/h3&gt;
&lt;p&gt;The Director of Product Management had been working on the concepts for a new project - codenamed &quot;Visual MyView&quot;. Below is a mockup he created for our kickoff meeting on January 4th.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a data-href=&quot;http://www.flickr.com/photos/mraible/8904352595/&quot; href=&quot;http://farm3.staticflickr.com/2872/8904352595_1678cfd1ab_c.jpg&quot; title=&quot;My Dashboard - Original Mockup&quot; rel=&quot;lightbox[angular-dashboard1]&quot;&gt;&lt;img src=&quot;//farm3.staticflickr.com/2872/8904352595_1678cfd1ab.jpg&quot; width=&quot;500&quot; height=&quot;296&quot; alt=&quot;My Dashboard - Original Mockup&quot; style=&quot;border: 1px solid silver; box-shadow: 5px 5px 10px #888&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
From his original email about the above mockup:
&lt;/p&gt;
&lt;div class=&quot;quote&quot;&gt;
    &lt;p&gt;The intent here is that one of the columns has rows that have a similar width. The rows could be dragged and dropped into a different order &#8211; or potentially the two columns could also be reordered. The rows will basically be comprised of similar widgets. You can see in the mockup how the first two rows might look &#8211; and sample widgets. The widgets shown can be configured by the end user, as well as the order in which they are displayed. Other requirements given to us were the following.
    &lt;/p&gt;
    &lt;ul&gt;&lt;li&gt;Row 1 is comprised of &apos;summary&apos; widgets that are &apos;todo&apos; items. Reviews needing done &#8211; approvals required &#8211; etc.&lt;/li&gt;
        &lt;li&gt;Row 2 will be a graph row &#8211; having graphs and charts to display information &#8211; larger squares will build this row.&lt;/li&gt;
        &lt;li&gt;Row 3&apos;s content was not determined yet.&lt;/li&gt;&lt;/ul&gt;
&lt;/div&gt;
&lt;p&gt;I started the initial layout with static HTML and CSS and had a wireframe to show by mid January.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a data-href=&quot;http://www.flickr.com/photos/mraible/8904969226/&quot; href=&quot;http://farm3.staticflickr.com/2885/8904969226_c33d020e07_c.jpg&quot; title=&quot;Wireframe&quot; rel=&quot;lightbox[angular-dashboard1]&quot;&gt;&lt;img src=&quot;//farm3.staticflickr.com/2885/8904969226_c33d020e07_n.jpg&quot; width=&quot;320&quot; height=&quot;268&quot; alt=&quot;Wireframe&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;By the end of January, we&apos;d renamed the project to My Dashboard and had a working prototype using &lt;a href=&quot;http://randomibis.com/coolclock/&quot;&gt;CoolClock&lt;/a&gt; and &lt;a href=&quot;http://momentjs.com/&quot;&gt;moment.js&lt;/a&gt; for the clock in the top right, &lt;a href=&quot;http://angularjs.org/&quot;&gt;AngularJS&lt;/a&gt; to display widget data, &lt;a href=&quot;http://jqueryui.com/&quot;&gt;jQuery UI&lt;/a&gt; for drag-n-drop of rows and widgets, Bootstrap&apos;s &lt;a href=&quot;http://twitter.github.com/bootstrap/javascript.html#carousel&quot;&gt;Carousel&lt;/a&gt; for holding charts and &lt;a href=&quot;http://www.highcharts.com/&quot;&gt;Highcharts&lt;/a&gt; for rendering charts. For this prototype, we included 4 types of widgets:&lt;/p&gt;
&lt;ol&gt;&lt;li&gt;Summary&lt;/li&gt;&lt;li&gt;Tasks&lt;/li&gt;&lt;li&gt;Charts&lt;/li&gt;&lt;li&gt;Reports&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;To create widgets, we had to decide on a common schema for them.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
&quot;id&quot;: 1, // not necessary for display, but likely needed if we modify and save preferences
&quot;title&quot;: &quot;Appointments Today&quot;,
&quot;type&quot;: &quot;summary&quot;, // others include: task, chart, report
&quot;value&quot;: 3, 
&quot;description&quot;: &quot;10:30 Jim Smith&quot;,
&quot;events&quot;: &quot;url&quot;, // this can have click events
&quot;order&quot;: 1 // used to determine order
&lt;/pre&gt;
&lt;p&gt;Below is a screenshot of our wireframe with some sample widgets.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a data-href=&quot;http://www.flickr.com/photos/mraible/8904969540/&quot; href=&quot;http://farm9.staticflickr.com/8556/8904969540_1cfe0e56c7_c.jpg&quot; title=&quot;Wireframe with Data&quot; rel=&quot;lightbox[angular-dashboard1]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8556/8904969540_1cfe0e56c7.jpg&quot; width=&quot;500&quot; height=&quot;342&quot; alt=&quot;Wireframe with Data&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;h3 id=&quot;basics&quot;&gt;Angular Basics&lt;/h3&gt;
&lt;p&gt;The decision to use AngularJS came early on in the project, after I read Tyler Renelle&apos;s &lt;a href=&quot;https://gist.github.com/lefnire/4454814&quot;&gt;Rant: Backbone, Angular, Meteor, Derby&lt;/a&gt;. To learn AngularJS, I briefly looked at its homepage documentation and played with some examples. Then I stumbled upon Misko Hevery and Igor Minar&apos;s &lt;a href=&quot;http://parleys.com/play/5148922b0364bc17fc56c91b/about&quot;&gt;AngularJS Presentation from Devoxx 2012&lt;/a&gt;. At that time, the video wasn&apos;t publicly available (it&apos;s free now), so I had to buy a Parley&apos;s subscription ($79). It was well worth the money because that one hour video greatly contributed to my understanding of how AngularJS works. Another resource I used frequently to figure out how to do things was John Lindquist&apos;s &lt;a href=&quot;http://www.egghead.io/&quot;&gt;egghead.io&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To begin with, we wrote the JSON for a bunch of sample widgets and embedded them into the page as a &lt;code&gt;widgetData&lt;/code&gt; JavaScript variable.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
var widgetData = [
    {&quot;id&quot;: 1, &quot;title&quot;: &quot;Appointments Today&quot;, &quot;type&quot;: &quot;summary&quot;, &quot;value&quot;: 3, &quot;description&quot;: &quot;10:30 Jim Smith&quot;, &quot;events&quot;: {&quot;click&quot;: &quot;alert(&apos;foo&apos;);&quot;}, &quot;order&quot;: 1},
    {&quot;id&quot;: 12, &quot;order&quot;: 2, &quot;title&quot;: &quot;Offer Approvals&quot;, &quot;type&quot;: &quot;task&quot;, &quot;class&quot;: &quot;sticky-note&quot;, &quot;value&quot;: 1},
    {&quot;id&quot;: 103, &quot;title&quot;: &quot;Browser market shares at a specific website, 2010&quot;, &quot;order&quot;: 1, &quot;type&quot;: &quot;chart&quot;, &quot;chartType&quot;: &quot;pie&quot;, 
         &quot;tooltip&quot;: {&quot;pointFormat&quot;: &quot;{series.name}: &amp;lt;b&gt;{point.percentage}%&amp;lt;/b&gt;&quot;, &quot;percentageDecimals&quot;: 1}, &quot;series&quot;: [
         {&quot;type&quot;: &quot;pie&quot;, &quot;name&quot;: &quot;Browser share&quot;, &quot;data&quot;: [
             [&quot;Firefox&quot;, 45.0],
             [&quot;IE&quot;, 26.8],
             {&quot;name&quot;: &quot;Chrome&quot;, &quot;y&quot;: 12.8, &quot;sliced&quot;: true, &quot;selected&quot;: true},
             [&quot;Safari&quot;, 8.5],
             [&quot;Opera&quot;, 6.2],
             [&quot;Others&quot;, 0.7]
         ]}
    ]},
    ...
];
&lt;/pre&gt;
&lt;p&gt;I used &lt;a href=&quot;https://github.com/angular/angular-seed&quot;&gt;angular-seed&lt;/a&gt; to create the initial structure of the prototype, and continued using the same JavaScript file names when we moved it into the product I worked on. Since the application takes a while to login and render the My Dashboard page (when working remotely), I decided not to use the &lt;a href=&quot;http://karma-runner.github.io/0.8/index.html&quot;&gt;Karma&lt;/a&gt; testing framework that ships with Angular. Below is what our directory structure looked like for our prototype.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a data-href=&quot;http://www.flickr.com/photos/mraible/8904352305/&quot; href=&quot;http://farm3.staticflickr.com/2860/8904352305_e3b3d40f80_c.png&quot; title=&quot;Angular Seed Directory Structure&quot; rel=&quot;lightbox[angular-dashboard1]&quot;&gt;&lt;img src=&quot;//farm3.staticflickr.com/2860/8904352305_e3b3d40f80_o.png&quot; width=&quot;327&quot; height=&quot;459&quot; alt=&quot;Angular Seed Directory Structure&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;The JavaScript files in the &quot;js&quot; folder are the most important for Angular. The first file, &lt;code&gt;app.js&lt;/code&gt;, loads the other files:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
angular.module(&apos;dashboard&apos;, [&apos;dashboard.filters&apos;, &apos;dashboard.services&apos;, &apos;dashboard.directives&apos;]);
&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;controllers.js&lt;/code&gt; file contains the Controllers (functions) that get the data and make it available to the page. Here&apos;s the code for our first controller:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
&apos;use strict&apos;;

/* Controllers */
function WidgetController($scope) {
    $scope.widgets = widgetData;
}
&lt;/pre&gt;
&lt;p&gt;This puts the widgets in scope and then we were able to render them using Angular&apos;s &lt;a href=&quot;http://docs.angularjs.org/api/ng.directive:ngRepeat&quot;&gt;ngRepeat&lt;/a&gt; directive and the following HTML:&lt;/p&gt;
&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;div ng-app=&quot;dashboard&quot; class=&quot;dashboard&quot;&amp;gt;
    &amp;lt;div class=&quot;container-widgets&quot; ng-controller=&quot;WidgetController&quot; ng-cloak&amp;gt;
        &amp;lt;div class=&quot;row-fluid&quot;&amp;gt;
            &amp;lt;div class=&quot;span9&quot;&amp;gt;
                &amp;lt;ul class=&quot;widgets&quot;&amp;gt;
                    &amp;lt;li id=&quot;summary-bar&quot;&amp;gt;
                        &amp;lt;div class=&quot;heading&quot;&amp;gt;Summary&amp;lt;/div&amp;gt;
                        &amp;lt;ul class=&quot;tiles&quot;&amp;gt;
                            &amp;lt;li class=&quot;span3&quot; ng-repeat=&quot;widget in widgets | filter:{type: &apos;summary&apos;} | orderBy: &apos;order&apos;&quot;&amp;gt;
                                &amp;lt;h3 class=&quot;events&quot;&amp;gt;{{widget.value}}&amp;lt;/h3&amp;gt;
                                &amp;lt;div class=&quot;title&quot;&amp;gt;{{widget.title}}&amp;lt;/div&amp;gt;
                                &amp;lt;div class=&quot;desc&quot;&amp;gt;{{widget.description}}&amp;lt;/div&amp;gt;
                            &amp;lt;/li&amp;gt;
                        &amp;lt;/ul&amp;gt;
                    &amp;lt;/li&amp;gt;
                    &amp;lt;li id=&quot;task-bar&quot;&amp;gt;
                        &amp;lt;div class=&quot;heading&quot;&amp;gt;My Tasks&amp;lt;/div&amp;gt;
                        &amp;lt;ul class=&quot;tasks&quot;&amp;gt;
                            &amp;lt;li class=&quot;task {{widget.class}}&quot; ng-repeat=&quot;widget in widgets | filter: {type: &apos;task&apos;} | orderBy: &apos;order&apos;&quot;&amp;gt;
                                &amp;lt;div class=&quot;title events&quot;&amp;gt;{{widget.title}}&amp;lt;/div&amp;gt;
                                &amp;lt;div class=&quot;value&quot;&amp;gt;{{widget.value}}&amp;lt;/div&amp;gt;
                            &amp;lt;/li&amp;gt;
                        &amp;lt;/ul&amp;gt;
                    &amp;lt;/li&amp;gt;
                    &amp;lt;li id=&quot;chart-bar&quot;&amp;gt;
                        &amp;lt;div class=&quot;heading&quot;&amp;gt;Charts&amp;lt;/div&amp;gt;
                        &amp;lt;div id=&quot;chartCarousel&quot; class=&quot;carousel slide&quot;&amp;gt;
                            &amp;lt;ol class=&quot;carousel-indicators&quot;&amp;gt;
                                &amp;lt;li data-target=&quot;#chartCarousel&quot;
                                    ng-repeat=&quot;widget in widgets | filter: {type: &apos;chart&apos;} | orderBy: &apos;order&apos;&quot;
                                    data-slide-to=&quot;{{$index}}&quot; ng-class=&quot;{active: $index == 0}&quot;&amp;gt;&amp;lt;/li&amp;gt;
                            &amp;lt;/ol&amp;gt;
                            &amp;lt;div class=&quot;carousel-inner&quot;&amp;gt;
                                &amp;lt;div class=&quot;item chart&quot;
                                     ng-repeat=&quot;widget in widgets | filter: {type: &apos;chart&apos;} | orderBy: &apos;order&apos;&quot;
                                     ng-class=&quot;{active: $index == 0}&quot;&amp;gt;
                                    &amp;lt;chart class=&quot;widget&quot; value=&quot;{{widget}}&quot; type=&quot;{{widget.chartType}}&quot;&amp;gt;&amp;lt;/chart&amp;gt;
                                &amp;lt;/div&amp;gt;
                            &amp;lt;/div&amp;gt;
                            &amp;lt;a class=&quot;left carousel-control&quot; href=&quot;#chartCarousel&quot; data-slide=&quot;prev&quot;&amp;gt;&#8249;&amp;lt;/a&amp;gt;
                            &amp;lt;a class=&quot;right carousel-control&quot; href=&quot;#chartCarousel&quot; data-slide=&quot;next&quot;&amp;gt;&#8250;&amp;lt;/a&amp;gt;
                        &amp;lt;/div&amp;gt;
                    &amp;lt;/li&amp;gt;
                &amp;lt;/ul&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;div class=&quot;span3&quot;&amp;gt;
                &amp;lt;!-- clock and reports --&amp;gt;
            &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/pre&gt;
&lt;p&gt;The beginning of this HTML shows how Angular is instantiated: &lt;strong&gt;ng-app&lt;/strong&gt; matches the name defined in &lt;code&gt;app.js&lt;/code&gt;, &lt;strong&gt;ng-controller&lt;/strong&gt; instantiates the &lt;code&gt;WidgetController&lt;/code&gt; and &lt;strong&gt;ng-cloak&lt;/strong&gt; is used to hide everything until its processed.&lt;/p&gt;
&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;div ng-app=&quot;dashboard&quot; class=&quot;dashboard&quot;&amp;gt;
    &amp;lt;div class=&quot;container-widgets&quot; ng-controller=&quot;WidgetController&quot; ng-cloak&amp;gt;
&lt;/pre&gt;
&lt;p&gt;If you take a closer look at the way ng-repeat attributes, you&apos;ll see how &lt;strong&gt;filters&lt;/strong&gt; are used to filter data. There&apos;s &lt;a href=&quot;http://docs.angularjs.org/api/ng.filter:filter&quot;&gt;&lt;em&gt;filter&lt;/em&gt;&lt;/a&gt;&lt;em&gt; &lt;/em&gt;and &lt;a href=&quot;http://docs.angularjs.org/api/ng.filter:orderBy&quot;&gt;&lt;em&gt;orderBy&lt;/em&gt;&lt;/a&gt; filters that are built in and allow you to filter data. The &lt;em&gt;filter &lt;/em&gt;filter allows you to query arrays by strings, objects and even functions. In the following code block, &quot;task&quot; widgets are filtered, ordered and displayed.&lt;/p&gt;
&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;li class=&quot;task {{widget.class}}&quot; ng-repeat=&quot;widget in widgets | filter: {type: &apos;task&apos;} | orderBy: &apos;order&apos;&quot;&amp;gt;
    &amp;lt;div class=&quot;title events&quot;&amp;gt;{{widget.title}}&amp;lt;/div&amp;gt;
    &amp;lt;div class=&quot;value&quot;&amp;gt;{{widget.value}}&amp;lt;/div&amp;gt;
&amp;lt;/li&amp;gt;
&lt;/pre&gt;
&lt;p&gt;This was pretty straightforward, but we quickly noticed that if a widget had HTML in its title, it didn&apos;t display correctly (rendering the raw HTML). To process the HTML, we had to use the &lt;a href=&quot;http://docs.angularjs.org/api/ngSanitize.directive:ngBindHtml&quot;&gt;ngBindHtml&lt;/a&gt; directive (tip: directives are camelCase, but written with dashes in HTML).&lt;/p&gt;
&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;div class=&quot;title events&quot; ng-bind-html=&quot;widget.title&quot;&amp;gt;&amp;lt;/div&amp;gt;
&lt;/pre&gt;
&lt;p&gt;After getting this to work, we noticed that some titles weren&apos;t fully rendered because they were hidden with overflow: hidden. We tried adding a tooltip with &lt;code&gt;title=&quot;{{widget.title}}&quot;&lt;/code&gt;, but ran into the same issue. I &lt;a href=&quot;https://groups.google.com/d/topic/angular/hG-T1bsmlnk/discussion&quot;&gt;sent an email &lt;/a&gt;to the AngularJS Google Group and received a solution: create an htmlTitle directive:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
.directive(&apos;htmlTitle&apos;, function ($sanitize) {
    return {
        restrict: &apos;A&apos;,
        link: function (scope, element, attrs) {
            attrs.$observe(&apos;htmlTitle&apos;, function (title) {
                // convert &amp;amp;value; to HTML
                var html = angular.element(&apos;&amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;&apos;).html($sanitize(title)).text();
                element.attr(&apos;title&apos;, html);
                element.html(html);
            });
        }
    }
})
&lt;/pre&gt;
&lt;p&gt;Usage:&lt;/p&gt;
&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;div class=&quot;title events&quot; ng-bind-html=&quot;widget.title&quot; html-title=&quot;{{widget.title}}&quot;&amp;gt;&amp;lt;/div&amp;gt;
&lt;/pre&gt;
&lt;h3 id=&quot;dragndrop&quot;&gt;Drag-and-Drop&lt;/h3&gt;
&lt;p&gt;To implement drag-and-drop functionality, I originally used jQuery UI&apos;s &lt;a href=&quot;http://jqueryui.com/sortable/&quot;&gt;sortable&lt;/a&gt;. At the bottom of the page, the following code initialized sorting for the various lists:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
$(document).ready(function() {
    $(&apos;.widgets&apos;).sortable({
        cursor: &quot;move&quot;,
        handle: &quot;.heading&quot;
    }).disableSelection();
    $(&apos;.tiles,.tasks&apos;).sortable();
    var carousel = $(&apos;.carousel&apos;);
    $(carousel).carousel({
        interval: 0
    });
};
&lt;/pre&gt;
&lt;p&gt;As you can see, it also initializes the carousel and stops it from cycling automatically.&lt;/p&gt;
&lt;h3 id=&quot;carousel-issues&quot;&gt;Carousel Issues&lt;/h3&gt;
&lt;p&gt;The first problem I ran into with Bootstrap&apos;s Carousel was a strange error from Highcharts. If you look in the above HTML, you&apos;ll see there&apos;s a &lt;code&gt;&amp;lt;chart&amp;gt;&lt;/code&gt; element. This is processed by a &lt;a href=&quot;https://github.com/rootux/angular-highcharts-directive/blob/master/src/directives/highchart.js&quot;&gt;highcharts directive&lt;/a&gt;. When I tried to use this directive for Highcharts in a carousel, it results in the following error:&lt;/p&gt;
&lt;p class=&quot;alert alert-error&quot;&gt;
TypeError: Cannot read property &apos;length&apos; of undefined at Object.ob.setMaxTicks
&lt;/p&gt;
&lt;p&gt;This seemed to be caused by the following css in Bootstrap:&lt;/p&gt;
&lt;pre class=&quot;brush: css&quot;&gt;
.carousel-inner &gt; .item { display: none }
&lt;/pre&gt;
&lt;p&gt;When I added an override with &quot;display: block&quot; to my stylesheet, everything worked, but the charts were stacked instead of in a carousel. To fix this, I modified the directive to show/hide the &quot;item&quot; element so Highcharts was able to write to it. I also &lt;a href=&quot;https://github.com/rootux/angular-highcharts-directive/issues/1&quot;&gt;logged an issue&lt;/a&gt; for this.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
if (element.parent().not(&apos;:visible&apos;)) {
    element.parent().show();
}
var chart = new Highcharts.Chart(newSettings);
element.parent().attr(&apos;style&apos;, &apos;&apos;);
&lt;/pre&gt;
&lt;h3 id=&quot;grouping&quot;&gt;ngRepeat and Grouping&lt;/h3&gt;
&lt;p&gt;The last thing I accomplished in our end-of-January prototype was rendering 2 charts side-by-side. I got it working with plain HTML, created a &quot;groupBy&quot; filter for Angular and tried to get it to work with the following:&lt;/p&gt;
&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;div id=&quot;chartCarousel&quot; class=&quot;carousel slide&quot;&amp;gt;
    &amp;lt;ol class=&quot;carousel-indicators&quot;&amp;gt;
        &amp;lt;li data-target=&quot;#chartCarousel&quot; ng-repeat=&quot;widget in widgets | filter: {type: &apos;chart&apos;} | groupBy&quot;
            data-slide-to=&quot;{{$index}}&quot; ng-class=&quot;{active: $index == 0}&quot;&amp;gt;&amp;lt;/li&amp;gt;
    &amp;lt;/ol&amp;gt;
    &amp;lt;div class=&quot;carousel-inner&quot;&amp;gt;
        &amp;lt;div class=&quot;item&quot; ng-repeat=&quot;widget in widgets | filter: {type: &apos;chart&apos;} | groupBy&quot; ng-class=&quot;{active: $index == 0}&quot;&amp;gt;
            &amp;lt;div class=&quot;widget&quot;&amp;gt;{{widget&amp;#91;0&amp;#93;.title}}&amp;lt;/div&amp;gt;
            &amp;lt;div class=&quot;widget&quot;&amp;gt;{{widget&amp;#91;1&amp;#93;.title}}&amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;a class=&quot;left carousel-control&quot; href=&quot;#chartCarousel&quot; data-slide=&quot;prev&quot;&amp;gt;&#8249;&amp;lt;/a&amp;gt;
    &amp;lt;a class=&quot;right carousel-control&quot; href=&quot;#chartCarousel&quot; data-slide=&quot;next&quot;&amp;gt;&#8250;&amp;lt;/a&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/pre&gt;
&lt;p&gt;This all worked like I expected it to in Chrome, but I the following errors showed in my console.&lt;/p&gt;
&lt;p class=&quot;alert alert-error&quot;&gt;
Error: 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations:
&lt;/p&gt;
&lt;p&gt;I &lt;a href=&quot;https://groups.google.com/d/topic/angular/gEv1-YV-Ojg/discussion&quot;&gt;sent an email&lt;/a&gt; to the Angular Google Group and received a link to &lt;a href=&quot;https://groups.google.com/d/msg/angular/IEIQok-YkpU/oKuLvzCnAcoJ&quot;&gt;a discussion&lt;/a&gt; where I found a &quot;chunk&quot; filter that solved the problem. This worked great, but I wanted to make it more responsive.&lt;/p&gt;
&lt;ol&gt;&lt;li&gt;If the user has a screen size big enough to fit 2 charts, show 2 charts and paginate by 2.&lt;/li&gt;&lt;li&gt;If the user has a small screen size that only fits 1 chart, show 1 and paginate by 1.&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;To solve #1 and #2, I ended up rendering two different sections (with classes .oneup and .twoup) and displayed them based on screen size.&lt;/p&gt;
&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;li id=&quot;chart-bar&quot;&amp;gt;
    &amp;lt;div class=&quot;heading&quot;&amp;gt;Charts&amp;lt;/div&amp;gt;
    &amp;lt;div id=&quot;chartCarousel1&quot; class=&quot;carousel slide oneup&quot; style=&quot;display: none&quot;&amp;gt;
        &amp;lt;ol class=&quot;carousel-indicators&quot;&amp;gt;
            &amp;lt;li data-target=&quot;#chartCarousel1&quot;
                ng-repeat=&quot;widget in widgets | filter: {type: &apos;chart&apos;} | orderBy: &apos;order&apos;&quot;
                data-slide-to=&quot;{{$index}}&quot; ng-class=&quot;{active: $index == 0}&quot;&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;/ol&amp;gt;
        &amp;lt;div class=&quot;carousel-inner&quot;&amp;gt;
            &amp;lt;div class=&quot;item chart&quot;
                 ng-repeat=&quot;widget in widgets | filter: {type: &apos;chart&apos;} | orderBy: &apos;order&apos;&quot;
                 ng-class=&quot;{active: $index == 0}&quot;&amp;gt;
                &amp;lt;chart class=&quot;widget&quot; value=&quot;{{widget}}&quot; type=&quot;{{widget.chartType}}&quot;&amp;gt;&amp;lt;/chart&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;a class=&quot;left carousel-control&quot; href=&quot;#chartCarousel1&quot; data-slide=&quot;prev&quot;&amp;gt;&#8249;&amp;lt;/a&amp;gt;
        &amp;lt;a class=&quot;right carousel-control&quot; href=&quot;#chartCarousel1&quot; data-slide=&quot;next&quot;&amp;gt;&#8250;&amp;lt;/a&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;div id=&quot;chartCarousel2&quot; class=&quot;carousel slide twoup&quot; style=&quot;display: none&quot;&amp;gt;
        &amp;lt;ol class=&quot;carousel-indicators&quot;&amp;gt;
            &amp;lt;li data-target=&quot;#chartCarousel2&quot;
                ng-repeat=&quot;widget in widgets | filter: {type: &apos;chart&apos;} | chunk: 2 | orderBy: &apos;order&apos;&quot;
                data-slide-to=&quot;{{$index}}&quot; ng-class=&quot;{active: $index == 0}&quot;&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;/ol&amp;gt;
        &amp;lt;div class=&quot;carousel-inner&quot;&amp;gt;
            &amp;lt;div class=&quot;item chart&quot;
                 ng-repeat=&quot;widget in widgets | filter: {type: &apos;chart&apos;} | chunk: 2 | orderBy: &apos;order&apos;&quot;
                 ng-class=&quot;{active: $index == 0}&quot;&amp;gt;
                &amp;lt;chart class=&quot;widget&quot; value=&quot;{{widget&amp;#91;0&amp;#93;}}&quot; type=&quot;{{widget&amp;#91;0&amp;#93;.chartType}}&quot;&amp;gt;&amp;lt;/chart&amp;gt;
                &amp;lt;chart class=&quot;widget&quot; value=&quot;{{widget&amp;#91;1&amp;#93;}}&quot; type=&quot;{{widget&amp;#91;1&amp;#93;.chartType}}&quot;&amp;gt;&amp;lt;/chart&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;a class=&quot;left carousel-control&quot; href=&quot;#chartCarousel2&quot; data-slide=&quot;prev&quot;&amp;gt;&#8249;&amp;lt;/a&amp;gt;
        &amp;lt;a class=&quot;right carousel-control&quot; href=&quot;#chartCarousel2&quot; data-slide=&quot;next&quot;&amp;gt;&#8250;&amp;lt;/a&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/li&amp;gt;
&lt;/pre&gt;
&lt;p&gt;The JavaScript to show the correct number of charts is below:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
var chartBar = $(&apos;#chart-bar&apos;);
function showCharts() {
    if (chartBar.width() &amp;lt; 960) {
        chartBar.find(&apos;.oneup&apos;).show();
        chartBar.find(&apos;.twoup&apos;).hide();
    } else {
        chartBar.find(&apos;.twoup&apos;).show();
        chartBar.find(&apos;.oneup&apos;).hide();
    }
}

$(document).ready(function () {
    showCharts();
});

$(window).resize(showCharts);
&lt;/pre&gt;
&lt;h3 id=&quot;summary&quot;&gt;Summary&lt;/h3&gt;
&lt;p&gt;Even though I got everything to work for our initial prototype using Angular and jQuery, it didn&apos;t quite feel like I was taking full advantage of Angular&apos;s power. In particular, I learned that Angular UI Bootstrap had their own carousel and Angular UI had a &lt;em&gt;sortable&lt;/em&gt; directive. My suspicions were confirmed when I read &lt;a href=&quot;http://stackoverflow.com/questions/14994391/how-do-i-think-in-angularjs-if-i-have-a-jquery-background&quot;&gt;How do I &quot;think in AngularJS&quot; if I have a jQuery background?&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&quot;http://raibledesigns.com/rd/entry/developing_with_angularjs_part_ii&quot;&gt;next article&lt;/a&gt;, I&apos;ll talk about how I migrated to use Angular UI&apos;s &lt;a href=&quot;http://angular-ui.github.io/bootstrap/#/carousel&quot;&gt;carousel&lt;/a&gt; and &lt;a href=&quot;https://github.com/angular-ui/ui-sortable&quot;&gt;sortable&lt;/a&gt; directives, as well as integrating dialogs.&lt;/p&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/happy_10_year_appfuse</id>
        <title type="html">Happy 10 Year AppFuse!</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/happy_10_year_appfuse"/>
        <published>2013-04-05T08:56:45-06:00</published>
        <updated>2014-05-08T19:47:26-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="appfuse" scheme="http://roller.apache.org/ns/tags/" />
        <category term="anniversary" scheme="http://roller.apache.org/ns/tags/" />
        <category term="10years" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">&lt;a href=&quot;http://appfuse.org&quot;&gt;&lt;img src=&quot;//raw.github.com/appfuse/appfuse/master/www/logo/appfuse_72_transparent.gif&quot; width=&quot;72&quot; height=&quot;72&quot; class=&quot;picture&quot; /&gt;&lt;/a&gt;
10 years ago yesterday, I released the &lt;a href=&quot;http://raibledesigns.com/rd/entry/announce_struts_resume_and_appfuse&quot;&gt;first version of AppFuse&lt;/a&gt;. It started with XDoclet generating ActionForms from POJOs and became very popular for Struts developers that wanted to use Hibernate. The project&apos;s popularity peaked in 2006, as you can see from the mailing list traffic below.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://appfuse.markmail.org&quot; title=&quot;AppFuse Mailing List Traffic&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8251/8621164287_0acb4af5de.jpg&quot; width=&quot;500&quot; height=&quot;162&quot; alt=&quot;AppFuse Mailing List Traffic&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;It&apos;s possible the decrease in traffic is because we re-wrote everything to be based on Maven. It&apos;s also possible it was because of more attractive full-stack frameworks like &lt;a href=&quot;http://raibledesigns.com/rd/entry/appfuse_vs_grails_vs_rails&quot;&gt;Grails and Rails&lt;/a&gt;. However, the real reason is likely that I stopped working on it all the time due to &lt;span style=&quot;text-decoration: line-through&quot;&gt;getting a divorce&lt;/span&gt; &lt;a href=&quot;http://raibledesigns.com/rd/entry/5_years&quot;&gt;becoming an awesome dad&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Below is a timeline of how the project evolved over its first 4 years.&lt;/p&gt;
&lt;p style=&quot;text-align: center; background: white&quot;&gt;
&lt;a href=&quot;http://static.raibledesigns.com/repository/images/appfuse-history.png&quot; rel=&quot;lightbox[appfuse-10yr]&quot; title=&quot;AppFuse History: 2003 - 2007&quot;&gt;&lt;img src=&quot;//static.raibledesigns.com/repository/images/appfuse-history.png&quot; alt=&quot;AppFuse History: 2003 - 2007&quot; width=&quot;500&quot;/&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;AppFuse has been a great project for me to work on and it&apos;s been a large source of my knowledge about Java, Web Frameworks, Spring, Hibernate - as well as build systems like Ant and Maven. We started with CVS, moved to SVN and now we&apos;re &lt;a href=&quot;http://github.com/appfuse&quot;&gt;on GitHub&lt;/a&gt;. We&apos;ve experienced &lt;a href=&quot;http://raibledesigns.com/rd/entry/appfuse_light_converted_to_maven&quot;&gt;migrating from Tapestry 4 to Tapestry 5&lt;/a&gt; (thanks Serge Eby!), upgrading to &lt;a href=&quot;http://raibledesigns.com/rd/entry/upgrading_to_jsf_2&quot;&gt;JSF 2&lt;/a&gt; and enjoyed the backwards compatibility of Spring and Struts 2 throughout the years. We&apos;ve also added REST support, a Web Services archetype and kept up with the latest Spring and Hibernate releases.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://appfuse.org/download/attachments/84/appfuse-history2.png?version=1&amp;amp;modificationDate=1356633078000&quot; rel=&quot;lightbox[appfuse-10yr]&quot; title=&quot;AppFuse History: 2007 - 2013&quot;&gt;&lt;img src=&quot;//appfuse.org/download/attachments/84/appfuse-history2.png?version=1&amp;amp;modificationDate=1356633078000&quot; width=&quot;500&quot; alt=&quot;AppFuse History: 2007 - 2013&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Last year, we &lt;a href=&quot;http://raibledesigns.com/rd/entry/appfuse_2_2_1_released&quot;&gt;added Bootstrap and jQuery&lt;/a&gt; as foundational front-end frameworks. For our next release, we&apos;re &lt;a href=&quot;http://raibledesigns.com/rd/entry/switching_appfuse_from_myfaces_to&quot;&gt;switching to PrimeFaces&lt;/a&gt;, adding Wicket and changing from jMock to Mockito. Most of these changes are already in source control, we just need to polish them up a bit and add &lt;a href=&quot;http://appfuse.org/display/APF/AppFuse+Maven+Plugin&quot; title=&quot;AppFuse Maven Plugin&quot;&gt;AMP&lt;/a&gt; support. I hope to release 3.0 &lt;a href=&quot;http://www.flickr.com/photos/mraible/sets/72157633155884796/&quot;&gt;before the bus is done&lt;/a&gt;. &lt;img src=&quot;https://raibledesigns.com/images/smileys/wink.gif&quot; class=&quot;smiley&quot; alt=&quot;;)&quot; title=&quot;;)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Thanks to all the enthusiastic users of and contributors to AppFuse over the years. It&apos;s been a great ride! </content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/the_modern_java_web_developer</id>
        <title type="html">The Modern Java Web Developer and Java Web Security at Denver JUG</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/the_modern_java_web_developer"/>
        <published>2013-02-14T10:23:18-07:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="denverjug" scheme="http://roller.apache.org/ns/tags/" />
        <category term="java" scheme="http://roller.apache.org/ns/tags/" />
        <category term="denver" scheme="http://roller.apache.org/ns/tags/" />
        <category term="web" scheme="http://roller.apache.org/ns/tags/" />
        <category term="security" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">Last night, I had the pleasure of delivering two talks at the &lt;a href=&quot;http://www.meetup.com/DenverJavaUsersGroup/events/93932082/&quot;&gt;Denver Java User Group&lt;/a&gt;. The first talk, The Modern Java Web Developer, was inspired by the book titled &lt;a href=&quot;http://raibledesigns.com/rd/entry/the_well_grounded_java_developer&quot;&gt;The Well-Grounded Java Developer&lt;/a&gt;. &lt;a href=&quot;http://boxcatjunction.blogspot.com/&quot;&gt;Ben Evans&lt;/a&gt; and &lt;a href=&quot;http://martijnverburg.blogspot.com/&quot;&gt;Martijn Verburg&lt;/a&gt; mention in the beginning of the book that they wrote it as a training guide to get new Java developers up to speed. For my talk, I wanted to do something similar, but for Java Web Developers.&lt;/p&gt;
&lt;p&gt;I mentioned that the first thing you have to do to become modern is to change your title from a Java Web Developer to a &lt;em&gt;JVM&lt;/em&gt; Web Developer. After doing that, you have a whole slew of new and wonderful technologies at your disposal. From there, I believe the Modern JVM Web Developer: 
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Starts with Fast Hardware&lt;/li&gt;
&lt;li&gt;Uses IntelliJ IDEA&lt;/li&gt;
&lt;li&gt;Leverages jQuery, HTML5, and CSS3&lt;/li&gt;
&lt;li&gt;Creates High Performance Web Sites&lt;/li&gt;
&lt;li&gt;For Mobile Devices&lt;/li&gt;
&lt;li&gt;In the Cloud&lt;/li&gt;
&lt;li&gt;And cares about Security&lt;/li&gt;
&lt;/ul&gt;

&lt;div style=&quot;text-align: center&quot;&gt;
&lt;iframe src=&quot;//www.slideshare.net/slideshow/embed_code/16515789&quot; width=&quot;514&quot; height=&quot;422&quot; frameborder=&quot;0&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; scrolling=&quot;no&quot; style=&quot;border:1px solid #CCC;border-width:1px 1px 0;margin-bottom:5px; &quot; allowfullscreen webkitallowfullscreen mozallowfullscreen&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;You can also &lt;a href=&quot;http://www.slideshare.net/mraible/the-modern-java-web-developer-denver-jug-2013&quot;&gt;view this presentation on Slideshare&lt;/a&gt; or &lt;a href=&quot;http://raibledesigns.com/rd/page/publications&quot;&gt;download it from my presentations page&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;
The second talk was on Java Web Application Security and was largely an updated version of the talk I gave a couple years ago, starting with &lt;a href=&quot;http://raibledesigns.com/rd/entry/java_web_application_security_part&quot;&gt;an appearance at the Utah JUG&lt;/a&gt;. It was mostly a live demo session using my &lt;a href=&quot;https://github.com/mraible/ajax-login&quot;&gt;Ajax Login&lt;/a&gt; application. To prepare the project for this talk, I created &lt;a href=&quot;https://github.com/mraible/ajax-login/branches&quot;&gt;branches for each step&lt;/a&gt;. This means you can checkout the &quot;baseline&quot; branch and use Git to compare it with the &quot;javaee&quot; branch. You can also compare the &quot;spring-security&quot; branch vs. the &quot;apache-shiro&quot; branch. Finally, you could see &lt;a href=&quot;https://github.com/mraible/ajax-login/commit/9a45b74b0b1c85c3e02f3824bc57f0cf76423000&quot;&gt;what I needed to do&lt;/a&gt; to fix many of the vulnerabilities found by &lt;a href=&quot;https://www.owasp.org/index.php/OWASP_Zed_Attack_Proxy_Project&quot;&gt;Zed Attack Proxy&lt;/a&gt;. &lt;/p&gt;

&lt;div style=&quot;text-align: center&quot;&gt;
&lt;iframe src=&quot;//www.slideshare.net/slideshow/embed_code/16515810&quot; width=&quot;514&quot; height=&quot;422&quot; frameborder=&quot;0&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; scrolling=&quot;no&quot; style=&quot;border:1px solid #CCC;border-width:1px 1px 0;margin-bottom:5px&quot; allowfullscreen webkitallowfullscreen mozallowfullscreen&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;You can also &lt;a href=&quot;http://www.slideshare.net/mraible/java-web-application-security-denver-jug-2013&quot;&gt;view this presentation on Slideshare&lt;/a&gt; or &lt;a href=&quot;http://raibledesigns.com/rd/page/publications&quot;&gt;download it from my presentations page&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Thanks to the DJUG and &lt;a href=&quot;http://www.businessatthrive.com/v3/&quot;&gt;Thrive&lt;/a&gt; folks for providing good beer (especially the Guinness!) and &lt;a href=&quot;http://www.fullcontact.com/&quot;&gt;FullContact&lt;/a&gt; for hosting. Also, I&apos;d like to thank Manning for the copies of &lt;a href=&quot;http://manning.com/evans/&quot;&gt;The Well-Grounded Java Developer&lt;/a&gt; they sent and No Starch Press for copies of Michal Zalewsky&apos;s &lt;a href=&quot;http://lcamtuf.coredump.cx/tangled/&quot;&gt;The Tangled Web: A Guide to Securing Modern Web Applications&lt;/a&gt;. Last, but certainly not least, thanks to all the good people who attended and listened to me ramble on about all this cool technology.&lt;/p&gt;
&lt;p&gt;
Future speaking engagements include &lt;a href=&quot;http://www.devoxx.com/display/FR13/Matt+Raible&quot;&gt;Devoxx France&lt;/a&gt; in March and Denver&apos;s &lt;a href=&quot;http://www.meetup.com/HTML5-Denver-Users-Group/events/93212272/&quot;&gt;HTML5 User Group&lt;/a&gt; in April.</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/appfuse_light_2_2_1</id>
        <title type="html">AppFuse Light 2.2.1 Released!</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/appfuse_light_2_2_1"/>
        <published>2013-01-24T19:43:20-07:00</published>
        <updated>2013-01-25T01:47:18-07:00</updated> 
        <category term="/Java" label="Java" />
        <category term="hibernate" scheme="http://roller.apache.org/ns/tags/" />
        <category term="spring" scheme="http://roller.apache.org/ns/tags/" />
        <category term="java" scheme="http://roller.apache.org/ns/tags/" />
        <category term="appfuse" scheme="http://roller.apache.org/ns/tags/" />
        <category term="security" scheme="http://roller.apache.org/ns/tags/" />
        <category term="appfuse-light" scheme="http://roller.apache.org/ns/tags/" />
        <category term="maven" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">In December, the &lt;a href=&quot;http://appfuse.org&quot;&gt;AppFuse&lt;/a&gt; Team &lt;a href=&quot;http://raibledesigns.com/rd/entry/appfuse_2_2_1_released&quot;&gt;released 2.2.1&lt;/a&gt;. Right before that release, I decided to wait on enhancing its &quot;light&quot; modules, a.k.a. &lt;a href=&quot;https://github.com/appfuse/appfuse-light&quot;&gt;AppFuse Light&lt;/a&gt;. I&apos;m glad I did, because it took some effort to get jQuery and Bootstrap integrated, as well as to make it more secure. 
&lt;/p&gt;
&lt;p&gt;
The good news is AppFuse Light 2.2.1 is released and it&apos;s sitting out on the &lt;a href=&quot;http://search.maven.org/#search%7Cga%7C1%7Cappfuse-light&quot;&gt;Central Repository&lt;/a&gt;. This release is a refactoring of all  archetypes to be up-to-date with the AppFuse 2.2.1 release. This means Java 7 compatibility, Servlet 3, Bootstrap/jQuery integration, Tapestry 5.3.6 upgrade and security improvements. I integrated Bootstrap and jQuery using &lt;a href=&quot;http://www.webjars.org/documentation&quot;&gt;WebJars Servlet 3 support&lt;/a&gt; since it was simple and straightforward. 
&lt;/p&gt;
&lt;p&gt;
You can create projects using AppFuse&apos;s light archetypes using a command such as the following:
&lt;/p&gt;
&lt;pre class=&quot;brush: shell&quot;&gt;
mvn archetype:generate -B -DarchetypeGroupId=org.appfuse.archetypes 
  -DarchetypeArtifactId=appfuse-light-spring-freemarker-archetype -DarchetypeVersion=2.2.1 
  -DgroupId=com.mycompany -DartifactId=myproject 
&lt;/pre&gt;
&lt;p&gt;
The list of archetypes is as follows:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;appfuse-light-jsf-archetype&lt;/li&gt;
&lt;li&gt;appfuse-light-spring-archetype&lt;/li&gt;
&lt;li&gt;appfuse-light-spring-freemarker-archetype&lt;/li&gt;
&lt;li&gt;appfuse-light-spring-security-archetype&lt;/li&gt;
&lt;li&gt;appfuse-light-stripes-archetype&lt;/li&gt;
&lt;li&gt;appfuse-light-struts-archetype&lt;/li&gt;
&lt;li&gt;appfuse-light-tapestry-archetype&lt;/li&gt;
&lt;li&gt;appfuse-light-wicket-archetype&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
The &lt;a href=&quot;http://appfuse.org/display/APF/AppFuse+QuickStart&quot;&gt;QuickStart Guide&lt;/a&gt; will help you get setup and demos are available at the following links:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://demo2.appfuse.org/appfuse-light-jsf&quot;&gt;JSF 2 + Hibernate&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://demo2.appfuse.org/appfuse-light-spring&quot;&gt;Spring MVC 3 + Hibernate&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://demo2.appfuse.org/appfuse-light-spring-freemarker&quot;&gt;Spring MVC 3 + FreeMarker + Hibernate&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://demo2.appfuse.org/appfuse-light-spring-security&quot;&gt;Spring MVC 3 + Spring Security + Hibernate&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://demo2.appfuse.org/appfuse-light-stripes&quot;&gt;Stripes + Hibernate&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://demo2.appfuse.org/appfuse-light-struts&quot;&gt;Struts 2 + Hibernate&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://demo2.appfuse.org/appfuse-light-tapestry&quot;&gt;Tapestry 5 + Hibernate&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://demo2.appfuse.org/appfuse-light-wicket&quot;&gt;Wicket + Hibernate&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have questions about AppFuse, we invite you to ask them on the &lt;a href=&quot;http://appfuse.org/display/APF/Mailing+Lists&quot;&gt;users mailing list&lt;/a&gt; or tweet using #appfuse. &lt;/p&gt;
&lt;p&gt;
For those enjoying &lt;a href=&quot;http://twitter.github.com/bootstrap/&quot;&gt;Bootstrap&lt;/a&gt; in your apps, I encourage you to check out &lt;a href=&quot;https://wrapbootstrap.com/&quot;&gt;{wrap}bootstrap&lt;/a&gt; and &lt;a href=&quot;http://bootswatch.com/&quot;&gt;Bootswatch&lt;/a&gt;.&lt;/p&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/what_s_the_best_way4</id>
        <title type="html">What&apos;s the best way to compare JVM Web Frameworks?</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/what_s_the_best_way4"/>
        <published>2013-01-09T08:29:17-07:00</published>
        <updated>2013-01-14T15:03:01-07:00</updated> 
        <category term="/Java" label="Java" />
        <category term="webframeworks" scheme="http://roller.apache.org/ns/tags/" />
        <category term="dzone" scheme="http://roller.apache.org/ns/tags/" />
        <category term="infoq" scheme="http://roller.apache.org/ns/tags/" />
        <summary type="html">&lt;p&gt;
I&apos;ve been comparing web frameworks ever since 2004. It was the first time I&apos;d ever proposed a talk for a conference. ApacheCon was in Vegas that year and my buddy Bruce suggested I speak at it. I submitted the talk, &lt;a href=&quot;http://raibledesigns.com/rd/entry/going_to_apachecon&quot;&gt;got accepted&lt;/a&gt; and went to work learning the frameworks I was talking about. At the time, I had a lot of Struts experience and I&apos;d made a good living learning it, consulting on it and blogging about it. However, there was a new kid on the block (Spring MVC) that was garnishing attention and some other frameworks (WebWork and Tapestry) that had a lot of high praise from developers. I was inspired to learn why so many people hated Struts. 
&lt;/p&gt;
&lt;p&gt;
Fast forward 8 years and I&apos;m still comparing web frameworks. Why? Because there still seems to be a large audience that&apos;s interested in the topic. Witness InfoQ&apos;s &lt;a href=&quot;http://www.infoq.com/research/jvm-web-frameworks&quot;&gt;Top 20 JVM Web Frameworks&lt;/a&gt;, which was one of their most-read articles for two months in a row. One of the beauties of the Java Community is that it&apos;s very diverse. There&apos;s &lt;em&gt;tons&lt;/em&gt; of folks that are part of this community and, like it or not, several folks that are &lt;em&gt;former&lt;/em&gt; Java Developers. However, these developers still seem to maintain an interest in the community and it&apos;s still one of the largest pools of talent out there. Java is still &lt;a href=&quot;http://redmonk.com/sogrady/2012/02/08/language-rankings-2-2012/&quot;&gt;quite viable&lt;/a&gt; and only seems to be &lt;a href=&quot;http://frankhinkel.blogspot.de/2012/11/java-8-closures-lambda-expressions.html&quot;&gt;getting better with age&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
So the topic of web frameworks on the JVM is still hot, and I still &lt;a href=&quot;http://raibledesigns.com/rd/entry/why_the_bias_against_jsf&quot;&gt;like to write about it&lt;/a&gt;. For those of you still enthusiastic about the topic, you&apos;re in luck. The two best websites for the Java Community, &lt;a href=&quot;http://infoq.com&quot;&gt;InfoQ&lt;/a&gt; and &lt;a href=&quot;http://java.dzone.com&quot;&gt;DZone&lt;/a&gt; (formerly Javalobby) are still very interested in the topic too!&lt;/p&gt;</summary>
        <content type="html">&lt;p&gt;
I&apos;ve been comparing web frameworks ever since 2004. It was the first time I&apos;d ever proposed a talk for a conference. ApacheCon was in Vegas that year and my buddy Bruce suggested I speak at it. I submitted the talk, &lt;a href=&quot;http://raibledesigns.com/rd/entry/going_to_apachecon&quot;&gt;got accepted&lt;/a&gt; and went to work learning the frameworks I was talking about. At the time, I had a lot of Struts experience and I&apos;d made a good living learning it, consulting on it and blogging about it. However, there was a new kid on the block (Spring MVC) that was garnishing attention and some other frameworks (WebWork and Tapestry) that had a lot of high praise from developers. I was inspired to learn why so many people hated Struts.
&lt;/p&gt;
&lt;p&gt;
Fast forward 8 years and I&apos;m still comparing web frameworks. Why? Because there still seems to be a large audience that&apos;s interested in the topic. Witness InfoQ&apos;s &lt;a href=&quot;http://www.infoq.com/research/jvm-web-frameworks&quot;&gt;Top 20 JVM Web Frameworks&lt;/a&gt;, which was one of their most-read articles for two months in a row. One of the beauties of the Java Community is that it&apos;s very diverse. There&apos;s &lt;em&gt;tons&lt;/em&gt; of folks that are part of this community and, like it or not, several folks that are &lt;em&gt;former&lt;/em&gt; Java Developers. However, these developers still seem to maintain an interest in the community and it&apos;s still one of the largest pools of talent out there. Java is still &lt;a href=&quot;http://redmonk.com/sogrady/2012/02/08/language-rankings-2-2012/&quot;&gt;quite viable&lt;/a&gt; and only seems to be &lt;a href=&quot;http://frankhinkel.blogspot.de/2012/11/java-8-closures-lambda-expressions.html&quot;&gt;getting better with age&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
So the topic of web frameworks on the JVM is still hot, and I still &lt;a href=&quot;http://raibledesigns.com/rd/entry/why_the_bias_against_jsf&quot;&gt;like to write about it&lt;/a&gt;. For those of you still enthusiastic about the topic, you&apos;re in luck. The two best websites for the Java Community, &lt;a href=&quot;http://infoq.com&quot;&gt;InfoQ&lt;/a&gt; and &lt;a href=&quot;http://java.dzone.com&quot;&gt;DZone&lt;/a&gt; (formerly Javalobby) are still very interested in the topic too!&lt;/p&gt;
&lt;!--p style=&quot;font-style: italic&quot;&gt;Sorry &lt;a href=&quot;http://theserverside.com&quot;&gt;TheServerSide.com&lt;/a&gt;, you were awesome at one time. Remember when Dion was pumping out the good content and there weren&apos;t ads in your face? Those where the days...&lt;/p--&gt; 
&lt;p&gt;
Both sites emailed me in November to get my advice for their research on the subject. InfoQ (specifically Dio Synodinos) was mostly interested in 1) having me analyze &lt;a href=&quot;http://www.infoq.com/research/jvm-web-frameworks&quot;&gt;their recently-gathered data&lt;/a&gt;, or 2) helping them create a new version. DZone (specifically Mitch Pronschinske) emailed about doing a similar survey to InfoQ&apos;s, but with more relevant data points (include GWT, specifying Struts 2 vs. Struts 1, etc.).
&lt;/p&gt;
&lt;p&gt;
My response to Mitch at DZone:
&lt;/p&gt;
&lt;div class=&quot;quote&quot;&gt;
&lt;p&gt;
Interestingly enough, the folks at InfoQ contacted me as well as they&apos;re thinking of doing a new survey. One of the things I mentioned to them is it&apos;d be interesting to see what folks are using AND which frameworks they admire. Often, devs don&apos;t get to choose their web framework at work. I wonder if it&apos;d be possible to collaborate with InfoQ to gather data from developers so it&apos;s not being done on two different sites?
&lt;/p&gt;
&lt;p&gt;
I submitted a talk for ApacheCon NA (in February) called Comparing Apache Web Frameworks. When choosing Web Frameworks, I&apos;ve often found it helps to eliminate frameworks and narrow the scope. Obviously, this makes sense for an Apache Conference, but not for all developers. However, I do plan on analyzing each framework based on a limited set of criteria. Here&apos;s what I have so far:
&lt;/p&gt;
&lt;p&gt;
Community, HTML5, REST, Mobile, Performance, Web Performance Optimization
&lt;/p&gt;
&lt;p&gt;
Obviously, community is important for Apache projects, but might not be for the wider audience. It might be good to limit to these 5 criteria, or expand it to 10, but not more. I think it&apos;d be interesting to get the community to rank the various frameworks on these criteria, and also try to find developer&apos;s biases while doing it. For example, I wonder if people would be willing to admit they&apos;re biased for/against certain frameworks and then take that into account as part of gathering the data?&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Unfortunately, ApacheCon denied my submission. This make sense since their &lt;a href=&quot;http://na.apachecon.com/schedule/&quot;&gt;schedule&lt;/a&gt; seems to be concentrating on &lt;a href=&quot;http://httpd.apache.org/&quot;&gt;HTTPD&lt;/a&gt;, &lt;a href=&quot;http://incubator.apache.org/cloudstack/&quot;&gt;Cloud&lt;/a&gt; and &lt;a href=&quot;http://hadoop.apache.org/&quot;&gt;Big Data&lt;/a&gt;.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;
For InfoQ, Dio asked for a list of web frameworks to include. Below is a list we started with, followed by my response.
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Spring MVC&lt;/li&gt;
&lt;li&gt;Play&lt;/li&gt;
&lt;li&gt;Grails&lt;/li&gt;
&lt;li&gt;JSF&lt;/li&gt;
&lt;li&gt;Struts 2&lt;/li&gt;
&lt;li&gt;Wicket&lt;/li&gt;
&lt;li&gt;Lift&lt;/li&gt;
&lt;li&gt;Tapestry 5&lt;/li&gt;
&lt;li&gt;Seam&lt;/li&gt;
&lt;li&gt;JRuby on Rails&lt;/li&gt;
&lt;li&gt;Wicket&lt;/li&gt;
&lt;li&gt;GWT&lt;/li&gt;
&lt;li&gt;Vaadin&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;quote&quot;&gt;
&lt;p&gt;
I would add Stripes, vert.x and maybe something like Apache Click. VRaptor is probably a good one to add too. There&apos;s always a few less-used frameworks that get a lot of complainers if you don&apos;t include them. 
&lt;/p&gt;
&lt;p&gt;
Comparing to your previous list, I don&apos;t think Seam should be in here since they&apos;ve &lt;a href=&quot;http://www.infoq.com/news/2012/04/seam-deltaspike&quot;&gt;split the project into separate bundles&lt;/a&gt; and are no longer developing Seam as a whole. JRuby on Rails is a tough one because if you say Ruby on Rails, you&apos;ll get a ton of responses, but probably not from the Java community. The Ruby community might chime it quite a bit if you can get in touch with them though.
&lt;/p&gt;
&lt;p&gt;
I believe you should include Clojure web frameworks, but I&apos;ve only heard of one of them: Compojure.
&lt;/p&gt;
&lt;p&gt;
SiteMesh, Netty, etc. - remove them.
&lt;/div&gt;
&lt;p&gt;
I also offered my advice on instructions:
&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
I actually like the two coordinates you used before, importance and adoptability. However, I don&apos;t know that everyone read the instructions this way. Most people didn&apos;t rank all frameworks and I believe that&apos;s part of the point. I only ranked the ones I&apos;d used, but I think it&apos;d be better if people ranked all of them. I also think having these two criteria opens it up to more than just developers. Project/Product Managers and stakeholders that&apos;ve been successful with certain frameworks should be able to vote too.
&lt;/p&gt;
&lt;p&gt;
For DZone, they wanted to include a set of criteria for ranking:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Project maturity, community support, and documentation quality (one criteria)&lt;/li&gt;
&lt;li&gt;UI Features Capability (maybe some are more graphics driven or form driven?)&lt;/li&gt;
&lt;li&gt;Code readability&lt;/li&gt;
&lt;li&gt;Flexibility (maybe this could be broken down into what types of projects the framework can handle) or several criteria that ask if it is &quot;Good for &apos;x&apos; type of project&quot;&lt;/li&gt;
&lt;li&gt;Performance/Speed&lt;/li&gt;
&lt;li&gt;Cross platform support&lt;/li&gt;
&lt;li&gt;Extensibility, Plugins, Community Libraries&lt;/li&gt;
&lt;li&gt;Architecture (this may just be information for later, not an opinion question)&lt;/li&gt;
&lt;li&gt;Web standards support&lt;/li&gt;
&lt;li&gt;REST support (is this something you would just rate a yes or no?  In that case it wouldn&apos;t need to be an opinion question)&lt;/li&gt;
&lt;li&gt;Mobile support&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
My response:&lt;/p&gt;

&lt;div class=&quot;quote&quot;&gt;
    &lt;p&gt;
For web frameworks, I believe the 5 I mentioned (Community, HTML5, REST, Mobile, Performance, Web Performance Optimization) are most important, with Security become more and more of a concern. For web standards support, I&apos;d almost change it to &quot;HTML5&quot; and to see how the various frameworks stack up. I think REST is very important, and I think it&apos;s cool that Struts 2, Spring MVC and Grails all have great support for it. It&apos;d be interesting to see how the component-based frameworks think of having REST support in the framework (vs. external like Jersey, CXF, etc.).
&lt;/p&gt;&lt;p&gt;
Of the list you provided, I don&apos;t know about Code readability or Flexibility. Code readability is kinda like Learnability. One of the nice things about Spring MVC and Grails is that you can learn how they work very quickly. Then you can use that knowledge and don&apos;t have to look things up much. Tapestry and Wicket might be similar for those writing large apps, but I haven&apos;t found that to be as true. The more traditional MVC Frameworks just make more sense to me.
&lt;/p&gt;
&lt;p&gt;
UI Feature Capability is a good one because frameworks with widgets are often popular with developers. Flex, GWT, jQuery UI, Sencha all do this very well.
&lt;/p&gt;
&lt;/div&gt;
&lt;p id=&quot;summary&quot;&gt;
&lt;strong&gt;Summary&lt;/strong&gt;&lt;br/&gt;The reason for this post is to add some transparency to the process of Comparing JVM Web Frameworks. I like to think that I&apos;ve tried to do this in the past (especially with my &lt;a href=&quot;http://raibledesigns.com/rd/entry/how_i_calculated_ratings_for&quot;&gt;reasons for rankings&lt;/a&gt;). Now, we&apos;d like to hear from you, the community that uses these web frameworks. 
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;What&apos;s the best way to compare JVM Web Frameworks?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;While it&apos;s nice to hear from the generous folks that create and maintain JVM Web Frameworks, we&apos;re mostly interested in hearing from the developers that are using these things on a day-to-day basis. The Blue Collar Developers, if you will. If you could design a JVM Web Framework comparison that answered your questions, how would it look? What questions would it ask? What conclusions would make you happy? Should commercial frameworks like ZK be included?
    &lt;/p&gt;
    &lt;p&gt;Your responses are very greatly appreciated.</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/appfuse_2_2_1_released</id>
        <title type="html">AppFuse 2.2.1 Released!</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/appfuse_2_2_1_released"/>
        <published>2012-12-11T15:21:44-07:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="maven" scheme="http://roller.apache.org/ns/tags/" />
        <category term="security" scheme="http://roller.apache.org/ns/tags/" />
        <category term="appfuse" scheme="http://roller.apache.org/ns/tags/" />
        <category term="java" scheme="http://roller.apache.org/ns/tags/" />
        <category term="hibernate" scheme="http://roller.apache.org/ns/tags/" />
        <category term="spring" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">&lt;a href=&quot;http://appfuse.org&quot;&gt;&lt;img src=&quot;//static.appfuse.org/images/appfuse-icon.gif&quot; class=&quot;picture&quot; style=&quot;border: 0&quot;&gt;&lt;/a&gt;
The AppFuse Team is pleased to announce the release of AppFuse 2.2.1. This release includes upgrades to all dependencies to bring them up-to-date with their latest releases. Most notable are Hibernate 4, Struts 2.3.7, Apache CXF 2.7.0 and Spring Security 3.1.3. In addition, we&apos;ve integrated HTML5, Twitter Bootstrap, jQuery and replaced Compass with Hibernate Search. Last but not least, we&apos;ve added full support for Java 7 and integrated many &lt;a href=&quot;http://raibledesigns.com/rd/entry/java_web_application_security_part4&quot;&gt;security improvements&lt;/a&gt;. For more details on specific changes see the &lt;a href=&quot;http://appfuse.org/display/APF/Release+Notes+2.2.1&quot;&gt;release notes&lt;/a&gt;.&lt;/p&gt;

&lt;p class=&quot;quote&quot;&gt;&lt;strong&gt;What is AppFuse?&lt;/strong&gt;&lt;br/&gt;AppFuse is a full-stack framework for building web applications on the JVM. It was originally developed to eliminate the ramp-up time when building new web applications. Over the years, it has matured into a very testable and secure system for creating Java-based webapps.
&lt;/p&gt;

&lt;p&gt;Demos for this release can be viewed at &lt;a href=&quot;http://demo.appfuse.org/&quot;&gt;http://demo.appfuse.org&lt;/a&gt;. Please see the &lt;a href=&quot;http://appfuse.org/display/APF/AppFuse+QuickStart&quot; style=&quot;font-weight: bold&quot;&gt;QuickStart Guide&lt;/a&gt; to get started with this release. &lt;/p&gt;

&lt;p&gt;A number of blog posts were written about features that went into this release while it was being developed:&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/improving_appfuse_s_pagespeed_with&quot;&gt;Improving AppFuse&apos;s PageSpeed with Apache&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/appfuse_news_liquibase_and_wro4j&quot;&gt;New Liquibase and wro4j Tutorials from J. Garc&#237;a&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/appfuse_news_github_hibernate_search&quot;&gt;AppFuse News: GitHub, Hibernate Search and The Future&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/refreshing_appfuse_s_ui_with&quot;&gt;Refreshing AppFuse&apos;s UI with Twitter Bootstrap&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/upgrading_appfuse_to_spring_security&quot;&gt;Upgrading AppFuse to Spring Security 3.1 and Spring 3.1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/integrating_oauth_with_appfuse_and&quot;&gt;Integrating OAuth with AppFuse and its REST API&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;

&lt;p&gt;If you have questions about AppFuse, please read the &lt;a href=&quot;http://appfuse.org/display/APF/FAQ&quot;&gt;FAQ&lt;/a&gt; or join the &lt;a href=&quot;http://appfuse.org/display/APF/Mailing+Lists&quot;&gt;mailing list&lt;/a&gt;. If you find any issues, please &lt;a href=&quot;http://appfuse.547863.n4.nabble.com/ANN-AppFuse-2-2-1-Released-td4656067.html&quot;&gt;report them on the users mailing list&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks to everyone for their help contributing patches, writing documentation and participating on the mailing lists.&lt;/p&gt;
&lt;p style=&quot;border-top: 1px dotted silver; padding-top: 5px; color: #666&quot;&gt;We greatly appreciate the help from &lt;a href=&quot;http://appfuse.org/display/APF/Sponsors&quot; title=&quot;Sponsors&quot; style=&quot;color: #666&quot;&gt;our sponsors&lt;/a&gt;, particularly &lt;a href=&quot;http://www.atlassian.com/c/NPOS/10160&quot; style=&quot;color: #666&quot;&gt;Atlassian&lt;/a&gt;, &lt;a href=&quot;http://contegix.com&quot; style=&quot;color: #666&quot;&gt;Contegix&lt;/a&gt; and &lt;a href=&quot;http://www.jetbrains.com&quot; style=&quot;color: #666&quot;&gt;JetBrains&lt;/a&gt;. We highly recommend using the new &lt;a href=&quot;http://blogs.jetbrains.com/idea/2012/12/intellij-idea-12-is-available-for-download/&quot;&gt;IntelliJ IDEA 12&lt;/a&gt; for developing web applications.&lt;/p&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/improving_appfuse_s_pagespeed_with</id>
        <title type="html">Improving AppFuse&apos;s PageSpeed with Apache</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/improving_appfuse_s_pagespeed_with"/>
        <published>2012-12-04T09:25:05-07:00</published>
        <updated>2012-12-05T02:34:08-07:00</updated> 
        <category term="/Java" label="Java" />
        <category term="contegix" scheme="http://roller.apache.org/ns/tags/" />
        <category term="appfuse" scheme="http://roller.apache.org/ns/tags/" />
        <category term="apache" scheme="http://roller.apache.org/ns/tags/" />
        <category term="pagespeed" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">&lt;p&gt;One of the most important things when developing webapps is to make them &lt;em&gt;fast&lt;/em&gt;. With AppFuse, we&apos;ve tried to incorporate many of the &lt;a href=&quot;http://stevesouders.com/hpws/rules.php&quot;&gt;14 rules for faster-loading websites&lt;/a&gt;. We had a gzip filter before it was cool (2003) and replaced it with &lt;a href=&quot;http://ehcache.org/documentation/user-guide/web-caching&quot;&gt;the one from EhCache&lt;/a&gt;. However, users experienced issues with both of these, both &lt;a href=&quot;http://issues.appfuse.org/browse/APF-863&quot;&gt;with XFire/CXF&lt;/a&gt; and &lt;a href=&quot;http://issues.appfuse.org/browse/APF-1037&quot;&gt;WebWork/Struts 2 and JSPs&lt;/a&gt;. Because of these issues, we disabled gzipping a few releases ago. &lt;/p&gt;

&lt;p&gt;This article is designed to show you how you can make your AppFuse webapp faster, without modifying any code. The good news is this applies to any webapp that you can deploy behind Apache. &lt;/p&gt;

&lt;p&gt;Last Friday, I sent an email to the good folks at &lt;a href=&quot;http://contegix.com&quot;&gt;Contegix&lt;/a&gt; to see if they could install &lt;a href=&quot;https://developers.google.com/speed/pagespeed/mod&quot;&gt;mod_pagespeed&lt;/a&gt; on the Apache server that sits in front of *.appfuse.org. My goal was to improve the YSlow and PageSpeed scores of the apps hosted on &lt;a href=&quot;http://demo.appfuse.or&quot;&gt;demo.appfuse.org&lt;/a&gt;. I discovered they were getting a dismal score of 24 and figured we could do a lot better. mod_pagespeed speeds up your site and reduces page load time by automatically applying web performance best practices. It seemed like an easy solution.
&lt;/p&gt;

&lt;p&gt;Unfortunately, we were unable to use mod_pagespeed. From the guys at Contegix:&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;Attempting to install mod_pagespeed as you requested, we find that it requires Apache httpd 2.2 and libstdc++ 4.1.2, both of which are unsupported in RHEL4. To get mod_pagespeed to work on your present operating system basically means re-rolling the core components, which would make them unsupported. I&apos;m afraid mod_pagespeed is simply not an option on your present configuration.&lt;/p&gt;

&lt;p&gt;Since I still wanted to improve performance, I opted for another route instead: using mod_deflate (for gzipping) and mod_expires (for expires headers). I also turned on KeepAlive as recommended by &lt;a href=&quot;https://developers.google.com/speed/docs/insights/using_chrome&quot;&gt;PageSpeed Insights&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;mod_deflate&lt;/strong&gt;&lt;br&gt;mod_deflate was already installed in Apache (version 2.0.52), so all I had to do was configure it. On RHEL4, Apache is installed at /etc/httpd and there&apos;s a &lt;em&gt;conf.d&lt;/em&gt; directory that contains all the configuration files. I created a file at &lt;em&gt;/etc/httpd/conf.d/deflate.conf&lt;/em&gt; and populated it with the following:&lt;/p&gt;

&lt;pre&gt;#
# mod_deflate configuration
#
&amp;lt;IfModule mod_deflate.c&gt;
    SetOutputFilter DEFLATE
    
    AddOutputFilterByType DEFLATE text/plain text/html text/xml text/css application/xml application/xhtml+xml application/rss+xml application/javascript application/x-javascript
    
    DeflateCompressionLevel 9
    
    BrowserMatch ^Mozilla/4 gzip-only-text/html
    BrowserMatch ^Mozilla/4\.0[678] no-gzip
    BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
    
    DeflateFilterNote Input instream
    DeflateFilterNote Output outstream
    DeflateFilterNote Ratio ratio
    
    LogFormat &apos;&quot;%r&quot; %{outstream}n/%{instream}n (%{ratio}n%%)&apos; deflate
    
&amp;lt;/IfModule&gt;&lt;/pre&gt;

&lt;p&gt;At first, I had separate lines for all the different content types (as recommended by &lt;a href=&quot;http://www.g-loaded.eu/2008/05/10/use-mod_deflate-to-compress-web-content-delivered-by-apache/&quot;&gt;this article&lt;/a&gt;). The Contegix support crew figured out the solution (everything needed to be on one line) in 14 minutes, updated the config and verified it worked using &lt;a href=&quot;http://www.whatsmyip.org/http-compression-test/&quot;&gt;an http compression testing page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;mod_expires&lt;/strong&gt;&lt;br&gt;mod_expires was already installed, so I added a config file at &lt;em&gt;/etc/httpd/conf.d/expires.conf&lt;/em&gt;. I used &lt;a href=&quot;http://www.howtoforge.com/make-browsers-cache-static-files-with-mod_expires-on-apache2-debian-squeeze&quot;&gt;this howto&lt;/a&gt; and asked Contegix for help when it didn&apos;t work. Their response took quite a bit longer this time (49 minutes), but they once again figured it out:&lt;/p&gt;

&lt;p class=&quot;quote&quot;&gt;It appears that FilesMatch does not like to play will JkMount. It does work using content type.&lt;/p&gt;

&lt;p&gt;My final config for &lt;em&gt;expires.conf&lt;/em&gt;:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;IfModule mod_expires.c&gt;
    ExpiresActive On
    
    &amp;lt;FilesMatch &quot;\.(jpe?g|png|gif|js|css)$&quot;&gt;
        ExpiresDefault &quot;access plus 1 week&quot;
    &amp;lt;/FilesMatch&gt;
    
    ExpiresByType image/jpeg &quot;access plus 1 week&quot;
    ExpiresByType image/png &quot;access plus 1 week&quot;
    ExpiresByType image/gif &quot;access plus 1 week&quot;
    ExpiresByType text/css &quot;access plus 1 week&quot;
    ExpiresByType application/javascript &quot;access plus 1 week&quot;
    ExpiresByType application/x-javascript &quot;access plus 1 week&quot;
&amp;lt;/IfModule&gt;
&lt;/pre&gt;

&lt;p&gt;I used &quot;1 week&quot; because we&apos;re changing things quite a bit right now and we haven&apos;t integrated &lt;a href=&quot;http://www.operatornew.com/2012/10/adding-web-resource-fingerprinting-to.html&quot;&gt;resource fingerprinting&lt;/a&gt; yet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;KeepAlive&lt;br&gt;&lt;/strong&gt;The last thing I did to improve performance was to turn on KeepAlive by editing &lt;em&gt;/etc/httpd/conf/httpd.conf &lt;/em&gt;and changing &lt;em&gt;Off &lt;/em&gt;to &lt;em&gt;On&lt;/em&gt;.&lt;/p&gt;
&lt;pre&gt;#
# KeepAlive: Whether or not to allow persistent connections (more than
# one request per connection). Set to &quot;Off&quot; to deactivate.
#
KeepAlive On&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;Summary&lt;br&gt;&lt;/strong&gt;As a result of these changes, our PageSpeed score went from 24 to 96 and YSlow went from a 90 to a 98. When I started this experiment, I was only trying to fix demo.appfuse.org. However, it also improved the speed of all the other *.appfuse.org sites, including &lt;a href=&quot;http://appfuse.org&quot;&gt;Confluence&lt;/a&gt;, &lt;a href=&quot;http://builds.appfuse.or&quot;&gt;Bamboo&lt;/a&gt;, &lt;a href=&quot;http://issues.appfuse.or&quot;&gt;JIRA&lt;/a&gt; and &lt;a href=&quot;http://source.appfuse.org&quot;&gt;FishEye&lt;/a&gt;. Thanks for all the help Contegix! There&apos;s a good chance you&apos;ve given me back a few minutes in each day.&lt;/p&gt;
&lt;p style=&quot;text-align: right; font-size: .95em; margin-top: -10px&quot;&gt;&lt;em&gt;Originally posted &lt;a href=&quot;http://appfuse.org/display/APF/2012/12/04/Improving+AppFuse%27s+PageSpeed+with+Apache&quot;&gt;on the AppFuse Blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/why_the_bias_against_jsf</id>
        <title type="html">Why the bias against JSF?</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/why_the_bias_against_jsf"/>
        <published>2012-11-08T09:24:27-07:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="java" scheme="http://roller.apache.org/ns/tags/" />
        <category term="jvm" scheme="http://roller.apache.org/ns/tags/" />
        <category term="jsf" scheme="http://roller.apache.org/ns/tags/" />
        <category term="webframeworks" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">In my last post about &lt;a href=&quot;http://raibledesigns.com/rd/entry/infoq_s_top_20_web&quot;&gt;InfoQ&apos;s Top 20 Web Frameworks for the JVM&lt;/a&gt;, I received a thought-provoking &lt;a href=&quot;http://raibledesigns.com/rd/entry/infoq_s_top_20_web#comment-1352305197000&quot;&gt;comment&lt;/a&gt; from &lt;a href=&quot;http://henk53.wordpress.com/&quot;&gt;henk53&lt;/a&gt;:&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
There is one little thing that does bother me in those presentations, and that&apos;s your fairly obvious bias against JSF. &lt;br/&gt;...&lt;br/&gt;
If you are presenting yourself as, more or less, an authority on comparing web frameworks, then having a fairly obvious biased against one of them is just peculiar. I, all of my team, and various clients distrust your ranking of JSF. We do look at your data if the choice is between other frameworks, but as soon as JSF comes into the picture we just look elsewhere.
&lt;br/&gt;&lt;br/&gt;
I&apos;m not really sure where this bias comes from. Yes, JSF 1.0 sucked and 1.2 was only marginally better, but 2.0 is really cool and productive and there are SUPERB component and utility libraries now like PrimeFaces and OmniFaces. As a researcher of this topic I think you should keep up the date and not stick to some old grudge.
&lt;/p&gt;
&lt;p&gt;This is true, I am biased against JSF. It all started with my &lt;a href=&quot;http://raibledesigns.com/rd/entry/my_jsf_experience&quot;&gt;first JSF experience&lt;/a&gt; back in August 2004. If you remember correctly, 2004 was a big year: JSF 1.0, Spring 1.0 and Flex 1.0 were all released. The &quot;AJAX&quot; term was coined in &lt;a href=&quot;http://www.adaptivepath.com/ideas/ajax-new-approach-web-applications&quot;&gt;early 2005&lt;/a&gt;.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://www.flickr.com/photos/mraible/4378559350/&quot; title=&quot;History of Web Frameworks by mraible, on Flickr&quot;&gt;&lt;img src=&quot;//farm5.staticflickr.com/4067/4378559350_13f0755403.jpg&quot; width=&quot;500&quot; height=&quot;234&quot; alt=&quot;History of Web Frameworks&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;By &lt;a href=&quot;http://raibledesigns.com/rd/entry/jsf_still_sucks&quot;&gt;2007&lt;/a&gt; and &lt;a href=&quot;http://raibledesigns.com/rd/entry/what_s_wrong_with_jsf&quot;&gt;2008&lt;/a&gt;, JSF still hadn&apos;t gotten any better. In late 2009, JSF 2.0 was released and &lt;a href=&quot;http://raibledesigns.com/rd/entry/upgrading_to_jsf_2&quot;&gt;I upgraded in March 2011&lt;/a&gt;. As you can see from the aforementioned post, I ran into quite a few issues upgrading. JSF was also the &lt;a href=&quot;http://raibledesigns.com/rd/entry/implementing_extensionless_urls_with_tapestry&quot;&gt;hardest one to get working with extension-less URLs&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;Most of my issues with JSF come from having maintained an application built with it since 2004. If I were to start a new application without any legacy migration issues, I imagine it wouldn&apos;t be as difficult. However, if you compare it to Struts 2 and Spring MVC, I&apos;ve had little-to-no issues upgrading those applications over the years. &lt;/p&gt;
&lt;p&gt;Also, I&apos;m not just biased against JSF, but most component-based web frameworks. Just ask the Tapestry and Wicket folks. They&apos;ve felt my criticisms over the years. My reason for preferring request-based frameworks like Struts 2/Spring MVC and Grails/Play has been because I&apos;ve never seen the appeal in component-based frameworks. Often I&apos;ve found that their components are just widgets that you can get from any decent JavaScript framework. And chances are that JavaScript framework can work with &lt;em&gt;any&lt;/em&gt; web framework. Also, I&apos;ve worked on a lot of high-traffic web applications that require statelessness for scalability.&lt;/p&gt;
&lt;p&gt;I see the value in component-based frameworks, I just don&apos;t think components should be authored on the server-side. Most of the Java-based component frameworks require 2+ files for components (one for the component, one for the view, possibly one for the config). I love GWT&apos;s component concept in that you can just extract a class and re-use it. With JS frameworks, you can often just include a script. These days, when I think of good component-based frameworks, I think of jQuery UI and Twitter Bootstrap.&lt;/p&gt;
&lt;p&gt;All that being said, there&apos;s a lot of folks praising JSF 2 (and &lt;a href=&quot;http://primefaces.org/&quot;&gt;PrimeFaces&lt;/a&gt; moreso). That&apos;s why I&apos;ll be integrating it (or merging your pull request) into the 2.3 release of AppFuse. Since PrimeFaces contains a Bootstrap theme, I hope this is a pleasant experience and my overall opinion of JSF improves.&lt;/p&gt;
&lt;p&gt;In other component-based frameworks in AppFuse news, Tapestry 5 has gotten &lt;em&gt;really fast&lt;/em&gt; in the last year. I imagine this is because we have a Tapestry expert, &lt;a href=&quot;http://www.linkedin.com/in/sergeeby&quot;&gt;Serge Eby&lt;/a&gt;, working on it. And we&apos;re planning on adding Wicket in the 2.3 release. &lt;/p&gt;
&lt;p&gt;So even though I &lt;em&gt;prefer&lt;/em&gt; request-based frameworks with REST support and Bootstrap, that doesn&apos;t mean everyone does. I&apos;ll do my best to be less-biased in the future. However, please remember that my view on web frameworks is as a developer, not an analyst. And aren&apos;t developers &lt;em&gt;supposed&lt;/em&gt; to be opinionated? &lt;img src=&quot;https://raibledesigns.com/images/smileys/wink.gif&quot; class=&quot;smiley&quot; alt=&quot;;)&quot; title=&quot;;)&quot; /&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/appfuse_news_github_hibernate_search</id>
        <title type="html">AppFuse News: GitHub, Hibernate Search and The Future</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/appfuse_news_github_hibernate_search"/>
        <published>2012-09-25T10:42:14-06:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="github" scheme="http://roller.apache.org/ns/tags/" />
        <category term="appfuse" scheme="http://roller.apache.org/ns/tags/" />
        <category term="roadmap" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">&lt;p&gt;It&apos;s been a while since I&apos;ve written anything about &lt;a href=&quot;http://appfuse.org&quot;&gt;AppFuse&lt;/a&gt;, but since the project
  has had quite a bit of activity lately, now seems like a good time.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;GitHub&lt;/strong&gt;&lt;br&gt;First of all,
  we moved the source code from java.net to &lt;a href=&quot;https://github.com/appfuse/appfuse&quot;&gt;GitHub&lt;/a&gt; way back
  in June. Thanks to &lt;a href=&quot;https://twitter.com/sdeby&quot;&gt;Serge&lt;/a&gt; for helping with
  this process and making it &lt;a href=&quot;http://appfuse.547863.n4.nabble.com/Migrating-to-GitHub-td4655148.html&quot;&gt;quick and
    painless&lt;/a&gt;. For some reason, shortly
  after moving, we started having quite a few build issues with &lt;a href=&quot;http://builds.appfuse.org&quot;&gt;Bamboo&lt;/a&gt;.
  I was able to diagnose the
  problem as not enough memory on our server. Thankfully, &lt;a href=&quot;http://www.contegix.com/&quot;&gt;Contegix&lt;/a&gt; was able to
  add another 2GB of
  RAM to our box and get everything back up-to-snuff.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;New Committer: J. Garcia&lt;/strong&gt;&lt;br&gt;
  J. Garcia has been a regular voice on the users and developers mailing list for several
  months. He&apos;s recently started contributing a lot of patches in JIRA and seems genuinely interested in the success of
  AppFuse. That&apos;s why we voted and &lt;a
        href=&quot;http://appfuse.547863.n4.nabble.com/VOTE-Add-J-Garcia-as-an-AppFuse-Committer-td4655395.html&quot;&gt;added him
    as a committer&lt;/a&gt;. To prove this was a smart move, he recently
  &lt;a href=&quot;http://issues.appfuse.org/browse/APF-1298&quot;&gt;replaced Compass with
    Hibernate Search&lt;/a&gt; and &lt;a href=&quot;http://issues.appfuse.org/browse/APF-1297&quot;&gt;upgraded to Hibernate 4&lt;/a&gt;.
  As part of this work, he removed iBATIS support, which brings me to my next point.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Future&lt;/strong&gt;&lt;br&gt;

  In mid-August, I sent an email to the
  community, asking them &quot;&lt;a href=&quot;http://appfuse.547863.n4.nabble.com/Anyone-using-iBATIS-td4655369.html&quot;&gt;Anyone using iBATIS?&lt;/a&gt;&quot;&lt;/p&gt;

&lt;p class=&quot;quote&quot;&gt;I&apos;m thinking of replacing AppFuse&apos;s Data Tier with Spring Data, especially because it has NoSQL and REST support. There&apos;s a good intro on InfoQ today: &lt;br&gt;&lt;br&gt;&lt;a href=&quot;http://www.infoq.com/articles/spring-data-intro&quot;&gt;http://www.infoq.com/articles/spring-data-intro&lt;/span&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
Does anyone see an issue with this? The lack of iBATIS support could be an issue, but I doubt it since if we wanted to continue supporting it, we should move to MyBATIS.
&lt;/p&gt;

&lt;p&gt;Everyone agreed this was a good idea and it seemed like a logical time to remove iBATIS support. In
  addition, I posted a roadmap I jotted down in early May. Since we&apos;ve missed all the dates so far, I&apos;ve removed them
  from the listing below. We hope to get 1-2 releases done by the end of this year, with 2.2 in the next 2-3 weeks.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;2.2&lt;/strong&gt;&lt;br&gt;Hibernate 4&lt;br&gt;Hibernate Search&lt;br&gt;Bootstrap&lt;br&gt;H5BP&lt;/p&gt;&lt;p&gt;&lt;strong&gt;2.3&lt;/strong&gt; &lt;br&gt;AMP for
  all light modules&lt;br&gt;Wicket&lt;br&gt;PrimeFaces&lt;/p&gt;&lt;p&gt;&lt;strong&gt;2.4&lt;/strong&gt;&lt;br&gt;JSR 303 (might require removing or developing
  client-side support)&lt;br&gt;Mockito instead of jMock/EasyMock&lt;/p&gt;&lt;p&gt;&lt;strong&gt;2.5&lt;/strong&gt;&lt;br&gt;AMP one-to-many&lt;br&gt;Spring Data&lt;br&gt;MyBatis
  (if there&apos;s interest in adding it back in)&lt;/p&gt;&lt;p&gt;&lt;strong&gt;2.6&lt;/strong&gt;&lt;br&gt;wro4j for concatenation and minimizing JS and
  CSS&lt;br&gt;pjax - &lt;a href=&quot;https://github.com/defunkt/jquery-pjax&quot;&gt;https://github.com/defunkt/jquery-pjax&lt;/a&gt;
&lt;/p&gt;&lt;p&gt;&lt;strong&gt;2.7&lt;/strong&gt;&lt;br&gt;Scala example&lt;br&gt;Gradle example&lt;br&gt;Article about examples&lt;/p&gt;&lt;p&gt;&lt;strong&gt;3.0&lt;/strong&gt;&lt;br&gt;GWT
&lt;/p&gt;
&lt;/blockquote&gt;

  &lt;p&gt;&lt;strong&gt;Maven Central Statistics&lt;/strong&gt;&lt;br&gt;To prove there&apos;s still a fair amount of folks using
    AppFuse, here&apos;s some statistic from Sonatype&apos;s &lt;a href=&quot;https://oss.sonatype.org&quot;&gt;OSS Repository Hosting
      Service&lt;/a&gt;.&lt;/p&gt;
  &lt;p style=&quot;text-align: center&quot;&gt;&lt;a rev=&quot;http://www.flickr.com/photos/mraible/8023640406/&quot; href=&quot;http://farm9.staticflickr.com/8452/8023640406_6df4a3c6f8_o.png&quot; rel=&quot;lightbox[appfuse-201209]&quot;
                                        title=&quot;AppFuse Downloads&quot;&gt;&lt;img
        src=&quot;//farm9.staticflickr.com/8452/8023640406_a7e3613b94.jpg&quot; width=&quot;500&quot; height=&quot;188&quot;
        alt=&quot;AppFuse Downloads&quot;&gt;&lt;/a&gt;&lt;/p&gt;

    &lt;p&gt;From this screenshot, you can see that AppFuse artifacts are downloaded around 7,000 times per month. The
      following graph is even more interesting. Apparently, around 3,000 new projects are created with AppFuse
      archetypes each month.&lt;/p&gt;

    &lt;p style=&quot;text-align: center&quot;&gt;
      &lt;a rev=&quot;http://www.flickr.com/photos/mraible/8023642969/&quot; href=&quot;http://farm9.staticflickr.com/8182/8023642969_00801e1ce9_o.png&quot; rel=&quot;lightbox[appfuse-201209]&quot;
         title=&quot;AppFuse Archetype Downloads&quot;&gt;&lt;img
            src=&quot;//farm9.staticflickr.com/8182/8023642969_14111307ca.jpg&quot; width=&quot;500&quot; height=&quot;186&quot;
            alt=&quot;AppFuse Archetype Downloads&quot;&gt;&lt;/a&gt;&lt;/p&gt;

    &lt;p&gt;&lt;strong&gt;The AppFuse Name&lt;br&gt;&lt;/strong&gt;Finally, I &lt;a href=&quot;https://twitter.com/#!/search/?q=appfuse&quot;&gt;recently discovered&lt;/a&gt;
      that ShoreTel decided to &lt;a
            href=&quot;http://www.zdnet.com/shoretel-intros-appfuse-integrated-phone-system-aimed-at-salesforce-customers-7000004374/&quot;
            &gt;name a new product AppFuse&lt;/a&gt;. I guess this signifies two things: 1)
      it&apos;s a good name for a product and 2) someone didn&apos;t do their research before naming it. At this point, I&apos;m not
      too concerned, but it is an interesting development.&lt;/p&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/refreshing_taleo_s_ui_with</id>
        <title type="html">Refreshing Taleo&apos;s UI with HTML5, Twitter Bootstrap and CSS3</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/refreshing_taleo_s_ui_with"/>
        <published>2012-08-20T12:27:21-06:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="taleo" scheme="http://roller.apache.org/ns/tags/" />
        <category term="css3" scheme="http://roller.apache.org/ns/tags/" />
        <category term="html5" scheme="http://roller.apache.org/ns/tags/" />
        <category term="twitterbootstrap" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">Back in December, I wrote about &lt;a href=&quot;http://raibledesigns.com/rd/entry/what_have_i_been_working&quot;&gt;what I&apos;ve been working on at Taleo&lt;/a&gt;. Shortly after finishing up the Profile Picture, Talent Card and Org Chart features for TBE, I spent two weeks doing page speed optimization. By following &lt;a href=&quot;http://code.google.com/speed/page-speed/docs/rules_intro.html&quot;&gt;Web Performance Best Practices&lt;/a&gt;, I was able to make the TBE application twice as fast and improve its score into the low 90s. 
&lt;/p&gt;
&lt;p&gt;
Next, I started working on a new project - refreshing the UI. &lt;a href=&quot;http://www.linkedin.com/pub/nick-morgan/1/449/637&quot;&gt;Nick&lt;/a&gt;, the Lead UX Designer at Taleo (at the time), had developed a number of mockups and presented it to the developers and product folks in early November. I listened to a WebEx of that meeting and learned that everyone thought it&apos;d take 6-9 months to complete the work. They figured they could release the new design in Q3 2012.  
&lt;/p&gt;
&lt;p&gt;Since I like to provide high-value for my clients, I offered to help with the redesign and do a spike to help estimate. They agreed it&apos;d be a good use of my time and I started working on it the week before Christmas. Since I&apos;d used &lt;a href=&quot;http://twitter.github.com/bootstrap/&quot;&gt;Twitter Bootstrap&lt;/a&gt; for my &lt;a href=&quot;http://raibledesigns.com/rd/entry/migrating_to_play_2_and&quot;&gt;&lt;em&gt;Play More!&lt;/em&gt; app&lt;/a&gt;, I recommended we use it as a foundation of the redesign. They agreed and I went to work. By the end of the week, I&apos;d made good progress and told them I thought the redesign was possible in 2-3 months (including QA and cross-browser compatibility).&lt;/p&gt;
&lt;p&gt;When I came back to work in January, we decided to split the redesign into two phases. Rather than moving elements around and introducing new features, we decided to do that in the 2nd phase. The 1st phase would entail simply re-skinning the existing UI, with minimal HTML changes. I spent a week refining my spike and integrating it into a branch. The next week, I switched images from individual images to &lt;a href=&quot;http://www.alistapart.com/articles/sprites&quot;&gt;CSS sprites&lt;/a&gt;. Next, I implemented a new theming system with different colors/icons and got everything looking good in Chrome, Safari and IE8/9. 
&lt;/p&gt;
&lt;p&gt;The result is something I&apos;m quite proud of. IE8 doesn&apos;t have the rounded corners (via border-radius), but it still looks good. Forms look much better thanks to Bootstrap&apos;s styling and even jQuery UI&apos;s widgets look good thanks to &lt;a href=&quot;http://addyosmani.github.com/jquery-ui-bootstrap/&quot;&gt;jQuery UI Bootstrap&lt;/a&gt;. I did have to override quite a few Bootstrap styles in the process, but the result is something that doesn&apos;t look too &lt;em&gt;bootstrappy&lt;/em&gt;.
&lt;/p&gt;
&lt;p&gt;One technique I found to be extremely useful during this process was to pair with Nick (the designer) as mentioned in &lt;a href=&quot;http://www.alistapart.com/articles/building-twitter-bootstrap/&quot;&gt;Building Twitter Bootstrap&lt;/a&gt;. At one point, when we were trying to refine slight nuances and spacing in the UI, I paired with the Product Manager and found this to be a real time-saving effort as well.&lt;/p&gt;

&lt;p&gt;Taleo&apos;s UI Refresh project has been a great experience for me in sharpening my CSS skills. I used quite a bit of &lt;a href=&quot;http://css-tricks.com/child-and-sibling-selectors/&quot;&gt;child and sibling selectors&lt;/a&gt;, which work great in all the browser&apos;s we&apos;re supporting. Also, by using CSS sprites and colors (vs. images), I was able to get the manual theme-creation process down to around 15 minutes. After getting the manual process greatly reduced, I wrote a Theme Generator (based on Ant, LESS and &lt;a href=&quot;http://code.google.com/p/wro4j/&quot;&gt;wro4j&lt;/a&gt;) and got it down to mere minutes. I found &lt;a href=&quot;http://www.spritecow.com/&quot;&gt;Sprite Cow&lt;/a&gt; to be an invaluable resources for working with CSS sprites.
&lt;/p&gt;
&lt;p&gt;Below are some before and after shots of what we&apos;ve been able to accomplish in the first quarter of this year.
&lt;/p&gt;  
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://farm8.staticflickr.com/7159/6777477811_66d6401bd9_b.jpg&quot; rel=&quot;lightbox[taleo-uirefresh]&quot; title=&quot;Old UI - My View&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7159/6777477811_66d6401bd9_m.jpg&quot; width=&quot;240&quot; height=&quot;148&quot; alt=&quot;Old UI - My View&quot;&gt;&lt;/a&gt;
&lt;a href=&quot;http://farm8.staticflickr.com/7007/6777477639_4ca42665a7_b.jpg&quot; rel=&quot;lightbox[taleo-uirefresh]&quot; title=&quot;Old UI - New Employee&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7007/6777477639_4ca42665a7_m.jpg&quot; style=&quot;margin-left: 10px&quot; width=&quot;240&quot; height=&quot;148&quot; alt=&quot;Old UI - New Employee&quot;&gt;&lt;/a&gt;
&lt;p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
  
  &lt;a href=&quot;http://farm8.staticflickr.com/7022/6777477527_b1abb3b204_b.jpg&quot; rel=&quot;lightbox[taleo-uirefresh]&quot; title=&quot;New UI - My View&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7022/6777477527_b1abb3b204_m.jpg&quot; width=&quot;240&quot; height=&quot;148&quot; alt=&quot;New UI - My View&quot;&gt;&lt;/a&gt;
  
  &lt;a href=&quot;http://farm8.staticflickr.com/7162/6777477443_37241a4e85_b.jpg&quot; rel=&quot;lightbox[taleo-uirefresh]&quot; title=&quot;New UI - New Employee&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7162/6777477443_37241a4e85_m.jpg&quot; style=&quot;margin-left: 10px&quot; width=&quot;240&quot; height=&quot;148&quot; alt=&quot;New UI - New Employee&quot;&gt;&lt;/a&gt;  
  &lt;/p&gt;
  &lt;p&gt;
I originally wrote this post at the end of January. We ran into some stumbling blocks shorty after its original composition: Nick (the designer) moved onto &lt;a href=&quot;http://www.salesforce.com/&quot;&gt;greener pastures&lt;/a&gt; and &lt;a href=&quot;http://www.businessweek.com/news/2012-02-10/oracle-buys-taleo-for-1-9-billion-adds-human-resource-tool.html&quot;&gt;Oracle bought Taleo&lt;/a&gt;. What I didn&apos;t expect when I wrote this was to spend the next two months fixing slight bugs that occurred with spacing, alignment and dependent applications I didn&apos;t know about at the time. And then there was IE7. We didn&apos;t realize we needed to support it until mid-March. Then it took us around a month to make it all work &lt;em&gt;good enough&lt;/em&gt;. 
&lt;/p&gt;
&lt;p&gt;The good news is the UI Refresh was released a few months ago and seems to be humming along just fine. Sure, there were slight nuances and customizations we had conflicts with (clashing CSS classes), but overall it seems to have gone well. I can&apos;t thank the Bootstrap developers enough for motivating us to move to HTML5 and CSS3. Also, cheers to the excellent co-workers that helped make this happen: &lt;a href=&quot;http://www.linkedin.com/in/mnewton&quot;&gt;Murray Newton&lt;/a&gt; (Product Manager) and &lt;a href=&quot;http://www.linkedin.com/in/vladimirbazarsky&quot;&gt;Vladimir Bazarsky&lt;/a&gt;. I couldn&apos;t have done it without you guys.</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/new_look_and_feel_designed</id>
        <title type="html">New Look and Feel, Designed by Gillen&apos;s Army</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/new_look_and_feel_designed"/>
        <published>2012-08-14T22:58:11-06:00</published>
        <updated>2012-08-15T05:28:37-06:00</updated> 
        <category term="/Roller" label="Roller" />
        <category term="html5" scheme="http://roller.apache.org/ns/tags/" />
        <category term="gillensarmy" scheme="http://roller.apache.org/ns/tags/" />
        <category term="design" scheme="http://roller.apache.org/ns/tags/" />
        <category term="roller" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">As part of my &lt;a href=&quot;http://raibledesigns.com/rd/entry/10_years_ago&quot;&gt;10-year blogiversary&lt;/a&gt;, I was hoping to refresh this site with a new look and feel. A few months ago, I contacted my friend &lt;a href=&quot;http://www.linkedin.com/pub/mark-waggoner/6/693/9a9&quot;&gt;Mark Waggoner&lt;/a&gt; to see about getting his design help. We promptly worked out a logo/business card/website deal and &lt;a href=&quot;http://gillensarmy.com/&quot;&gt;Gillen&apos;s Army&lt;/a&gt; went to work. &lt;/p&gt;
&lt;p&gt;I picked a logo from numerous choices in late June, finalized a business card for printing in July and received the HTML and CSS for the site on August 2nd. I started converting it to a Roller theme last week and did a whole bunch of other modifications in the process.&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;Upgraded to &lt;a href=&quot;http://rollerweblogger.org/project/entry/roller_501_security_fix&quot;&gt;Roller 5.0.1&lt;/a&gt;.&lt;/li&gt;
    &lt;li&gt;Upgraded &lt;a href=&quot;http://code.google.com/p/wro4j/&quot;&gt;wro4j&lt;/a&gt; to the latest version (1.4.8.1) to workaround using a &amp;rarr; (\2192) in CSS.&lt;/li&gt; 
    &lt;li&gt;Changed to use jQuery and &lt;a href=&quot;http://lokeshdhakar.com/projects/lightbox2/&quot;&gt;Lightbox2&lt;/a&gt; for pictures.&lt;/li&gt;
    &lt;li&gt;Upgraded to the latest version (3.0.83) of &lt;a href=&quot;http://alexgorbatchev.com/SyntaxHighlighter/&quot;&gt;SyntaxHighlighter&lt;/a&gt;. You might notice &lt;a href=&quot;https://github.com/alexgorbatchev/SyntaxHighlighter/issues/44&quot;&gt;there is no longer a toolbar&lt;/a&gt; in this version. However, you can still double-click on code and easily copy/paste it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In addition to these upgrades, I made a few enhancements. I converted to HTML5 (by switching the doctype), added Modernizr and a feature that detects if the sun is up in your location. If you allow your browser to send me your lat and long, I&apos;ll give you a dark theme when the sun is down and a light theme when it&apos;s daylight. I used Preston&apos;s Hunt&apos;s &lt;a href=&quot;http://prestonhunt.com/story/124&quot;&gt;JavaScript Class for Sunrise and Sunset Calculations&lt;/a&gt; to determine isDaylight. You can also change the theme to light or dark using the small rectangles above the search box on the right. This sets a cookie and overrides the HTML5 Geo check. You can see the implementation of this logic in &lt;a href=&quot;http://raibledesigns.com/themes/darklight/js/site.js&quot;&gt;site.js&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;The stylesheet switching doesn&apos;t happen as fast as I&apos;d hoped (there&apos;s a flash even if using cookies), so I&apos;ll likely be converting some theme-setting logic to the server-side. The HTML5 version of the &lt;a href=&quot;https://developers.facebook.com/docs/reference/plugins/like/&quot;&gt;FaceBook Like Button&lt;/a&gt; requires you to specify the &quot;data-colorscheme&quot; in markup so this further supports moving to the server.&lt;/p&gt;
&lt;p&gt;I have other minor adjustments I&apos;d like to make, but more importantly - I wanted to get it out to you all. Tell me what you like and don&apos;t like. Among other things, the form inputs for comments and &lt;a href=&quot;http://raibledesigns.com/contact.jsp&quot;&gt;contact&lt;/a&gt; forms have backgrounds that might not be great for those color-impaired. Also, you can see how the iframe on the contact page has a white background instead of one based on the theme.&lt;/p&gt;

&lt;p&gt;Here&apos;s some stats comparing my old &lt;em&gt;andreas08&lt;/em&gt; theme to the new &lt;em&gt;darklight&lt;/em&gt;:

&lt;table class=&quot;comparison&quot;&gt;
    &lt;thead&gt;
        &lt;tr&gt;
        &lt;th&gt;Metric&lt;/th&gt;
        &lt;th&gt;andreas08&lt;/th&gt;
        &lt;th&gt;darklight&lt;/th&gt;
        &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
        &lt;tr&gt;
            &lt;td&gt;Size and Speed&lt;/td&gt;                
            &lt;td&gt;167 requests, 3.6MB, 9.89s&lt;/td&gt;
            &lt;td&gt;148 requests, 3.2MB, 7.34s&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;YSlow&lt;/td&gt;    
            &lt;td&gt;76 &lt;!-- Grade F on Make fewer HTTP requests
            This page has 23 external Javascript scripts. Try combining them into one.
            This page has 4 external stylesheets. Try combining them into one.
            This page has 8 external background images. Try combining them with CSS sprites.
            --&gt;&lt;/td&gt;
            &lt;td&gt;87&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;PageSpeed&lt;/td&gt;    
            &lt;td&gt;91 &lt;!-- Optimize images, Prefer asynchronous resources --&gt;&lt;/td&gt;
            &lt;td&gt;96 &lt;!-- Optimize images --&gt;&lt;/td&gt;
        &lt;/tr&gt;
    &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Sweet! It looks like this site is faster than ever. Cheers to Mark and &lt;a href=&quot;http://gillensarmy.com/&quot;&gt;Gillen&apos;s Army&lt;/a&gt; for the new design. I dig it!
&lt;/p&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/upgrading_to_play_2_anorm</id>
        <title type="html">Upgrading to Play 2: Anorm and Testing</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/upgrading_to_play_2_anorm"/>
        <published>2012-06-05T20:55:40-06:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="specs2" scheme="http://roller.apache.org/ns/tags/" />
        <category term="uberconf" scheme="http://roller.apache.org/ns/tags/" />
        <category term="play2" scheme="http://roller.apache.org/ns/tags/" />
        <category term="anorm" scheme="http://roller.apache.org/ns/tags/" />
        <category term="scala" scheme="http://roller.apache.org/ns/tags/" />
        <category term="playframework" scheme="http://roller.apache.org/ns/tags/" />
        <category term="play-more" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">&lt;a href=&quot;http://playframework.org&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7234/7158522999_066b0e26a1_t.jpg&quot; width=&quot;100&quot; height=&quot;33&quot; class=&quot;picture&quot; style=&quot;border: 0&quot;/&gt;&lt;/a&gt;
This time last year, I decided I wanted to learn Scala. I chose the &lt;a href=&quot;http://www.playframework.org/&quot;&gt;Play Framework&lt;/a&gt; as my vehicle for learning and I added &lt;a href=&quot;http://coffeescript.org/&quot;&gt;CoffeeScript&lt;/a&gt; and &lt;a href=&quot;http://scalate.fusesource.org/documentation/jade.html&quot;&gt;Jade&lt;/a&gt; to the mix. I packaged it all up, learned a bunch and &lt;a href=&quot;http://raibledesigns.com/rd/entry/my_html5_with_play_scala&quot;&gt;presented it at Devoxx 2011&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;In January, I added SecureSocial, JSON Services and worked a bit on the mobile client. I presented my findings &lt;a href=&quot;http://raibledesigns.com/rd/entry/comparing_web_frameworks_and_html5&quot;&gt;at Jfokus&lt;/a&gt; shortly after. As part of my aforementioned post, I wrote:&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
Right before we left for Jfokus, I was able to get everything to work, but didn&apos;t spend as much time as I&apos;d like working on the mobile client. If this talk gets accepted for Devoxx France, I plan on spending most of my time enhancing the mobile client.
&lt;/p&gt;
&lt;p&gt;I had some complications (a.k.a. too much &lt;a href=&quot;http://raibledesigns.com/rd/entry/cruising_around_the_western_caribbean&quot;&gt;vacation&lt;/a&gt;) with Devoxx France and wasn&apos;t able to attend. To make up for it, I submitted the talk to &lt;a href=&quot;http://uberconf.com/conference/denver/2012/06/home&quot;&gt;&#220;berConf&lt;/a&gt;. It got accepted and I started working on my app a couple weeks ago. So far, I&apos;ve spent about 8 hours upgrading it to Play 2 and I hope to start re-writing the mobile client later this week. I plan on using &lt;a href=&quot;http://incubator.apache.org/cordova/&quot;&gt;Cordova&lt;/a&gt;, &lt;a href=&quot;http://www.jqtouch.com/&quot;&gt;jQTouch&lt;/a&gt; and releasing it in the App Store sometime this month.
&lt;/p&gt;
&lt;p&gt;
&lt;p id=&quot;upgrading-to-play2&quot;&gt;&lt;strong&gt;Upgrading to Play 2&lt;/strong&gt;&lt;br/&gt;
When I heard about Play 2, I thought it was a great thing. The developers were re-writing the framework to use Scala at the core and I was already using Scala in my app. Then I learned they were going to throw backwards compatibility out the window and I groaned. &quot;Really? Another web framework (like Tapestry of old) screwing its users and making them learn everything again?!&quot;, I thought. &quot;Maybe they should call it &lt;em&gt;Run&lt;/em&gt; instead of &lt;em&gt;Play&lt;/em&gt;, leaving the old framework that everyone loves intact.&quot; 
&lt;/p&gt;
&lt;p&gt;However, after hearing about it at &lt;a href=&quot;http://raibledesigns.com/rd/entry/play_2_0_a_web&quot;&gt;Devoxx&lt;/a&gt; and &lt;a href=&quot;http://raibledesigns.com/rd/entry/play_framework_2_0_with&quot;&gt;Jfokus&lt;/a&gt;, I figured I should at least &lt;em&gt;try&lt;/em&gt; to migrate. I downloaded Play 2.0.1, created a new project and went to work.
&lt;/p&gt;
&lt;p&gt;The first thing I learned about &lt;em&gt;upgrading&lt;/em&gt; from Play 1.x to Play 2.x is &lt;em&gt;there&apos;s no such thing&lt;/em&gt;. It&apos;s like saying you upgraded from Struts 1 to Struts 2 or Tapestry 4 to Tapestry 5. It&apos;s a migration, with a whole new project.
&lt;/p&gt;
&lt;p id=&quot;evolutions&quot;&gt;&lt;strong&gt;Evolutions&lt;/strong&gt;&lt;br/&gt;
I started by looking around to see if anyone had documented a similar migration. I found two very useful resources right off the bat:
&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://janhelwich.wordpress.com/2012/03/13/play-2-0-with-scala-and-scaml-part1-setup-of-test-infrastructure-model-and-persistence-with-anorm/&quot;&gt;Play 2.0 with Scala and Scaml, Part1: Setup of test infrastructure, model and persistence with Anorm&lt;/a&gt; by Jan Helwich&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.jamesward.com/2012/02/21/play-framework-2-with-scala-anorm-json-coffeescript-jquery-heroku&quot;&gt;Tutorial: Play Framework 2 with Scala, Anorm, JSON, CoffeeScript, jQuery &amp;amp; Heroku&lt;/a&gt; by James Ward&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;From Jan&apos;s Blog, I learned to copy my evolutions from my Play 1.x project into &lt;em&gt;conf/evolutions/default&lt;/em&gt;. I changed my &lt;em&gt;application.conf&lt;/em&gt; to use PostgreSQL and wrote an EvolutionsTest.scala to verify creating the tables worked.
&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
import org.specs2.mutable._

import play.api.db.DB
import play.api.Play.current

import anorm._

import play.api.test._
import play.api.test.Helpers._

class EvolutionsTest extends Specification {

  &quot;Evolutions&quot; should {
    &quot;be applied without errors&quot; in {
      evolutionFor(&quot;default&quot;)
      running(FakeApplication()) {
        DB.withConnection {
          implicit connection =&gt;
            SQL(&quot;select count(1) from athlete&quot;).execute()
            SQL(&quot;select count(1) from workout&quot;).execute()
            SQL(&quot;select count(1) from comment&quot;).execute()
        }
      }
      success
    }
  }
}
&lt;/pre&gt;
&lt;p&gt;Then I began looking for how to load seed data with Play 2.x. In Play 1.x, you could use a BootStrap job that would load sample data with YAML.
&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
import play.jobs._
import play.Play

@OnApplicationStart
class BootStrap extends Job {

  override def doJob() {

    import models._
    import play.test._

    // Import initial data if the database is empty
    if (Athlete.count().single() == 0) {
      Yaml[List[Any]](&quot;initial-data.yml&quot;).foreach {
        _ match {
          case a: Athlete =&gt; Athlete.create(a)
          case w: Workout =&gt; Workout.create(w)
          case c: Comment =&gt; Comment.create(c)
        }
      }
    }
  }
}
&lt;/pre&gt;
&lt;p&gt;This is no longer a recommended practice in Play 2. Instead, they &lt;a href=&quot;https://groups.google.com/d/msg/play-framework/4pVUPZIRFFM/vh4nVQ-v9UcJ&quot;&gt;recommend you turn your YAML into code&lt;/a&gt;. 10 minutes later, I had a Global.scala that loaded seed data.
  &lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
import models._
import play.api._
import play.api.Play.current

import anorm._

object Global extends GlobalSettings {

  override def onStart(app: Application) {
    InitialData.insert()
  }
}

/**
 * Initial set of data to be loaded
 */
object InitialData {

  def date(str: String) = new java.text.SimpleDateFormat(&quot;yyyy-MM-dd&quot;).parse(str)

  def insert() {

    if (Athlete.count() == 0) {

      Seq(
        Athlete(Id(1), &quot;mraible@gmail.com&quot;, &quot;beer&quot;, &quot;Matt&quot;, &quot;Raible&quot;),
        Athlete(Id(2), &quot;trishmcginity@gmail.com&quot;, &quot;whiskey&quot;, &quot;Trish&quot;, &quot;McGinity&quot;)
      ).foreach(Athlete.create)

      Seq(
        Workout(Id(1), &quot;Chainsaw Trail&quot;,
          &quot;&quot;&quot;
            A beautiful fall ride: cool breezes, awesome views and yellow leaves.

            Would do it again in a heartbeat.
          &quot;&quot;&quot;, 7, 90, date(&quot;2011-10-13&quot;), 1),
        Workout(Id(2), &quot;Monarch Lake Trail&quot;,
          &quot;Awesome morning ride through falling yellow leaves and cool fall breezes.&quot;,
          4, 90, date(&quot;2011-10-15&quot;), 1),
        Workout(Id(3), &quot;Creekside to Flume to Chainsaw&quot;,
          &quot;Awesome morning ride through falling yellow leaves and cool fall breezes.&quot;,
          12, 150, date(&quot;2011-10-16&quot;), 2)
      ).foreach(Workout.create)

      Seq(
        Comment(1, &quot;Jim&quot;, &quot;Nice day for it!&quot;),
        Comment(2, &quot;Joe&quot;, &quot;Love that trail.&quot;),
        Comment(2, &quot;Jack&quot;, &quot;Where there any kittens there?&quot;)
      ).foreach(Comment.create)
    }
  }
}
&lt;/pre&gt;
&lt;p id=&quot;anorm&quot;&gt;&lt;strong&gt;Anorm&apos;s Missing Magic&lt;/strong&gt;&lt;br/&gt;
Before starting with Play 2, I knew it had lost some of its magic. After all, the developers had mentioned they &lt;em&gt;wanted&lt;/em&gt; to get ride of the magic and moving to Scala allowed them to do that. However, I didn&apos;t think I&apos;d miss &lt;a href=&quot;http://www.playframework.org/modules/scala-0.9.1/anorm#AddingsomeMagicT&quot;&gt;Magic[T]&lt;/a&gt; as much as I do. Like &lt;a href=&quot;http://martinfowler.com/bliki/OrmHate.html&quot;&gt;Martin Fowler&lt;/a&gt;, I like ORMs and having to use SQL again seems painful. It seems like a strange shift for Play to reduce type-safety on the backend, but increase it in its templates. Oh well, to each their own. I may eventually move to &lt;a href=&quot;http://squeryl.org&quot;&gt;Squery&lt;/a&gt;, but I wanted to do a side-by-side comparison as part of my migration.
&lt;/p&gt;
&lt;p&gt;Using the aforementioned tutorial from James and Jan&apos;s blog posts, as well as Guillaume&apos;s &lt;a href=&quot;https://gist.github.com/2788715&quot;&gt;Play 2.0/Anorm&lt;/a&gt;, I set about creating new model objects. I wrote a bunch of SQL, typed up some &lt;a href=&quot;http://stackoverflow.com/questions/9371227/there-are-many-similar-dao-methods-in-anorm-is-it-right&quot;&gt;new finders&lt;/a&gt; and migrated my tests from ScalaTest to the new default, &lt;a href=&quot;http://etorreborre.github.com/specs2/&quot;&gt;specs2&lt;/a&gt;. The Mosh Pit&apos;s &lt;a href=&quot;http://tech.mindcandy.com/2012/05/migrating-a-play-1-2-website-to-play-2-0/&quot;&gt;Migrating a Play 1.2 website to Play 2.0&lt;/a&gt; was a great help in migrating tests.&lt;/p&gt;

&lt;p&gt;That&apos;s when I started &lt;a href=&quot;https://groups.google.com/d/topic/play-framework/-oJrmLWYGcg/discussion&quot;&gt;having issues&lt;/a&gt; with Anorm and figuring out how its parser syntax works. After struggling for a few days, I finally found &lt;a href=&quot;https://github.com/kenichiro22/yabe-play20-scala&quot;&gt;yabe-play20-scala&lt;/a&gt;. Since I&apos;d used the &lt;a href=&quot;http://www.playframework.org/documentation/1.2.3/guide1&quot;&gt;yabe tutorial from Play 1.x&lt;/a&gt;, it was familiar and helped me get past my problems. Now, things aren&apos;t perfect (Workouts aren&apos;t ordered by their posted date), but everything compiles and tests pass.
&lt;/p&gt;
&lt;p&gt;To illustrate how little code was required for Anorm 1.x, checkout &lt;a href=&quot;https://gist.github.com/2879214&quot;&gt;Workout.scala in Play 1.x vs. Play 2.x&lt;/a&gt;. The Play 1.x version is 66 lines; Play 2.x requires 193 lines. I don&apos;t know about you, but I kinda like a little magic in my frameworks to reduce the amount of code I have to maintain.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;I was pleasantly surprised by specs2. First of all, it was an easy migration from ScalaTest. Secondly, &lt;a href=&quot;http://www.playframework.org/documentation/2.0/ScalaTest&quot;&gt;Play&apos;s FakeApplication&lt;/a&gt; made it very easy to write unit tests. The line count on my &lt;a href=&quot;https://gist.github.com/2879222&quot;&gt;UnitTests.scala in Play 1.x vs. Play 2.x&lt;/a&gt; is almost identical.
&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;summary&quot;&gt;&lt;/a&gt;
&lt;strong&gt;Summary&lt;/strong&gt;&lt;br/&gt;
The first few hours of developing with Play 2 were frustrating, mostly because I felt like I had to learn everything over again. However, I was pleased to find good references on migrating from Play 1.x. Last night, I migrated all my Controllers, integrated Scalate and got most of my views rendering. I still have issues &lt;a href=&quot;https://groups.google.com/d/msg/play-framework/J3lXeRyti4M/UjzzYJnMAuQJ&quot;&gt;rendering validation errors&lt;/a&gt;, but I hope to figure that out soon. The last 2 hours have been &lt;em&gt;much&lt;/em&gt; more fun and I feel like my Scala skills are coming along. I think if the Play Team could eliminate those first few hours of struggling (and provide almost instant joy like Play 1.x) they&apos;d really be onto something.
&lt;/p&gt;
&lt;p&gt;As soon as I figure out how to validation and how to add a body class based on the URL, I&apos;ll write another post on the rest of my migration. A Play 2-compatible version of &lt;a href=&quot;https://github.com/jaliss/securesocial&quot;&gt;SecureSocial&lt;/a&gt; just came out this evening, so I may integrate that as well. In the meantime, I&apos;ll be working on the iPhone app and finishing up a Grails 2 application for James Ward and my &lt;a href=&quot;http://uberconf.com/conference/denver/2012/06/session?id=25584&quot;&gt;Grails vs. Play Smackdown&lt;/a&gt;.</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/play_framework_2_0_with</id>
        <title type="html">Play Framework 2.0 with Peter Hilton at Jfokus</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/play_framework_2_0_with"/>
        <published>2012-02-14T07:17:08-07:00</published>
        <updated>2012-02-14T13:19:40-07:00</updated> 
        <category term="/Java" label="Java" />
        <category term="playframework" scheme="http://roller.apache.org/ns/tags/" />
        <category term="webframeworks" scheme="http://roller.apache.org/ns/tags/" />
        <category term="jfokus" scheme="http://roller.apache.org/ns/tags/" />
        <category term="scala" scheme="http://roller.apache.org/ns/tags/" />
        <category term="http" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">This week, I&apos;m at &lt;a href=&quot;http://jfokus.se&quot;&gt;Jfokus&lt;/a&gt; in Stockholm, Sweden. After a fun speaker&apos;s dinner last night, I got up this morning and polished up my presentations and demo before attending the conference. The first session I attended was &lt;a href=&quot;http://twitter.com/PeterHilton&quot;&gt;Peter Hilton&apos;s&lt;/a&gt; &lt;a href=&quot;http://www.jfokus.se/jfokus/talks.jsp#Play%20Framework%202.0&quot;&gt;Play Framework 2.0&lt;/a&gt; presentation. Below are my notes from this talk.
&lt;/p&gt;
&lt;p style=&quot;border-top: 1px dotted silver; padding-top: 10px&quot;&gt;Peter is a Senior &lt;em&gt;Web&lt;/em&gt; Developer, not a Java Developer. His first slide states the following:&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&quot;Play brings type safe high-productivity web development to the JVM.&quot;
&lt;/p&gt;
&lt;p&gt;
New features in Play 2.0: type-safety, template syntax, compile-time checking and asynchronous HTTP programming. Java, Scala - the language you use is less important than the fact that Play is a &lt;em&gt;web&lt;/em&gt; framework. It&apos;s a full-stack framework and has everything you need out-of-the-box to build a web application. Play focuses on HTTP and doesn&apos;t try to hide it. It&apos;s designed &lt;em&gt;by&lt;/em&gt; web developers &lt;em&gt;for&lt;/em&gt; web developers.
&lt;/p&gt;
&lt;p&gt;
With Play, the Back button just works. Your web framework shouldn&apos;t break the first button on your browser&apos;s toolbar. The Reload button also works: make a change, hit reload and your changes (even in Scala classes) are shown. You design the URLs and you can use &quot;clean&quot; URLs. DX (Developer eXperience) is Peter&apos;s new term. Usability matters: as a developer, you deserve a framework that provides a good experience.
&lt;/p&gt;
&lt;p&gt;
Play doesn&apos;t fight HTTP or the browser. It&apos;s stateless and HTTP-centric. A few years ago, it seemed like a good idea to try and keep state on the server. It sounded like a good idea, but in practice, it&apos;s a really bad idea - especially for things like the back button. Play matches the web&apos;s stateless HTTP architecture. 
&lt;/p&gt;
&lt;p&gt;
As a Java EE developer, PHP and Rails developers have been laughing at us for years. Like Father Christmas, Peter&apos;s heard of class-reloading, but he hasn&apos;t actually seen it. Code reloading is the most important part of DX and about achieving high-productivity in web development. 
&lt;/p&gt;
&lt;p&gt;
URLs want to be loved too. REST architecture isn&apos;t just for web service APIs. When you have clean URLs, you can tweet them, post them and email them. 
&lt;/p&gt;
&lt;p&gt;
&quot;You would need to be a super-hero to successfully use some web frameworks.&quot; They show you a blank screen in the browser and you have to look at your console&apos;s stack trace to figure it out. With Play, the error is shown in your browser and you can see the exact line it happens on.
&lt;/p&gt;
&lt;p&gt;
In Play 1.x, there was a lot of magic and a lot of bytecode enhancement at runtime. This allowed the API to be a lot nicer than traditional Java APIs. However, it caused issues when users viewed the enhanced source and it also caused issues in IDEs. With Play 2.0, the framework itself is implemented in Scala. Scala removes the need for so much bytecode enhancement. There is less &apos;magic&apos; and strangeness in the API. The code you see in the IDE is the code that runs. Scala source code is not necessarily harder to read. 1.x had some pretty hairy Java code, and you could tell when you dug into it. Especially when you were deep into the source code and saw that a lot of the comments were in French.
&lt;/p&gt;
&lt;p&gt;
Play 2.0&apos;s template system is based on Scala. It&apos;s similar to the lightweight template syntax in Play 1.x. Templates are compiled into class files for run-time speed. For example:
&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
@(products: Seq[Product])

&amp;lt;ul&gt;
@for(product &amp;lt;- products) {
  &amp;lt;li&gt;@product.name&amp;lt;/li&gt;
}
&amp;lt;/ul&gt;

@summary(products)
&lt;/pre&gt;
&lt;p&gt;We used to think XML-based templates were great, but it turns out it&apos;s a terrible idea. Mostly because you end up having to invent an expression language to create valid XML (to avoid putting XML in your HTML attributes). With Play 2.0&apos;s templates, you can define tags in your templates as regular Scala methods.
&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
@display(product: models.Product) ={
 &amp;lt;a href=&quot;@routes.Product.details(product.id)&quot;&gt;@product.name&amp;lt;/a&gt; 
}

@for(product &amp;lt;- products) {
  @display(product)
}
&lt;/pre&gt;
&lt;p&gt;The compile-time checking in Play 2.0 is not just for Java and Scala classes. It also compiles your HTTP routes file (which maps requests to controller actions). Furthermore, it compiles your templates, JavaScript files (using Google Closure Compiler), CoffeeScript files and LESS stylesheets. 
&lt;/p&gt;
&lt;p&gt;
Play supports modern web development. It&apos;s designed to work with HTML5, but there&apos;s no constraints on HTML output. It&apos;s front-end developer friendly and has great DX. UI components belong in the client, e.g. jQuery UI. It also has built-in support for improvements to CSS (LESS) and JavaScript (CoffeeScript).
&lt;/p&gt;
&lt;p&gt;
A few years ago, it seemed like a really good idea to hide JavaScript from the web developer. Web frameworks used to say &quot;You don&apos;t need to see the JavaScript or the HTML, we&apos;ll handle generating your components for you.&quot; Now, if you&apos;re building a web application and you don&apos;t know any jQuery, you doing it the hard way. You should learn how to work with front-end developers or learn how to do it yourself. And make sure your web framework allows this sort of development. 
&lt;/p&gt;
&lt;p&gt;
The future of web programming is asynchronous. You&apos;ll perform simultaneous web service requests. You&apos;ll process streams of data, instead of filling up memory or disk. You&apos;ll publish real-time data and have predictable and minimal resource consumption. In the long term, this changes everything. The future of the web is real-time and asynchronous. With Play 2.0, it&apos;s not just another feature, it&apos;s a fundamental aspect of the architecture. Play&apos;s internal architecture uses a reactive model based on Iteratee IO. 
&lt;/p&gt;
&lt;p&gt;In summary, use Play 2, use HTML5, deploy to the Cloud. There&apos;s two forthcoming books on Play (both from Manning) and Play 2.0 RC1 will be released today.&lt;/p&gt;
&lt;p style=&quot;border-top: 1px dotted silver; padding-top: 10px&quot;&gt;
I think Peter did a good job of summarizing the new features in Play 2.0, especially how templates work. I enjoyed his emphasis on HTTP and how Play leverages the browser (back, reload and as a console). I liked his humorous speaking style, and agree with his emphasis that client-side development skills are important for modern web applications. I think Play 2.0 is making a big bet on Scala and asynchronous programming, but if they live up to the hype, it should be a very enjoyable web framework to develop with.</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/secure_json_services_with_play</id>
        <title type="html">Secure JSON Services with Play Scala and SecureSocial</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/secure_json_services_with_play"/>
        <published>2012-02-12T16:02:43-07:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="securesocial" scheme="http://roller.apache.org/ns/tags/" />
        <category term="json" scheme="http://roller.apache.org/ns/tags/" />
        <category term="jfokus" scheme="http://roller.apache.org/ns/tags/" />
        <category term="scala" scheme="http://roller.apache.org/ns/tags/" />
        <category term="playframework" scheme="http://roller.apache.org/ns/tags/" />
        <category term="rest" scheme="http://roller.apache.org/ns/tags/" />
        <category term="play-more" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">&lt;a href=&quot;http://www.flickr.com/photos/mcginityphoto/6716294395/&quot; title=&quot;AntwerpTownSquare by McGinityPhoto, on Flickr&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7002/6716294395_28ce2c5f5b_t.jpg&quot; width=&quot;100&quot; height=&quot;66&quot; alt=&quot;AntwerpTownSquare&quot; class=&quot;picture&quot;&gt;&lt;/a&gt;
Last November, I &lt;a href=&quot;http://raibledesigns.com/rd/entry/my_html5_with_play_scala&quot;&gt;traveled to Antwerp to speak at Devoxx&lt;/a&gt;. After my talk on HTML5 with Play Scala, &lt;a href=&quot;http://twitter.com/matkar&quot;&gt;Mattias Karlsson&lt;/a&gt; approached me and we had a chat about doing the same talk at &lt;a href=&quot;http://www.jfokus.se/&quot;&gt;Jfokus&lt;/a&gt; in Stockholm. I agreed and we began talking details after Trish and I returned to the US. 
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://jfokus.se&quot;&gt;
&lt;img width=&quot;450&quot; style=&quot;border-radius: 10px&quot; height=&quot;200&quot; src=&quot;//static.raibledesigns.com/repository/images/Jfokus2012_450x200.jpg&quot; alt=&quot;Jfokus&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;I wrote this article on a plane between Denver and Seattle and will be hopping over the North Pole to Stockholm via Iceland tonight. For the past couple of weeks, I&apos;ve been updating my &lt;em&gt;Play More!&lt;/em&gt; HTML5/mobile app to add some new features. Most notably, I wanted to upgrade to Play 2.0, create JSON services and add authentication. 
&lt;/p&gt;
&lt;p id=&quot;play2&quot;&gt;&lt;strong&gt;Upgrading to Play 2.0&lt;/strong&gt;&lt;br/&gt;
My attempt to upgrade to Play 2.0 involved &lt;a href=&quot;https://github.com/playframework/play20&quot;&gt;checking out the source from GitHub&lt;/a&gt;, building and installing the RC1 snapshot. As I tried to upgrade my app and started getting failed imports, I turned to the internet (specifically StackOverflow) to &lt;a href=&quot;http://stackoverflow.com/questions/8264010/todays-options-for-an-easier-migration-path-to-play-2&quot;&gt;see if it was a good idea&lt;/a&gt;. The first answer for that question suggested I stay with 1.x.
  &lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
If it&apos;s a critical project, to be finished before next March 2012, I would go with Play 1.x. If it&apos;s a less important project, which could be delayed, and that in any case won&apos;t be released before March 2012, try Play 2.0.&lt;/p&gt;
&lt;p&gt;While I didn&apos;t plan on releasing &lt;em&gt;Play More!&lt;/em&gt; before Jfokus, I decided upgrading didn&apos;t add a whole lot to the talk. Also, I couldn&apos;t find a Play Scala 0.9.1 to Play 2.0 upgrade guide and I didn&apos;t have enough time to create one. So I decided to stick with Play 1.2.4 and add some JSON services for my iPhone client. 
&lt;/p&gt;
&lt;p id=&quot;play-json&quot;&gt;&lt;strong&gt;JSON Servers&lt;/strong&gt;&lt;br/&gt;
I found Manuel Bernhardt&apos;s &lt;a href=&quot;http://logician.free.fr/index.php/2011/09/16/play-scala-and-json/&quot;&gt;Play! Scala and JSON&lt;/a&gt;. This led me to &lt;a href=&quot;https://github.com/codahale/jerkson&quot;&gt;Jerkson&lt;/a&gt;, built by the &lt;a href=&quot;http://codahale.com/the-rest-of-the-story/&quot;&gt;now infamous&lt;/a&gt; &lt;a href=&quot;https://twitter.com/coda&quot;&gt;@coda&lt;/a&gt;.  
I was able to easily get things working fairly quickly and wrote the following WorkoutService.scala:
&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
package controllers.api

import play.mvc.Controller
import models._
import com.codahale.jerkson.Json._

object WorkoutService extends Controller {

  def workouts = {
    response.setContentTypeIfNotSet(&quot;application/json&quot;)
    generate(Workout.find().list())
  }
  def edit(id: Long) = {
    generate(Workout.byIdWithAthleteAndComments(id))
  }

  def create() = {
    var workout = params.get(&quot;workout&quot;, classOf[Workout])
    Workout.create(workout)
  }

  def save(id: Option[Long]) = {
    var workout = params.get(&quot;workout&quot;, classOf[Workout])
    Workout.update(workout)
  }

  def delete(id: Long) = {
    Workout.delete(&quot;id={id}&quot;).on(&quot;id&quot; -&gt; id).executeUpdate()
  }
}
&lt;/pre&gt;
&lt;p&gt;Next, I added routes for my new API to &lt;em&gt;conf/routes&lt;/em&gt;:
&lt;/p&gt;
&lt;pre&gt;
GET     /api/workouts               api.WorkoutService.workouts
GET     /api/workout/{id}           api.WorkoutService.edit
POST    /api/workout                api.WorkoutService.create
PUT     /api/workout/{id}           api.WorkoutService.save
DELETE  /api/workout/{id}           api.WorkoutService.delete
&lt;/pre&gt;
&lt;p&gt;
Then I created an ApiTest.scala class that verifies the first method works as expected.&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
import play.test.FunctionalTest
import play.test.FunctionalTest._
import org.junit._

class ApiTests extends FunctionalTest {
  
    @Test
    def testGetWorkouts() {
        var response = GET(&quot;/api/workouts&quot;);
        assertStatus(200, response);
        assertContentType(&quot;application/json&quot;, response)
        println(response.out)
    }
}
&lt;/pre&gt;
&lt;p&gt;
I ran &quot;play test&quot;, opened my browser to http://localhost:9000/@tests and clicked ApiTests -&gt; Start to verify it worked. All the green made me happy.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://farm8.staticflickr.com/7180/6869080391_5a163cb28e.jpg&quot; title=&quot;Play More API Tests&quot; rel=&quot;lightbox[playmore-json]&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7180/6869080391_5a163cb28e_m.jpg&quot; width=&quot;240&quot; height=&quot;184&quot; alt=&quot;Play More API Tests&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;Finally, I wrote some CoffeeScript and jQuery to allow users to delete workouts and make sure delete functionality worked.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
$(&apos;#delete&apos;).click -&gt;
  $.ajax
    type: &apos;POST&apos;
    url: $(this).attr(&apos;rel&apos;)
    error: -&gt;
      alert(&apos;Delete failed, please try again.&apos;)
    success: (data) -&gt;
      location.href = &quot;/more&quot;
&lt;/pre&gt;
&lt;p&gt;I was very impressed with how easy Play made it to create JSON services and I smiled as my CoffeeScript skills got a refresher.&lt;/p&gt;
&lt;p&gt;The Friday before we left for Devoxx, I saw the &lt;a href=&quot;http://groups.google.com/group/play-framework/browse_thread/thread/1cdebc6f54ec3e6f&quot;&gt;module registration request for SecureSocial&lt;/a&gt;.
&lt;p id=&quot;securesocial&quot;&gt;&lt;strong&gt;SecureSocial with Play Scala&lt;/strong&gt;&lt;br/&gt;
From SecureSocial&apos;s &lt;a href=&quot;https://github.com/jaliss/securesocial&quot;&gt;README&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;quote&quot; style=&quot;margin-left: 0; margin-bottom: 10px&quot;&gt;
&lt;p&gt;SecureSocial allows you to add an authentication UI to your app that works with services based on OAuth1, OAuth2, OpenID and OpenID+OAuth hybrid protocols.
&lt;/p&gt;
&lt;p&gt;
It also provides a Username and Password mechanism for users that do not wish to use existing accounts in other networks.
&lt;/p&gt;
&lt;p&gt;
The following services are supported in this release:
&lt;/p&gt;
&lt;ul style=&quot;margin-bottom: 0&quot;&gt;
&lt;li&gt;Twitter (OAuth1)&lt;/li&gt;
	&lt;li&gt;Facebook (OAuth2)&lt;/li&gt;
	&lt;li&gt;Google (OpenID + OAuth Hybrid)&lt;/li&gt;
	&lt;li&gt;Yahoo (OpenID + OAuth Hybrid)&lt;/li&gt;
	&lt;li&gt;LinkedIn (OAuth1)&lt;/li&gt;
	&lt;li&gt;Foursquare (OAuth2)&lt;/li&gt;
	&lt;li&gt;MyOpenID (OpenID)&lt;/li&gt;
	&lt;li&gt;Wordpress (OpenID)&lt;/li&gt;
	&lt;li&gt;Username and Password&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;p&gt;In other words, it sounded like a dream come true and I resolved to try it once I found the time. That time found me last Monday evening and I sent a direct message to &lt;a href=&quot;http://twitter.com/jaliss&quot;&gt;@jaliss&lt;/a&gt; (the module&apos;s author) via Twitter.
&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
Does Secure Social work with Play Scala? I&apos;d like to use it in my Play More! project.
&lt;/p&gt;
&lt;p&gt;Jorge responded 16 minutes later saying that he hadn&apos;t used Play Scala and he&apos;d need to do some research. At 8 o&apos;clock that night (1.5 hours after my original DM), Jorge had a sample working and emailed it to me. 10 minutes later I was adding a Secure trait to my project.
&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
package controllers

import play.mvc._
import controllers.securesocial.SecureSocial

/*
 * @author Jorge Aliss &amp;lt;jaliss@gmail.com&gt; of Secure Social fame.
 */
trait Secure {
  self: Controller =&gt;

  @Before def checkAccess() {
    SecureSocial.DeadboltHelper.beforeRoleCheck()
  }

  def currentUser = {
    SecureSocial.getCurrentUser
  }
}
&lt;/pre&gt;
&lt;p&gt;I configured Twitter and Username + Password as my providers by adding the following to &lt;em&gt;conf/application.conf&lt;/em&gt;.
&lt;/p&gt;
&lt;pre&gt;
securesocial.providers=twitter,userpass
&lt;/pre&gt;
&lt;p&gt;I also had to configure a number of securesocial.twitter.* properties. Next, I made sure my routes were aware of SecureSocial by adding the following to the top of &lt;em&gt;conf/routes&lt;/em&gt;:
&lt;pre&gt;
  *       /auth               module:securesocial
&lt;/pre&gt;
&lt;p&gt;Then I specified it as a dependency in &lt;em&gt;conf/dependencies.yml&lt;/em&gt; and ran &quot;play deps&quot;.
&lt;/p&gt;
&lt;pre&gt;
    - play -&gt; securesocial 0.2.4
&lt;/pre&gt;
&lt;p&gt;After adding &quot;with Secure&quot; to my Profile.scala controller, I tried to access its route and was prompted to login. Right off the bat, I was shown an error about a missing jQuery 1.5.2 file in my &quot;javascripts&quot; folder, so I added it and rejoiced when I was presented with a login screen. I had to add the app on Twitter to use its OAuth servers, but I was pumped when both username/password authentication worked (complete with signup!) as well as Twitter. 
&lt;/p&gt;
&lt;p&gt;
The only issue I ran into with SecureSocial was that it didn&apos;t find the default implementation of SecureSocial&apos;s UserService.Service when running in prod mode. I was able to workaround this by adding a SecureService.scala implementation to my project and coding it to talk to my Athlete model. I didn&apos;t bother to hook in creating a new user when they logged in from Twitter, but that&apos;s something I&apos;ll want to do in the future. I was also pleased to find out customizing SecureSocial&apos;s views was a breeze. I simply copied them from the module into my app&apos;s views and &lt;em&gt;voila!&lt;/em&gt;
&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
package services

import play.db.anorm.NotAssigned
import play.libs.Codec
import collection.mutable.{SynchronizedMap, HashMap}
import models.Athlete
import securesocial.provider.{ProviderType, UserService, SocialUser, UserId}

class SecureService extends UserService.Service {
  val activations = new HashMap[String, SocialUser] with SynchronizedMap[String, SocialUser]

  def find(userId: UserId): SocialUser = {
    val user = Athlete.find(&quot;email={email}&quot;).on(&quot;email&quot; -&gt; userId.id).first()

    user match {
      case Some(user) =&gt; {
        val socialUser = new SocialUser
        socialUser.id = userId
        socialUser.displayName = user.firstName
        socialUser.email = user.email
        socialUser.isEmailVerified = true
        socialUser.password = user.password
        socialUser
      }
      case None =&gt; {
        if (!userId.provider.eq(ProviderType.userpass)) {
          var socialUser = new SocialUser
          socialUser.id = userId
          socialUser
        } else {
          null
        }
      }
    }
  }

  def save(user: SocialUser) {
    if (find(user.id) == null) {
      val firstName = user.displayName
      val lastName = user.displayName
      Athlete.create(Athlete(NotAssigned, user.email, user.password, firstName, lastName))
    }
  }

  def createActivation(user: SocialUser): String = {
    val uuid: String = Codec.UUID()
    activations.put(uuid, user)
    uuid
  }

  def activate(uuid: String): Boolean = {
    val user: SocialUser = activations.get(uuid).asInstanceOf[SocialUser]
    var result = false

    if (user != null) {
      user.isEmailVerified = true
      save(user)
      activations.remove(uuid)
      result = true
    }

    result
  }

  def deletePendingActivations() {
    activations.clear()
  }
}
&lt;/pre&gt;
&lt;p&gt;Jorge was a great help in getting my authentication needs met and he even wrote a BasicAuth.scala trait to implement Basic Authentication on my JSON services.
&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
package controllers

import _root_.securesocial.provider.{UserService, ProviderType, UserId}
import play._
import play.mvc._
import play.libs.Crypto

import controllers.securesocial.SecureSocial

/*
 * @author Jorge Aliss &amp;lt;jaliss@gmail.com&gt; of Secure Social fame.
 */
trait BasicAuth {
  self: Controller =&gt;

  @Before def checkAccess = {
    if (currentUser != null) {
      // this allows SecureSocial.getCurrentUser() to work.
      renderArgs.put(&quot;user&quot;, currentUser)
      Continue
    }

    val realm =
      Play.configuration.getProperty(&quot;securesocial.basicAuth.realm&quot;, &quot;Unauthorized&quot;)

    if (request.user == null || request.password == null) {
      Unauthorized(realm)
    } else {
      val userId = new UserId
      userId.id = request.user
      userId.provider = ProviderType.userpass
      val user = UserService.find(userId)

      if (user == null ||
        !Crypto.passwordHash(request.password).equals(user.password)) {
        Unauthorized(realm)
      } else {
        // this allows SecureSocial.getCurrentUser() to work.
        renderArgs.put(&quot;user&quot;, user)
        Continue
      }
    }
  }

  def currentUser = {
    SecureSocial.getCurrentUser()
  }
}
&lt;/pre&gt;
&lt;p id=&quot;summary&quot;&gt;
&lt;strong&gt;Summary&lt;/strong&gt;&lt;br/&gt;
My latest pass at developing with Scala and leveraging Play to build my app was a lot of fun. While there were issues with class reloading every-so-often and &lt;a href=&quot;http://groups.google.com/group/scalate/browse_thread/thread/ddf455ec8676abf1&quot;&gt;Scala versions with Scalate&lt;/a&gt;, I was able to add the features I wanted. I wasn&apos;t able to upgrade to Play 2.0, but I didn&apos;t try that hard and figured it&apos;s best to wait until its upgrade guide has been published. 
&lt;/p&gt;
&lt;p&gt;
I&apos;m excited to describe my latest experience to the developers at Jfokus this week. In addition, the conference has talks on &lt;a href=&quot;http://www.jfokus.se/jfokus/talks.jsp#Play%20Framework%202.0&quot;&gt;Play 2.0&lt;/a&gt;, &lt;a href=&quot;http://www.jfokus.se/jfokus/talks.jsp#CoffeeScript%3A%20JavaScript%20without%20the%20Fail&quot;&gt;CoffeeScript&lt;/a&gt;, &lt;a href=&quot;http://www.jfokus.se/jfokus/talks.jsp#Client-side%20Storage%3A%20When%20%26%20How&quot;&gt;HTML5&lt;/a&gt;, &lt;a href=&quot;http://www.jfokus.se/jfokus/talks.jsp#Scala%20in%20Action&quot;&gt;Scala&lt;/a&gt; and &lt;a href=&quot;http://www.jfokus.se/jfokus/talks.jsp#Introducing%20Scalate%2C%20the%20Scala%20Template%20Engine&quot;&gt;Scalate&lt;/a&gt;. I hope to attend many of these and learn some new tricks to improve my skills and my app. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; The Delving developers have written an article on &lt;a href=&quot;http://delving.eu/node/27&quot;&gt;Migration to Play 2&lt;/a&gt;. While it doesn&apos;t provide specific details on what they needed to change, it does have good information on how long it took and things to watch for.</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/twitter_s_open_source_summit</id>
        <title type="html">Twitter&apos;s Open Source Summit: Bootstrap 2.0 Edition</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/twitter_s_open_source_summit"/>
        <published>2012-02-01T11:28:40-07:00</published>
        <updated>2012-02-01T17:32:46-07:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="bootstrap" scheme="http://roller.apache.org/ns/tags/" />
        <category term="opensource" scheme="http://roller.apache.org/ns/tags/" />
        <category term="twitter" scheme="http://roller.apache.org/ns/tags/" />
        <category term="ossummit" scheme="http://roller.apache.org/ns/tags/" />
        <category term="hogan.js" scheme="http://roller.apache.org/ns/tags/" />
        <category term="nasa" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">Every few months, &lt;a href=&quot;http://twitter.com&quot;&gt;Twitter&lt;/a&gt; hosts an Open Source Summit to talk about tools they&apos;re using. Since I happened to be near San Fransisco, I happily attended their latest &lt;a href=&quot;https://twitter.com/search?q=%23ossummit&quot;&gt;#ossummit&lt;/a&gt; to learn about &lt;a href=&quot;http://twitter.github.com/bootstrap/&quot;&gt;Bootstrap 2.0&lt;/a&gt;. Below are my notes from last night&apos;s event.
&lt;/p&gt;
&lt;p&gt;
95% of Twitter&apos;s infrastructure is powered by open source. They hope to contributing back to open source by doing 2-3 summits per year. Without open source, there would be no Twitter. You can find a bunch of Twitter&apos;s open source contributions at &lt;a href=&quot;http://twitter.github.com&quot;&gt;twitter.github.com&lt;/a&gt;. They&apos;re also big fans of Apache and commit to a wide variety of projects there.
&lt;/p&gt;
&lt;p id=&quot;bootstrap&quot;&gt;
&lt;strong&gt;Bootstrap&lt;/strong&gt;&lt;br/&gt;
Bootstrap is developed by two main guys: &lt;a href=&quot;http://twitter.com/mdo&quot;&gt;@mdo&lt;/a&gt; and &lt;a href=&quot;http://twitter.com/fat&quot;&gt;@fat&lt;/a&gt;. 
Mark (@mdo) has been a designer at Twitter for 2 years. He started on the Revenue Team with ads, but has been working on redesign for last 4 months. Has been doing HTML and CSS for about 11 years. He used Notepad on Windows to build his GeoCities site. 
&lt;/p&gt;
&lt;p&gt;boot&amp;middot;strap: simple and flexible HTML, CSS, and JavaScript for popular user interface components and interactions.
&lt;/p&gt;
&lt;p&gt;
Work on Bootstrap started about a 1.5 years ago. Internal tools didn&apos;t get the proper attention they needed. They figured out there was a lot of people that wanted good looking UIs and interactions. It became Twitter Blueprint and was mostly used internally. Jacob (@fat) started shortly after first release and decided to add some JavaScript on top of it. The JavaScript for Bootstrap was &lt;a href=&quot;https://twitter.com/#!/pamelafox/status/164532457654329347&quot;&gt;originally the &quot;Twitter Internal Toolkit&quot; or &quot;TIT&quot;&lt;/a&gt; and was built on Moo Tools. Jacob was like &quot;we gotta open source this, it&apos;s gonna be huge!&quot; (he was right).
&lt;/p&gt;
&lt;p&gt;The 1.0 release supported Chrome, Safari and Firefox (everyone at Twitter was on Macs). 1.3 added cross-browser support and JavaScript plugins. 
&lt;/p&gt;
&lt;p&gt;
Now there&apos;s Bootstrap 2 (just released!). They rewrote all the documentation and components and removed legacy code. 
&lt;/p&gt;
&lt;p&gt;So, &lt;em&gt;what&apos;s new?&lt;/em&gt;
The biggest thing is the docs. Previously had live examples, now shows live examples and why you would do something, as well as additional options. The &quot;topbar&quot; has been renamed to &quot;navbar&quot;, but it&apos;s still got all the hotness. It&apos;s &lt;a href=&quot;http://www.alistapart.com/articles/responsive-web-design/&quot;&gt;responsive&lt;/a&gt; with CSS media queries for small devices, tablets, small desktops and large desktops. This means the layout &lt;em&gt;breaks&lt;/em&gt; at certain points and stacks elements to fit on smaller screens.
&lt;/p&gt;
&lt;p&gt;
CSS: smarter defaults, better classes. In 1.4, all forms were stacked. Now they can flow horizontally. Tables are now namespaced so Bootstrap&apos;s styles don&apos;t apply to all tables. The available table, form and navigation classes are as follows:
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
// Tables
.table { ... }
.table-striped { ... }
.table-bordered { ... }
.table-condensed { ... }

// Forms
.form-inline { ... }
.form-search { ... }
.form-horizontal { ... }

// Nav
.nav { ... }
.nav-tabs { ... }
.nav-pills { ... }
&lt;/pre&gt;
&lt;p&gt;The goals with 2.0 are consistency, simplification and future-proofing styles. With 1.4, buttons used &quot;btn primary&quot; and it caused problems if you wanted to have a &quot;primary&quot; class in your project. With 2.0, buttons and all elements are namespaced to avoid collisions (now it&apos;s .btn-primary).
&lt;/p&gt;
&lt;p&gt;After Mark finished talking about the design of Bootstrap, Jacob (@fat) started talking about Bootstrap&apos;s JavaScript. Jacob works on The Platform Team at Twitter and claims he made a lot of mistakes with 1.x. However, thanks to semantic versioning, 2.0 is a new version and he got to start over!
&lt;/p&gt;
&lt;p&gt;The biggest change in 2.0 is the use of data attributes (a.k.a. data-*). They were using them in 1.x, but not to the full potential of what they can be and should be. The first class API for Bootstrap JavaScript is data attributes (or HTML), not JavaScript.
&lt;/p&gt;
&lt;p&gt;With 1.x, you could add an anchor to close modals and alerts.
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
// 1.x closing modal/alerts
&amp;lt;a class=&quot;close&quot; href=&quot;#&quot;&gt;&amp;times;&amp;lt;/a&gt;
&lt;/pre&gt;
&lt;p&gt;However, if you put your alerts in your modals, you close them all when you likely only wanted to close one. 2.0 uses a &quot;data-dismiss&quot; attribute.
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;a class=&quot;close&quot; data-dismiss=&quot;model&quot;&gt;&amp;times&amp;lt;/a&gt;
&lt;/pre&gt;
&lt;p&gt;This allows you to target what you want closed (modals or alerts, etc.). You know exactly what&apos;s going to happen just by reading the code. Another example is the &quot;href&quot; attribute of an anchor. Rather than using &quot;href&quot;, you can now use &quot;data-target&quot;. 
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
// 1.x href = target
&amp;lt;a href=&quot;#myModal&quot; data-toggle=&quot;modal&quot;&gt;Launch&amp;lt;/a&gt;

// 2.x data-target = target
&amp;lt;a data-target=&quot;.fat&quot; data-toggle=&quot;modal&quot;&gt;Launch&amp;lt;/a&gt;
&lt;/pre&gt;
&lt;p&gt;If you&apos;d rather turn off the data attribute API, or just part of it, you can do so by using the following:
&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
// Turn off all data-api
$(&apos;body&apos;).off(&apos;.data-api&apos;)

// Turn off alert data-api
$(&apos;body).off(&apos;.alert.data-api&apos;)
&lt;/pre&gt;
&lt;p&gt;2.0&apos;s JavaScript API has the same stuff, but better. You can turn off the data-api and do everything with JavaScript. They copied jQuery UI in a lot of ways (defaults, constructors, etc.). &lt;a href=&quot;http://twitter.github.com/bootstrap/javascript.html&quot;&gt;Bootstrap&apos;s JavaScript&lt;/a&gt; has 12 plugins. New ones include collapse, carousel and typeahead. &lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://twitter.github.com/bootstrap/download.html&quot;&gt;Customize&lt;/a&gt; - a new tab that lets you customize and download Bootstrap. It&apos;s basically an alternative to customizing .less files and allows you to choose components, select jQuery plugins, customize variables (colors, font-sizes, backgrounds) and download. &lt;/p&gt;

&lt;p&gt;What does the future hold? Internationalization, improving responsiveness, more new features and bug fixes. 
&lt;/p&gt;
&lt;p style=&quot;border-top: 1px dotted silver; padding-top: 10px&quot;&gt;
After both Mark and Jacob gave their talks, they talked together about Community and how great it&apos;s been. Even if you&apos;re not into writing CSS and JavaScript, they mentioned they still wanted to hear from you. To give an example of great community contributions, one guy opened 50 issues in the last 2 days.&lt;/p&gt;
&lt;p&gt;Someone in the audience asked why they used LESS over SASS. Jacob said the main reason they use LESS is because they&apos;re good friends with the guy who invented it (&lt;a href=&quot;http://cloudhead.io/&quot;&gt;Alexis&lt;/a&gt;). SASS turns CSS into a programming language, but they wanted to maintain the approachability of CSS, which LESS does. There&apos;s no plans to do an official SASS port, but there is talk of doing one. One advantage of the current LESS compiler is they rewrote it to have better output so it&apos;s far more readable.
&lt;/p&gt;
&lt;p id=&quot;nasa&quot;&gt;&lt;strong&gt;NASA&lt;/strong&gt;&lt;br/&gt;
After Mark and Jacob finished, there was a 5 minute break to grab beers and snacks. Then Sean Herron (&lt;a href=&quot;http://twitter.com/seanherron&quot;&gt;@seanherron&lt;/a&gt;) (a.k.a. &quot;NASA Bro&quot;) talked about Bootstrap at NASA. He actually didn&apos;t talk about Bootstrap much, except that they used it for &lt;a href=&quot;http://code.nasa.gov&quot;&gt;code.NASA&lt;/a&gt;. He talked about NASA and how it&apos;s playing a key role in the movement towards open data, open source and open standards in our federal government. He mentioned how &lt;a href=&quot;http://data.nasa.gov/&quot;&gt;data.NASA&lt;/a&gt; was launched last August and that they helped develop &lt;a href=&quot;http://openstack.org/&quot;&gt;OpenStack&lt;/a&gt;. Finally, he mentioned &lt;a href=&quot;http://open.nasa.gov/&quot;&gt;open.NASA&lt;/a&gt;, which is a collaborative approach to open, direct and transparent communication about our space program.&lt;/p&gt;
&lt;/p&gt;
&lt;p id=&quot;hogan.js&quot;&gt;&lt;strong&gt;Hogan.js&lt;/strong&gt;&lt;br/&gt;
Next up, Rob Sayre (&lt;a href=&quot;http://twitter.com/sayrer&quot;&gt;@sayrer&lt;/a&gt;) talked about &lt;a href=&quot;http://twitter.github.com/hogan.js/&quot;&gt;Hogan.js&lt;/a&gt;. 
Rob has been at Twitter for a few months, before that he wrote JavaScript at other places. Hogan.js is a compiler for &lt;a href=&quot;http://mustache.github.com/&quot;&gt;Mustache templates&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
&lt;em&gt;Why Mustache?&lt;/em&gt; Because it&apos;s similar to HTML and easy to edit. You can mock data as JSON files and programmers are not required.
&lt;/p&gt;
&lt;p&gt;
At Twitter, designers can do the CSS and Mustache without connecting to the backend. It has cross-language support in Ruby, Java and JavaScript. However, client-side template compilation has performance problems, especially in IE7 on a Windows XP box with 4 viruses. 
&lt;/p&gt;
&lt;p&gt;So they had a few choices: work on mustache.js, or use Dust.js or Handlebars.js. The compilers are very nice for Dust.js and Handlebars.js, but they&apos;re huge. Handlebar&apos;s parser is 4000 lines. The entire Hogan.js file is 500 lines. They decided they were too large to send to the browser&apos;s of their users, so they chose to write a better compiler for Mustache.
&lt;/p&gt;
&lt;p&gt;
Hogan.js&apos;s main features:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Compile on the server&lt;/li&gt;
&lt;li&gt;Parser API&lt;/li&gt;
&lt;li&gt;Performance&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Performance is &lt;em&gt;much&lt;/em&gt; better with Hogan.js than Mustache.js. On IE7 - Hogan is 5x faster than Mustache. On an iPhone, it&apos;s about the same (and an iPhone&apos;s browser is faster than IE7 on a decent computer). With modern browsers (Chrome 17, Safari 5 and Firefox 10), it&apos;s more than 10x faster.
&lt;/p&gt;
&lt;p&gt;Hogan.js is currently used at Twitter for Tweet embedding, the Bootstrap build process and soon, Twitter.com.
&lt;/p&gt;
&lt;p style=&quot;border-top: 1px dotted silver; padding-top: 10px&quot;&gt;
It&apos;s been awhile since I got excited about an open source project. Bootstrap has helped me a lot recently, in my &lt;a href=&quot;http://raibledesigns.com/rd/entry/my_html5_with_play_scala&quot;&gt;&lt;em&gt;Play More!&lt;/em&gt;&lt;/a&gt; mobile app, on some client projects and I&apos;m in the process of &lt;a href=&quot;http://raibledesigns.com/rd/entry/refreshing_appfuse_s_ui_with&quot;&gt;refreshing AppFuse&apos;s UI to use it&lt;/a&gt;. I love how you can add a class or two to an element and all of a sudden they pop with good looks. The main problem with Bootstrap at this point is that a lot of Bootstrapped apps look the same. There&apos;s talk of adding themes in a future release to help alleviate this problem. In the meantime, there&apos;s a lot to get excited about with 2.0.
&lt;/p&gt;
&lt;p&gt;Thanks to Twitter for hosting this event and kudos to Mark and Jacob (and the community!) for such a fantastic project. </content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/refreshing_appfuse_s_ui_with</id>
        <title type="html">Refreshing AppFuse&apos;s UI with Twitter Bootstrap</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/refreshing_appfuse_s_ui_with"/>
        <published>2012-01-31T17:12:17-07:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="springmvc" scheme="http://roller.apache.org/ns/tags/" />
        <category term="twitter" scheme="http://roller.apache.org/ns/tags/" />
        <category term="struts2" scheme="http://roller.apache.org/ns/tags/" />
        <category term="bootstrap" scheme="http://roller.apache.org/ns/tags/" />
        <category term="appfuse" scheme="http://roller.apache.org/ns/tags/" />
        <category term="ui" scheme="http://roller.apache.org/ns/tags/" />
        <category term="css" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">The last time AppFuse had an update done to its look and feel was in way back in 2006. I&apos;ve done a lot of consulting since then, which has included a fair bit of page speed optimization, HTML5 development and integrating smarter CSS. It was way back in &apos;05 when we first started looking at adding a CSS Framework to AppFuse.  It was Mike Stenhouse&apos;s &lt;a href=&quot;http://www.contentwithstyle.co.uk/content/a-css-framework/&quot;&gt;CSS Framework&lt;/a&gt; that provided the &lt;a href=&quot;http://raibledesigns.com/rd/entry/a_css_framework&quot;&gt;inspiration&lt;/a&gt; and my &lt;a href=&quot;http://raibledesigns.com/rd/entry/css_framework_design_contest_final&quot;&gt;CSS Framework Design Contest&lt;/a&gt; that provided its current themes (&lt;a href=&quot;http://css.appfuse.org/themes/puzzlewithstyle&quot;&gt;puzzlewithstyle&lt;/a&gt;, &lt;a href=&quot;http://css.appfuse.org/themes/andreas01&quot;&gt;andreas01&lt;/a&gt; and &lt;a href=&quot;http://css.appfuse.org/themes/simplicity&quot;&gt;simplicity&lt;/a&gt;).
&lt;/p&gt;
&lt;p&gt;Since then, a lot of CSS Frameworks have been invented, including &lt;a href=&quot;http://www.blueprintcss.org/&quot;&gt;Blueprint&lt;/a&gt; in 2007 and &lt;a href=&quot;http://compass-style.org/&quot;&gt;Compass&lt;/a&gt; in 2008. However, neither has taken the world by storm like &lt;a href=&quot;http://twitter.github.com/bootstrap/&quot;&gt;Twitter Bootstrap&lt;/a&gt;. From &lt;a href=&quot;http://www.alistapart.com/articles/building-twitter-bootstrap/&quot;&gt;Building Twitter Bootstrap&lt;/a&gt;:
&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
A year-and-a-half ago, a small group of Twitter employees set out to improve our team&#8217;s internal analytical and administrative tools. After some early meetings around this one product, we set out with a higher ambition to create a toolkit for anyone to use within Twitter, and beyond. Thus, we set out to build a system that would help folks like us build new projects on top of it, and Bootstrap was conceived.&lt;br/&gt;
...&lt;br/&gt;
Today, it has grown to include dozens of components and has become the most popular project on GitHub with more than 13,000 watchers and 2,000 forks.&lt;/p&gt;
&lt;p&gt;&lt;p&gt;The fact that Bootstrap has become the most popular project on GitHub says a lot. For &lt;a href=&quot;http://appfuse.547863.n4.nabble.com/AppFuse-next-td3634415.html&quot;&gt;AppFuse.next&lt;/a&gt;, I&apos;d like to integrate a lot of my learnings over the past few years, as well as support HTML5 and modern browsers as best we can. This means &lt;a href=&quot;http://code.google.com/speed/page-speed/docs/rules_intro.html&quot;&gt;page speed optimizations&lt;/a&gt;, getting rid of Prototype and Scriptaculous in favor of jQuery, adding &lt;a href=&quot;http://code.google.com/p/wro4j/&quot;&gt;wro4j&lt;/a&gt; for resource optimization and integrating &lt;a href=&quot;http://html5boilerplate.com/&quot;&gt;HTML5 Boilerplate&lt;/a&gt;. I&apos;ve used Twitter Bootstrap for my &lt;a href=&quot;http://raibledesigns.com/rd/entry/my_html5_with_play_scala&quot;&gt;&lt;em&gt;Play More!&lt;/em&gt; app&lt;/a&gt;, as well as some recent client projects. Its excellent documentation has made it easy to use and I love the way you can simply add classes to elements to make them transform into something beautiful.
&lt;/p&gt;
&lt;p&gt;Last week, I spent a couple late nights integrating &lt;a href=&quot;http://thinkvitamin.com/design/twitter-bootstrap-2-0/&quot;&gt;Twitter Bootstrap 2.0&lt;/a&gt; into the Struts 2 and Spring MVC versions of AppFuse. The layout was pretty straightforward thanks to &lt;a href=&quot;http://markdotto.com/bs2/docs/scaffolding.html&quot;&gt;Scaffolding&lt;/a&gt;. Creating the Struts Menu Velocity template to produce &lt;a href=&quot;http://markdotto.com/bs2/docs/javascript.html#dropdowns&quot;&gt;dropdowns&lt;/a&gt; wasn&apos;t too difficult. I added &lt;a href=&quot;http://markdotto.com/bs2/docs/base-css.html#tables&quot;&gt;class=&quot;table table-condensed&quot;&lt;/a&gt; to the list screen tables, &lt;a href=&quot;http://markdotto.com/bs2/docs/base-css.html#forms&quot;&gt;class=&quot;well form-horizontal&quot;&lt;/a&gt; to forms and &lt;a href=&quot;http://markdotto.com/bs2/docs/base-css.html#buttons&quot;&gt;class=&quot;btn primary&quot;&lt;/a&gt; to buttons. 
&lt;/p&gt;
&lt;p&gt;  
I also added validation errors with the &quot;help-inline&quot; class. This is also where things got tricky with Struts and Spring MVC. For the form elements in Bootstrap, they recommend you use a &quot;control-group&quot; element that contains your label and a &quot;controls&quot; element. The control contains the input/select/textarea and also the error message if there is one. Here&apos;s a sample element waiting for data: 
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;div class=&quot;control-group&quot;&amp;gt;
    &amp;lt;label for=&quot;name&quot; class=&quot;control-label&quot;&amp;gt;Name&amp;lt;/label&amp;gt;
    &amp;lt;div class=&quot;controls&quot;&amp;gt;
        &amp;lt;input type=&quot;text&quot; id=&quot;name&quot; name=&quot;name&quot;&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Below is what that element should look like to display a validation error:&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;div class=&quot;control-group error&quot;&amp;gt;
    &amp;lt;label for=&quot;name&quot; class=&quot;control-label&quot;&amp;gt;Name&amp;lt;/label&amp;gt;
    &amp;lt;div class=&quot;controls&quot;&amp;gt;
        &amp;lt;input type=&quot;text&quot; id=&quot;name&quot; name=&quot;name&quot; value=&quot;&quot;&amp;gt;
        &amp;lt;span class=&quot;help-inline&quot;&amp;gt;Please enter your name.&amp;lt;/span&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/pre&gt;
&lt;p&gt;You can see this markup is pretty easy, you just need to add an &quot;error&quot; class to &lt;em&gt;control-group&lt;/em&gt; and span to show the error message. With Struts 2, this was pretty easy thanks to its customizable templates for its &lt;a href=&quot;http://struts.apache.org/2.x/docs/struts-tags.html&quot;&gt;tags&lt;/a&gt;. All I had to do was create a &quot;template/css_xhtml&quot; directory in &lt;em&gt;src/main/webapp&lt;/em&gt; and modify checkbox.ftl, controlfooter.ftl, controlheader-core.ftl and controlheader.ftl to match Bootstrap&apos;s conventions. 
&lt;/p&gt;
&lt;p&gt;
Spring MVC was a bit trickier. Since its tags don&apos;t have the concept of writing an entire control (label and field), I had to do a bit of finagling to get things to work. In the current implementation, Struts 2 forms have a single line for a &lt;em&gt;control-group&lt;/em&gt; and its &lt;em&gt;control-label&lt;/em&gt; and &lt;em&gt;controls&lt;/em&gt;.&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;&amp;lt;s:textfield key=&quot;user.firstName&quot; required=&quot;true&quot;/&gt;&lt;/pre&gt;
&lt;p&gt;With Spring MVC, it&apos;s a bit more complex:&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;spring:bind path=&quot;user.firstName&quot;&amp;gt;
&amp;lt;fieldset class=&quot;control-group${(not empty status.errorMessage) ? &apos; error&apos; : &apos;&apos;}&quot;&amp;gt;
&amp;lt;/spring:bind&amp;gt;
    &amp;lt;appfuse:label styleClass=&quot;control-label&quot; key=&quot;user.firstName&quot;/&amp;gt;
    &amp;lt;div class=&quot;controls&quot;&amp;gt;
        &amp;lt;form:input path=&quot;firstName&quot; id=&quot;firstName&quot; maxlength=&quot;50&quot;/&amp;gt;
        &amp;lt;form:errors path=&quot;firstName&quot; cssClass=&quot;help-inline&quot;/&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/fieldset&amp;gt;
&lt;/pre&gt;
&lt;p&gt;You could probably overcome this verbosity with &lt;a href=&quot;http://today.java.net/pub/a/today/2003/11/14/tagfiles.html&quot;&gt;Tag Files&lt;/a&gt;. 
&lt;/p&gt;
&lt;p&gt;
Figuring out if a &lt;em&gt;control-group&lt;/em&gt; needed an error class before the input tag was rendered was probably the hardest part of this exercise. This was mostly due to Bootstrap&apos;s great documentation and useful examples (viewed by inspecting the markup). Below are some screenshots of the old screens and new ones. 
&lt;/p&gt;
&lt;p style=&quot;text-align: center; vertical-align: top&quot;&gt;
&lt;a href=&quot;http://farm8.staticflickr.com/7173/6787781357_c4c65c7c74_b.jpg&quot; title=&quot;Old UI - Login&quot; rel=&quot;lightbox[appfuse-bootstrap]&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7173/6787781357_c4c65c7c74_t.jpg&quot; width=&quot;100&quot; height=&quot;60&quot; alt=&quot;Old UI - Login&quot;&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm8.staticflickr.com/7142/6787781421_0c7851b414_b.jpg&quot; title=&quot;Old UI - Users&quot; rel=&quot;lightbox[appfuse-bootstrap]&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7142/6787781421_0c7851b414_t.jpg&quot; width=&quot;100&quot; height=&quot;60&quot; alt=&quot;Old UI - Users&quot; style=&quot;margin-left: 10px&quot;&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm8.staticflickr.com/7035/6787781725_3a1f0218c1_b.jpg&quot; title=&quot;Old UI - Edit Profile&quot; rel=&quot;lightbox[appfuse-bootstrap]&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7035/6787781725_3a1f0218c1_t.jpg&quot; width=&quot;100&quot; height=&quot;82&quot; alt=&quot;Old UI - Edit Profile&quot; style=&quot;margin-left: 10px&quot;&gt;&lt;/a&gt;

&lt;p&gt;  
&lt;p style=&quot;text-align: center; vertical-align: top&quot;&gt;
&lt;a href=&quot;http://farm8.staticflickr.com/7025/6787781477_ec2ac7a93b_b.jpg&quot; title=&quot;New UI - Login&quot; rel=&quot;lightbox[appfuse-bootstrap]&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7025/6787781477_ec2ac7a93b_t.jpg&quot; width=&quot;100&quot; height=&quot;60&quot; alt=&quot;New UI - Login&quot;&gt;&lt;/a&gt;
  
&lt;a href=&quot;http://farm8.staticflickr.com/7015/6787781597_6558d94bb5_b.jpg&quot; title=&quot;New UI - Users&quot; rel=&quot;lightbox[appfuse-bootstrap]&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7015/6787781597_6558d94bb5_t.jpg&quot; width=&quot;100&quot; height=&quot;60&quot; alt=&quot;New UI - Users&quot; style=&quot;margin-left: 10px&quot;&gt;&lt;/a&gt;
  
  &lt;a href=&quot;http://farm8.staticflickr.com/7010/6787781681_81b7977414_b.jpg&quot; title=&quot;New UI - Edit Profile&quot; rel=&quot;lightbox[appfuse-bootstrap]&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7010/6787781681_81b7977414_t.jpg&quot; width=&quot;100&quot; height=&quot;82&quot; alt=&quot;New UI - Edit Profile&quot; style=&quot;margin-left: 10px&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #666; text-align: center&quot;&gt;Check out the &lt;a style=&quot;color: #666&quot; href=&quot;http://www.flickr.com/photos/mraible/sets/72157629094630763/&quot;&gt;full set on Flickr&lt;/a&gt; if you&apos;d like a closer look.
  &lt;/p&gt;
  &lt;p&gt;
Even though I like the look of the old UI, I can&apos;t help but think a lot of the themes are designed for blogs and content sites, not webapps. The old &lt;a href=&quot;http://wufoo.com/&quot;&gt;Wufoo&lt;/a&gt; forms were a lot better looking though. And if you&apos;re going to develop &lt;a href=&quot;http://blogs.atlassian.com/2012/01/modern-principles-in-web-development/&quot;&gt;kick-ass webapps&lt;/a&gt;, you need to make them look good. Bootstrap goes a long way in doing this, but it certainly doesn&apos;t replace a good UX Designer. Bootstap simply helps you get into HTML5-land, start using CSS3 and it takes the pain out of making things work cross-browser. Its fluid layouts and responsive web design seems to work great for business applications, which I&apos;m guessing AppFuse is used for the most. 
&lt;/p&gt;
&lt;p&gt;
I can&apos;t thank the Bootstrap developers enough for helping me make this all look good. With Bootstrap 2 &lt;a href=&quot;http://www.markdotto.com/2012/01/24/bootstrap-2-ready-for-testing-and-feedback/&quot;&gt;dropping this week&lt;/a&gt;, I can see myself using this more and more on projects. In the near future, I&apos;ll be helping integrate Bootstrap into AppFuse&apos;s &lt;a href=&quot;http://appfuse.547863.n4.nabble.com/Tapestry-5-3-2-td4339578.html&quot;&gt;Tapestry 5 and JSF versions&lt;/a&gt;. 
&lt;/p&gt;
&lt;p&gt;What do you think of this CSS change? Do you change your CSS and layout a fair bit when starting with AppFuse archetypes? What can we do to make AppFuse apps look better out-of-the-box?
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; I &lt;a href=&quot;http://source.appfuse.org/changelog/appfuse/?cs=3593&quot;&gt;updated&lt;/a&gt; AppFuse to the final &lt;a href=&quot;http://raibledesigns.com/rd/entry/twitter_s_open_source_summit&quot;&gt;Bootstrap 2.0 release&lt;/a&gt;. Also, Johannes Geppert wrote a &lt;a href=&quot;http://www.jgeppert.com/2012/02/new-struts2-bootstrap-plugin-released/&quot;&gt;Struts 2 Bootstrap Plugin&lt;/a&gt;. I hope to integrate this into AppFuse in the near future.</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/2011_a_year_in_review</id>
        <title type="html">2011 - A Year in Review</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/2011_a_year_in_review"/>
        <published>2012-01-11T09:45:20-07:00</published>
        <updated>2014-07-26T21:04:50-06:00</updated> 
        <category term="/Roller" label="Roller" />
        <category term="yearinreview" scheme="http://roller.apache.org/ns/tags/" />
        <category term="2011" scheme="http://roller.apache.org/ns/tags/" />
        <category term="blogging" scheme="http://roller.apache.org/ns/tags/" />
        <category term="roller" scheme="http://roller.apache.org/ns/tags/" />
        <summary type="html">&lt;a href=&quot;http://raibledesigns.com/rd/entry/2010_a_year_in_review&quot;&gt;2010 was an awesome year&lt;/a&gt;, but 2011 rocked the house! At the end of last year, I kept my goals simple:&lt;/p&gt;
&lt;p class=&quot;quote&quot; style=&quot;color: #666&quot;&gt;
In 2011, I plan on doing two main things: keep rockin&apos; it with Trish and finishing The Bus. Everything else is gravy. 
&lt;/p&gt;
&lt;p&gt;As predicted, it &lt;em&gt;was&lt;/em&gt; a spectacular year, but I only accomplished 50% of my goals. That is, Trish and I had a great time skiing (especially in Utah), moving in together, traveling the world and getting engaged in Versailles. I even satisfied some 5-year goals: building a sauna in my basement and getting a ski shack in the mountains.&lt;/p&gt;
&lt;p&gt;However, I didn&apos;t get much done with The Bus. Or rather, the guys at &lt;a href=&quot;http://www.motorworksrestorations.com/&quot;&gt;MotorWorks Restorations&lt;/a&gt; didn&apos;t drain me for all I&apos;m worth in 2011. We did make good progress with estimating the final cost and obtaining many hard-to-find parts though. I now have a Porsche 911 Engine (1983 3.0L 6 cylinder), a Porsche 901 5 speed transmission, Porsche &quot;Turbo Twist&quot; wheels and a &lt;a href=&quot;https://www.facebook.com/media/set/?set=a.10150332577286054.392700.672811053&amp;amp;type=1&quot;&gt;Custom Air Ride Front Beam&lt;/a&gt; from &lt;a href=&quot;http://www.franklinsvwwerks.com/&quot;&gt;Franklin&apos;s VW Works&lt;/a&gt;. The thing that slowed our progress the most was the custom beam, as it took almost 6 months from order to delivery. When it arrived in September, I decided to put things on hold. I didn&apos;t want to get my bus back in the midst of winter and not be able to drive it.&lt;/p&gt;</summary>
        <content type="html">&lt;a href=&quot;http://raibledesigns.com/rd/entry/2010_a_year_in_review&quot;&gt;2010 was an awesome year&lt;/a&gt;, but 2011 rocked the house! At the end of last year, I kept my goals simple:&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
In 2011, I plan on doing two main things: keep rockin&apos; it with Trish and finishing The Bus. Everything else is gravy. 
&lt;/p&gt;
&lt;p&gt;As predicted, it &lt;em&gt;was&lt;/em&gt; a spectacular year, but I only accomplished 50% of my goals. That is, Trish and I had a great time skiing (especially in Utah), moving in together, traveling the world and getting engaged in Versailles. I even satisfied some 5-year goals: building a sauna in my basement and getting a ski shack in the mountains.
&lt;/p&gt;
&lt;p&gt;However, I didn&apos;t get much done with The Bus. Or rather, the guys at &lt;a href=&quot;http://www.motorworksrestorations.com/&quot;&gt;MotorWorks Restorations&lt;/a&gt; didn&apos;t drain me for all I&apos;m worth in 2011. We did make good progress with estimating the final cost and obtaining many hard-to-find parts though. I now have a Porsche 911 Engine (1983 3.0L 6 cylinder), a Porsche 901 5 speed transmission, Porsche &quot;Turbo Twist&quot; wheels and a &lt;a href=&quot;https://www.facebook.com/media/set/?set=a.10150332577286054.392700.672811053&amp;amp;type=1&quot;&gt;Custom Air Ride Front Beam&lt;/a&gt; from &lt;a href=&quot;http://www.franklinsvwwerks.com/&quot;&gt;Franklin&apos;s VW Works&lt;/a&gt;. The thing that slowed our progress the most was the custom beam, as it took almost 6 months from order to delivery. When it arrived in September, I decided to put things on hold. I didn&apos;t want to get my bus back in the midst of winter and not be able to drive it.
&lt;/p&gt;
&lt;p&gt;
&lt;a href=&quot;http://farm5.staticflickr.com/4136/5396188811_13d0eab8ae.jpg&quot; title=&quot;Welcome to Alta&quot; rel=&quot;lightbox[yearinreview2011]&quot;&gt;&lt;img src=&quot;//farm5.staticflickr.com/4136/5396188811_13d0eab8ae_t.jpg&quot; width=&quot;100&quot; height=&quot;74&quot; alt=&quot;Welcome to Alta&quot; class=&quot;picture&quot; style=&quot;margin-top: 5px; border: 1px solid black&quot;&gt;&lt;/a&gt;
  
In January, I started working at &lt;a href=&quot;http://overstock.com&quot;&gt;Overstock.com&lt;/a&gt; and flew out to Salt Lake City the first two weeks to ramp up. In setting up my development environment, I discovered some inefficiencies and wrote about how I fixed them in &lt;a href=&quot;http://raibledesigns.com/rd/entry/making_code_generation_smarter_with&quot;&gt;Making Code Generation Smarter with Maven&lt;/a&gt;. I also discovered that JRebel slowed my build process down by almost 50% and stopped using it as part of my daily process. Now I simply run in debug mode or use &lt;a href=&quot;http://www.playframework.org/&quot;&gt;fancy frameworks&lt;/a&gt; that allow instant reload.
&lt;/p&gt;
&lt;p&gt;At the end of the month, I &lt;a href=&quot;http://raibledesigns.com/rd/entry/alta&quot;&gt;skied Alta&lt;/a&gt; for the first time on a blue bird day. That night, I flew back to Denver, had a gorgeous &lt;a href=&quot;http://farm5.static.flickr.com/4119/5396080419_7cd9f5cb87.jpg&quot; rel=&quot;lightbox[yearinreview2011]&quot; title=&quot;Dear Denver: You&apos;re the best.&quot;&gt;lunch ride&lt;/a&gt; in 70&amp;deg;F weather on Friday afternoon, then picked up my lovely girlfriend, Trish, and drove to Crested Butte for a &lt;a href=&quot;http://raibledesigns.com/rd/entry/crested_butte&quot;&gt;weekend of skiing with James Ward and friends&lt;/a&gt;. It was my first time visiting Crested Butte and I can see why people call it Paradise.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a data-href=&quot;http://www.flickr.com/photos/mraible/5407159019&quot; href=&quot;http://farm6.staticflickr.com/5096/5407159019_f65b19983d.jpg&quot; title=&quot;Crested Butte, The Town&quot; rel=&quot;lightbox[yearinreview2011]&quot;&gt;&lt;img src=&quot;//farm6.staticflickr.com/5096/5407159019_f65b19983d_m.jpg&quot; width=&quot;240&quot; style=&quot;border: 1px solid black&quot; alt=&quot;Crested Butte, The Town&quot;&gt;&lt;/a&gt;
&lt;a data-href=&quot;http://www.flickr.com/photos/mraible/5407165843&quot; href=&quot;http://farm6.staticflickr.com/5094/5407165843_8c6328f2d9.jpg&quot; title=&quot;Top of Crested Butte&quot; rel=&quot;lightbox[yearinreview2011]&quot;&gt;&lt;img src=&quot;//farm6.staticflickr.com/5094/5407165843_8c6328f2d9_m.jpg&quot; width=&quot;240&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot; alt=&quot;Top of Crested Butte&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;As of February 1st, Trish and I both had 17 ski days. As I&apos;m writing this, we&apos;re both up to 14 for this season and very proud to say Abbie and Jack each have 10. Both kids are shredding like I&apos;ve always dreamed they would.
&lt;/p&gt;
&lt;p&gt;My parents (both retired) came to visit for the month of February and helped me start building a sauna in my basement. This was something I&apos;ve wanted to do for years, so it was a lot of fun getting things started. While we didn&apos;t finish construction, we did get the plumbing/electrical done, stove installed and walls framed. We also had a fun night with &lt;a href=&quot;http://www.flickr.com/photos/mraible/sets/72157626083451336/&quot;&gt;Chef Navarro and Friends&lt;/a&gt;.
&lt;p&gt;
The rest of my posts in February were purely technical. I wrote about how I &lt;a href=&quot;http://raibledesigns.com/rd/entry/implementing_extensionless_urls_with_tapestry&quot;&gt;implemented extensionless URLs with Tapestry, Spring MVC, Struts 2 and JSF&lt;/a&gt;. Then I started delving into &lt;a href=&quot;http://raibledesigns.com/rd/entry/integration_testing_with_http_https&quot;&gt;security-related topics&lt;/a&gt;, largely motivated by a new feature I was working on at Overstock. When I wrote about &lt;a href=&quot;http://raibledesigns.com/rd/entry/implementing_ajax_authentication_using_jquery&quot;&gt;Implementing Ajax Authentication using jQuery, Spring Security and HTTPS&lt;/a&gt;, I explained what I was trying to accomplish.&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
The feature hasn&apos;t been released yet, but basically boils down to allowing users to login without leaving a page. For example, if they want to leave a review on a product, they would click a link, be prompted to login, enter their credentials, then continue to leave their review. The login prompt and subsequent review would likely be implemented using a lightbox. While lightboxes are often seen in webapps these days because they look good, it&apos;s also possible Lightbox UIs provide a poor user experience.
&lt;/p&gt;
&lt;p&gt;I was able to get everything working, &lt;a href=&quot;http://demo.raibledesigns.com/ajax-login/&quot;&gt;posted a demo&lt;/a&gt; and wrote about &lt;a href=&quot;http://raibledesigns.com/rd/entry/upcoming_conferences_tssjs_in_las&quot;&gt;upcoming conferences in Las Vegas and Krak&amp;#243;w&lt;/a&gt;. In that article, I described a new talk I was going to create: &lt;em&gt;Webapp Security: Develop. Penetrate. Protect. Relax.&lt;/em&gt; I ended the month with a post on &lt;a href=&quot;http://raibledesigns.com/rd/entry/fixing_xss_in_jsp_2&quot;&gt;how to fix XSS in JSP 2&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;
&lt;p&gt;On February 25th, I experienced &lt;a href=&quot;http://raibledesigns.com/rd/entry/the_greatest_snow_on_earth&quot;&gt;The Greatest Snow on Earth&lt;/a&gt; with thigh-deep powder and face shots on every run.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;iframe width=&quot;500&quot; height=&quot;311&quot; frameborder=&quot;0&quot; src=&quot;//www.youtube.com/embed/viBBPot4WKY?rel=0&amp;amp;hd=1&quot; title=&quot;YouTube video player&quot;&gt;&lt;/iframe&gt;
&lt;/p&gt;
&lt;p&gt;That night, we saw &lt;a href=&quot;http://www.hotbutteredrum.net/&quot;&gt;Hot Buttered Rum&lt;/a&gt; and woke up early for 27&quot; of fresh powder at Alta. The picture of Trish free-heeling at Alta is one of my favorites of the year.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://farm6.staticflickr.com/5216/5490614643_00cb94c020.jpg&quot; title=&quot;Free Heeling at Alta&quot; rel=&quot;lightbox[yearinreview2011]&quot;&gt;&lt;img src=&quot;//farm6.staticflickr.com/5216/5490614643_00cb94c020.jpg&quot; width=&quot;500&quot; height=&quot;375&quot; alt=&quot;Free Heeling at Alta&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;In early March, we experienced a &lt;a href=&quot;http://www.flickr.com/photos/mraible/sets/72157626199804847/&quot;&gt;suite night at the Avs game&lt;/a&gt;, then I kept it technical and &lt;a href=&quot;http://raibledesigns.com/rd/entry/upgraded_to_apache_roller_5&quot;&gt;upgraded this blog to Apache Roller 5.0, RC4&lt;/a&gt;. I &lt;a href=&quot;http://raibledesigns.com/rd/entry/upgrading_to_jsf_2&quot;&gt;upgraded AppFuse to JSF 2&lt;/a&gt;, wrote about &lt;a href=&quot;http://raibledesigns.com/rd/entry/jsr_303_and_web_framework&quot;&gt;JSR 303 and web framework support&lt;/a&gt; and &lt;a href=&quot;http://raibledesigns.com/rd/entry/adding_search_to_appfuse&quot;&gt;added search to AppFuse using Compass&lt;/a&gt;. When writing about JSF 2, I mentioned replacing Ajax4JSF with RichFaces. 9 months later, it seems like &lt;a href=&quot;http://primefaces.org/&quot;&gt;PrimeFaces&lt;/a&gt; has more momentum, so I may try that instead.
&lt;/p&gt;
&lt;p&gt;For St. Patrick&apos;s Day, we flew to Vegas for TSSJS and &lt;a href=&quot;http://raibledesigns.com/rd/entry/livin_it_up_in_vegas&quot;&gt;had a blast&lt;/a&gt;. Not only did my talks go well, but we had a bunch of friends in town and had much success gambling the nights away.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://farm6.static.flickr.com/5305/5550023760_e8f128457a.jpg&quot; title=&quot;Caesars Pool&quot; rel=&quot;lightbox[yearinreview2011]&quot;&gt;&lt;img src=&quot;//farm6.static.flickr.com/5305/5550023760_e8f128457a_m.jpg&quot; width=&quot;240&quot; height=&quot;159&quot; alt=&quot;Caesars Pool&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm6.static.flickr.com/5053/5550024190_b272c2f012.jpg&quot; title=&quot;The Bellagio Fountains&quot; rel=&quot;lightbox[yearinreview2011]&quot;&gt;&lt;img src=&quot;//farm6.static.flickr.com/5053/5550024190_b272c2f012_m.jpg&quot; width=&quot;240&quot; height=&quot;159&quot; alt=&quot;The Bellagio Fountains&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot;&gt;&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;After delivering my Comparing JVM Web Frameworks talk, I had a very interesting conversation with &lt;a href=&quot;http://macstrac.blogspot.com/&quot;&gt;James Strachan&lt;/a&gt;. He recommended I take a look at &lt;a href=&quot;http://coffeescript.org/&quot;&gt;CoffeeScript&lt;/a&gt; and &lt;a href=&quot;http://jade-lang.com/&quot;&gt;Jade&lt;/a&gt;. A couple months later, when I was thinking of talks for &lt;a href=&quot;http://devoxx.com&quot;&gt;Devoxx&lt;/a&gt;, I added these to my list of technologies to learn and submitted a talk about HTML5 with Play Scala, CoffeeScript and Jade.
&lt;/p&gt;
&lt;p&gt;That Friday, we returned to Denver, got a good night&apos;s sleep then headed on a &lt;a href=&quot;http://raibledesigns.com/rd/entry/peter_estin_hut_trip_in&quot;&gt;hut trip in Colorado&apos;s High Country&lt;/a&gt;. The name of the hut was &lt;a href=&quot;http://www.huts.org/hut_details/peter_estin_hut_details.html&quot;&gt;Peter Estin Hut&lt;/a&gt; and it was a bit of a hike to get to. My friend, Joe, set my expectations correctly when he warned me it&apos;d be a &lt;em&gt;5 hour death march&lt;/em&gt;. It took us 4 hours, 30 minutes and we skied up 2200 vertical feet of switchbacks.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;

&lt;a rel=&quot;lightbox[yearinreview2011]&quot; title=&quot;Livin&apos; it up, hut style&quot; href=&quot;http://farm6.static.flickr.com/5251/5549474427_56d389dc71.jpg&quot;&gt;&lt;img width=&quot;100&quot; height=&quot;66&quot; style=&quot;border: 1px solid black&quot; alt=&quot;Livin&apos; it up, hut style&quot; src=&quot;//farm6.static.flickr.com/5251/5549474427_56d389dc71_t.jpg&quot;&gt;&lt;/a&gt;

&lt;a rel=&quot;lightbox[yearinreview2011]&quot; title=&quot;We made it!&quot; href=&quot;http://farm6.static.flickr.com/5027/5549475091_bcff5560a0.jpg&quot;&gt;&lt;img width=&quot;100&quot; height=&quot;75&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot; alt=&quot;We made it!&quot; src=&quot;//farm6.static.flickr.com/5027/5549475091_bcff5560a0_t.jpg&quot;&gt;&lt;/a&gt;

&lt;a rel=&quot;lightbox[yearinreview2011]&quot; title=&quot;At the top of Charles Peak&quot; href=&quot;http://farm6.static.flickr.com/5025/5550058284_18d8a3b874.jpg&quot;&gt;&lt;img width=&quot;100&quot; height=&quot;66&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot; alt=&quot;At the top of Charles Peak&quot; src=&quot;//farm6.static.flickr.com/5025/5550058284_18d8a3b874_t.jpg&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;Trish and I started off April with a bang by moving in together, then embarking on a &lt;a href=&quot;http://raibledesigns.com/rd/entry/two_opening_days_with_a&quot;&gt;whirlwind trip of two opening days with a stopover in Krak&#243;w&lt;/a&gt;. We flew Business Class from Chicago to Munich (releasing &lt;a href=&quot;http://raibledesigns.com/rd/entry/appfuse_2_1_released&quot;&gt;AppFuse 2.1&lt;/a&gt; just before departing) and were very impressed with Krak&#243;w&apos;s Old Town upon arrival.
  &lt;p style=&quot;text-align: center&quot;&gt;

  &lt;a rel=&quot;lightbox[yearinreview2011]&quot; title=&quot;St. Mary&apos;s Basilica, Krak&#243;w&quot; href=&quot;http://farm6.static.flickr.com/5188/5618498585_0267da32ce.jpg&quot;&gt;&lt;img width=&quot;75&quot; height=&quot;75&quot; style=&quot;border: 1px solid black&quot; alt=&quot;St. Mary&apos;s Basilica, Krak&#243;w&quot; src=&quot;//farm6.static.flickr.com/5188/5618498585_0267da32ce_s.jpg&quot;&gt;&lt;/a&gt;

  &lt;a rel=&quot;lightbox[yearinreview2011]&quot; title=&quot;Main Market Square&quot; href=&quot;http://farm6.static.flickr.com/5268/5618498729_613983abaf.jpg&quot;&gt;&lt;img width=&quot;75&quot; height=&quot;75&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot; alt=&quot;Main Market Square&quot; src=&quot;//farm6.static.flickr.com/5268/5618498729_613983abaf_s.jpg&quot;&gt;&lt;/a&gt;

  &lt;a rel=&quot;lightbox[yearinreview2011]&quot; title=&quot;St. Mary&apos;s&quot; href=&quot;http://farm6.static.flickr.com/5101/5619084930_87a76dee6b.jpg&quot;&gt;&lt;img width=&quot;75&quot; height=&quot;75&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot; alt=&quot;St. Mary&apos;s&quot; src=&quot;//farm6.static.flickr.com/5101/5619084930_87a76dee6b_s.jpg&quot;&gt;&lt;/a&gt;

  &lt;a rel=&quot;lightbox[yearinreview2011]&quot; title=&quot;Flowers&quot; href=&quot;http://farm6.static.flickr.com/5030/5618499011_462ac088a6.jpg&quot;&gt;&lt;img width=&quot;75&quot; height=&quot;75&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot; alt=&quot;Flowers&quot; src=&quot;//farm6.static.flickr.com/5030/5618499011_462ac088a6_s.jpg&quot;&gt;&lt;/a&gt;

  &lt;/p&gt;
  &lt;p&gt;On that trip, I especially enjoyed meeting Trish&apos;s brother for the first time and watching the Red Sox win their first game of the year.&lt;/p&gt;
  &lt;p style=&quot;text-align: center&quot;&gt;

  &lt;a rel=&quot;lightbox[yearinreview2011]&quot; title=&quot;Fenway Paak!&quot; href=&quot;http://farm6.static.flickr.com/5222/5618502949_cdfbc9d030.jpg&quot;&gt;&lt;img width=&quot;75&quot; height=&quot;75&quot; style=&quot;border: 1px solid black&quot; alt=&quot;Fenway Paak!&quot; src=&quot;//farm6.static.flickr.com/5222/5618502949_cdfbc9d030_s.jpg&quot;&gt;&lt;/a&gt;

  &lt;a rel=&quot;lightbox[yearinreview2011]&quot; title=&quot;Morse and Kidder&quot; href=&quot;http://farm6.static.flickr.com/5263/5618503117_28b4b2b8cc.jpg&quot;&gt;&lt;img width=&quot;75&quot; height=&quot;75&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot; alt=&quot;Morse and Kidder&quot; src=&quot;//farm6.static.flickr.com/5263/5618503117_28b4b2b8cc_s.jpg&quot;&gt;&lt;/a&gt;

  &lt;a rel=&quot;lightbox[yearinreview2011]&quot; title=&quot;Happy Siblings&quot; href=&quot;http://farm6.static.flickr.com/5061/5618503321_c766f1e7bc.jpg&quot;&gt;&lt;img width=&quot;75&quot; height=&quot;75&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot; alt=&quot;Happy Siblings&quot; src=&quot;//farm6.static.flickr.com/5061/5618503321_c766f1e7bc_s.jpg&quot;&gt;&lt;/a&gt;

  &lt;a rel=&quot;lightbox[yearinreview2011]&quot; title=&quot;Erika and Julie&quot; href=&quot;http://farm6.static.flickr.com/5146/5619089798_4aca920bfb.jpg&quot;&gt;&lt;img width=&quot;75&quot; height=&quot;75&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot; alt=&quot;Erika and Julie&quot; src=&quot;//farm6.static.flickr.com/5146/5619089798_4aca920bfb_s.jpg&quot;&gt;&lt;/a&gt;

  &lt;a rel=&quot;lightbox[yearinreview2011]&quot; title=&quot;Red Sox Win!&quot; href=&quot;http://farm6.static.flickr.com/5145/5619090216_2198246d63.jpg&quot;&gt;&lt;img width=&quot;75&quot; height=&quot;75&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot; alt=&quot;Red Sox Win!&quot; src=&quot;//farm6.static.flickr.com/5145/5619090216_2198246d63_s.jpg&quot;&gt;&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;After returning, I realized I lost a lot of data in my Roller 5 upgrade and &lt;a href=&quot;http://raibledesigns.com/rd/entry/be_careful_when_switching_mysql&quot;&gt;wrote about what happened and how I recovered it&lt;/a&gt;. Then we &lt;a href=&quot;http://raibledesigns.com/rd/entry/farewell_to_the_2010_2011&quot;&gt;bid farewell to the 2010-2011 ski season&lt;/a&gt; and got ready for mountain bike season.
  &lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
For next year, I think I&apos;ll keep my goal at 30 days. If everything works out as planned, we&apos;ll have a place in the mountains this fall and it&apos;ll be a bit easier to hit the slopes without sitting in traffic. For now, I&apos;m pumped about the beginning of mountain bike season.
&lt;/p&gt;
&lt;p&gt;
&lt;a rel=&quot;lightbox[yearinreview2011]&quot; ref=&quot;http://farm3.staticflickr.com/2181/5712084350_217de950a5.jpg&quot; title=&quot;Door to Sauna&quot;&gt;&lt;img src=&quot;//farm3.staticflickr.com/2181/5712084350_217de950a5_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Door to Sauna&quot; class=&quot;picture&quot;&gt;&lt;/a&gt;
  
May arrived and Trish and I started looking for a place in the mountains. We found an &lt;a href=&quot;https://www.facebook.com/media/set/?set=a.10150158165601712.292395.571296711&amp;amp;type=1&amp;amp;l=80b0b43473&quot;&gt;very cool cabin&lt;/a&gt;, but later decided it was &lt;em&gt;too&lt;/em&gt; nice and we&apos;d rather have a condo. I finished &lt;a href=&quot;http://raibledesigns.com/rd/entry/the_basement_sauna_project&quot;&gt;The Basement Sauna Project&lt;/a&gt; and wrote about Java Web Application Security: &lt;a href=&quot;http://raibledesigns.com/rd/entry/java_web_application_security_part&quot;&gt;Java EE 6&lt;/a&gt;, &lt;a href=&quot;http://raibledesigns.com/rd/entry/java_web_application_security_part1&quot;&gt;Spring Security&lt;/a&gt; and &lt;a href=&quot;http://raibledesigns.com/rd/entry/java_web_application_security_part2&quot;&gt;Apache Shiro&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;On May 21st, we again visited Winter Park and Fraser to look at condos, then drove back to Denver for &lt;a href=&quot;http://www.denverpost.com/commented/ci_18114909&quot;&gt;U2 at Mile High Stadium&lt;/a&gt;. I&apos;m not a big fan of U2, but this was quite possibly the best concert I&apos;ve ever been to. The experience of 70,000+ fans, all screaming, doing the wave, and loving life was epic. We decided that night that we liked one of the condos more than the rest and put in an offer the following week. The kids headed off to Florida for a month with their Mom and we drove to Utah for &lt;a href=&quot;http://raibledesigns.com/rd/entry/music_mountain_biking_and_memories&quot;&gt;Desert Rocks and mountain biking in Moab&lt;/a&gt;. 
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;https://fbcdn-sphotos-f-a.akamaihd.net/hphotos-ak-ash3/253034_2099846942595_2382489_n.jpg&quot; title=&quot;La Sal Mountains from Desert Rocks&quot; rel=&quot;lightbox[desertrocks2011]&quot;&gt;&lt;img src=&quot;//fbcdn-photos-a.akamaihd.net/hphotos-ak-snc6/253034_2099846942595_1438065285_32404850_2382489_a.jpg&quot; width=&quot;180&quot; height=&quot;120&quot; alt=&quot;La Sal Mountains from Desert Rocks&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

&lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/8358915594/&quot; href=&quot;http://farm9.staticflickr.com/8467/8358915594_e79cd2231d_c.jpg&quot; title=&quot;Scenic Byway&quot; rel=&quot;lightbox[desertrocks2011]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8467/8358915594_e79cd2231d.jpg&quot; width=&quot;180&quot; height=&quot;120&quot; alt=&quot;Scenic Byway&quot; style=&quot;border: 1px solid black; margin-left: 20px&quot;&gt;&lt;/a&gt;

&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;

&lt;a href=&quot;https://sphotos-a.xx.fbcdn.net/hphotos-frc1/250385_2099847942620_1801230_n.jpg&quot; title=&quot;Bar-B or Killer B?&quot; rel=&quot;lightbox[desertrocks2011]&quot;&gt;&lt;img src=&quot;//sphotos-a.xx.fbcdn.net/hphotos-frc1/p206x206/250385_2099847942620_1801230_n.jpg&quot; width=&quot;180&quot; height=&quot;120&quot; alt=&quot;Bar-B or Killer B?&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

&lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/8359032204/&quot; href=&quot;http://farm9.staticflickr.com/8049/8359032204_ace553c370_c.jpg&quot; title=&quot;Three Gossips and Tower of Babel&quot; rel=&quot;lightbox[desertrocks2011]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8049/8359032204_ace553c370.jpg&quot; width=&quot;180&quot; height=&quot;120&quot; alt=&quot;Three Gossips and Tower of Babel&quot; style=&quot;border: 1px solid black; margin-left: 20px&quot;&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;I &lt;a href=&quot;http://raibledesigns.com/rd/entry/upgraded_to_roller_5_and&quot;&gt;Upgraded to Roller 5.0&lt;/a&gt; in June and showed how to add a &lt;a href=&quot;https://developers.facebook.com/docs/reference/plugins/like/&quot;&gt;Facebook Like button&lt;/a&gt;. Next, I transcribed a couple more Java Web Application Security articles: &lt;a href=&quot;http://raibledesigns.com/rd/entry/java_web_application_security_part3&quot;&gt;programmatic login APIs&lt;/a&gt; and &lt;a href=&quot;http://raibledesigns.com/rd/entry/java_web_application_security_part4&quot;&gt;penetrating with Zed Attack Proxy&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;The kids returned to us just before the &lt;a href=&quot;http://raibledesigns.com/rd/entry/good_times_on_the_annual&quot;&gt;annual Father&apos;s Day Camping Trip&lt;/a&gt;. We drove to Lake Granby and stayed at &lt;a href=&quot;http://campincolorado.com/federal/arapaho_roosevelt_nf/stillwater/stillwater.html&quot;&gt;Stillwater Campground&lt;/a&gt; for the weekend.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://farm6.static.flickr.com/5275/5850616117_57971bfd55.jpg&quot; title=&quot;Stillwater Campground Rocks!&quot; rel=&quot;lightbox[yearinreview2011]&quot;&gt;&lt;img src=&quot;//farm6.static.flickr.com/5275/5850616117_57971bfd55_m.jpg&quot; width=&quot;240&quot; height=&quot;180&quot; alt=&quot;Stillwater Campground Rocks!&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm4.static.flickr.com/3182/5851171484_b745090d35.jpg&quot; title=&quot;Beautiful Colorado&quot; rel=&quot;lightbox[yearinreview2011]&quot;&gt;&lt;img src=&quot;//farm4.static.flickr.com/3182/5851171484_b745090d35_m.jpg&quot; width=&quot;240&quot; height=&quot;180&quot; alt=&quot;Beautiful Colorado&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;From my Father&apos;s Day post:&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;I really enjoyed spending so much time with my family this weekend. The good news is we get to do it again when I meet them at The Cabin in Montana next week. In the meantime, Trish and I are off to &lt;a href=&quot;http://jazoon.com/&quot;&gt;Jazoon&lt;/a&gt; for a few days of fun in Switzerland.
&lt;/p&gt;
&lt;p&gt;A few days later, we were &lt;a href=&quot;http://raibledesigns.com/rd/entry/a_sweet_trip_to_switzerland&quot;&gt;in Zurich for the first time&lt;/a&gt;, enjoying our accommodations and marveling at Switzerland&apos;s excellent transportation system.
I experienced an interesting situation while speaking, but quickly forgot when we visited Rhine Falls, celebrated our anniversary at Gruelich and played in the Swiss Alps.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;

&lt;a href=&quot;http://farm7.static.flickr.com/6030/5879428306_a793f2a67e.jpg&quot; title=&quot;View from Great Church&quot; rel=&quot;lightbox[yearinreview2011]&quot;&gt;&lt;img src=&quot;//farm7.static.flickr.com/6030/5879428306_a793f2a67e_s.jpg&quot; width=&quot;75&quot; height=&quot;75&quot; alt=&quot;View from Great Church&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm6.static.flickr.com/5080/5879428652_e847f2ff0c.jpg&quot; title=&quot;Happy Couple in Z&#252;rich&quot; rel=&quot;lightbox[yearinreview2011]&quot;&gt;&lt;img src=&quot;//farm6.static.flickr.com/5080/5879428652_e847f2ff0c_s.jpg&quot; width=&quot;75&quot; height=&quot;75&quot; alt=&quot;Happy Couple in Z&#252;rich&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;a href=&quot;http://farm6.static.flickr.com/5108/5879429020_21dd2b6ddf.jpg&quot; title=&quot;Riding the Train&quot; rel=&quot;lightbox[yearinreview2011]&quot;&gt;&lt;img src=&quot;//farm6.static.flickr.com/5108/5879429020_21dd2b6ddf_s.jpg&quot; width=&quot;75&quot; height=&quot;75&quot; alt=&quot;Riding the Train&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;a href=&quot;http://farm7.static.flickr.com/6005/5879429424_275a73ac3d.jpg&quot; title=&quot;Rhine Falls!&quot; rel=&quot;lightbox[yearinreview2011]&quot;&gt;&lt;img src=&quot;//farm7.static.flickr.com/6005/5879429424_275a73ac3d_s.jpg&quot; width=&quot;75&quot; height=&quot;75&quot; alt=&quot;Rhine Falls!&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot;&gt;&lt;/a&gt;

&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;

&lt;a href=&quot;http://farm6.static.flickr.com/5112/5878867297_cac4cd43c9.jpg&quot; title=&quot;Whoa!&quot; rel=&quot;lightbox[yearinreview2011]&quot;&gt;&lt;img src=&quot;//farm6.static.flickr.com/5112/5878867297_cac4cd43c9_m.jpg&quot; width=&quot;240&quot; height=&quot;180&quot; alt=&quot;Whoa!&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm6.static.flickr.com/5183/5881479860_7ef4ba409e.jpg&quot; title=&quot;Rhine Falls Flowers&quot; rel=&quot;lightbox[yearinreview2011]&quot;&gt;&lt;img src=&quot;//farm6.static.flickr.com/5183/5881479860_7ef4ba409e_m.jpg&quot; width=&quot;240&quot; height=&quot;180&quot; alt=&quot;Rhine Falls Flowers&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot;&gt;&lt;/a&gt;

&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;

&lt;a href=&quot;http://farm6.static.flickr.com/5320/5880917345_38968f556c.jpg&quot; title=&quot;Olives and Sprouts&quot; rel=&quot;lightbox[yearinreview2011]&quot;&gt;&lt;img src=&quot;//farm6.static.flickr.com/5320/5880917345_38968f556c_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Olives and Sprouts&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm6.static.flickr.com/5144/5880917787_cfcb0012ce.jpg&quot; title=&quot;Scallops and Tomato&quot; rel=&quot;lightbox[yearinreview2011]&quot;&gt;&lt;img src=&quot;//farm6.static.flickr.com/5144/5880917787_cfcb0012ce_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Scallops and Tomato&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot;&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm7.static.flickr.com/6018/5878966083_8bef3238da.jpg&quot; title=&quot;Dessert at Greulich&quot; rel=&quot;lightbox[yearinreview2011]&quot;&gt;&lt;img src=&quot;//farm7.static.flickr.com/6018/5878966083_8bef3238da_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Dessert at Greulich&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot;&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm6.static.flickr.com/5151/5878966527_7634f8a94e.jpg&quot; title=&quot;Greulich&apos;s Backyard Trees&quot; rel=&quot;lightbox[yearinreview2011]&quot;&gt;&lt;img src=&quot;//farm6.static.flickr.com/5151/5878966527_7634f8a94e_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Greulich&apos;s Backyard Trees&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;p&gt;We arrived back in Denver at 6pm on Sunday, June 26th and I flew to Montana at 8am the next morning. I worked from The Cabin that week and &lt;a href=&quot;http://raibledesigns.com/rd/entry/agile_hiring_book_review&quot;&gt;wrote a book review for Agile Hiring&lt;/a&gt;.
&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
I think Sean [Landis] does a great job in showing how you can apply agile principles (people over process, tracking tools, failing fast, continuous improvement, constant feedback) to improve your hiring process. As a person that interviews often, I think it also gives great insights into how companies interview and what they&apos;re looking for. I&apos;ve often thought that being honest about my skills and what I&apos;m looking for is a good tactic and this book seems to confirm that.
&lt;/p&gt;
&lt;p&gt;That weekend, it was the &quot;90s Reunion&quot; for the Seeley Swan High School, which I attended my freshman and sophomore year of high school. There were several highlights of the trip, but one of my favorites was our float in the Swan Valley Parade. Abbie came up with the idea (Happy Birthday America) and we decorated an old trailer with streamers, balloons and a birthday cake to celebrate. Abbie made up a song and sang it throughout the entire parade (with a portable microphone and amp).
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a rel=&quot;lightbox[yearinreview2011]&quot; title=&quot;Abbie singing &amp;quot;Happy Birthday America&amp;quot;&quot; href=&quot;http://farm7.static.flickr.com/6130/5934498303_10250c2760.jpg&quot;&gt;&lt;img width=&quot;100&quot; height=&quot;75&quot; style=&quot;border: 1px solid black&quot; alt=&quot;Abbie singing &amp;quot;Happy Birthday America&amp;quot;&quot; src=&quot;//farm7.static.flickr.com/6130/5934498303_10250c2760_t.jpg&quot;&gt;&lt;/a&gt;
&lt;a rel=&quot;lightbox[yearinreview2011]&quot; title=&quot;Abbie before getting dunked&quot; href=&quot;http://farm7.static.flickr.com/6020/5934498549_38f1793c46.jpg&quot;&gt;&lt;img width=&quot;100&quot; height=&quot;75&quot; style=&quot;margin-left: 0px; border: 1px solid black&quot; alt=&quot;Abbie before getting dunked&quot; src=&quot;//farm7.static.flickr.com/6020/5934498549_38f1793c46_t.jpg&quot;&gt;&lt;/a&gt;
&lt;a rel=&quot;lightbox[4thofjuly2011]&quot; title=&quot;Fiddlin&apos; Trish&quot; href=&quot;http://farm7.static.flickr.com/6002/5934499269_c40b347316.jpg&quot;&gt;&lt;img width=&quot;100&quot; height=&quot;75&quot; style=&quot;margin-left: 0px; border: 1px solid black&quot; alt=&quot;Fiddlin&apos; Trish&quot; src=&quot;//farm7.static.flickr.com/6002/5934499269_c40b347316_t.jpg&quot;&gt;&lt;/a&gt;
&lt;a rel=&quot;lightbox[yearinreview2011]&quot; title=&quot;Paddleboating&quot; href=&quot;http://farm7.static.flickr.com/6123/5935065040_59657b06fa.jpg&quot;&gt;&lt;img width=&quot;100&quot; height=&quot;75&quot; style=&quot;margin-left: 0px; border: 1px solid black&quot; alt=&quot;Paddleboating&quot; src=&quot;//farm7.static.flickr.com/6123/5935065040_59657b06fa_t.jpg&quot;&gt;&lt;/a&gt;
&lt;a rel=&quot;lightbox[yearinreview2011]&quot; title=&quot;Lake McDonald, Glacier National Park&quot; href=&quot;http://farm7.static.flickr.com/6006/5935068822_cfd7c86009.jpg&quot;&gt;&lt;img width=&quot;100&quot; height=&quot;75&quot; style=&quot;margin-left: 0px; border: 1px solid black&quot; alt=&quot;Lake McDonald, Glacier National Park&quot; src=&quot;//farm7.static.flickr.com/6006/5935068822_cfd7c86009_t.jpg&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;I went swimming with my iPhone 4 that Sunday and Trish and her Dad showed up a few hours later. On the 4th, we had a blast celebrating the holiday with mine and my parents&apos; old friends. The next day, we adventured to Glacier National Park, where Trish shot an &lt;a href=&quot;http://www.mcginityphoto.com/Nature/Nature/4725995_JRpvJB/9/1373210164_W4mgd99#!i=1373210164&amp;amp;k=W4mgd99&quot;&gt;awesome sunrise at Lake McDonald&lt;/a&gt;.
She also captured a beautiful picture of The Cabin at sunset that week.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a rel=&quot;lightbox[4thofjuly2011]&quot; title=&quot;The Raible Cabin by Trish McGinity&quot; href=&quot;http://farm7.static.flickr.com/6122/5933924991_8dde37f131.jpg&quot;&gt;&lt;img width=&quot;500&quot; height=&quot;333&quot; style=&quot;border: 1px solid black&quot; alt=&quot;The Raible Cabin&quot; src=&quot;//farm7.static.flickr.com/6122/5933924991_8dde37f131.jpg&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;In between vacationing in Montana and writing about it, I wrote some technical posts:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/integrating_oauth_with_appfuse_and&quot;&gt;Integrating OAuth with AppFuse and its REST API&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/installing_openjdk_7_on_os&quot;&gt;Installing OpenJDK 7 on OS X&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We returned to Denver in mid-July and shipped the kids off to Florida just before my annual Birthday Golf Debacle. We teed off at Wellshire Golf Course and had one of the most well-behaved rounds in a few years. The next weekend, we closed on our &quot;Ski Shack&quot; in Fraser and I penned &lt;a href=&quot;http://raibledesigns.com/rd/entry/another_dream_realized_mountain_views&quot;&gt;Another Dream Realized: Mountain Views&lt;/a&gt;. Trish&apos;s &lt;a href=&quot;http://www.flickr.com/photos/mcginityphoto/6716291441&quot;&gt;October Sunset photo&lt;/a&gt; is one of my favorite glimpses of our view.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://farm8.staticflickr.com/7155/6716291441_c70eb5e378.jpg&quot; rel=&quot;lightbox[yearinreview2011]&quot; title=&quot;October Sunset from our dock&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7155/6716291441_c70eb5e378.jpg&quot; alt=&quot;October Sunset in Fraser&quot; width=&quot;400&quot; height=&quot;269&quot; style=&quot;border: 1px solid black&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;Next, I blogged about &lt;a href=&quot;http://raibledesigns.com/rd/entry/how_do_you_get_started&quot;&gt;How do you get started in programming?&lt;/a&gt; and we headed to the airport a few hours later for a whirlwind trip from Oregon to Cape Cod. &lt;em&gt;That&lt;/em&gt; was a wonderful trip and it took me almost a month &lt;a href=&quot;http://raibledesigns.com/rd/entry/oregon_cape_cod_and_fun&quot;&gt;to write about it&lt;/a&gt;. We flew into Portland and spent the weekend hiking in Silver Falls and drinking good beer at the &lt;a href=&quot;http://www.oregonbrewfest.com/&quot;&gt;Oregon Brewers Festival&lt;/a&gt; and &lt;a href=&quot;http://www.mcmenamins.com/54-edgefield-home&quot;&gt;Edgefield&lt;/a&gt;. We stayed with my good friends Clint and Autumn (married in &lt;a href=&quot;http://raibledesigns.com/rd/entry/costa_rica_was_awesome&quot;&gt;Costa Rica in 2008&lt;/a&gt;), who had their first child only a few weeks after our visit (August 26th).
&lt;/p&gt;
&lt;p&gt;We had an awful early flight out of Portland to Boston that Sunday. On Monday afternoon, we picked up Abbie and Jack from the Boston airport. They flew up from West Palm Beach on their first unaccompanied minors flight. We drove them to Cape Cod and settled into Trish&apos;s parents house near the water. The rest of the week, we had a blast with our friends Chris and Julie and the entire McGinity clan. 
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a rel=&quot;lightbox[yearinreview2011]&quot; title=&quot;Wheeee!!&quot; href=&quot;http://farm7.static.flickr.com/6022/6008443089_cc4f752c0f.jpg&quot;&gt;&lt;img width=&quot;100&quot; height=&quot;75&quot; style=&quot;border: 1px solid black&quot; alt=&quot;Wheeee!!&quot; src=&quot;//farm7.static.flickr.com/6022/6008443089_cc4f752c0f_t.jpg&quot;&gt;&lt;/a&gt;

&lt;a rel=&quot;lightbox[yearinreview2011]&quot; title=&quot;Hercules!&quot; href=&quot;http://farm7.static.flickr.com/6006/6008999952_78fd13f415.jpg&quot;&gt;&lt;img width=&quot;100&quot; height=&quot;75&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot; alt=&quot;Hercules!&quot; src=&quot;//farm7.static.flickr.com/6006/6008999952_78fd13f415_t.jpg&quot;&gt;&lt;/a&gt;

&lt;a rel=&quot;lightbox[yearinreview2011]&quot; title=&quot;Michael and Jack&quot; href=&quot;http://farm7.static.flickr.com/6125/6044153539_84019058e4.jpg&quot;&gt;&lt;img width=&quot;100&quot; height=&quot;75&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot; alt=&quot;Michael and Jack&quot; src=&quot;//farm7.static.flickr.com/6125/6044153539_84019058e4_t.jpg&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a rel=&quot;lightbox[yearinreview2011]&quot; title=&quot;Monkey Shadows&quot; href=&quot;http://farm7.static.flickr.com/6133/6044703126_cb16505a0c.jpg&quot;&gt;&lt;img width=&quot;240&quot; height=&quot;180&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot; alt=&quot;Monkey Shadows&quot; src=&quot;//farm7.static.flickr.com/6133/6044703126_cb16505a0c_m.jpg&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
We spent the rest of August going to concerts at Red Rocks, beer festivals in Winter Park, mountain biking, hiking, taking the kids to the Ski Shack and playing with Crazy Sagan. 
&lt;a href=&quot;http://farm7.staticflickr.com/6087/6044208823_60c83e67c9.jpg&quot; title=&quot;Crazy Sagan!&quot; rel=&quot;lightbox[yearinreview2011]&quot;&gt;&lt;img src=&quot;//farm7.staticflickr.com/6087/6044208823_60c83e67c9_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Crazy Sagan!&quot; class=&quot;picture&quot; style=&quot;margin-top: 5px&quot;&gt;&lt;/a&gt;
In my only August post, I talked a lot about the Broncos, Hawaii, Devoxx and leaving Overstock.&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
  That pretty much catches you up-to-date with the goings on in my life. As you can tell, it&apos;s been a &lt;em&gt;great&lt;/em&gt; summer so far. I&apos;m really looking forward to the fall too. Broncos season, a trip to Hawaii, learning new technologies, talking about them at Devoxx and a new gig.
  &lt;br/&gt;...&lt;br/&gt;
  Tomorrow is Jack&apos;s birthday. I gotta run ... it&apos;s time to go buy a bike, play a little golf, hit the pool and go to the BBQ before tonight&apos;s CD Release Party. 
&lt;/p&gt;
&lt;p&gt;We watched proudly as Jack turned 7 the next day and loved his excitement when he got his first mountain bike.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;&lt;a href=&quot;http://farm7.staticflickr.com/6173/6168228852_44f34ab78d.jpg&quot; title=&quot;7 Miles and ready for more&quot; rel=&quot;lightbox[yearinreview2011]&quot;&gt;&lt;img src=&quot;//farm7.staticflickr.com/6173/6168228852_44f34ab78d_m.jpg&quot; width=&quot;240&quot; height=&quot;180&quot; alt=&quot;7 Miles and ready for more&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;September started with mountain breezes and &lt;a href=&quot;http://raibledesigns.com/rd/entry/labor_day_weekend_in_grand&quot;&gt;fishing at Lake Granby&lt;/a&gt;. Lots of friends, family and delicious food make for a heckuva 3-day weekend. Jack was the only one to catch a fish and you couldn&apos;t wipe the smile off his face for the rest of the day.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a rel=&quot;lightbox[yearinreview2011]&quot; title=&quot;The delicious fish that Jack caught at Lake Granby&quot; href=&quot;http://farm7.static.flickr.com/6189/6112833896_59bd8ecc99.jpg&quot;&gt;&lt;img width=&quot;500&quot; height=&quot;375&quot; style=&quot;border: 1px solid black&quot; alt=&quot;The delicious fish that Jack caught at Lake Granby&quot; src=&quot;//farm7.static.flickr.com/6189/6112833896_59bd8ecc99.jpg&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;I described my &lt;a href=&quot;http://raibledesigns.com/rd/entry/the_digital_diet&quot;&gt;Digital Diet&lt;/a&gt; and wrote my first article related to my Devoxx talk: &lt;a href=&quot;http://raibledesigns.com/rd/entry/integrating_scalate_and_jade_with&quot;&gt;Integrating Scalate and Jade with Play 1.2.3&lt;/a&gt;. The 2nd weekend in September, we enjoyed a &lt;a href=&quot;http://www.flickr.com/photos/mraible/sets/72157627717258988/&quot;&gt;September Sports Weekend&lt;/a&gt;, going to two Rockies games and the Monday night Broncos Opener before heading to Kauai. While the Broncos didn&apos;t win that game, Trish&apos;s shot of Brett, Joe and I sums up many-a-game this season.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://farm7.staticflickr.com/6168/6185423596_e62d1e1efd.jpg&quot; title=&quot;Broncos Home Opener&quot; rel=&quot;lightbox[yearinreview2011]&quot;&gt;&lt;img src=&quot;//farm7.staticflickr.com/6168/6185423596_e62d1e1efd.jpg&quot; width=&quot;500&quot; height=&quot;333&quot; alt=&quot;Broncos Home Opener&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;p&gt;As expected, &lt;a href=&quot;http://raibledesigns.com/rd/entry/mahalo_kauai&quot;&gt;Kauai blew our minds&lt;/a&gt;. We saw several spectacular sunsets, did some Stand Up Paddling (it&apos;s harder than it looks), played golf, took a doorless helicopter ride and kayaked along the Na Pali coast. &lt;/p&gt;
&lt;table style=&quot;margin: 0 auto 5px auto&quot; cellspacing=&quot;10&quot;&gt;
  &lt;tr&gt;
    &lt;td rowspan=&quot;3&quot;&gt;
  &lt;a rel=&quot;lightbox[yearinreview2011]&quot; href=&quot;http://farm7.staticflickr.com/6154/6174598580_f440fdfc48.jpg&quot; title=&quot;HDR Sunset next to the palm trees on the beach at Hanalei Bay by McGinityPhoto, on Flickr&quot;&gt;&lt;img src=&quot;//farm7.staticflickr.com/6154/6174598580_f440fdfc48_m.jpg&quot; width=&quot;156&quot; height=&quot;240&quot; alt=&quot;HDR Sunset next to the palm trees on the beach at Hanalei Bay&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
  &lt;/td&gt;
  &lt;td&gt;
    &lt;a rel=&quot;lightbox[yearinreview2011]&quot; href=&quot;http://farm7.staticflickr.com/6153/6174087039_b3740f60b7.jpg&quot; title=&quot;Hanalei Bay Sunset HDR by McGinityPhoto, on Flickr&quot;&gt;&lt;img src=&quot;//farm7.staticflickr.com/6153/6174087039_b3740f60b7_t.jpg&quot; width=&quot;100&quot; height=&quot;66&quot; alt=&quot;Hanalei Bay Sunset HDR&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
    &lt;a rel=&quot;lightbox[yearinreview2011]&quot; href=&quot;http://farm7.staticflickr.com/6159/6174622520_87c18de29c.jpg&quot; title=&quot;Makai Golf Club my pitch out of the sand is next to the pin.  Matt&apos;s is on the edge of the green :) by McGinityPhoto, on Flickr&quot;&gt;&lt;img src=&quot;//farm7.staticflickr.com/6159/6174622520_87c18de29c_t.jpg&quot; width=&quot;100&quot; height=&quot;67&quot; alt=&quot;Makai Golf Club my pitch out of the sand is next to the pin.  Matt&apos;s is on the edge of the green :)&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot;&gt;&lt;/a&gt;
  &lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;
    &lt;a rel=&quot;lightbox[yearinreview2011]&quot; href=&quot;http://farm7.staticflickr.com/6154/6174624336_9065acfafa.jpg&quot; title=&quot;Bougainvillia on the Makai Golf course looking across Hanalei Bay by McGinityPhoto, on Flickr&quot;&gt;&lt;img src=&quot;//farm7.staticflickr.com/6154/6174624336_9065acfafa_t.jpg&quot; width=&quot;100&quot; height=&quot;66&quot; alt=&quot;Bougainvillia on the Makai Golf course looking across Hanalei Bay&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
    &lt;a rel=&quot;lightbox[yearinreview2011]&quot; href=&quot;http://farm7.staticflickr.com/6153/6174630996_bee864d25d.jpg&quot; title=&quot;2 Blue Hawai&apos;i on the beach in Hanalei Bay by McGinityPhoto, on Flickr&quot;&gt;&lt;img src=&quot;//farm7.staticflickr.com/6153/6174630996_bee864d25d_t.jpg&quot; width=&quot;100&quot; height=&quot;67&quot; alt=&quot;2 Blue Hawai&apos;i on the beach in Hanalei Bay&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot;&gt;&lt;/a&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;
    &lt;a rel=&quot;lightbox[yearinreview2011]&quot; href=&quot;http://farm7.staticflickr.com/6178/6174149421_49cc20532c.jpg&quot; title=&quot;Rainbow to Kalalau Valley Napali Coast State Park by McGinityPhoto, on Flickr&quot;&gt;&lt;img src=&quot;//farm7.staticflickr.com/6178/6174149421_49cc20532c.jpg&quot; width=&quot;100&quot; height=&quot;67&quot; alt=&quot;Rainbow to Kalalau Valley Napali Coast State Park&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
    &lt;a rel=&quot;lightbox[yearinreview2011]&quot; href=&quot;http://farm7.staticflickr.com/6174/6174681482_01c2422fd3.jpg&quot; title=&quot;Wahooo That was Awesome! :) by McGinityPhoto, on Flickr&quot;&gt;&lt;img src=&quot;//farm7.staticflickr.com/6174/6174681482_01c2422fd3_t.jpg&quot; width=&quot;100&quot; height=&quot;67&quot; alt=&quot;Wahooo That was Awesome! :)&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot;&gt;&lt;/a&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;We never got bored of the view from our room.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a rel=&quot;lightbox[yearinreview2011]&quot; title=&quot;Aloha in the morning at the St Regis Hanalei Bay by McGinityPhoto, on Flickr&quot; href=&quot;http://farm7.staticflickr.com/6157/6174599098_879e9d9aa4.jpg&quot;&gt;&lt;img width=&quot;500&quot; height=&quot;331&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot; alt=&quot;Aloha in the morning at the St Regis Hanalei Bay&quot; src=&quot;//farm7.staticflickr.com/6157/6174599098_879e9d9aa4.jpg&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;Upon our return, I &lt;a href=&quot;http://raibledesigns.com/rd/entry/trying_to_make_coffeescript_work&quot;&gt;tried to make CoffeeScript work with Scalate&lt;/a&gt; and 
&lt;a href=&quot;http://raibledesigns.com/rd/entry/integrating_html5_boilerplate_with_scalate&quot;&gt;integrated HTML5 Boilerplate with Play&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
&lt;a rel=&quot;lightbox[yearinreview2011]&quot; href=&quot;http://fbcdn-photos-a.akamaihd.net/hphotos-ak-snc7/309585_10150334101446712_571296711_8306949_270832760_n.jpg&quot; title=&quot;Halloween&quot;&gt;&lt;img src=&quot;//fbcdn-photos-a.akamaihd.net/hphotos-ak-snc7/309585_10150334101446712_571296711_8306949_270832760_a.jpg&quot; alt=&quot;Halloween&quot; width=&quot;180&quot; height=&quot;139&quot; class=&quot;picture&quot; style=&quot;margin-top: 0; border: 1px solid black&quot;&gt;&lt;/a&gt;
October was a quiet month on this blog. I only wrote two articles, one about &lt;a href=&quot;http://raibledesigns.com/rd/entry/increasing_my_developer_happiness&quot;&gt;increasing my developer happiness&lt;/a&gt; and another about &lt;a href=&quot;http://raibledesigns.com/rd/entry/developing_with_html5_coffeescript_and&quot;&gt;developing with HTML5, CoffeeScript and Twitter&apos;s Bootstrap&lt;/a&gt;. Watching the Broncos in October was painful at first, buy many rejoiced when Tebow started against Miami (10/23) and we had the amazing comeback to win. We went to the next game against the Lions and were quickly shown they had a long ways to go. We dressed up as the Scary Family for Halloween and had a ball.
&lt;/p&gt;
&lt;p&gt;I made up for October&apos;s lack of writing by writing 9 articles in November. I started with &lt;a href=&quot;http://raibledesigns.com/rd/entry/play_scala_s_anorm_heroku&quot;&gt;complaints about Play Scala&apos;s Anorm and PostgreSQL&lt;/a&gt;, then &lt;a href=&quot;http://raibledesigns.com/rd/entry/happy_9th_birthday_abbie&quot;&gt;celebrated Abbie&apos;s 9th Birthday&lt;/a&gt; with a &lt;a href=&quot;http://www.flickr.com/photos/mraible/6316702824&quot;&gt;Magic Show&lt;/a&gt;.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
  &lt;a href=&quot;http://farm7.staticflickr.com/6117/6316702372_40594e7cbf.jpg&quot; title=&quot;Happy Kids&quot; rel=&quot;lightbox[yearinreview2011]&quot;&gt;&lt;img src=&quot;//farm7.staticflickr.com/6117/6316702372_40594e7cbf_m.jpg&quot; width=&quot;240&quot; height=&quot;160&quot; alt=&quot;Happy Kids&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
  &lt;a href=&quot;http://farm7.staticflickr.com/6047/6316702824_e46bacdc72.jpg&quot; title=&quot;Birthday Crew&quot; rel=&quot;lightbox[yearinreview2011]&quot;&gt;&lt;img src=&quot;//farm7.staticflickr.com/6047/6316702824_e46bacdc72_m.jpg&quot; width=&quot;240&quot; height=&quot;160&quot; alt=&quot;Birthday Crew&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot;&gt;&lt;/a&gt;
&lt;/p&gt;The next week, I wrote about my &lt;a href=&quot;http://raibledesigns.com/rd/entry/new_macbook_pro_and_imac&quot;&gt;new MacBook Pro and iMac&lt;/a&gt; and &lt;a href=&quot;http://raibledesigns.com/rd/entry/more_scalate_goodness_for_play&quot;&gt;more Scalate goodness for Play&lt;/a&gt;. At this point, I was scrambling to finish up my &lt;em&gt;Play More!&lt;/em&gt; application for &lt;a href=&quot;http://www.devoxx.com/display/DV11/Home&quot;&gt;Devoxx&lt;/a&gt; and worked a lot of late nights. That weekend, I &lt;a href=&quot;http://raibledesigns.com/rd/entry/phonegap_to_the_rescue&quot;&gt;used PhoneGap&lt;/a&gt; to turn my webapp into a native app and finished everything in the nick of time. We flew to Paris that Monday, took the train to Antwerp on Tuesday and settled in for a memorable week of interesting talks.
&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/play_2_0_a_web&quot;&gt;Play 2.0, A web framework for a new era&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/phonegap_for_hybrid_app_development&quot;&gt;PhoneGap for Hybrid App Development&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/deploying_java_and_play_framework&quot;&gt;Deploying Java and Play Framework Apps to the Cloud with James Ward&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/my_html5_with_play_scala&quot;&gt;My HTML5 with Play Scala, CoffeeScript and Jade Presentation from Devoxx 2011&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a rel=&quot;lightbox[yearinreview2011]&quot; title=&quot;Crew at Devoxx dinner by McGinity Photo&quot; href=&quot;http://farm8.staticflickr.com/7006/6448345655_df403b1cfd.jpg&quot;&gt;&lt;img width=&quot;100&quot; height=&quot;67&quot; style=&quot;border: 1px solid black&quot; alt=&quot;Matt Raible and Crew at Devoxx dinner&quot; src=&quot;//farm8.staticflickr.com/7006/6448345655_df403b1cfd_t.jpg&quot;&gt;&lt;/a&gt;

  
  &lt;a rel=&quot;lightbox[yearinreview2011]&quot; title=&quot;At Pelgrom by McGinity Photo&quot; href=&quot;http://farm8.staticflickr.com/7019/6448347213_5e9c59ba8c.jpg&quot;&gt;&lt;img width=&quot;100&quot; height=&quot;67&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot; alt=&quot;Matt Raible at Pelgrom&quot; src=&quot;//farm8.staticflickr.com/7019/6448347213_5e9c59ba8c_t.jpg&quot;&gt;&lt;/a&gt;

  &lt;a rel=&quot;lightbox[yearinreview2011]&quot; title=&quot;James Ward speaking on Heroku at Devoxx by McGinity Photo&quot; href=&quot;http://farm8.staticflickr.com/7147/6448349163_313bc48dd5.jpg&quot;&gt;&lt;img width=&quot;100&quot; height=&quot;67&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot; alt=&quot;James Ward speaking on Heroku at Devoxx&quot; src=&quot;//farm8.staticflickr.com/7147/6448349163_313bc48dd5_t.jpg&quot;&gt;&lt;/a&gt;
  &lt;a rel=&quot;lightbox[yearinreview2011]&quot; title=&quot;Speaking at Devoxx Belgium 2011 by McGinity Photo&quot; href=&quot;http://farm8.staticflickr.com/7031/6448350561_e62da3327b.jpg&quot;&gt;&lt;img width=&quot;100&quot; height=&quot;67&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot; alt=&quot;Matt Raible speaking at Devoxx Belgium 2011&quot; src=&quot;//farm8.staticflickr.com/7031/6448350561_e62da3327b_t.jpg&quot;&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p style=&quot;text-align: center&quot;&gt;
  &lt;a rel=&quot;lightbox[yearinreview2011]&quot; title=&quot;Antwerp Town Square by McGinity Photo&quot; href=&quot;http://farm8.staticflickr.com/7002/6716294395_28ce2c5f5b.jpg&quot;&gt;&lt;img width=&quot;240&quot; height=&quot;160&quot; style=&quot;border: 1px solid black&quot; alt=&quot;Antwerp Town Square&quot; src=&quot;//farm8.staticflickr.com/7002/6716294395_28ce2c5f5b_m.jpg&quot;&gt;&lt;/a&gt;
&lt;a rel=&quot;lightbox[yearinreview2011]&quot; title=&quot;Riverside in Antwerp by McGinity Photo&quot; href=&quot;http://farm8.staticflickr.com/7010/6448353693_f63f7afee6.jpg&quot;&gt;&lt;img width=&quot;240&quot; height=&quot;160&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot; alt=&quot;Riverside in Antwerp&quot; src=&quot;//farm8.staticflickr.com/7010/6448353693_f63f7afee6_m.jpg&quot;&gt;&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;That Saturday, we took the train back to Paris and checked into our hotel for a few days of romance. I proposed to Trish at Versailles on Sunday at sunset and we rolled around in our joy for a few days before we told any of our friends. 
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
  &lt;a rel=&quot;lightbox[yearinreview2011]&quot; title=&quot;The Versailles Orangerie&quot; href=&quot;http://farm8.staticflickr.com/7009/6402228135_6ffc860b9f.jpg&quot;&gt;&lt;img width=&quot;100&quot; height=&quot;75&quot; style=&quot;border: 1px solid black&quot; alt=&quot;The Versailles Orangerie&quot; src=&quot;//farm8.staticflickr.com/7009/6402228135_6ffc860b9f_t.jpg&quot;&gt;&lt;/a&gt;
  &lt;a rel=&quot;lightbox[yearinreview2011]&quot; title=&quot;Ch&#226;teau de Versailles&quot; href=&quot;http://farm8.staticflickr.com/7141/6402228667_2e908c6fb0.jpg&quot;&gt;&lt;img width=&quot;100&quot; height=&quot;75&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot; alt=&quot;Ch&#226;teau de Versailles&quot; src=&quot;//farm8.staticflickr.com/7141/6402228667_2e908c6fb0_t.jpg&quot;&gt;&lt;/a&gt;
  &lt;a rel=&quot;lightbox[yearinreview2011]&quot; title=&quot;Gardens of Versailles&quot; href=&quot;http://farm8.staticflickr.com/7161/6402229087_0d34f8f6f9.jpg&quot;&gt;&lt;img width=&quot;100&quot; height=&quot;75&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot; alt=&quot;Gardens of Versailles&quot; src=&quot;//farm8.staticflickr.com/7161/6402229087_0d34f8f6f9_t.jpg&quot;&gt;&lt;/a&gt;
  &lt;a rel=&quot;lightbox[yearinreview2011]&quot; title=&quot;Trees in the Versailles Gardens&quot; href=&quot;http://farm8.staticflickr.com/7023/6402230475_9251f0664c.jpg&quot;&gt;&lt;img width=&quot;100&quot; height=&quot;75&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot; alt=&quot;Trees in the Versailles Gardens&quot; src=&quot;//farm8.staticflickr.com/7023/6402230475_9251f0664c_t.jpg&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a rel=&quot;lightbox[yearinreview2011]&quot; title=&quot;Bassin d&#8217;Apollon &amp;ndash; the Apollo Fountain by McGinity Photo&quot; href=&quot;http://farm8.staticflickr.com/7156/6716294893_7a8ed53bf5.jpg&quot;&gt;&lt;img width=&quot;500&quot; height=&quot;329&quot; alt=&quot;Bassin d&#8217;Apollon &amp;ndash; the Apollo Fountain&quot; src=&quot;//farm8.staticflickr.com/7156/6716294893_7a8ed53bf5.jpg&quot;&gt;&lt;/a&gt;
&lt;/p&gt;  
&lt;p&gt;
We flew back to the US the Wednesday before Christmas and spent the weekend in Boston with Trish&apos;s family. For full details of that trip, see &lt;a href=&quot;http://raibledesigns.com/rd/entry/our_engaging_trip_to_paris&quot;&gt;Our Engaging Trip to Paris and Antwerp&lt;/a&gt;.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a rel=&quot;lightbox[yearinreview2011]&quot; title=&quot;Happy&quot; href=&quot;http://farm7.staticflickr.com/6031/6402231033_9a0f51938c.jpg&quot;&gt;&lt;img width=&quot;240&quot; height=&quot;180&quot; style=&quot;border: 1px solid black&quot; alt=&quot;Happy&quot; src=&quot;//farm7.staticflickr.com/6031/6402231033_9a0f51938c_m.jpg&quot;&gt;&lt;/a&gt;
&lt;a rel=&quot;lightbox[yearinreview2011]&quot; title=&quot;Versailles Sunset&quot; href=&quot;http://farm8.staticflickr.com/7018/6402232723_e61d57cc98.jpg&quot;&gt;&lt;img width=&quot;240&quot; height=&quot;180&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot; alt=&quot;Versailles Sunset&quot; src=&quot;//farm8.staticflickr.com/7018/6402232723_e61d57cc98_m.jpg&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;To end the year, I kept it light and only spun up one blog post in December: &lt;a href=&quot;http://raibledesigns.com/rd/entry/what_have_i_been_working&quot;&gt;What have I been working on at Taleo?&lt;/a&gt; A week after writing that post, we moved to the Ski Shack for 3 weeks and enjoyed a &lt;a href=&quot;http://www.flickr.com/photos/mraible/sets/72157628628439665/&quot;&gt;wonderful holiday break&lt;/a&gt; with &lt;a href=&quot;http://www.flickr.com/photos/mraible/sets/72157628725392081/&quot;&gt;many visitors&lt;/a&gt;.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
  &lt;a rel=&quot;lightbox[yearinreview2011]&quot; href=&quot;http://farm8.staticflickr.com/7150/6638934399_2fb67c7883.jpg&quot; title=&quot;Shredders&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7150/6638934399_2fb67c7883_m.jpg&quot; width=&quot;240&quot; height=&quot;180&quot; alt=&quot;Shredders&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
  &lt;a rel=&quot;lightbox[yearinreview2011]&quot; href=&quot;http://farm8.staticflickr.com/7031/6638937219_8623aa8e63.jpg&quot; title=&quot;Happy New Year!&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7031/6638937219_8623aa8e63_m.jpg&quot; width=&quot;240&quot; height=&quot;180&quot; alt=&quot;Happy New Year!&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;Yes, 2011 was a spectacular year. We traveled to four different countries, did a lot of camping and mountain biking and accomplished 2/3 of my 5 year goals (basement sauna and mountain views). I wrote a lot of technical blog posts in the 2nd half of the year; most inspired my extracurricular activities&lt;/a&gt;. I plan to continue that this year as I learn more about HTML5, Play Framework, Scala and CoffeeScript. At the end of last year&apos;s &lt;a href=&quot;http://raibledesigns.com/rd/entry/2010_a_year_in_review&quot;&gt;Year in Review&lt;/a&gt;, I wrote:
&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
In 2011, I plan on doing two main things: keep rockin&apos; it with Trish and finishing The Bus.
&lt;/p&gt;
&lt;p&gt;I think I can safely say I kept it rockin&apos; with Trish - we&apos;re living together, engaged and still amazed we found one another. As for The Bus, I think &lt;a href=&quot;http://www.motorworksrestorations.com/&quot;&gt;MotorWorks Restorations&lt;/a&gt; and I are in a good position to make a final push and finish the sucker. My hope is to have it done by May so I can start driving it and taking it to VW Shows. In the meantime, Trish has started working on &lt;a href=&quot;http://www.mcginityphoto.com/&quot;&gt;McGinity Photo&lt;/a&gt; full-time and loving it. We have trips planned to Stockholm (for &lt;a href=&quot;http://jfokus.se&quot;&gt;Jfokus&lt;/a&gt;), Madrid (for &lt;a href=&quot;http://www.springio.net/&quot;&gt;Spring I/O&lt;/a&gt;) and Paris (for &lt;a href=&quot;http://www.devoxx.fr/display/FR12/Accueil&quot;&gt;Devoxx Paris&lt;/a&gt;).
&lt;/p&gt;
&lt;p&gt;Last weekend, we &lt;a href=&quot;http://www.flickr.com/photos/mraible/sets/72157628804308427/&quot;&gt;witnessed&lt;/a&gt; one of the greatest playoff games &lt;em&gt;ever&lt;/em&gt; when the Broncos beat the Steelers. I think the picture of Trish celebrating the final touchdown pretty much sums up 2011. 
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
  &lt;a rel=&quot;lightbox[yearinreview2011]&quot; href=&quot;http://farm8.staticflickr.com/7161/6671393297_d6f01b715f.jpg&quot; title=&quot;Broncos Win!&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7161/6671393297_d6f01b715f.jpg&quot; width=&quot;500&quot; height=&quot;375&quot; alt=&quot;Broncos Win!&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;I look forward to all the skiing, celebrating, camping, traveling and bus rides in 2012. It&apos;s gonna be a fantastic year.</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/what_have_i_been_working</id>
        <title type="html">What have I been working on at Taleo?</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/what_have_i_been_working"/>
        <published>2011-12-09T12:57:36-07:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="javascript" scheme="http://roller.apache.org/ns/tags/" />
        <category term="jquery" scheme="http://roller.apache.org/ns/tags/" />
        <category term="happiness" scheme="http://roller.apache.org/ns/tags/" />
        <category term="overstock" scheme="http://roller.apache.org/ns/tags/" />
        <category term="taleo" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">2011 has been a year of great clients for me. I started working with &lt;a href=&quot;http://overstock.com&quot;&gt;O.co&lt;/a&gt; and very much enjoyed my time there, especially on &lt;a href=&quot;http://raibledesigns.com/rd/entry/the_greatest_snow_on_earth&quot;&gt;powder days in Utah&lt;/a&gt;. The people were great, the contract was great (no end date), but the work was not my forte. I was on a project to &lt;em&gt;modularize&lt;/em&gt; the main shopping site&apos;s codebase, which involved mostly refactoring. By refactoring, I mean creating new Maven projects, modifying lots of pom.xml files and literally moving files from one directory to another. IntelliJ made this easy, the hard part was refactoring tests, moving from EasyMock to Mockito and splitting classes into interfaces and implementations where appropriate. As a developer who likes developing UIs and visually seeing my accomplishments, the project wasn&apos;t that exciting. However, I knew that it was strategically important to O.co, so I didn&apos;t complain much.
&lt;/p&gt;
&lt;p&gt;In mid-May, I received a &lt;a href=&quot;http://www.linkedin.com&quot;&gt;LinkedIn&lt;/a&gt; message from the Director of Software Engineering at &lt;a href=&quot;http://www.taleo.com/&quot;&gt;Taleo&lt;/a&gt;. 
&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
This is OB, I am the Director of Software Engineering at Taleo. We are the 2nd largest Software as a Service company. I am building a new specialist UI team that will take the product to the next level. I am looking for someone to lead this initiative. If you are interested to have a chat about it, please let me know.
&lt;/p&gt;
&lt;p&gt;At that time, I&apos;d never heard of Taleo and quickly recommended they not hire me.&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
This probably isn&apos;t the best position for me. While I am a good leader, I&apos;m not willing to relocate from Denver. I&apos;ve found that leaders usually do best when face-to-face with their developers.
&lt;/p&gt;
&lt;p&gt;This conversation continued back-and-forth where I explained how I wasn&apos;t willing to go full-time and I didn&apos;t want to leave Overstock. In the end, OB was persistent and explained how the position would entail lots of UI work and wouldn&apos;t require me to travel much. Our negotiations trailed off in June and resumed in July after I returned &lt;a href=&quot;http://raibledesigns.com/rd/entry/4th_of_july_adventures_in&quot;&gt;from vacation in Montana&lt;/a&gt;. Shortly after, we met each other&apos;s expectations, agreed on a start date and I started working at Taleo in early September. 
&lt;/p&gt;
&lt;p&gt;When I started, there were three features they wanted to add to to &lt;a href=&quot;http://www.taleo.com/solutions/taleo-business-edition&quot;&gt;Taleo Business Edition&lt;/a&gt;: Profile Pictures, Talent Cards and Org Charts. They knew the schedule was tight (8 weeks), but I was confident I could make it happen. At first, I groaned at the fact that they were using Ant to build the project. Then I smiled when I learned they&apos;d standardized on IntelliJ and set things up so you could do everything from the IDE. After using Maven for many years, this setup has actually become refreshing and I rarely have to restart or long for something like JRebel. Of course, a &lt;a href=&quot;http://raibledesigns.com/rd/entry/new_macbook_pro_and_imac&quot;&gt;new kick-ass laptop&lt;/a&gt; and &lt;a href=&quot;http://blogs.jetbrains.com/idea/2011/12/intellij-idea-11-is-out-get-ready-for-a-productivity-takeoff/&quot;&gt;awesome IDE&lt;/a&gt; make it so I rarely wait for anything to happen.
&lt;/p&gt;
&lt;p&gt;
To give you a taste of how I implemented each of these new features in 8 weeks, I&apos;ve broken them into sections below.
&lt;/p&gt;
&lt;p id=&quot;profile-pictures&quot;&gt;&lt;strong&gt;Profile Pictures&lt;/strong&gt;&lt;br/&gt;
Adding profile pictures was a pretty simple concept, one you see on my social networking sites today. I needed to give users the ability to upload a JPEG or PNG and crop it so it looked good. The uploading was a pretty straightforward process and I used a lot of internal APIs to grab the file from the request and save it to disk. The more difficult part was scaling the image to certain dimensions on upload (to save space) and allowing users to crop it after. 
&lt;/p&gt;
&lt;p&gt;Most of Taleo Business Edition (TBE) is written in good ol&apos; servlets and JSPs, with lots of scriptlets in their JSPs. When I saw the amount of HTML produced from Java, I laughed out loud and cringed. Soon after, I breathed a sigh of relief when I learned that any new features could be written using &lt;a href=&quot;http://freemarker.sourceforge.net/&quot;&gt;FreeMarker&lt;/a&gt; templates, which IntelliJ has excellent support for.
&lt;/p&gt;
&lt;p&gt;For image resizing on upload, I used Chris Campbell&apos;s &lt;a href=&quot;http://today.java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html&quot;&gt;Image.getScaledInstance() tutorial&lt;/a&gt;. For creating thumbnails, I used a combination of scaling, &lt;a href=&quot;http://docs.oracle.com/javase/6/docs/api/java/awt/image/BufferedImage.html#getSubimage(int, int, int, int)&quot;&gt;getSubimage()&lt;/a&gt; and the &lt;a href=&quot;http://docs.oracle.com/javase/6/docs/api/javax/imageio/package-summary.html&quot;&gt;Java Image I/O API&lt;/a&gt;. I made sure to &lt;a href=&quot;http://stackoverflow.com/questions/3204280/is-imageio-write-buffered&quot;&gt;write to BufferedOutputStream&lt;/a&gt; for scalability. For cropping images client-side, I used &lt;a href=&quot;http://jqueryui.com/demos/dialog/&quot;&gt;jQuery UI&apos;s Dialog&lt;/a&gt; and &lt;a href=&quot;http://deepliquid.com/content/Jcrop.html&quot;&gt;Jcrop&lt;/a&gt;, the jQuery image cropping plugin. Below is a screenshot of what the cropping UI looks like:
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://farm8.staticflickr.com/7142/6475456591_6831fd119e_b.jpg&quot; title=&quot;Taleo&apos;s TBE: Profile Picture&quot; rel=&quot;lightbox[taleo]&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7142/6475456591_6831fd119e.jpg&quot; width=&quot;500&quot; height=&quot;176&quot; alt=&quot;Taleo&apos;s TBE: Profile Picture&quot;&gt;&lt;/a&gt;
&lt;/a&gt;
&lt;/p&gt;
&lt;p id=&quot;talent-cards&quot;&gt;&lt;strong&gt;Talent Cards&lt;/strong&gt;&lt;br/&gt;
Talent Cards were a whole different beast. Not only did they need to display profile pictures, they also needed to contain contact information, work history and a number of other data points. Not just for employees, but for candidates as well. 
They also needed to be rendered with tabs at the bottom that allowed you to navigate between different data sections. 
&lt;/p&gt;
&lt;p&gt;

&lt;a href=&quot;http://farm8.staticflickr.com/7032/6475456677_ae342be56e_o.png&quot; title=&quot;Taleo&apos;s TBE: Talent Card&quot; rel=&quot;lightbox[taleo]&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7032/6475456677_a06514020d_m.jpg&quot; width=&quot;187&quot; height=&quot;240&quot; alt=&quot;Taleo&apos;s TBE: Talent Card&quot; class=&quot;picture&quot; style=&quot;border: 0&quot;&gt;&lt;/a&gt;

I&apos;ll admit, most of the hard work for this feature was done by the server-side developers (Harish and &lt;a href=&quot;http://www.linkedin.com/in/vladimirbazarsky&quot;&gt;Vlad&lt;/a&gt;) on my team. Vlad built the tabbed interface and Harish built the administrative section that allows you to add/remove/sort fields, as well as show and hide certain tabs. I performed most of my magic with jQuery, its &lt;a href=&quot;http://plugins.learningjquery.com/cluetip/&quot;&gt;clueTip plugin&lt;/a&gt; and good ol&apos; CSS. I was particularly thankful for CSS3 and its border-radius, box-shadow and Justin Maxwell&apos;s tutorial on &lt;a href=&quot;http://mattsnider.com/css/css-string-truncation-with-ellipsis/&quot;&gt;CSS String Truncation with Ellipsis&lt;/a&gt;. I used &lt;a href=&quot;http://directwebremoting.org/dwr/index.html&quot;&gt;DWR&lt;/a&gt; to fetch all the data from the server using Ajax. 
&lt;/p&gt;
&lt;p&gt;Talent Cards are a slick feature in TBE 11.5 and I think they&apos;re a great way to see a lot of information about someone very quickly. If you enable them for your company, you&apos;ll be able to mouse over any employee or candidate&apos;s names and see their information.
&lt;/p&gt;
&lt;p id=&quot;org-chart&quot;&gt;&lt;strong&gt;Org Chart&lt;/strong&gt;&lt;br/&gt;
The last feature I completed in this 8-week sprint was creating an organization chart. For this, I was given a rough prototype based on &lt;a href=&quot;http://www.capricasoftware.co.uk/corp/index.php&quot;&gt;Caprica Software&apos;s&lt;/a&gt; &lt;a href=&quot;http://www.capricasoftware.co.uk/jquery/orgchart/orgchart.html&quot;&gt;JQuery/CSS Organisation Chart&lt;/a&gt;. When I received it, it had all kinds of cool CSS 3 transformations (like &lt;a href=&quot;http://css3.bradshawenterprises.com/flip/&quot;&gt;this one&lt;/a&gt;), but they only worked in Safari and Chrome. I ended up removing the transformations and adding the ability to navigate up and down the org tree with Ajax (we currently only show three levels at a time). 
&lt;/p&gt;
&lt;p&gt;
The Org Chart feature also allows you to see how many direct/indirect reports an employee has, as well as access their Talent Card by hovering over their name. It&apos;s one of my favorite features because it&apos;s so visual and because it builds upon all the other features we&apos;ve built.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;&lt;a href=&quot;http://farm8.staticflickr.com/7148/6483181659_619285347d_o.png&quot; rel=&quot;lightbox[taleo]&quot; title=&quot;Taleo&apos;s TBE: Org Chart&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7148/6483181659_7434afaeee.jpg&quot; width=&quot;487&quot; height=&quot;500&quot; alt=&quot;Taleo&apos;s TBE: Org Chart&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;br/&gt;
As you might&apos;ve guessed by now, I&apos;ve been having a lot of fun doing UI development over the last few months. While I seem to have a knack for backend Java development, I enjoy developing UIs a lot more. The smile you see on people&apos;s faces during demos is priceless. I can&apos;t help but think this kind of thing contributes greatly to my developer happiness. All these features will be in next week&apos;s release of TBE and I couldn&apos;t be happier.
&lt;/p&gt;
&lt;p&gt;If you&apos;d like to work on my team at Taleo (or even take over my current role as UI Architect), please &lt;a href=&quot;http://raibledesigns.com/contact.jsp&quot;&gt;drop me a line&lt;/a&gt;. If you live near their headquarters (Dublin, CA), it&apos;d also be great to see you at the next Silicon Valley Spring User Group meetup. I&apos;ll be speaking about &lt;a href=&quot;http://www.meetup.com/SV-SUG/events/43376082/&quot;&gt;What&apos;s New in Spring 3.1 on February 1st&lt;/a&gt;.
</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/deploying_java_and_play_framework</id>
        <title type="html">Deploying Java and Play Framework Apps to the Cloud with James Ward</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/deploying_java_and_play_framework"/>
        <published>2011-11-18T08:14:45-07:00</published>
        <updated>2011-12-02T20:23:49-07:00</updated> 
        <category term="/Java" label="Java" />
        <category term="maven" scheme="http://roller.apache.org/ns/tags/" />
        <category term="playframework" scheme="http://roller.apache.org/ns/tags/" />
        <category term="sbt" scheme="http://roller.apache.org/ns/tags/" />
        <category term="java" scheme="http://roller.apache.org/ns/tags/" />
        <category term="springroo" scheme="http://roller.apache.org/ns/tags/" />
        <category term="scala" scheme="http://roller.apache.org/ns/tags/" />
        <category term="heroku" scheme="http://roller.apache.org/ns/tags/" />
        <category term="devoxx2011" scheme="http://roller.apache.org/ns/tags/" />
        <category term="jamesward" scheme="http://roller.apache.org/ns/tags/" />
        <category term="devoxx" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">Yesterday, I attended &lt;a href=&quot;http://www.jamesward.com/&quot;&gt;James Ward&lt;/a&gt;&apos;s presentation on &lt;a href=&quot;http://www.devoxx.com/pages/viewpage.action?pageId=5015973&quot;&gt;Deploying Java &amp;amp; Play Framework Apps to the Cloud&lt;/a&gt; at Devoxx. I arrived a bit late, but still managed to get there in time to see a lot of demos and learn more about &lt;a href=&quot;http://heroku.com&quot;&gt;Heroku&lt;/a&gt;. Below are my notes from James&apos;s talk.
&lt;/p&gt;
&lt;p&gt;When I arrived, James was doing a demo using &lt;a href=&quot;http://www.springsource.org/spring-roo&quot;&gt;Spring Roo&lt;/a&gt;. He was using Roo&apos;s Petclinic sample app and showed us how you could use Git to create a local repository of the new project and install Heroku&apos;s command line tool. From there, he ran the following command to create a new application on Heroku.
&lt;/p&gt;
&lt;pre&gt;heroku create -s cedar&lt;/pre&gt;
&lt;p&gt;The &lt;a href=&quot;http://devcenter.heroku.com/articles/cedar&quot;&gt;Cedar Stack&lt;/a&gt; is what supports Java, Scala and Play Framework. It&apos;s the 3rd generation stack for Heroku. The command above created two endpoints, one for HTTP and one for Git. It picks from a list of randomly generated names, which all seem to have some humor in them. James ended up with &quot;electric-sword-8877&quot; for this demo.&lt;/p&gt;
&lt;p&gt;From there, he ran &lt;code&gt;git push heroku master&lt;/code&gt; to deploy the project to Heroku. Unfortunately, this resulted in a login error and there was an akward moment where we all thought the Demo Gods were angry. However, James was able to resolve this by using Heroku&apos;s sharing feature with the following command.
&lt;/p&gt;
&lt;pre&gt;
heroku sharing:add jw@heroku.com
&lt;/pre&gt;
&lt;p&gt;For Java projects, Heroku looks for a pom.xml file in the root directory and runs a Maven build on project. All the dependencies get downloaded on the cloud rather than put them into a WAR and requiring you to upload a large WAR file. You don&apos;t have to upload your source code to Heroku; James did it for the sake of the demo because it was faster.
&lt;/p&gt;
&lt;p&gt;After the build finishes, it creates a &lt;em&gt;slug&lt;/em&gt; file. This file contains everything Heroku needs to run your application. 
&lt;p&gt;Next, James showed a demo of the running application and added a new Pet through its UI. Then he scaled it to two servers using the following command:
&lt;/p&gt;
&lt;pre&gt;
heroku scale web=2
&lt;/pre&gt;
&lt;p&gt;
He proved this was working by running &lt;code&gt;heroku ps&lt;/code&gt;, which showed there were two running processes. He showed the app again, but noted that the record he added was missing. This is because when it started up a new dyno, Hibernate created the schema again and deleted all records. To fix, James changed Hibernate to only update the schema instead of create a new one. If you&apos;re a Hibernate user, you know this is as simple as changing:
&lt;/p&gt;
&lt;pre&gt;
hibernate.hbm2ddl.auto=create
&lt;/pre&gt;
&lt;p&gt;to:
&lt;pre&gt;
hibernate.hbm2ddl.auto=update
&lt;/pre&gt;
&lt;p&gt;After committing this change, James redeployed using Git.
&lt;/p&gt;
&lt;pre&gt;
git push heroku master
&lt;/pre&gt;
&lt;p&gt;The slug file got built again and Heroku deployed the new slug onto both dynos, automatically load balancing the app across two servers. James then ran &lt;code&gt;heroku logs&lt;/code&gt; to see the logs of his dynos and prove that a request to his app&apos;s HTTP endpoint made requests to both dynos. The logging is powered by &lt;a href=&quot;https://github.com/heroku/logplex&quot;&gt;Logplex&lt;/a&gt; and you can read about how it works in the article &lt;a href=&quot;http://blog.heroku.com/archives/2010/12/13/logging/&quot;&gt;Heroku Gets Sweet Logging&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;James mentioned that Roo has a Heroku plugin, but after watching his talk and searching a bit on the internet, it seems it&apos;s just the jetty-runner setup as described in &lt;a href=&quot;http://devcenter.heroku.com/articles/spring-mvc-hibernate&quot;&gt;Getting Started with Spring MVC Hibernate on Heroku/Cedar&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
What about autoscaling? There are some 3rd party tools that do this. Heroku&apos;s Management infrastructure has APIs that these tools talk too. Heroku hasn&apos;t built autoscaling into the platform because they don&apos;t know where the bottlenecks are in your application.
&lt;/p&gt;
&lt;p&gt;
Heroku = Polyglot + PaaS + Cloud Components. It supports Ruby, node.js, Java, Clojure, Play and Scala and they&apos;re working on native Grails and Gradle support. There&apos;s currently 534,374 apps running on Heroku.
&lt;/p&gt;
&lt;p&gt;
Heroku is a cloud application platform and there&apos;s 5 different components. 
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Instant deployment&lt;/li&gt;
&lt;li&gt;HTTP Routing / Load Balancing&lt;/li&gt;
&lt;li&gt;Elastic Polyglot Runtime&lt;/li&gt;
&lt;li&gt;Management &amp;amp; Logging&lt;/li&gt;
&lt;li&gt;Component as a Service Ecosystem&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;
For instant deployment, it&apos;s a pretty simple process:
&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;You add files to a git repo&lt;/li&gt;
&lt;li&gt;You provision the app on Heroku (heroku create)&lt;/li&gt;
&lt;li&gt;You upload the files to Heroku (git push heroku master)&lt;/li&gt;
&lt;li&gt;Heroku runs the build and assembles a &quot;slug&quot; file&lt;/li&gt;
&lt;li&gt;Heroku starts a &quot;dyno&quot;&lt;/li&gt;
&lt;li&gt;Heroku copies the &quot;slug&quot; to the &quot;dyno&quot;&lt;/li&gt;
&lt;li&gt;Heroku starts the web application&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Most apps will contain a &lt;em&gt;Procfile&lt;/em&gt; that contains information about how to run the web process. For Spring Roo, it has:
&lt;/p&gt;
&lt;pre&gt;
web: java $JAVA_OPTS -jar target/dependency/jetty-runner.jar --port $PORT target/*.war
&lt;/pre&gt;
&lt;p&gt;So how does Heroku decide what application server to use? It doesn&apos;t, you do. You need to get your application server into the slug file. 
The easiest way to do this is to specify your application server as a dependency in your pom.xml. In the Roo example, James uses the maven-dependency-plugin to get the jetty-runner dependency and copy it to the target directory. On Heroku, you bring your application server with you.
&lt;/p&gt;
&lt;p&gt;Heroku gives you 750 free dyno hours per app, per month. For developers, it&apos;s very easy to get started and use. Once you extend past one dyno, it&apos;s
  $.05 per dyno hour, which works out to around $30/month.

It&apos;s only when you want to scale beyond one dyno where you get charged by Heroku, no matter how much data you transfer.

&lt;a href=&quot;http://www.scalatest.org/&quot;&gt;Scalatest&lt;/a&gt; is running on Heroku. It has one dyno and is doing fine with that. Bill Venners doesn&apos;t have to pay anything for it.
&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://java.herokuapp.com&quot;&gt;java.herokuapp.com&lt;/a&gt; is a site James created that allows you to clone example apps and get started quickly with Heroku&apos;s Cedar Stack.&lt;/p&gt;
&lt;p&gt;
For 
HTTP Routing, 
Heroku uses an Erlang-based routing system to route all the HTTP requests across your dynos. Heroku doesn&apos;t support sticky sessions. Distributed session management does not work well, because it does not scale well. Heroku recommends you use a stateless web architecture or move your state into something like &lt;a href=&quot;http://memcached.org/&quot;&gt;memcached&lt;/a&gt;. Jetty has (in the latest version) the ability to automatically serialize your session into a Mongo system. This works fine on Heroku. The problem with this is if you have 2 dynos running, each request can hit a different dyno and get different session state. Hence the recommendation for an external storage mechanism that can synchronize between dynos.
&lt;/p&gt;
You can also run non-web applications on Heroku. You can have one web process, but as many non-web processes as you want.
&lt;/p&gt;
&lt;p&gt;Heroku has native support for the &lt;a href=&quot;http://www.playframework.org/&quot;&gt;Play framework&lt;/a&gt;. To detect Play applications, it look for a &lt;em&gt;conf/application.conf&lt;/em&gt; file. You don&apos;t need to have a Procfile in your root directory because Heroku knows how to start a Play application.
&lt;/p&gt;
&lt;p&gt;At this point, James created a new Play application, created a new Heroku app (he got &quot;young-night-7104&quot; this time) and pushed it to Heroku. He created a simple model object, a controller to allow adding new data and then wrote some jQuery to show new records via Ajax and JSON. He also showed how to configure the application to talk to Heroku&apos;s PostgreSQL database using the DATABASE_URL environment variable. He explained how you can use the &lt;code&gt;heroku config&lt;/code&gt; command to see your environment variables.
&lt;/p&gt;
&lt;p&gt;
The reason they use environment variables is so Heroku can update DATABASE_URL (and other variables) without having to call up all their customers and have them change them in their source code.
&lt;/p&gt;
&lt;p&gt;
Play on Heroku supports Scala if you create your app with Scala. Play 2.0 uses Scala, Akka and SBT. Heroku added support for SBT a couple month ago, so everything will work just fine.
&lt;/p&gt;
&lt;p&gt;Heroku also supports Scala, detecting it by looking for the &lt;code&gt;build.sbt&lt;/code&gt; file in the root directory. Heroku supports SBT 0.11.0 and it builds the &apos;stage&apos; task.
It currently does not support Lift because Lift uses an older version of SBT and because it&apos;s a very stateful framework that would require sticky sessions. Use Play, &lt;a href=&quot;https://github.com/jdegoes/blueeyes&quot;&gt;BlueEyes&lt;/a&gt; or &lt;a href=&quot;http://www.scalatra.org/&quot;&gt;Scalatra&lt;/a&gt; if you want Scala on Heroku.
&lt;/p&gt;
&lt;p&gt;Heroku has &lt;a href=&quot;http://addons.heroku.com&quot;&gt;addons&lt;/a&gt; for adding functionality to your application, including Custom DNS, HTTPS, Amazon RDS, NoSQL and many more. They&apos;re also working on making their add-on and management APIs available via Java, so you&apos;ll (hopefully) be able to use them from your IDE in the future.
&lt;/p&gt;
&lt;p&gt;From there, James showed us how Heroku keeps slug files around so you can do rollbacks with &lt;code&gt;heroku rollback&lt;/code&gt;. He also showed how you can use:
&lt;/p&gt;
&lt;pre&gt;heroku run &quot;your bash command&quot;&lt;/pre&gt;
to run any Bash command on the cloud.
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;br&gt;
I attended James&apos;s talk because he&apos;s a good friend, but also because I&apos;ve been using Heroku to host &lt;a href=&quot;http://raibledesigns.com/rd/entry/phonegap_to_the_rescue&quot;&gt;my latest adventures with Play, Scala, CoffeeScript and Jade&lt;/a&gt;. I&apos;m glad I attended because I learned some good tips and tricks and more about how Heroku works. 
&lt;/p&gt;
&lt;p&gt;Heroku seems like a great development tool to me. In my experience, it&apos;s been really nice to have instant deployments using Git. In fact, I&apos;ve created a &apos;push&apos; alias so I can push to my project&apos;s repo and heroku at the same time.
&lt;/p&gt;
&lt;pre&gt;
alias push=&apos;git push origin master &amp;&amp; git push heroku master&apos;
&lt;/pre&gt;
&lt;p&gt;I&apos;d like to see more organizations embrace something like Heroku for developers. It&apos;d be great if everyone had their own sandbox that business owners and product managers could see. I can&apos;t help but think this would be awesome for demos, prototyping, etc.
&lt;/p&gt;
&lt;p&gt;
There were some other talks I wanted to attend at the same time, particularly Martin Odersky&apos;s &lt;a href=&quot;http://www.devoxx.com/pages/viewpage.action?pageId=5015952&quot;&gt;What&apos;s in store for Scala?&lt;/a&gt; and &lt;a href=&quot;http://www.devoxx.com/display/DV11/WWW++World+Wide+Wait++A+Performance+Comparison+of+Java+Web+Frameworks&quot;&gt;WWW: World Wide Wait? A Performance Comparison of Java Web Frameworks&lt;/a&gt;. The WWW talk has &lt;a href=&quot;http://prezi.com/dr3on1qcajzw/www-world-wide-wait-devoxx-edition/&quot;&gt;posted their presentation&lt;/a&gt; but I&apos;m sure it&apos;d be more fun to watch. 
&lt;/p&gt;
&lt;p&gt;
It&apos;s pretty awesome that all the talks from Devoxx 2011 will be up on &lt;a href=&quot;http://www.parleys.com/&quot;&gt;Parleys.com&lt;/a&gt; soon.
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; James has &lt;a href=&quot;http://www.jamesward.com/2011/11/29/heroku-preso-from-devoxx-2011&quot;&gt;posted his slides&lt;/a&gt; from this talk.</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/phonegap_for_hybrid_app_development</id>
        <title type="html">PhoneGap for Hybrid App Development</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/phonegap_for_hybrid_app_development"/>
        <published>2011-11-16T10:22:16-07:00</published>
        <updated>2016-11-28T18:17:50-07:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="devoxx" scheme="http://roller.apache.org/ns/tags/" />
        <category term="ios" scheme="http://roller.apache.org/ns/tags/" />
        <category term="javascript" scheme="http://roller.apache.org/ns/tags/" />
        <category term="phonegap" scheme="http://roller.apache.org/ns/tags/" />
        <category term="android" scheme="http://roller.apache.org/ns/tags/" />
        <category term="adobe" scheme="http://roller.apache.org/ns/tags/" />
        <category term="devoxx2011" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">This afternoon, I attended &lt;a href=&quot;http://brian.io&quot; title=&quot;Brian LeRoux&quot;&gt;Brian LeRoux&lt;/a&gt;&apos;s talk on &lt;a href=&quot;http://www.devoxx.com/display/DV11/PhoneGap+for+Hybrid+App+Development&quot;&gt;PhoneGap for Hybrid App Development&lt;/a&gt; at Devoxx. You might remember that I &lt;a href=&quot;http://raibledesigns.com/rd/entry/phonegap_to_the_rescue&quot;&gt;tried PhoneGap last week&lt;/a&gt; and really enjoyed my experience. Below are my notes from Brian&apos;s talk.
&lt;/p&gt;
&lt;p&gt;
PhoneGap is a project for creating native applications using HTML, CSS and JavaScript. PhoneGap started out as a hack. In 2007, Apple shipped the iPhone and Steve Jobs told everyone they should develop webapps. PhoneGap started in 2008 as a lofty summertime hack and gained traction as a concept at Nitobi with Android and Blackberry implementations in the fall. In 2009, people started to pay attention when PhoneGap got rejected by Apple. They added Symbian and webOS support and Sony Ericsson started contributing to the project. They got rejected because all PhoneGap-developed apps were named &quot;PhoneGap&quot;. This turned out to be good press for the project and Apple let them in shortly after.
&lt;/p&gt;
&lt;p&gt;
In 2010, IBM began tag-teaming with Nitobi and added 5 developers to the project after meeting them at OSCON. In 2011, RIM started contributing as well as Microsoft. Then Adobe bought the company, so they&apos;re obviously contributing.
&lt;/p&gt;
&lt;p&gt;PhoneGaps Goals: the web is a first class platform, so let people create installable web apps. Their second goal is to cease to exist and get browsers to adopt their model.
&lt;/p&gt;
&lt;p&gt;
PhoneGap is NOT a runtime or a compiler/transpiler. It&apos;s not an IDE or predefined framework or proprietary lockin. It&apos;s Apache, MIT and BSD licensed to guarantee it&apos;s as free as free software gets. You can do whatever you want to do with it. PhoneGap has &lt;a href=&quot;http://arstechnica.com/open-source/news/2011/10/phonegap-to-become-an-apache-project-as-adobe-acquires-nitobi.ars&quot;&gt;recently been contributed to the Apache Software Foundation&lt;/a&gt;.
&lt;/p&gt;
As far as Adobe vs. PhoneGap is concerned, the Nitobi team remains contributors to PhoneGap. Adobe is a software tools company and has Apache and WebKit contributors. PhoneGap/Build integration will be added to &lt;a href=&quot;http://www.adobe.com/products/creativecloud.html&quot;&gt;Creative Cloud&lt;/a&gt;. 
&lt;/p&gt;
&lt;p&gt;
The biggest issues with contributing PhoneGap to Apache is renaming the project and source control. I&apos;m not sure why it needs to be renamed, but it&apos;s likely that &lt;em&gt;Apache Callback&lt;/em&gt; is out. There seems to be some consensus on Apache Cordova. Apache likes SVN and the PhoneGap community currently uses Git. They&apos;re trying to find a medium road there, but would prefer to stay on Git.
&lt;/p&gt;
&lt;p&gt;
The PhoneGap technique is colloquially called &quot;the bridge&quot;. It&apos;s a 3 step process: they instantiate a WebView, then they call JavaScript from native code, then they call native code from JavaScript. Apparently, all device APIs are available via JavaScript in a WebView.
&lt;/p&gt;
&lt;p&gt;The primary platforms supported are iOS &gt;= 3, Android &gt;= 1.5 and BlackBerry &gt;= 5.x. They also support webOS, Symbian, Samsung Bada and Windows Phone. No mobile dev platform supports as many deploy targets as PhoneGap. Primary contributors are Adobe, IBM, RIM and Microsoft. 
&lt;/p&gt;
&lt;p&gt;Documentation for PhoneGap is available at &lt;a href=&quot;http://docs.phonegap.com&quot;&gt;http://docs.phonegap.com&lt;/a&gt;. Device APIs for PhoneGap 1.0 included sensors, data and outputs, which all devices have. Examples of sensors are geolocation and camera. Data examples are the filesystem, contacts and media. Outputs are screens, speakers and the speaker jack. All PhoneGap APIs are plugins, but any native API is permitted. 
&lt;/p&gt;
&lt;p&gt;
What about security? Brian recommends looking at the &lt;a href=&quot;http://html5sec.org&quot;&gt;HTML5 Security Cheatsheet&lt;/a&gt;. PhoneGap has added a lot of security measures since they&apos;ve found the native API pretty much opens up everything. 
&lt;/p&gt;
&lt;p&gt;
PhoneGap doesn&apos;t bundle a UI framework, but they support any JavaScript framework that works in the browser. PhoneGap is just a fancy browser, so your code run in less fancy web browsers too. This means you can develop and test your app in your desktop browser and only use PhoneGap to package and distribute your app.
&lt;/p&gt;
&lt;p&gt;Competition? PhoneGap has no competition. 
&lt;/p&gt;
&lt;p&gt;
&lt;a href=&quot;https://build.phonegap.com/&quot;&gt;PhoneGap/Build&lt;/a&gt; is for compiling your apps in the cloud and free for open source projects. The biggest reason they did this is because they couldn&apos;t redistribute all the SDKs and it was a pain for developers to download and install SDKs in training classes.
&lt;/p&gt;
&lt;p&gt;
For mobile app development, you should have a singular goal. Do one thing really well if you want to be successful. Great UX happens iteratively. You know that the web works and has been widely successfully cross-platform. It&apos;s likely you&apos;ve already invested in the web. Start by building a mobile web client and use PhoneGap as a progressive enhancement technique. 
&lt;/p&gt;
&lt;p&gt;
Shipping and unit testing should be a daily activity. Automate everything so you can have one-click builds (test/dev/release). For web client design, constraints are your ally in the battle against complexity and &quot;clients who are not &lt;em&gt;chill&lt;/em&gt;&quot;. Phones suck and consume a lot: cpu, ram, bandwidth, battery, network... everything! Start with a benchmark of app performance and monitor that benchmark. If you have tons and tons of features, consider splitting into multiple apps.
&lt;/p&gt;
The mobile web is not WebKit! Opera is huge, Firefox is making strides and IE still happens. For layouts: use flex-box rules (anyone got a link to these?), css media queries and meta tags for viewport. You should try to develop your app without frameworks because they come with a ton of code and can effect the size of your app.
&lt;/p&gt;
&lt;p&gt;
Looks can kill: aesthetics that can hurt performance: border-radius, box-shadow and gradients can slow down your apps. Chances are, you really don&apos;t need these features. Design your app for your brand, not for the device manufacturer. An app that looks like an iPhone app on Android doesn&apos;t give a positive impression.&lt;/p&gt;
&lt;p&gt;
For JavaScript libraries, start with your problem, not a generic solution like Sencha or jQuery Mobile. &lt;a href=&quot;http://zeptojs.com/&quot;&gt;Zepto&lt;/a&gt; and its older brother &lt;a href=&quot;http://xuijs.com/&quot;&gt;XUI&lt;/a&gt; are all you need to start. &lt;a href=&quot;http://joapp.com/&quot;&gt;Jo&lt;/a&gt; is a fantastic option. &lt;a href=&quot;http://backbonejs.org/&quot;&gt;Backbone&lt;/a&gt; and &lt;a href=&quot;http://spine.github.io/&quot;&gt;Spine&lt;/a&gt; are worth watching.
&lt;/p&gt;
&lt;p&gt;
For testing, &lt;a href=&quot;http://docs.jquery.com/QUnit&quot;&gt;QUnit&lt;/a&gt; and &lt;a href=&quot;http://pivotal.github.com/jasmine/&quot;&gt;Jasmine&lt;/a&gt; are pretty popular. For deployment, concat, minify and obfuscate your JavaScript and CSS. Or you can inline everything into the markup to minimize HTTP chatter. Gmail inlines and comments all their JavaScript and then evals it.
&lt;/p&gt;
&lt;p&gt;From there, Brian recommended leveraging HTML5&apos;s &lt;a href=&quot;http://www.html5rocks.com/en/tutorials/appcache/beginner/&quot;&gt;AppCache&lt;/a&gt; and and using RESTful JSON endpoints for legacy systems. Next, he tried to show us a demo of a photo sharing application. Unfortunately, the Demo Gods were grumpy and Brian couldn&apos;t get his computer to recognize his Android phone. He did show us the &lt;a href=&quot;http://gist.github.com/1219277&quot;&gt;client code&lt;/a&gt; and it&apos;s pretty impressive you can use 1 line of code to take a picture on a phone.
&lt;/p&gt;
&lt;p&gt;The last thing we looked at was &lt;a href=&quot;http://debug.phonegap.com&quot;&gt;debug.phonegap.com&lt;/a&gt;. This is an app that&apos;s powered by &lt;a href=&quot;http://phonegap.github.com/weinre/&quot;&gt;weinre&lt;/a&gt;. It lets you enter a line of JavaScript in your client and then remotely debug it in a tool that looks like Chrome&apos;s Web Inspector. Very cool stuff if you ask me.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;br/&gt;
I really enjoyed learning more about PhoneGap, particularly because Brain emphasized all my web development skills can be used. I don&apos;t have to learn Objective-C or Android to develop native apps and I don&apos;t even have to install an SDK if I use PhoneGap/Build. Of course, my mobile developer friends might disagree with this approach. In the meantime, I look forward to using PhoneGap to turn my mobile web clients into native apps and finding out if it&apos;s really as good as they say it is.
</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/play_2_0_a_web</id>
        <title type="html">Play 2.0, A web framework for a new era</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/play_2_0_a_web"/>
        <published>2011-11-16T05:58:09-07:00</published>
        <updated>2011-11-16T12:07:33-07:00</updated> 
        <category term="/Java" label="Java" />
        <category term="devoxx2011" scheme="http://roller.apache.org/ns/tags/" />
        <category term="devoxx" scheme="http://roller.apache.org/ns/tags/" />
        <category term="scala" scheme="http://roller.apache.org/ns/tags/" />
        <category term="playframework" scheme="http://roller.apache.org/ns/tags/" />
        <category term="akka" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">This week, I&apos;m in Antwerp, Belgium for the annual &lt;a href=&quot;http://devoxx.com&quot;&gt;Devoxx&lt;/a&gt; conference. After traveling 21 hours door-to-door yesterday, I woke up and came to the conference to attend some talks on Play and PhoneGap. I just got out of the &lt;a href=&quot;http://devoxx.com/display/DV11/Play+2.0%2C+a+Web+framework+for+a+new+era&quot;&gt;session on Play 2.0&lt;/a&gt;, which was presented by &lt;a href=&quot;http://devoxx.com/display/DV11/Sadek+Drobi&quot;&gt;Sadek Drobi&lt;/a&gt; and &lt;a href=&quot;http://devoxx.com/display/DV11/Guillaume+Bort&quot;&gt;Guillaume Bort&lt;/a&gt;. Below are my notes from this presentation.&lt;/p&gt;
&lt;p&gt;The Play 2.0 beta is out! You can read more about this release &lt;a href=&quot;http://groups.google.com/group/play-framework/browse_thread/thread/6d5783e28efb6931&quot;&gt;on the mailing list&lt;/a&gt;. This beta includes native support for both Scala and Java, meaning you can use both in the same project. The release also bundles &lt;a href=&quot;http://akka.io/&quot;&gt;Akka&lt;/a&gt; and &lt;a href=&quot;https://github.com/harrah/xsbt/wiki&quot;&gt;SBT&lt;/a&gt; by default.
&lt;/p&gt;
&lt;p&gt;In other news, &lt;a href=&quot;http://blog.typesafe.com/typesafe-stack-adds-play-framework&quot;&gt;Play 2.0 is now part of the Typesafe Stack&lt;/a&gt;. Typesafe is the Scala company, started by the founder of Scala (&lt;a href=&quot;http://ch.linkedin.com/in/odersky&quot;&gt;Martin Odersky&lt;/a&gt;) and the founder of Akka (&lt;a href=&quot;http://se.linkedin.com/in/jonasboner&quot;&gt;Jonas Bon&#233;r&lt;/a&gt;). Guillaume is also joining the Typesafe Advisory Board.
&lt;/p&gt;
&lt;p&gt;Sadek and Guillaume both work at &lt;a href=&quot;http://www.zenexity.com/&quot;&gt;zenexity&lt;/a&gt;, where Play is the secret weapon for the web applications they&apos;ve built for the last decade. Play was born in the real world. They kept listening to the market to see what they should add to the project. At some point, they realized they couldn&apos;t keep adding to the old model and they needed to create something new.&lt;/p&gt;
&lt;p&gt;
The web has evolved from static pages to dynamic pages (ASP, PHP). From there, we moved to structured web applications with frameworks and MVC. Then the web moved to Ajax and long-polling to more real-time, live features.  And this changes everything.
&lt;/p&gt;
&lt;p&gt;
Now we need to adapt our tools. We need to handle tremendous flows of data. Need to improve expressiveness for concurrent code. We need to pick the appropriate datastore for the problem (not only SQL). We need to integrate with rapidly-evolving client side technologies like JavaScript, CoffeeScript, and Dart. We need to use elastic deployment that allows scaling up and scaling down.&lt;/p&gt;
&lt;p&gt;zenexity wanted to integrated all of these modern web needs into Play 2.0. But they also wanted to keep Play approachable. They wanted to maintain fast turnaround so you can change your code and hit reload to see the changes. They wanted to keep it as a full stack framework with support for JSON, XML, Web Services, Jobs, etc. And they wanted to continue to use and conventions over configuration.
&lt;/p&gt;
&lt;p&gt;At this point, Guillaume did a Play 2.0 Beta demo, show us how it uses SBT and has a console so everything so it runs really fast. You can have both Scala and Java files in the same project. Play 2.0 templates are based on Scala, but you don&apos;t need to know Scala to use them. You might have to learn how to write a for loop in Scala, but it&apos;s just a subset of Scala for templates and views. SBT is used for the build system, but you don&apos;t have to learn or know SBT. All the old &lt;code&gt;play&lt;/code&gt; commands still work, they&apos;re just powered by a different system.
&lt;/p&gt;
&lt;p&gt;After the demo, Sadek took over and started discussing the key features of Play 2.0.
&lt;/p&gt;
&lt;p&gt;
To handle tremendous amounts of data, you need to do chunking of data and be able to process a stream of data, not just wait until it&apos;s finished. Java&apos;s InputStream is outdated and too low level. Its &lt;em&gt;read()&lt;/em&gt; method reads the next byte of data from the input and this method can block until input data is available.&lt;/p&gt;
&lt;p&gt;
To solve this, Play includes a reactive programming feature, which they borrowed from Haskell. It uses Iteratee/Enumerator IO and leverages inversion of control (not like dependency injection, but more like not micro-managing). The feature allows you to have control when you need it so you don&apos;t have to wait for the input stream to complete. The Enumerator is the component that sends data and the Iteratee is the component that receives data. The Iteratee does incremental processing and can tell the Enumerator when it&apos;s done. The Iteratee can also send back a continuation, where it tells the Enumerator it wants more data and how to give it. With this paradigm, you can do a lot of cool stuff without consuming resources and blocking data flow.
&lt;/p&gt;
&lt;p&gt;
Akka is an actor system that is a great model for doing concurrent code. An Actor could be both an Enumerator and an Iteratee. This vastly improves the expressiveness for concurrent code. For example, here&apos;s how you&apos;d use Akka in Play:
&lt;pre class=&quot;brush: scala&quot;&gt;
def search(keyword: String) = Action {
  AsyncResult {
    // do something with result
  }
}
&lt;/pre&gt;
&lt;p&gt;
Play does not try to abstract data access because datastores are different now. You don&apos;t want to think of everything as objects if you&apos;re using something like MongoDB or navigating a Social Graph. Play 2.0 will provide some default modules for the different datastores, but they also expect a lot of contributed modules. Anorm will be the default SQL implementation for Play Scala and &lt;a href=&quot;https://github.com/playframework/Play20/wiki/JavaEbean&quot;&gt;Ebean&lt;/a&gt; will be the default ORM implementation for Play Java. The reason they&apos;ve moved away from Hibernate is because they needed something that was more stateless.
&lt;/p&gt;
&lt;p&gt;
On the client side, there&apos;s so many technologies (LESS, CoffeeScript, DART, Backbone.js, jQuery, SASS), they didn&apos;t want to bundle any because they move too fast. Instead, there&apos;s plugins you can add that help you leverage these technologies. There&apos;s a lot of richness you can take advantage of on the client side and you need to have the tools for that.&lt;/p&gt;
&lt;p&gt;Lastly, there&apos;s a new type of deployment: container-less deployment to the cloud. Akka allows you to distribute your jobs across many servers and &lt;a href=&quot;http://heroku.com&quot;&gt;Heroku&lt;/a&gt; is an implementation of elastic deployment that has built-in support for Play.
&lt;/p&gt;
&lt;p&gt;
They&apos;ve explained what they tried to design and the results of this new, clean architecture have been suprising. Side effects include: type-safety everywhere for rock-solid applications. There&apos;s an awesome performance boost from Scala. There&apos;s easier integration with existing projects via SBT. And it only takes 10 lines of code to develop an HTTP Server that responds to web requests.
&lt;p&gt;The memory consumption is amazing: only 2MB of heap is used when a Play 2.0 app is started. Tests on Guillaume&apos;s laptop have shown that it can handle up to 40,000 requests per second, without any optimization of the JVM. Not only that, but after the requests subside, garbage collection cleans up everything and reduces the memory consumption back to 2MB.
&lt;/p&gt;
&lt;p&gt;At this point, Guillaume did another demo, showing how everything is type-safe in 2.0, including the routes file. If you mistype (or comment one out) any routes, the compiler will find it and notify you. Play 2.0 also contains a &lt;em&gt;compiled assets&lt;/em&gt; feature. This allows you to &lt;a href=&quot;https://github.com/playframework/Play20/wiki/AssetsGoogleClosureCompiler&quot;&gt;use Google&apos;s Closure Compiler&lt;/a&gt;, &lt;a href=&quot;https://github.com/playframework/Play20/wiki/AssetsCoffeeScript&quot;&gt;CoffeeScript&lt;/a&gt; and &lt;a href=&quot;https://github.com/playframework/Play20/wiki/AssetsLess&quot;&gt;LESS&lt;/a&gt;. If you put your LESS files in &lt;em&gt;app/assets/stylesheets&lt;/em&gt;, compilation errors will show up in your browser. If you put JavaScript files in &lt;em&gt;app/assets/javascripts&lt;/em&gt;, the Closure compiler will be used and compilation errors will show up in your browser.
&lt;/p&gt;
&lt;p&gt;
Play 2.0 ships with 3 different sample applications, all implemented in both Java and Scala. HelloWorld is more than just text in a browser, it includes a form that shows how validation works. Another app is computer-database. When Guillaume started it, we saw how evolutions were used to create the database schema from the browser. The Play Team has done their best to make the development process a browser-based experience rather than having to look in your console. The computer-database is a nice example of how to do CRUD and leverages Twitter&apos;s Bootstrap for its look and feel.
&lt;/p&gt;
The last sample application is zentasks. It uses Ajax and implements security so you can see how to create a login form. It uses LESS for CSS and CoffeeScript and contains features like in-place editing. If you&apos;d like to see any of these applications in action, you can stop by the Typesafe booth this week at Devoxx.
&lt;/p&gt;
&lt;p&gt;Unfortunately, there will be no migrating path for Play 1.x applications. The API seems very similar, but there are subtle changes that make this difficult. The biggest thing is how templating has changed from Groovy to Scala. To migrate from 1.2.x would be mostly a copy/paste, modify process. There are folks working on getting Groovy templates working in 2.0. The good news is zenexity has hundreds of 1.x applications in production, so 1.x will likely be maintained for many years.
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;br/&gt;
This was a great talk on what&apos;s new in Play 2.0. I especially like the native support for LESS and CoffeeScript and the emphasis on trying to keep developers using two tools: their editor and the browser. The sample apps look great, but the documentation look sparse. I doubt I&apos;ll get a chance to migrate my Play 1.2.3 app to 2.0 this month, but I hope to try migrating sometime before the end of the year.</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/developing_with_html5_coffeescript_and</id>
        <title type="html">Developing with HTML5, CoffeeScript and Twitter&apos;s Bootstrap </title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/developing_with_html5_coffeescript_and"/>
        <published>2011-10-20T14:47:36-06:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="less" scheme="http://roller.apache.org/ns/tags/" />
        <category term="play-more" scheme="http://roller.apache.org/ns/tags/" />
        <category term="html5" scheme="http://roller.apache.org/ns/tags/" />
        <category term="bootstrap" scheme="http://roller.apache.org/ns/tags/" />
        <category term="coffeescript" scheme="http://roller.apache.org/ns/tags/" />
        <category term="devoxx2011" scheme="http://roller.apache.org/ns/tags/" />
        <category term="html5boilerplate" scheme="http://roller.apache.org/ns/tags/" />
        <category term="geolocation" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">&lt;a href=&quot;http://www.w3.org/html/logo/&quot;&gt;&lt;img src=&quot;//www.w3.org/html/logo/downloads/HTML5_Logo_128.png&quot; width=&quot;128&quot; height=&quot;128&quot; alt=&quot;HTML5 Logo&quot; class=&quot;picture&quot; style=&quot;border: 0&quot;&gt;&lt;/a&gt;
This article is the fourth in a series about my adventures developing a Fitness Tracking application with HTML5, Play Scala, CoffeeScript and Jade. Previous articles can be found at:
&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/integrating_scalate_and_jade_with&quot;&gt;Integrating Scalate and Jade with Play 1.2.3&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/trying_to_make_coffeescript_work&quot;&gt;Trying to make CoffeeScript work with Scalate and Play&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/integrating_html5_boilerplate_with_scalate&quot;&gt;Integrating HTML5 Boilerplate with Scalate and Play&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p id=&quot;features&quot;&gt;&lt;strong&gt;Developing Features&lt;/strong&gt;&lt;br/&gt;After getting my desired infrastructure setup, I started coding like a madman. The first feature I needed was a stopwatch to track the duration of a workout, so I started writing one with CoffeeScript. After spending 20 minutes playing with dates and setTimeout, I searched and found a &lt;a href=&quot;http://www.kellishaver.com/projects/stopwatch/&quot;&gt;stopwatch jQuery plug-in&lt;/a&gt;. I added this to my app, deployed it to &lt;a href=&quot;http://www.heroku.com/&quot;&gt;Heroku&lt;/a&gt;, brought up the app on my iPhone 3G, clicked &lt;em&gt;Start&lt;/em&gt; and started riding my bike to work. 
&lt;/p&gt;
&lt;p&gt;
When I arrived, I unlocked my phone and discovered that the time had stopped. At first, I thought this was a major setback. My disappointed disappeared when I found a &lt;a href=&quot;http://proft.50megs.com/stopwatch.html&quot;&gt;Super Neat JavaScript Stopwatch&lt;/a&gt; and &lt;a href=&quot;http://www.timpelen.com/extra/sidebars/stopwatch/stopwatch.htm&quot;&gt;K&#229;re Byberg&apos;s version&lt;/a&gt; that worked just fine. This stopwatch used setTimeout, so by keeping the start time, the app on the phone would &lt;em&gt;catch up&lt;/em&gt; as soon as you unlocked it. I ported K&#229;re&apos;s script to CoffeeScript and rejoiced in my working stopwatch. 
&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
# Created by K&#229;re Byberg &#169; 21.01.2005. Please acknowledge if used 
# on other domains than http://www.timpelen.com.
# Ported to CoffeeScript by Matt Raible. Also added hours support.
flagClock = 0
flagStop = 0
stopTime = 0
refresh = null
clock = null

start = (button, display) -&gt;
  clock = display
  startDate = new Date()
  startTime = startDate.getTime()
  if flagClock == 0
    $(button).html(&quot;Stop&quot;)
    flagClock = 1
    counter startTime, display
  else
    $(button).html(&quot;Start&quot;)
    flagClock = 0
    flagStop = 1

counter = (startTime) -&gt;
  currentTime = new Date()
  timeDiff = currentTime.getTime() - startTime
  timeDiff = timeDiff + stopTime  if flagStop == 1
  if flagClock == 1
    $(clock).val formatTime timeDiff, &quot;&quot;
    callback = -&gt; counter startTime
    refresh = setTimeout callback, 10
  else
    window.clearTimeout refresh
    stopTime = timeDiff

formatTime = (rawTime, roundType) -&gt;
  if roundType == &quot;round&quot;
    ds = Math.round(rawTime / 100) + &quot;&quot;
  else
    ds = Math.floor(rawTime / 100) + &quot;&quot;
  sec = Math.floor(rawTime / 1000)
  min = Math.floor(rawTime / 60000)
  hour = Math.floor(rawTime / 3600000)
  ds = ds.charAt(ds.length - 1)
  start() if hour &gt;= 24
  sec = sec - 60 * min + &quot;&quot;
  sec = prependZeroCheck sec
  min = min - 60 * hour + &quot;&quot;
  min = prependZeroCheck min
  hour = prependZeroCheck hour
  hour + &quot;:&quot; + min + &quot;:&quot; + sec + &quot;.&quot; + ds

prependZeroCheck = (time) -&gt;
  time = time + &quot;&quot; # convert from int to string
  unless time.charAt(time.length - 2) == &quot;&quot;
    time = time.charAt(time.length - 2) + time.charAt(time.length - 1)
  else
    time = 0 + time.charAt(time.length - 1)

reset = -&gt;
  flagStop = 0
  stopTime = 0
  window.clearTimeout refresh
  if flagClock == 1
    resetDate = new Date()
    resetTime = resetDate.getTime()
    counter resetTime
  else
    $(clock).val &quot;00:00:00.0&quot;

@StopWatch = {
  start: start
  reset: reset
}
&lt;/pre&gt;
&lt;p&gt;The Scalate/Jade template to render this stopwatch looks as follows:
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
script(type=&quot;text/javascript&quot; src={uri(&quot;/public/javascripts/stopwatch.coffee&quot;)})

#display
  input(id=&quot;clock&quot; class=&quot;xlarge&quot; type=&quot;text&quot; value=&quot;00:00:00.0&quot; readonly=&quot;readonly&quot;)
#controls
  button(id=&quot;start&quot; type=&quot;button&quot; class=&quot;btn primary&quot;) Start
  button(id=&quot;reset&quot; type=&quot;button&quot; class=&quot;btn :disabled&quot;) Reset

:plain
  &amp;lt;script type=&quot;text/coffeescript&quot;&gt;
    $(document).ready -&gt;
      $(&apos;#start&apos;).click -&gt;
        StopWatch.start this, $(&apos;#clock&apos;)

      $(&apos;#reset&apos;).click -&gt;
        StopWatch.reset()
  &amp;lt;/script&gt;
&lt;/pre&gt;
&lt;p&gt;Next, I wanted to create a map that would show your location. For this, I used &lt;a href=&quot;http://merged.ca/iphone/html5-geolocation&quot;&gt;
  Merge Design&apos;s HTML 5 Geolocation Demo&lt;/a&gt; as a guide. The &lt;a href=&quot;http://dev.w3.org/geo/api/spec-source.html&quot;&gt;HTML5 Geo API&lt;/a&gt; is pretty 
  simple, containing only three methods:
&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
// Gets the users current position
navigator.geolocation.getCurrentPosition(successCallback,
                                         errorCallback,
                                         options);
// Request repeated updates of position
watchId = navigator.geolocation.watchPosition(successCallback, errorCallback);

// Cancel the updates
navigator.geolocation.clearWatch(watchId);
&lt;/pre&gt;
&lt;p&gt;After rewriting the geolocation example in CoffeeScript, I ended up with the following code in my map.coffee script. You&apos;ll notice it uses 
  &lt;a href=&quot;http://code.google.com/apis/maps/documentation/javascript/&quot;&gt;Google Maps JavaScript API&lt;/a&gt; to show an actual map
  with a marker.
&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
# Geolocation with HTML 5 and Google Maps API based on example from maxheapsize: 
# http://maxheapsize.com/2009/04/11/getting-the-browsers-geolocation-with-html-5/
# This script is by Merge Database and Design, http://merged.ca/ -- if you use some, 
# all, or any of this code, please offer a return link.

map = null
mapCenter = null
geocoder = null
latlng = null
timeoutId = null

initialize = -&gt;
  if Modernizr.geolocation
    navigator.geolocation.getCurrentPosition showMap

showMap = (position) -&gt;
  latitude = position.coords.latitude
  longitude = position.coords.longitude
  mapOptions = {
    zoom: 15,
    mapTypeId: google.maps.MapTypeId.ROADMAP
  }
  map = new google.maps.Map(document.getElementById(&quot;map&quot;), mapOptions)
  latlng = new google.maps.LatLng(latitude, longitude)
  map.setCenter(latlng)

  geocoder = new google.maps.Geocoder()
  geocoder.geocode({&apos;latLng&apos;: latlng}, addAddressToMap)

addAddressToMap = (results, status) -&gt;
  if (status == google.maps.GeocoderStatus.OK) 
    if (results[1]) 
      marker = new google.maps.Marker({
          position: latlng,
          map: map
      })
      $(&apos;#location&apos;).html(&apos;Your location: &apos; + results[0].formatted_address)
  else 
    alert &quot;Sorry, we were unable to geocode that address.&quot;

start = -&gt;
  timeoutId = setTimeout initialize, 500

reset = -&gt;
  if (timeoutId)
    clearTimeout timeoutId

@Map = {
  start: start
  reset: reset
}
&lt;/pre&gt;
&lt;p&gt;The template to show the map is a mere 20 lines of Jade:
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
script(type=&quot;text/javascript&quot; src=&quot;//www.google.com/jsapi&quot;)
script(type=&quot;text/javascript&quot; src=&quot;//maps.googleapis.com/maps/api/js?sensor=false&quot;)

:css
  .demo-map {
    border: 1px solid silver;
    height: 200px;
    margin: 10px auto;
    width: 280px;
  }

#map(class=&quot;demo-map&quot;)

p(id=&quot;location&quot;)
  span(class=&quot;label success&quot;) New
  | Fetching your location with HTML 5 geolocation...

script(type=&quot;text/javascript&quot; src={uri(&quot;/public/javascripts/map.coffee&quot;)})
:javascript
    Map.start();
&lt;/pre&gt;
&lt;p&gt;The last two features I wanted were 1) distance traveled and 2) drawing the route taken on the map. For this I learned from 
  &lt;a href=&quot;http://www.html5rocks.com/en/tutorials/geolocation/trip_meter/&quot;&gt;A Simple Trip Meter using the Geolocation API&lt;/a&gt;.
  As I was beginning to port the JS to CoffeeScript, I thought, &quot;there&apos;s got to be a better way.&quot;  I searched and found &lt;a href=&quot;http://js2coffee.org/&quot;&gt;Js2coffee&lt;/a&gt; to do most of the conversion for me. If you know JavaScript and you&apos;re learning CoffeeScript, this is an invaluable tool. 
&lt;/p&gt;
&lt;p&gt;
I tried out the trip meter that evening
  on a bike ride and noticed it said I&apos;d traveled 3 miles when I&apos;d really gone 6. I quickly figured out it was only calculating
  start point to end point and not taking into account all the turns in between. To view what was happening, I integrated my
  odometer.coffee with my map using &lt;a href=&quot;http://code.google.com/apis/maps/documentation/javascript/overlays.html#Polylines&quot;&gt;
  Google Maps Polylines&lt;/a&gt;. Upon finishing the integration, I discovered two things, 1) HTML5 geolocation was highly inaccurate and
  2) &lt;a href=&quot;http://stackoverflow.com/questions/7676423/is-it-possible-to-make-an-html5-trip-meter-that-tracks-distance-traveled/7681295&quot;&gt;geolocation doesn&apos;t run in the background&lt;/a&gt;. 
&lt;/p&gt;
&lt;p&gt;
I was able to solve the first problem by passing in {enableHighAccuracy: true} to navigator.geolocation.watchPosition(). Below are two screenshots showing before high accuracy and after. Both screenshots are from the same two-block walk.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
  &lt;a href=&quot;http://farm7.static.flickr.com/6101/6264033565_0353120a06.jpg&quot; rel=&quot;lightbox[html5geo]&quot; title=&quot;Without {enableHighAccuracy: true} by mraible, on Flickr&quot;&gt;&lt;img src=&quot;//farm7.static.flickr.com/6101/6264033565_0353120a06_m.jpg&quot; width=&quot;160&quot; height=&quot;240&quot; alt=&quot;Without {enableHighAccuracy: true}&quot; style=&quot;border: 1px solid silver&quot;&gt;&lt;/a&gt;
  
  &lt;a href=&quot;http://farm7.static.flickr.com/6032/6264033561_cf9a8cb311.jpg&quot; rel=&quot;lightbox[html5geo]&quot; title=&quot;With {enableHighAccuracy: true} by mraible, on Flickr&quot;&gt;&lt;img src=&quot;//farm7.static.flickr.com/6032/6264033561_cf9a8cb311_m.jpg&quot; width=&quot;160&quot; height=&quot;240&quot; alt=&quot;With {enableHighAccuracy: true}&quot; style=&quot;margin-left: 30px; border: 1px solid silver&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
The second
issue is a slight show-stopper. &lt;a href=&quot;http://www.phonegap.com/&quot;&gt;PhoneGap&lt;/a&gt; might be able to solve the problem, but I&apos;m currently using a
workaround &amp;rarr; turning off auto-lock and keeping Safari in the foreground. 
&lt;/p&gt;
&lt;p id=&quot;style&quot;&gt;&lt;strong&gt;Making it look good&lt;/strong&gt;&lt;br/&gt;After I got all my desired features developed, I moved onto making the app look good. I started by using &lt;a href=&quot;http://sass-lang.com/&quot;&gt;SASS&lt;/a&gt; for my CSS and installed &lt;a href=&quot;http://www.playframework.org/modules/sass&quot;&gt;Play&apos;s SASS module&lt;/a&gt;. I then switched to &lt;a href=&quot;http://lesscss.org/&quot;&gt;LESS&lt;/a&gt; when I discovered and added &lt;a href=&quot;http://twitter.github.com/bootstrap/&quot;&gt;Twitter&apos;s Bootstrap&lt;/a&gt; to my project. At first I used &lt;a href=&quot;http://www.playframework.org/modules/less-0.3/home&quot;&gt;Play&apos;s LESS module&lt;/a&gt; (version 0.3), but ran into &lt;a href=&quot;http://groups.google.com/group/play-framework/browse_thread/thread/4d76688608105dd/f886dc32c724b7cd&quot;&gt;compilation issues&lt;/a&gt;. I then tried &lt;a href=&quot;https://github.com/greenlaw110/play-greenscript&quot;&gt;Play&apos;s GreenScript module&lt;/a&gt;, but gave up on it when I found it was incompatible with the &lt;a href=&quot;http://www.playframework.org/modules/coffee&quot;&gt;CoffeeScript module&lt;/a&gt;. Switching back to the LESS module and using the &quot;0.3.compatibility&quot; version solved all remaining issues.
&lt;/p&gt;
&lt;p&gt;You might remember that &lt;a href=&quot;http://raibledesigns.com/rd/entry/integrating_html5_boilerplate_with_scalate&quot;&gt;I integrated HTML5 Boilerplate&lt;/a&gt; and wondering why I have both Bootstrap and Boilerplate in my project. At this point, I don&apos;t think Boilerplate is needed, but I&apos;ve kept it just in case it&apos;s doing something for HTML5 cross-browser compatibility. I&apos;ve renamed its style.css to style.less and added the following so it has access to Bootstrap&apos;s variables.
&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
/* Variables from Bootstrap */
@import &quot;libs/variables.less&quot;;
&lt;/pre&gt;
&lt;p&gt;Then I made my app look a lot better with &lt;a href=&quot;http://twitter.github.com/bootstrap/examples/container-app.html&quot;&gt;layouts&lt;/em&gt;, &lt;a href=&quot;http://twitter.github.com/bootstrap/#forms&quot;&gt;stylish forms&lt;/a&gt;, a &lt;a href=&quot;http://twitter.github.com/bootstrap/#navigation&quot;&gt;fixed topbar&lt;/a&gt; and &lt;a href=&quot;http://twitter.github.com/bootstrap/#alerts&quot;&gt;alerts&lt;/a&gt;. For example, here&apos;s the CoffeeScript I wrote to display geolocation errors:
&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
geolocationError = (error) -&gt;
  msg = &apos;Unable to locate position. &apos;
  switch error.code
    when error.TIMEOUT then msg += &apos;Timeout.&apos;
    when error.POSITION_UNAVAILABLE then msg += &apos;Position unavailable.&apos;
    when error.PERMISSION_DENIED then msg += &apos;Please turn on location services.&apos;
    when error.UNKNOWN_ERROR then msg += error.code
  $(&apos;.alert-message&apos;).remove()
  alert = $(&apos;&amp;lt;div class=&quot;alert-message error fade in&quot; data-alert=&quot;alert&quot;&amp;gt;&apos;)
  alert.html(&apos;&amp;lt;a class=&quot;close&quot; href=&quot;#&quot;&amp;gt;&amp;times;&amp;lt;/a&amp;gt;&apos; + msg);
  alert.insertBefore($(&apos;.span10&apos;))
&lt;/pre&gt;
&lt;p&gt;Then I set about styling up the app so it looked good on a smartphone with &lt;a href=&quot;http://thinkvitamin.com/design/getting-started-and-gotchas-of-css-media-queries/&quot;&gt;CSS3 Media Queries&lt;/a&gt;. Below is the LESS code I used to hide elements and squish the widths for smaller devices.
&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
@media all and (max-device-width: 480px) {
  /* hide scrollbar on mobile */
  html { overflow-y:hidden }
  /* hide sidebar on mobile */
  .home .span4, .home .page-header, .topbar form {
    display: none
  }
  .home .container {
    width: 320px;
  } 
  .about {
    .container, .span10 {
      width: 280px;
    }
    .span10 {
      padding-top: 0px;
    }
  }
&lt;/pre&gt;
&lt;p id=&quot;tools&quot;&gt;&lt;strong&gt;Tools&lt;/strong&gt;&lt;br/&gt;
In the process of developing a stopwatch, odometer, displaying routes and making everything look good, I used a number of tools. I started out primarily with &lt;a href=&quot;http://macromates.com/&quot;&gt;TextMate&lt;/a&gt; and its bundles for &lt;a href=&quot;https://github.com/appden/less.tmbundle&quot;&gt;LESS&lt;/a&gt;, &lt;a href=&quot;https://github.com/jashkenas/coffee-script-tmbundle&quot;&gt;CoffeeScript&lt;/a&gt; and &lt;a href=&quot;https://github.com/miksago/jade-tmbundle&quot;&gt;Jade&lt;/a&gt;. When I started writing more Scala, I installed the &lt;a href=&quot;https://github.com/mads379/scala.tmbundle&quot;&gt;Scala TextMate Bundle&lt;/a&gt;. When I needed some debugging, I switched to &lt;a href=&quot;http://www.jetbrains.com/idea/&quot;&gt;IntelliJ&lt;/a&gt; and installed its Scala plugin. CoffeeScript, LESS and HAML plugins (for Jade) were already installed by default. I also used James Ward&apos;s &lt;a href=&quot;http://www.jamesward.com/2011/07/28/setup-play-framework-with-scala-in-intellij&quot;&gt;Setup Play Framework with Scala in IntelliJ&lt;/a&gt;.
&lt;/p&gt;
&lt;p id=&quot;issues&quot;&gt;
  &lt;strong&gt;Issues&lt;/strong&gt;&lt;br/&gt;
  I think it&apos;s obvious that my biggest issue so far is the fact that a webapp can&apos;t multitask in the background like a native app can. Beyond that, there&apos;s accuracy issues with HTML5&apos;s geolocation that I haven&apos;t seen in native apps. 
&lt;/p&gt;
&lt;p&gt;
I also ran into a caching issue when calling getCurrentPosition(). It only worked the first time and I had to refresh my browser to get it to work again. Strangely enough, this only happened on my desktop (in Safari and Firefox) and worked fine on my iPhone. Unfortunately, it looks like &lt;a href=&quot;https://github.com/phonegap/phonegap-iphone/issues/197&quot;&gt;PhoneGap has issues&lt;/a&gt; similar to this.
&lt;/p&gt;
&lt;p&gt;My workaround for no webapp multitasking is turning off auto-lock and leaving the browser in the foreground while I exercise. The downside to this is it &lt;em&gt;really&lt;/em&gt; drains the battery quickly (~ 3 hours). I constantly have to charge my phone if I&apos;m testing it throughout the day. The testing is a real pain too. I have to deploy to Heroku (which is easy enough), then go on a walk or bike ride. If something&apos;s broke, I have to return home, tweak some things, redeploy and go again. Also, there&apos;s been a few times where Safari crashes halfway through and I lose all the tracking data. This happens with native apps too, but seemingly not as often.  
&lt;/p&gt;
&lt;p&gt;
  If you&apos;d like to try the app on your mobile phone and see if you experience these issues, checkout &lt;a href=&quot;http://play-more.com&quot;&gt;play-more.com&lt;/a&gt;.
&lt;/p&gt;
&lt;p id=&quot;summary&quot;&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;br/&gt;Going forward, there&apos;s still more HTML5 features I&apos;d like to use. In particular, I&apos;d like to play music while the fitness tracker is running. I&apos;d love it if cloud music services (e.g. Pandora or Spotify) had an API I could use to play music in a webapp. &lt;a href=&quot;http://soundcloud.com/&quot;&gt;Soundcloud&lt;/a&gt; might be an option, but I&apos;ve also thought of just uploading some MP3s and playing them with the &amp;lt;audio&amp;gt; tag. 
&lt;/p&gt;
&lt;p&gt;I&apos;ve really enjoyed developing with all these technologies and haven&apos;t experienced much frustration so far. The majority has come from integrating Scalate into Play, but I&apos;ve resolved most problems. Next, I&apos;ll talk about how I&apos;ve improved Play&apos;s Scalate support and my experience working with &lt;a href=&quot;http://scala.playframework.org/documentation/scala-0.9.1/anorm&quot;&gt;Anorm&lt;/a&gt;. 
</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/integrating_html5_boilerplate_with_scalate</id>
        <title type="html">Integrating HTML5 Boilerplate with Scalate and Play</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/integrating_html5_boilerplate_with_scalate"/>
        <published>2011-09-28T08:49:35-06:00</published>
        <updated>2012-06-25T22:46:30-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="scalate" scheme="http://roller.apache.org/ns/tags/" />
        <category term="devoxx2011" scheme="http://roller.apache.org/ns/tags/" />
        <category term="html5boilerplate" scheme="http://roller.apache.org/ns/tags/" />
        <category term="coffeescript" scheme="http://roller.apache.org/ns/tags/" />
        <category term="play-more" scheme="http://roller.apache.org/ns/tags/" />
        <category term="playframework" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">HTML5 Boilerplate is a project that provides a number of basic files to help you build an HTML5 application. At its core, it&apos;s an HTML template that puts CSS at the top, JavaScript at the bottom, installs Chrome Frame for IE6 users and leverages Modernizr for legacy browser support. It also includes jQuery with the download. One of the major benefits of HTML5 Boilerplate is it ships with a build system (powered by Ant) that concatenates and minimizes CSS and JS for maximum performance. From &lt;a href=&quot;http://html5boilerplate.com/&quot;&gt;html5boilerplate.com&lt;/a&gt;:
&lt;/p&gt;
&lt;p class=&quot;smokey&quot;&gt;
Boilerplate is not a framework, nor does it prescribe any philosophy of development, it&apos;s just got some tricks to get your project off the ground quickly and right-footed.
&lt;/p&gt;
&lt;p&gt;
&lt;p&gt;
I like the idea of its build system to minify and gzip, but I&apos;d probably only use it if I was working on a project that uses Ant. Since I&apos;m using it in a Play project, the whole Ant build system doesn&apos;t help me. Besides, I prefer something like &lt;a href=&quot;http://code.google.com/p/wro4j/&quot;&gt;wro4j&lt;/a&gt;. Wro4j allows you to specify a group of files and then it compiles, minimizes and gzips them all on-the-fly. As far as I know, Play doesn&apos;t have any support for Servlet Filters, so using wro4j in Play is not trivial.
&lt;/p&gt;
&lt;p&gt;
The good news is Play has a &lt;a href=&quot;http://www.playframework.org/modules/greenscript&quot;&gt;GreenScript module&lt;/a&gt; that contains much of the wro4j functionality. However, since I&apos;m using &lt;a href=&quot;http://scalate.fusesource.org/presentations/scalate#(1)&quot;&gt;Scalate&lt;/a&gt; in my project, this goodness is unavailable to me. In the future, the Scalate Team is considering adding &lt;a href=&quot;http://groups.google.com/group/scalate/browse_thread/thread/2904bbdc8ca9dd46&quot;&gt;better wro4j, JavaScript and CSS integration&lt;/a&gt;. In the meantime, I&apos;m going to pretend I don&apos;t care about concatenation and minimization and trundle along without this feature.
&lt;/p&gt;
&lt;p&gt;To add HTML5 Boilerplate to my Play project, I performed the following steps:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Downloaded the &lt;a href=&quot;http://github.com/paulirish/html5-boilerplate/zipball/v2.0&quot;&gt;2.0 Zipball&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Copied all the static files to my project. Below are the commands I used (where &lt;em&gt;$boilerplate-download&lt;/em&gt; is the expanded download directory and &lt;em&gt;~/dev/play-more&lt;/em&gt; is my project):
&lt;pre style=&quot;margin-top: 5px&quot;&gt;
cd $boilerplate-download
cp 404.html ~/dev/play-more/app/views/errors/404.html
cp *.png ~/dev/play-more/public/.
cp crossdomain.xml ~/dev/play-more/public/.
cp -r css ~/dev/play-more/public/stylesheets/.
cp favicon.ico ~/dev/play-more/public/.
cp humans.txt ~/dev/play-more/public/.
cp -r js/libs ~/dev/play-more/public/javascripts/.
cp robots.txt ~/dev/play-more/public/.
&lt;/pre&gt;
  &lt;/li&gt;
  &lt;li&gt;Copied the index.html to &lt;em&gt;~/dev/play-more/app/templates/layouts/default.jade&lt;/em&gt; and modified it to use &lt;a href=&quot;http://scalate.fusesource.org/documentation/jade.html&quot;&gt;Jade syntax&lt;/a&gt;. Since I downloaded the comments-heavy version, I modified many of them to be hidden in the final output.
&lt;pre class=&quot;brush: xml&quot;&gt;
-@ val body: String 
-@ var title: String = &quot;Play More&quot;
-@ var header: String = &quot;&quot;
-@ var footer: String = &quot;&quot;
!!! 5
/ paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/ 
&amp;lt;!--&amp;#91;if lt IE 7&amp;#93;&amp;gt; &amp;lt;html class=&quot;no-js ie6 oldie&quot; lang=&quot;en&quot;&amp;gt; &amp;lt;!&amp;#91;endif&amp;#93;--&amp;gt;
&amp;lt;!--&amp;#91;if IE 7&amp;#93;&amp;gt;    &amp;lt;html class=&quot;no-js ie7 oldie&quot; lang=&quot;en&quot;&amp;gt; &amp;lt;!&amp;#91;endif&amp;#93;--&amp;gt;
&amp;lt;!--&amp;#91;if IE 8&amp;#93;&amp;gt;    &amp;lt;html class=&quot;no-js ie8 oldie&quot; lang=&quot;en&quot;&amp;gt; &amp;lt;!&amp;#91;endif&amp;#93;--&amp;gt;
-# Consider adding an manifest.appcache: h5bp.com/d/Offline 
&amp;lt;!--&amp;#91;if gt IE 8&amp;#93;&amp;gt;&amp;lt;!--&amp;gt; &amp;lt;html class=&quot;no-js&quot; lang=&quot;en&quot;&amp;gt; &amp;lt;!--&amp;lt;!&amp;#91;endif&amp;#93;--&amp;gt;
head
  meta(charset=&quot;utf-8&quot;)

  -# Use the .htaccess and remove these lines to avoid edge case issues. More info: h5bp.com/b/378 
  meta(http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge,chrome=1&quot;)

  title=title
  meta(name=&quot;description&quot; content=&quot;&quot;)
  meta(name=&quot;author&quot; content=&quot;Matt Raible ~ matt@raibledesigns.com&quot;)

  -# Mobile viewport optimized: j.mp/bplateviewport 
  meta(name=&quot;viewport&quot; content=&quot;width=device-width,initial-scale=1&quot;)

  -# Place favicon.ico and apple-touch-icon.png in the root directory: mathiasbynens.be/notes/touch-icons

  -# CSS: implied media=all
  link(rel=&quot;stylesheet&quot; href={uri(&quot;/public/stylesheets/style.css&quot;)})
  -# end CSS

  -# More ideas for your &amp;lt;head&amp;gt; here: h5bp.com/d/head-Tips 
  -#
    All JavaScript at the bottom, except for Modernizr / Respond.
    Modernizr enables HTML5 elements &amp;amp; feature detects; Respond is a polyfill for min/max-width CSS3 Media Queries
    For optimal performance, use a custom Modernizr build: www.modernizr.com/download/ 

  script(type=&quot;text/javascript&quot; src={uri(&quot;/public/javascripts/libs/modernizr-2.0.6.min.js&quot;)})
body
  #container
    header = header
    #main(role=&quot;main&quot;)
      != body
    footer = footer

  -# JavaScript at the bottom for fast page loading 
  
  / Grab Google CDN&apos;s jQuery, with a protocol relative URL; fall back to local if offline 
  script(type=&quot;text/javascript&quot; src=&quot;//ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js&quot;)
  :javascript
    window.jQuery || document.write(&apos;&amp;lt;script src={uri(&quot;/public/javascripts/libs/jquery-1.6.2.min.js&quot;)}&amp;gt;&amp;lt;\/script&amp;gt;&apos;)

  -# Change UA-XXXXX-X to be your site&apos;s ID 
  :javascript
    window._gaq = &amp;#91;&amp;#91;&apos;_setAccount&apos;,&apos;UA-25859875-1&apos;&amp;#93;,&amp;#91;&apos;_trackPageview&apos;&amp;#93;,&amp;#91;&apos;_trackPageLoadTime&apos;&amp;#93;&amp;#93;;
    Modernizr.load({
      load: (&apos;https:&apos; == location.protocol ? &apos;//ssl&apos; : &apos;//www&apos;) + &apos;.google-analytics.com/ga.js&apos;
    });

  -# Prompt IE 6 users to install Chrome Frame. Remove this if you want to support IE 6. 
  -# http://chromium.org/developers/how-tos/chrome-frame-getting-started 
  /&amp;#91;if lt IE 7&amp;#93;
    script(src=&quot;//ajax.googleapis.com/ajax/libs/chrome-frame/1.0.3/CFInstall.min.js&quot;)
    :javascript
      window.attachEvent(&apos;onload&apos;,function(){CFInstall.check({mode:&apos;overlay&apos;})})
        
!= &quot;&amp;lt;/html&amp;gt;&quot;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Next, I had to add support for layouts to my &lt;a href=&quot;http://raibledesigns.com/rd/entry/integrating_scalate_and_jade_with&quot;&gt;homegrown Scalate support&lt;/a&gt;. I did this by specifying a &lt;code&gt;layoutStrategy&lt;/code&gt; when initializing the TemplateEngine. From &lt;em&gt;play-more/app/controllers/ScalateTemplate.scala&lt;/em&gt;:
&lt;pre class=&quot;brush: scala&quot;&gt;
engine.classLoader = Play.classloader
engine.layoutStrategy = new DefaultLayoutStrategy(engine, 
  Play.getFile(&quot;/app/templates/layouts/default&quot; + scalateType).getAbsolutePath)
engine
&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That&apos;s it! Now I have HTML5 Boilerplate integrated into my Play/Scalate/Jade application. To set the title and header in my &lt;em&gt;index.jade&lt;/em&gt;, I simply added the following lines at the top:&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
- attributes(&quot;title&quot;) = &quot;Counting&quot;
- attributes(&quot;header&quot;) = &quot;HTML5 Rocks!&quot;
&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;CoffeeScript Tip&lt;/strong&gt;&lt;br/&gt;
Yesterday, I mentioned that I was &lt;a href=&quot;http://raibledesigns.com/rd/entry/trying_to_make_coffeescript_work&quot;&gt;having issues getting CoffeeScript to work with Scalate&lt;/a&gt; and that I was going to try and get the in-browser compiler working. First of all, reverting to Scalate 1.4.1 didn&apos;t work because there is no CoffeeScript support in 1.4.1. So I stayed with 1.5.2 and used PandaWood&apos;s &lt;a href=&quot;http://forgivingworm.wordpress.com/2010/09/27/running-coffeescript-in-browser/&quot;&gt;Running CoffeeScript In-Browser Tutorial&lt;/a&gt;. I copied &lt;a href=&quot;http://github.com/jashkenas/coffee-script/raw/master/extras/coffee-script.js&quot;&gt;coffee-script.js&lt;/a&gt; to &lt;em&gt;~/dev/play-more/public/javascripts/libs&lt;/em&gt; and added a reference to it in my default.jade layout:
&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
-# JavaScript at the bottom for fast page loading 
script(type=&quot;text/javascript&quot; src={uri(&quot;/public/javascripts/libs/coffee-script.js&quot;)})
&lt;/pre&gt;
&lt;p&gt;Then I was able to write CoffeeScript in a .jade template using the following syntax:&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
:plain
  &amp;lt;script type=&quot;text/coffeescript&quot;&amp;gt;
    alert &quot;hello world&quot;
  &amp;lt;/script&amp;gt;
&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;br/&gt;
If you&apos;ve integrated HTML5 Boilerplate into your Play application, I&apos;d love to hear about it. Now that I have all the infrastructure in place (Jade, CoffeeScript, HTML5 Boilerplate), I&apos;m looking forward to getting some development done. Who knows, maybe I&apos;ll even come up with my own &lt;a href=&quot;http://www.zeroturnaround.com/blog/play-framework-unfeatures-that-irk-my-inner-geek/&quot;&gt;Play Un-Features That Really Irk My Inner Geek&lt;/a&gt;.</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/integrating_oauth_with_appfuse_and</id>
        <title type="html">Integrating OAuth with AppFuse and its REST API</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/integrating_oauth_with_appfuse_and"/>
        <published>2011-07-05T10:56:48-06:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="appfuse" scheme="http://roller.apache.org/ns/tags/" />
        <category term="springsecurity" scheme="http://roller.apache.org/ns/tags/" />
        <category term="rest" scheme="http://roller.apache.org/ns/tags/" />
        <category term="oauth" scheme="http://roller.apache.org/ns/tags/" />
        <category term="enunciate" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">One of the new features in &lt;a href=&quot;http://raibledesigns.com/rd/entry/appfuse_2_1_released&quot;&gt;AppFuse 2.1&lt;/a&gt; is an &lt;a href=&quot;http://issues.appfuse.org/browse/APF-897&quot;&gt;appfuse-ws&lt;/a&gt; archetype. This archetype leverages &lt;a href=&quot;http://enunciate.codehaus.org/&quot;&gt;Enunciate&lt;/a&gt; and &lt;a href=&quot;http://cxf.apache.org/&quot;&gt;CXF&lt;/a&gt; to create a project with a REST API and generated HTML documentation. Enunciate is a very useful tool, allowing you to develop web services with JAX-RS and JAX-WS annotations and have all types of client libraries generated. For me, it seems very useful for developing the backend of &lt;abbr title=&quot;Service Oriented Front End Applications&quot;&gt;SOFEA&lt;/abbr&gt; (a.k.a. modern) applications. &lt;/p&gt;
&lt;p&gt;Back in March, &lt;a href=&quot;http://www.java.net/blogs/stoicflame/&quot;&gt;Ryan Heaton&lt;/a&gt; published a nice article on &lt;a href=&quot;http://docs.codehaus.org/display/ENUNCIATE/Securing+Web+Services&quot;&gt;Securing Web Services&lt;/a&gt; in an Enunciate application. I decided to take his tutorial a step further and not only secure my web services, but also to integrate with  OAuth 2. In this tutorial, I&apos;ll show you how to create a new application with AppFuse WS, secure it, add OAuth support, and then use a client app to authenticate and retrieve data.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#create-appfuse-ws&quot;&gt;Create a New AppFuse WS Project&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#integrate-oauth&quot;&gt;Integrate Spring Security and OAuth&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#oauth-client&quot;&gt;Authenticate and Retrieve Data with Client&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;create-appfuse-ws&quot;&gt;
&lt;strong&gt;Create a New AppFuse WS Project&lt;/strong&gt;&lt;br/&gt;
To begin, I visited the &lt;a href=&quot;http://static.appfuse.org/archetypes.html&quot;&gt;Create AppFuse Archetypes&lt;/a&gt; page and created a new application using the &quot;Web Services Only&quot; option in the &lt;em&gt;Web Framework&lt;/em&gt; dropdown. Below is the command I used to create the &quot;appfuse-oauth&quot; project.
&lt;/p&gt;
&lt;pre&gt;
mvn archetype:generate -B -DarchetypeGroupId=org.appfuse.archetypes \
-DarchetypeArtifactId=appfuse-ws-archetype -DarchetypeVersion=2.1.0 \
-DgroupId=org.appfuse.example -DartifactId=appfuse-oauth 
&lt;/pre&gt;
&lt;p&gt;After doing this, I started the app using &lt;strong&gt;mvn jetty:run&lt;/strong&gt; and confirmed it started OK. At this point, I was able to view the generated documentation for the application at &lt;a href=&quot;http://localhost:8080&quot;&gt;http://localhost:8080&lt;/a&gt;. The screenshot below shows what the app looks like at this point.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://farm7.static.flickr.com/6004/5891597325_7f9829e158.jpg&quot; title=&quot;AppFuse WS Homepage&quot; rel=&quot;lightbox[appfuse-oauth]&quot;&gt;&lt;img src=&quot;//farm7.static.flickr.com/6004/5891597325_7f9829e158_m.jpg&quot; width=&quot;240&quot; height=&quot;198&quot; alt=&quot;AppFuse WS Homepage&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p style=&quot;font-weight: italic; color: #666; margin-left: 10px&quot;&gt;
NOTE: You might notice the REST endpoint of /{username}. This is &lt;a href=&quot;http://issues.appfuse.org/browse/APF-1246&quot; style=&quot;color: #666&quot;&gt;a bug&lt;/a&gt; in AppFuse 2.1.0 and has been fixed in SVN. It does not affect this tutorial.&lt;/p&gt;
&lt;p id=&quot;integrate-oauth&quot;&gt;
&lt;strong&gt;Integrate Spring Security and OAuth&lt;/strong&gt;&lt;br/&gt;
I originally tried to integrate Spring Security with Enunciate&apos;s &lt;a href=&quot;http://docs.codehaus.org/display/ENUNCIATE/Securing+Web+Services&quot;&gt;Securing Web Services Tutorial&lt;/a&gt;. However, it only secures endpoints and doesn&apos;t do enough filtering for OAuth support, so I ended up using a custom web.xml. I put this file in &lt;em&gt;src/main/resources&lt;/em&gt; and loaded it in my &lt;em&gt;enunciate.xml&lt;/em&gt; file. I also upgraded Spring Security and imported my security.xml file.
&lt;/p&gt;
&lt;pre class=&quot;brush: xml; auto-links: false&quot;&gt;
  &amp;lt;?xml version=&quot;1.0&quot;?&amp;gt;
  &amp;lt;enunciate xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
             xsi:noNamespaceSchemaLocation=&quot;http://enunciate.codehaus.org/schemas/enunciate-1.22.xsd&quot;&amp;gt;
      ...
      &amp;lt;webapp mergeWebXML=&quot;src/main/resources/web.xml&quot;/&amp;gt;
      &amp;lt;modules&amp;gt;
      ...
          &amp;lt;spring-app disabled=&quot;false&quot; springVersion=&quot;3.0.5.RELEASE&quot;&amp;gt;
              &amp;lt;springImport uri=&quot;classpath:/applicationContext-resources.xml&quot;/&amp;gt;
              &amp;lt;springImport uri=&quot;classpath:/applicationContext-dao.xml&quot;/&amp;gt;
              &amp;lt;springImport uri=&quot;classpath:/applicationContext-service.xml&quot;/&amp;gt;
              &amp;lt;springImport uri=&quot;classpath:/applicationContext.xml&quot;/&amp;gt;
              &amp;lt;springImport uri=&quot;classpath:/security.xml&quot;/&amp;gt;
          &amp;lt;/spring-app&amp;gt;
      &amp;lt;/modules&amp;gt;
  &amp;lt;/enunciate&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Then I created &lt;em&gt;src/main/resources/web.xml&lt;/em&gt; with a filter for Spring Security and a DispatcherServlet for OAuth support.
&lt;/p&gt;
&lt;pre class=&quot;brush: xml; auto-links: false&quot;&gt;
&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;web-app xmlns=&quot;http://java.sun.com/xml/ns/javaee&quot;
         xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
         xsi:schemaLocation=&quot;http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd&quot;
         version=&quot;3.0&quot;&amp;gt;

    &amp;lt;filter&amp;gt;
        &amp;lt;filter-name&amp;gt;securityFilter&amp;lt;/filter-name&amp;gt;
        &amp;lt;filter-class&amp;gt;org.springframework.web.filter.DelegatingFilterProxy&amp;lt;/filter-class&amp;gt;
        &amp;lt;init-param&amp;gt;
            &amp;lt;param-name&amp;gt;targetBeanName&amp;lt;/param-name&amp;gt;
            &amp;lt;param-value&amp;gt;springSecurityFilterChain&amp;lt;/param-value&amp;gt;
        &amp;lt;/init-param&amp;gt;
    &amp;lt;/filter&amp;gt;

    &amp;lt;filter-mapping&amp;gt;
        &amp;lt;filter-name&amp;gt;securityFilter&amp;lt;/filter-name&amp;gt;
        &amp;lt;url-pattern&amp;gt;/*&amp;lt;/url-pattern&amp;gt;
    &amp;lt;/filter-mapping&amp;gt;

    &amp;lt;servlet&amp;gt;
        &amp;lt;servlet-name&amp;gt;appfuse-oauth&amp;lt;/servlet-name&amp;gt;
        &amp;lt;servlet-class&amp;gt;org.springframework.web.servlet.DispatcherServlet&amp;lt;/servlet-class&amp;gt;
        &amp;lt;load-on-startup&amp;gt;1&amp;lt;/load-on-startup&amp;gt;
    &amp;lt;/servlet&amp;gt;

    &amp;lt;servlet-mapping&amp;gt;
        &amp;lt;servlet-name&amp;gt;appfuse-oauth&amp;lt;/servlet-name&amp;gt;
        &amp;lt;url-pattern&amp;gt;/oauth/*&amp;lt;/url-pattern&amp;gt;
    &amp;lt;/servlet-mapping&amp;gt;
&amp;lt;/web-app&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Next, I created a &lt;em&gt;src/main/resources/security.xml&lt;/em&gt; and used it to secure my API, specify a login page, supply the users and integrate OAuth (see the last 4 beans below). &lt;/p&gt;
&lt;pre class=&quot;brush: xml; auto-links: false&quot;&gt;
&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;beans:beans xmlns=&quot;http://www.springframework.org/schema/security&quot;
             xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
             xmlns:beans=&quot;http://www.springframework.org/schema/beans&quot;
             xmlns:oauth=&quot;http://www.springframework.org/schema/security/oauth2&quot;
             xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
                           http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2.xsd&quot;&amp;gt;

    &amp;lt;http auto-config=&quot;true&quot;&amp;gt;
        &amp;lt;intercept-url pattern=&quot;/api/**&quot; access=&quot;ROLE_USER&quot;/&amp;gt;
        &amp;lt;intercept-url pattern=&quot;/oauth/**&quot; access=&quot;ROLE_USER&quot;/&amp;gt;
        &amp;lt;intercept-url pattern=&quot;/**&quot; access=&quot;IS_AUTHENTICATED_ANONYMOUSLY&quot;/&amp;gt;
        &amp;lt;form-login login-page=&quot;/login.jsp&quot; authentication-failure-url=&quot;/login.jsp?error=true&quot;
                    login-processing-url=&quot;/j_security_check&quot;/&amp;gt;
    &amp;lt;/http&amp;gt;

    &amp;lt;authentication-manager&amp;gt;
        &amp;lt;authentication-provider&amp;gt;
            &amp;lt;user-service&amp;gt;
                &amp;lt;user name=&quot;admin&quot; password=&quot;admin&quot; authorities=&quot;ROLE_USER,ROLE_ADMIN&quot;/&amp;gt;
                &amp;lt;user name=&quot;user&quot; password=&quot;user&quot; authorities=&quot;ROLE_USER&quot;/&amp;gt;
            &amp;lt;/user-service&amp;gt;
        &amp;lt;/authentication-provider&amp;gt;
    &amp;lt;/authentication-manager&amp;gt;

    &amp;lt;!--hook up the spring security filter chain--&amp;gt;
    &amp;lt;beans:alias name=&quot;springSecurityFilterChain&quot; alias=&quot;securityFilter&quot;/&amp;gt;

    &amp;lt;beans:bean id=&quot;tokenServices&quot;
                class=&quot;org.springframework.security.oauth2.provider.token.InMemoryOAuth2ProviderTokenServices&quot;&amp;gt;
        &amp;lt;beans:property name=&quot;supportRefreshToken&quot; value=&quot;true&quot;/&amp;gt;
    &amp;lt;/beans:bean&amp;gt;

    &amp;lt;oauth:provider client-details-service-ref=&quot;clientDetails&quot; token-services-ref=&quot;tokenServices&quot;&amp;gt;
        &amp;lt;oauth:verification-code user-approval-page=&quot;/oauth/confirm_access&quot;/&amp;gt;
    &amp;lt;/oauth:provider&amp;gt;

    &amp;lt;oauth:client-details-service id=&quot;clientDetails&quot;&amp;gt;
        &amp;lt;!--&amp;lt;oauth:client clientId=&quot;my-trusted-client&quot; authorizedGrantTypes=&quot;password,authorization_code,refresh_token&quot;/&amp;gt;
        &amp;lt;oauth:client clientId=&quot;my-trusted-client-with-secret&quot;
                      authorizedGrantTypes=&quot;password,authorization_code,refresh_token&quot; secret=&quot;somesecret&quot;/&amp;gt;
        &amp;lt;oauth:client clientId=&quot;my-less-trusted-client&quot; authorizedGrantTypes=&quot;authorization_code&quot;/&amp;gt;--&amp;gt;
        &amp;lt;oauth:client clientId=&quot;ajax-login&quot; authorizedGrantTypes=&quot;authorization_code&quot;/&amp;gt;
    &amp;lt;/oauth:client-details-service&amp;gt;
&amp;lt;/beans:beans&amp;gt;
&lt;/pre&gt;
&lt;p&gt;I used the &lt;a href=&quot;http://static.springsource.org/spring-security/oauth/tutorial.html&quot;&gt;OAuth for Spring Security sample apps&lt;/a&gt; to figure this out. In this example, I used authorizedGrantTypes=&quot;authorization_code&quot;, but you can see from the commented &amp;lt;oauth:client&gt; elements above that there&apos;s a few different options. You should also note that the clientId is hard-coded to &quot;ajax-login&quot;, signifying I only want to allow a single application to authenticate.
&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;At this point, I&apos;d like to give a shoutout to Ryan Heaton for creating &lt;em&gt;both&lt;/em&gt; Enunciate and Spring Security&apos;s OAuth support. Nice work Ryan!&lt;/p&gt;
&lt;p&gt;At this point, I needed to do a number of additional tasks to finish integrating oauth. The first was to modify the Jetty Plugin&apos;s configuration to 1) run on port 9000, 2) load my custom files and 3) allow jetty:run to recognize Enunciate&apos;s generated files. Below is the final configuration in my pom.xml.
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;plugin&amp;gt;
    &amp;lt;groupId&amp;gt;org.mortbay.jetty&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;maven-jetty-plugin&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;6.1.26&amp;lt;/version&amp;gt;
    &amp;lt;configuration&amp;gt;
        &amp;lt;connectors&amp;gt;
            &amp;lt;connector implementation=&quot;org.mortbay.jetty.nio.SelectChannelConnector&quot;&amp;gt;
                &amp;lt;port&amp;gt;9000&amp;lt;/port&amp;gt;
                &amp;lt;maxIdleTime&amp;gt;60000&amp;lt;/maxIdleTime&amp;gt;
            &amp;lt;/connector&amp;gt;
        &amp;lt;/connectors&amp;gt;
        &amp;lt;webAppConfig&amp;gt;
            &amp;lt;baseResource implementation=&quot;org.mortbay.resource.ResourceCollection&quot;&amp;gt;
                &amp;lt;resourcesAsCSV&amp;gt;
                    ${basedir}/src/main/webapp,
                    ${project.build.directory}/${project.build.finalName}
                &amp;lt;/resourcesAsCSV&amp;gt;
            &amp;lt;/baseResource&amp;gt;
            &amp;lt;contextPath&amp;gt;/appfuse-oauth&amp;lt;/contextPath&amp;gt;
        &amp;lt;/webAppConfig&amp;gt;
        &amp;lt;webXml&amp;gt;${project.build.directory}/${project.build.finalName}/WEB-INF/web.xml&amp;lt;/webXml&amp;gt;
    &amp;lt;/configuration&amp;gt;
&amp;lt;/plugin&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Next, I added the necessary OAuth dependencies for Spring Security to my pom.xml. Since the latest release is a milestone release, I had to add Spring&apos;s milestone repo too. 
&lt;/p&gt;
&lt;pre class=&quot;brush: xml; auto-links: false&quot;&gt;
&amp;lt;repository&amp;gt;
    &amp;lt;id&amp;gt;spring-milestone&amp;lt;/id&amp;gt;
    &amp;lt;url&amp;gt;http://s3.amazonaws.com/maven.springframework.org/milestone&amp;lt;/url&amp;gt;
&amp;lt;/repository&amp;gt;
...
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.springframework.security&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;spring-security-taglibs&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;${spring.version}&amp;lt;/version&amp;gt;
    &amp;lt;exclusions&amp;gt;
        &amp;lt;exclusion&amp;gt;
            &amp;lt;groupId&amp;gt;org.springframework&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;spring-web&amp;lt;/artifactId&amp;gt;
        &amp;lt;/exclusion&amp;gt;
        &amp;lt;exclusion&amp;gt;
            &amp;lt;groupId&amp;gt;org.springframework&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;spring-support&amp;lt;/artifactId&amp;gt;
        &amp;lt;/exclusion&amp;gt;
    &amp;lt;/exclusions&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.springframework.security.oauth&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;spring-security-oauth&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;1.0.0.M3&amp;lt;/version&amp;gt;
    &amp;lt;exclusions&amp;gt;
        &amp;lt;exclusion&amp;gt;
            &amp;lt;groupId&amp;gt;org.springframework&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;spring-beans&amp;lt;/artifactId&amp;gt;
        &amp;lt;/exclusion&amp;gt;
        &amp;lt;exclusion&amp;gt;
            &amp;lt;groupId&amp;gt;org.springframework&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;spring-context&amp;lt;/artifactId&amp;gt;
        &amp;lt;/exclusion&amp;gt;
        &amp;lt;exclusion&amp;gt;
            &amp;lt;groupId&amp;gt;org.springframework&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;spring-core&amp;lt;/artifactId&amp;gt;
        &amp;lt;/exclusion&amp;gt;
    &amp;lt;/exclusions&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.springframework&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;spring-webmvc&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;${spring.version}&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;javax.servlet&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;servlet-api&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;2.5&amp;lt;/version&amp;gt;
    &amp;lt;scope&amp;gt;provided&amp;lt;/scope&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;javax.servlet&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;jstl&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;1.1.2&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;taglibs&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;standard&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;1.1.2&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Since I named my DispatcherServlet &quot;appfuse-oauth&quot; in web.xml, I created a &lt;em&gt;src/main/webapp/WEB-INF/appfuse-oauth-servlet.xml&lt;/em&gt; to configure Spring MVC. I had to create the &lt;em&gt;src/main/webapp/WEB-INF&lt;/em&gt; directory. &lt;/p&gt;
&lt;pre class=&quot;brush: xml; auto-links: false&quot;&gt;
&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
       xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
       xmlns:context=&quot;http://www.springframework.org/schema/context&quot;
       xmlns:mvc=&quot;http://www.springframework.org/schema/mvc&quot;
       xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
                http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd&quot;&amp;gt;

    &amp;lt;!-- Scans the classpath of this application for @Components to deploy as beans --&amp;gt;
    &amp;lt;context:component-scan base-package=&quot;org.appfuse.examples.webapp&quot;/&amp;gt;

    &amp;lt;!-- Configures the @Controller programming model --&amp;gt;
    &amp;lt;mvc:annotation-driven/&amp;gt;

    &amp;lt;!-- Resolves view names to protected .jsp resources within the /WEB-INF/views directory --&amp;gt;
    &amp;lt;bean class=&quot;org.springframework.web.servlet.view.InternalResourceViewResolver&quot;&amp;gt;
        &amp;lt;property name=&quot;viewClass&quot; value=&quot;org.springframework.web.servlet.view.JstlView&quot;/&amp;gt;
        &amp;lt;property name=&quot;prefix&quot; value=&quot;/&quot;/&amp;gt;
        &amp;lt;property name=&quot;suffix&quot; value=&quot;.jsp&quot;/&amp;gt;
    &amp;lt;/bean&amp;gt;
&amp;lt;/beans&amp;gt;
&lt;/pre&gt;
&lt;p&gt;In order to show the OAuth confirmation page, I needed to create &lt;em&gt;src/main/java/org/appfuse/examples/webapp/AccessConfirmationController.java&lt;/em&gt; and map it to /oauth/confirm_access. I copied this from one of the sample projects and modified to use Spring&apos;s annotations.
&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
package org.appfuse.examples.webapp;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.provider.ClientAuthenticationToken;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.verification.ClientAuthenticationCache;
import org.springframework.security.oauth2.provider.verification.DefaultClientAuthenticationCache;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.TreeMap;

/**
 * Controller for retrieving the model for and displaying the confirmation page
 * for access to a protected resource.
 *
 * @author Ryan Heaton
 */
@Controller
@RequestMapping(&quot;/confirm_access&quot;)
public class AccessConfirmationController {

    private ClientAuthenticationCache authenticationCache = new DefaultClientAuthenticationCache();
    @Autowired
    private ClientDetailsService clientDetailsService;

    @RequestMapping(method = RequestMethod.GET)
    protected ModelAndView confirm(HttpServletRequest request, HttpServletResponse response) throws Exception {
        ClientAuthenticationToken clientAuth = authenticationCache.getAuthentication(request, response);
        if (clientAuth == null) {
            throw new IllegalStateException(&quot;No client authentication request to authorize.&quot;);
        }

        TreeMap&amp;lt;String, Object&amp;gt; model = new TreeMap&amp;lt;String, Object&amp;gt;();
        ClientDetails client = clientDetailsService.loadClientByClientId(clientAuth.getClientId());
        model.put(&quot;auth_request&quot;, clientAuth);
        model.put(&quot;client&quot;, client);

        return new ModelAndView(&quot;access_confirmation&quot;, model);
    }
}
&lt;/pre&gt;
&lt;p&gt;This controller delegates to &lt;em&gt;src/main/webapp/access_confirmation.jsp&lt;/em&gt;. I created this file and filled it with code to display Accept and Deny buttons.
&lt;/p&gt;
&lt;pre class=&quot;brush: xml; auto-links: false&quot;&gt;
&amp;lt;%@ page import=&quot;org.springframework.security.core.AuthenticationException&quot; %&amp;gt;
&amp;lt;%@ page import=&quot;org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException&quot; %&amp;gt;
&amp;lt;%@ page import=&quot;org.springframework.security.oauth2.provider.verification.BasicUserApprovalFilter&quot; %&amp;gt;
&amp;lt;%@ page import=&quot;org.springframework.security.oauth2.provider.verification.VerificationCodeFilter&quot; %&amp;gt;
&amp;lt;%@ page import=&quot;org.springframework.security.web.WebAttributes&quot; %&amp;gt;
&amp;lt;%@ taglib prefix=&quot;authz&quot; uri=&quot;http://www.springframework.org/security/tags&quot; %&amp;gt;
&amp;lt;%@ taglib uri=&quot;http://java.sun.com/jsp/jstl/core&quot; prefix=&quot;c&quot; %&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Confirm Access&amp;lt;/title&amp;gt;
    &amp;lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; media=&quot;all&quot;
          href=&quot;http://demo.appfuse.org/appfuse-struts/styles/simplicity/theme.css&quot;/&amp;gt;
    &amp;lt;style type=&quot;text/css&quot;&amp;gt;
        h1 {
            margin-left: -300px;
            margin-top: 50px
        }
    &amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;

&amp;lt;body&amp;gt;

&amp;lt;h1&amp;gt;Confirm Access&amp;lt;/h1&amp;gt;

&amp;lt;div id=&quot;content&quot;&amp;gt;

    &amp;lt;% if (session.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION) != null &amp;amp;&amp;amp; 
                 !(session.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION) instanceof UnapprovedClientAuthenticationException)) { %&amp;gt;
    &amp;lt;div class=&quot;error&quot;&amp;gt;
        &amp;lt;h2&amp;gt;Woops!&amp;lt;/h2&amp;gt;

        &amp;lt;p&amp;gt;Access could not be granted.
            (&amp;lt;%= ((AuthenticationException) session.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION)).getMessage() %&amp;gt;)&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;% } %&amp;gt;
    &amp;lt;c:remove scope=&quot;session&quot; var=&quot;SPRING_SECURITY_LAST_EXCEPTION&quot;/&amp;gt;

    &amp;lt;authz:authorize ifAnyGranted=&quot;ROLE_USER,ROLE_ADMIN&quot;&amp;gt;
        &amp;lt;h2&amp;gt;Please Confirm&amp;lt;/h2&amp;gt;

        &amp;lt;p&amp;gt;You hereby authorize &quot;&amp;lt;c:out value=&quot;${client.clientId}&quot; escapeXml=&quot;true&quot;/&amp;gt;&quot; to access your protected resources.&amp;lt;/p&amp;gt;

        &amp;lt;form id=&quot;confirmationForm&quot; name=&quot;confirmationForm&quot;
              action=&quot;&amp;lt;%=request.getContextPath() + VerificationCodeFilter.DEFAULT_PROCESSING_URL%&amp;gt;&quot; method=&quot;POST&quot;&amp;gt;
            &amp;lt;input name=&quot;&amp;lt;%=BasicUserApprovalFilter.DEFAULT_APPROVAL_REQUEST_PARAMETER%&amp;gt;&quot;
                   value=&quot;&amp;lt;%=BasicUserApprovalFilter.DEFAULT_APPROVAL_PARAMETER_VALUE%&amp;gt;&quot; type=&quot;hidden&quot;/&amp;gt;
            &amp;lt;label&amp;gt;&amp;lt;input name=&quot;authorize&quot; value=&quot;Authorize&quot; type=&quot;submit&quot;&amp;gt;&amp;lt;/label&amp;gt;
        &amp;lt;/form&amp;gt;
        &amp;lt;form id=&quot;denialForm&quot; name=&quot;denialForm&quot;
              action=&quot;&amp;lt;%=request.getContextPath() + VerificationCodeFilter.DEFAULT_PROCESSING_URL%&amp;gt;&quot; method=&quot;POST&quot;&amp;gt;
            &amp;lt;input name=&quot;&amp;lt;%=BasicUserApprovalFilter.DEFAULT_APPROVAL_REQUEST_PARAMETER%&amp;gt;&quot;
                   value=&quot;not_&amp;lt;%=BasicUserApprovalFilter.DEFAULT_APPROVAL_PARAMETER_VALUE%&amp;gt;&quot; type=&quot;hidden&quot;/&amp;gt;
            &amp;lt;label&amp;gt;&amp;lt;input name=&quot;deny&quot; value=&quot;Deny&quot; type=&quot;submit&quot;&amp;gt;&amp;lt;/label&amp;gt;
        &amp;lt;/form&amp;gt;
    &amp;lt;/authz:authorize&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Finally, I needed to create &lt;em&gt;src/main/webapp/login.jsp&lt;/em&gt; to allow users to login.&lt;/p&gt;
&lt;pre class=&quot;brush: xml; auto-links: false&quot;&gt;
&amp;lt;%@ page language=&quot;java&quot; pageEncoding=&quot;UTF-8&quot; contentType=&quot;text/html;charset=utf-8&quot; %&amp;gt;
&amp;lt;%@ taglib uri=&quot;http://java.sun.com/jsp/jstl/core&quot; prefix=&quot;c&quot; %&amp;gt;
&amp;lt;%@ taglib uri=&quot;http://java.sun.com/jsp/jstl/fmt&quot; prefix=&quot;fmt&quot; %&amp;gt;
&amp;lt;%@ taglib uri=&quot;http://java.sun.com/jsp/jstl/core&quot; prefix=&quot;c&quot; %&amp;gt;

&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Login&amp;lt;/title&amp;gt;
    &amp;lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; media=&quot;all&quot;
          href=&quot;http://demo.appfuse.org/appfuse-struts/styles/simplicity/theme.css&quot;/&amp;gt;
    &amp;lt;style type=&quot;text/css&quot;&amp;gt;
        h1 {
            margin-left: -300px;
            margin-top: 50px
        }
    &amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;h1&amp;gt;Login&amp;lt;/h1&amp;gt;

&amp;lt;form method=&quot;post&quot; id=&quot;loginForm&quot; action=&quot;&amp;lt;c:url value=&apos;/j_security_check&apos;/&amp;gt;&quot;&amp;gt;
    &amp;lt;fieldset style=&quot;padding-bottom: 0&quot;&amp;gt;
        &amp;lt;ul&amp;gt;
            &amp;lt;c:if test=&quot;${param.error != null}&quot;&amp;gt;
                &amp;lt;li class=&quot;error&quot;&amp;gt;
                    ${sessionScope.SPRING_SECURITY_LAST_EXCEPTION.message}
                &amp;lt;/li&amp;gt;
            &amp;lt;/c:if&amp;gt;
            &amp;lt;li&amp;gt;
                &amp;lt;label for=&quot;j_username&quot; class=&quot;required desc&quot;&amp;gt;
                    Username &amp;lt;span class=&quot;req&quot;&amp;gt;*&amp;lt;/span&amp;gt;
                &amp;lt;/label&amp;gt;
                &amp;lt;input type=&quot;text&quot; class=&quot;text medium&quot; name=&quot;j_username&quot;
                       id=&quot;j_username&quot; tabindex=&quot;1&quot;/&amp;gt;
            &amp;lt;/li&amp;gt;

            &amp;lt;li&amp;gt;
                &amp;lt;label for=&quot;j_password&quot; class=&quot;required desc&quot;&amp;gt;
                    Password &amp;lt;span class=&quot;req&quot;&amp;gt;*&amp;lt;/span&amp;gt;
                &amp;lt;/label&amp;gt;
                &amp;lt;input type=&quot;password&quot; class=&quot;text medium&quot; name=&quot;j_password&quot;
                       id=&quot;j_password&quot; tabindex=&quot;2&quot;/&amp;gt;
            &amp;lt;/li&amp;gt;
            &amp;lt;li&amp;gt;
                &amp;lt;input type=&quot;submit&quot; class=&quot;button&quot; name=&quot;login&quot; value=&quot;Login&quot;
                       tabindex=&quot;3&quot;/&amp;gt;
            &amp;lt;/li&amp;gt;
        &amp;lt;/ul&amp;gt;
    &amp;lt;/fieldset&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;
&lt;p&gt;All the changes described in the above section are necessary to implement OAuth if you create a project with AppFuse WS 2.1. It may seem like a lot of code, but I was able to copy/paste and get it all working in an app in under 5 minutes. Hopefully you can do the same. I&apos;m also considering adding it by default to the next version of AppFuse. Now let&apos;s look at integrating OAuth into a client to authenticate and retrieve data from this application.
&lt;/p&gt;
&lt;p id=&quot;oauth-client&quot;&gt;&lt;strong&gt;Authenticate and Retrieve Data with Client&lt;/strong&gt;&lt;br/&gt;
I originally thought my &lt;a href=&quot;http://raibledesigns.com/rd/entry/implementing_oauth_with_gwt&quot;&gt;GWT OAuth&lt;/a&gt; application would provide a nice client. However, after 30 minutes of trying to get GWT 1.7.1 and the GWT Maven plugin (1.1) working with my 64-bit Java 6 JDK on OS X, I gave up. So I opted to use the &lt;a href=&quot;https://github.com/mraible/ajax-login&quot;&gt;Ajax Login&lt;/a&gt; application I&apos;ve been using in my recent security tutorials.
&lt;/p&gt;
&lt;p&gt;In this example, I used OAuth2RestTemplate from Spring Security OAuth. While this works, and works well, I&apos;d still like to get things working with GWT (or jQuery) to demonstrate how to do it from a pure client-side perspective.
&lt;/p&gt;
&lt;p&gt;To begin, I got the latest source of Ajax Login from GitHub (as of this morning) and made some changes. First of all, I added the Spring Security OAuth dependencies to pom.xml:
&lt;/p&gt;
&lt;pre class=&quot;brush: xml; auto-links: false&quot;&gt;
&amp;lt;repository&amp;gt;
    &amp;lt;id&amp;gt;spring-milestone&amp;lt;/id&amp;gt;
    &amp;lt;url&amp;gt;http://s3.amazonaws.com/maven.springframework.org/milestone&amp;lt;/url&amp;gt;
&amp;lt;/repository&amp;gt;
...
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.springframework.security.oauth&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;spring-security-oauth&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;1.0.0.M3&amp;lt;/version&amp;gt;
    &amp;lt;exclusions&amp;gt;
        &amp;lt;exclusion&amp;gt;
            &amp;lt;groupId&amp;gt;org.springframework&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;spring-beans&amp;lt;/artifactId&amp;gt;
        &amp;lt;/exclusion&amp;gt;
        &amp;lt;exclusion&amp;gt;
            &amp;lt;groupId&amp;gt;org.springframework&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;spring-context&amp;lt;/artifactId&amp;gt;
        &amp;lt;/exclusion&amp;gt;
        &amp;lt;exclusion&amp;gt;
            &amp;lt;groupId&amp;gt;org.springframework&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;spring-core&amp;lt;/artifactId&amp;gt;
        &amp;lt;/exclusion&amp;gt;
    &amp;lt;/exclusions&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Then I modified &lt;em&gt;src/main/webapp/WEB-INF/security.xml&lt;/em&gt; and added an OAuth Token Service and defined the location of the OAuth server.&lt;/p&gt; 
&lt;pre class=&quot;brush: xml; auto-links: false&quot;&gt;
&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;beans:beans xmlns=&quot;http://www.springframework.org/schema/security&quot;
             xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
             xmlns:beans=&quot;http://www.springframework.org/schema/beans&quot;
             xmlns:oauth=&quot;http://www.springframework.org/schema/security/oauth2&quot;
             xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
              http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd
              http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2.xsd&quot;&amp;gt;

...
    &amp;lt;oauth:client token-services-ref=&quot;oauth2TokenServices&quot;/&amp;gt;

    &amp;lt;beans:bean id=&quot;oauth2TokenServices&quot;
                class=&quot;org.springframework.security.oauth2.consumer.token.InMemoryOAuth2ClientTokenServices&quot;/&amp;gt;

    &amp;lt;oauth:resource id=&quot;appfuse&quot; type=&quot;authorization_code&quot; clientId=&quot;ajax-login&quot;
                    accessTokenUri=&quot;http://localhost:9000/appfuse-oauth/oauth/authorize&quot;
                    userAuthorizationUri=&quot;http://localhost:9000/appfuse-oauth/oauth/user/authorize&quot;/&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Next, I created a Controller that uses OAuth2RestTemplate to make the request and get the data from the AppFuse OAuth application&apos;s API. I created &lt;em&gt;src/main/java/org/appfuse/examples/webapp/oauth/UsersApiController.java&lt;/em&gt; and filled it with the following code:&lt;/p&gt;
&lt;pre class=&quot;brush: java; auto-links: false&quot;&gt;
package org.appfuse.examples.webapp.oauth;

import org.appfuse.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.consumer.*;
import org.springframework.security.oauth2.consumer.token.OAuth2ClientTokenServices;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.ArrayList;
import java.util.List;

@RequestMapping(&quot;/appfuse/users&quot;)
@Controller
public class UsersApiController {

    private OAuth2RestTemplate apiRestTemplate;
    @Autowired
    private OAuth2ClientTokenServices tokenServices;

    private static final String REMOTE_DATA_URL = &quot;http://localhost:9000/appfuse-oauth/api/users&quot;;

    @Autowired
    public UsersApiController(OAuth2ProtectedResourceDetails resourceDetails) {
        this.apiRestTemplate = new OAuth2RestTemplate(resourceDetails);
    }

    @RequestMapping(method = RequestMethod.GET)
    @ResponseBody
    public List&amp;lt;User&amp;gt; getUsers() {
        try {
            List users = apiRestTemplate.getForObject(REMOTE_DATA_URL, List.class);
            return new ArrayList&amp;lt;User&amp;gt;(users);
        } catch (InvalidTokenException badToken) {
            //we&apos;ve got a bad token, probably because it&apos;s expired.
            OAuth2ProtectedResourceDetails resource = apiRestTemplate.getResource();
            OAuth2SecurityContext context = OAuth2SecurityContextHolder.getContext();
            if (context != null) {
                // this one is kind of a hack for this application
                // the problem is that the sparklr photos page doesn&apos;t remove the &apos;code=&apos; request parameter.
                ((OAuth2SecurityContextImpl) context).setVerificationCode(null);
            }
            //clear any stored access tokens...
            tokenServices.removeToken(SecurityContextHolder.getContext().getAuthentication(), resource);
            //go get a new access token...
            throw new OAuth2AccessTokenRequiredException(resource);
        }
    }
}
&lt;/pre&gt;
&lt;p&gt;At this point, I thought everything would work and I spent quite some time banging my head against the wall when it didn&apos;t. As I was composing an email to the Enunciate users mailing list, I realized the issue. It appeared to be working, but from the server side, and the redirect back to the client was not happening. The Ajax Login app uses UrlRewriteFilter (for pretty URLs) to redirect from /app/* to /$1 and this redirect was losing the code parameter in the URL. 
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;rule&amp;gt;
    &amp;lt;from&amp;gt;/app/**&amp;lt;/from&amp;gt;
    &amp;lt;to last=&quot;true&quot; type=&quot;redirect&quot;&amp;gt;%{context-path}/$1&amp;lt;/to&amp;gt;
&amp;lt;/rule&amp;gt;
&lt;/pre&gt;
&lt;p&gt;To fix this, I added use-query-string=&quot;true&quot; to the root element in &lt;em&gt;src/main/webapp/WEB-INF/urlrewrite.xml&lt;/em&gt;:
&lt;pre class=&quot;brush: xml; toolbar: false&quot;&gt;
&amp;lt;urlrewrite default-match-type=&quot;wildcard&quot; use-query-string=&quot;true&quot;&gt;
&lt;/pre&gt;
&lt;p&gt;After making all these changes, I ran &lt;strong&gt;mvn jetty:run&lt;/strong&gt; on both apps and opened &lt;a href=&quot;http://localhost:8080/appfuse/users&quot;&gt;http://localhost:8080/appfuse/users&lt;/a&gt; in my browser. It all worked and a smile crept across my face. I&apos;ve checked in the client changes into &lt;a href=&quot;https://github.com/mraible/ajax-login&quot;&gt;ajax-login on GitHub&lt;/a&gt; and the appfuse-oauth example into &lt;a href=&quot;http://code.google.com/p/appfuse-demos/&quot;&gt;AppFuse Demos on Google Code&lt;/a&gt;. If you&apos;d like to see this example in action, I&apos;d encourage you to checkout both projects and let me know if you find any issues.</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/java_web_application_security_part3</id>
        <title type="html">Java Web Application Security - Part IV: Programmatic Login APIs</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/java_web_application_security_part3"/>
        <published>2011-06-06T21:44:09-06:00</published>
        <updated>2012-11-11T02:00:40-07:00</updated> 
        <category term="/Java" label="Java" />
        <category term="javaee" scheme="http://roller.apache.org/ns/tags/" />
        <category term="security" scheme="http://roller.apache.org/ns/tags/" />
        <category term="esapi" scheme="http://roller.apache.org/ns/tags/" />
        <category term="tutorial" scheme="http://roller.apache.org/ns/tags/" />
        <category term="apacheshiro" scheme="http://roller.apache.org/ns/tags/" />
        <category term="springsecurity" scheme="http://roller.apache.org/ns/tags/" />
        <category term="java" scheme="http://roller.apache.org/ns/tags/" />
        <category term="appsec" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">Over the last month, I&apos;ve posted a number of articles on implementing authentication with &lt;a href=&quot;http://raibledesigns.com/rd/entry/java_web_application_security_part&quot;&gt;Java EE 6&lt;/a&gt;, &lt;a href=&quot;http://raibledesigns.com/rd/entry/java_web_application_security_part1&quot;&gt;Spring Security&lt;/a&gt; and &lt;a href=&quot;http://raibledesigns.com/rd/entry/java_web_application_security_part2&quot;&gt;Apache Shiro&lt;/a&gt;. One of the things I demonstrated in my live demos (at Utah&apos;s JUG Meetings) was programmatic authentication. I left this out of my screencasts and previous tutorials because I thought it&apos;d fit better in a comparison article.&lt;/p&gt;
&lt;p&gt;In this article, I&apos;d like to show you how you can programmatically login to an application using the aforementioned security frameworks. To do this, I&apos;ll be using my &lt;a href=&quot;https://github.com/mraible/ajax-login&quot;&gt;ajax-login&lt;/a&gt; application that I wrote for &lt;a href=&quot;http://raibledesigns.com/rd/entry/implementing_ajax_authentication_using_jquery&quot;&gt;Implementing Ajax Authentication using jQuery, Spring Security and HTTPS&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To begin, I implemented a LoginController as a Spring MVC Controller that returns JSON. 
&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
package org.appfuse.examples.webapp.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping(&quot;/api/login.json&quot;)
public class LoginController {

    @Autowired
    LoginService loginService;

    @RequestMapping(method = RequestMethod.GET)
    @ResponseBody
    public LoginStatus getStatus() {
        return loginService.getStatus();
    }

    @RequestMapping(method = RequestMethod.POST)
    @ResponseBody
    public LoginStatus login(@RequestParam(&quot;j_username&quot;) String username,
                             @RequestParam(&quot;j_password&quot;) String password) {

        return loginService.login(username, password);
    }
}
&lt;/pre&gt;
&lt;p&gt;
This controller delegates its logic to a LoginService interface.&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
package org.appfuse.examples.webapp.security;

public interface LoginService {

  LoginStatus getStatus();

  LoginStatus login(String username, String password);
}
&lt;/pre&gt;
&lt;p id=&quot;theclient&quot;&gt;&lt;strong&gt;The Client&lt;/strong&gt;&lt;br/&gt;
The client for this controller is the same as mentioned in my &lt;a href=&quot;http://raibledesigns.com/rd/entry/implementing_ajax_authentication_using_jquery&quot;&gt;previous article&lt;/a&gt;, but I&apos;ll post it again for your convenience. I used jQuery and jQuery UI to implement a dialog that opens the login page on the same page rather than redirecting to the login page. The &quot;#demo&quot; locator refers to a button in the page.
&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
var dialog = $(&apos;&amp;lt;div&gt;&amp;lt;/div&gt;&apos;);

$(document).ready(function() {
    $.get(&apos;/login?ajax=true&apos;, function(data) {
        dialog.html(data);
        dialog.dialog({
            autoOpen: false,
	       title: &apos;Authentication Required&apos;
        });
    });

    $(&apos;#demo&apos;).click(function() {
      dialog.dialog(&apos;open&apos;);
      // prevent the default action, e.g., following a link
      return false;
    });
});
&lt;/pre&gt;
&lt;p&gt;The login page then has the following JavaScript to add a click handler to the &quot;login&quot; button that submits the request securely to the LoginController.
&lt;/p&gt;
&lt;pre class=&quot;brush: js; auto-links: false&quot;&gt;
var getHost = function() {
    var port = (window.location.port == &quot;8080&quot;) ? &quot;:8443&quot; : &quot;&quot;;
    return ((secure) ? &apos;https://&apos; : &apos;http://&apos;) + window.location.hostname + port;
};

var loginFailed = function(data, status) {
    $(&quot;.error&quot;).remove();
    $(&apos;#username-label&apos;).before(&apos;&lt;div class=&quot;error&quot;&gt;Login failed, please try again.&lt;/div&gt;&apos;);
};

$(&quot;#login&quot;).live(&apos;click&apos;, function(e) {
    e.preventDefault();
    $.ajax({url: getHost() + &quot;${ctx}/api/login.json&quot;,
        type: &quot;POST&quot;,
        beforeSend: function(xhr) {
            xhr.withCredentials = true;
        },
        data: $(&quot;#loginForm&quot;).serialize(),
        success: function(data, status) {
            if (data.loggedIn) {
                // success
                dialog.dialog(&apos;close&apos;);
                location.href = getHost() + &apos;${ctx}/users&apos;;
            } else {
                loginFailed(data);
            }
        },
        error: loginFailed
    });
});
&lt;/pre&gt;
&lt;p&gt;The biggest secret to making this all work (the HTTP -&gt; HTTPS communication, which is considered cross-domain), is the &lt;a href=&quot;http://www.sitepen.com/blog/2008/07/22/windowname-transport/&quot;&gt;window.name Transport&lt;/a&gt; and the &lt;a href=&quot;http://friedcellcollective.net/outbreak/jsjquerywindownameplugin/&quot;&gt;jQuery plugin&lt;/a&gt; that implements it. To make this plugin work with Firefox 3.6, I had to implement a Filter that adds Access-Control headers. 
&lt;/p&gt;
&lt;pre class=&quot;brush: java; auto-links: false&quot;&gt;
public class OptionsHeadersFilter implements Filter {

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) res;

        response.setHeader(&quot;Access-Control-Allow-Origin&quot;, &quot;http://&quot; + req.getServerName());
        response.setHeader(&quot;Access-Control-Allow-Methods&quot;, &quot;GET,POST&quot;);
        response.setHeader(&quot;Access-Control-Max-Age&quot;, &quot;360&quot;);
        response.setHeader(&quot;Access-Control-Allow-Headers&quot;, &quot;x-requested-with&quot;);
        response.setHeader(&quot;Access-Control-Allow-Credentials&quot;, &quot;true&quot;);

        chain.doFilter(req, res);
    }

    public void init(FilterConfig filterConfig) {
    }

    public void destroy() {
    }
}
&lt;/pre&gt;
&lt;/p&gt;
&lt;p id=&quot;javaee6-loginservice&quot;&gt;&lt;strong&gt;Java EE 6 LoginService&lt;/strong&gt;&lt;br/&gt;
Java EE 6 has a few new methods in &lt;a href=&quot;http://download.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html&quot;&gt;HttpServletRequest&lt;/a&gt;:
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;authenticate(response)&lt;/li&gt;
&lt;li&gt;login(user, pass)&lt;/li&gt;
&lt;li&gt;logout()&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;In this example, I&apos;ll use the new &lt;em&gt;login(username, password)&lt;/em&gt; method. The hardest part about getting this working was finding the right Maven dependency. At first, I tried the one that seemed to make the most sense:
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;javax&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;javaee-web-api&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;6.0&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Unfortunately, this resulted in a &lt;a href=&quot;http://hi.baidu.com/8741659422/blog/item/8410bf16104aef44f3de32b5.html&quot;&gt;strange error&lt;/a&gt; that  means the dependency has the interfaces, but not the implementation classes. I ended up using GlassFish&apos;s dependency instead (thanks to &lt;a href=&quot;http://stackoverflow.com/questions/1979957/maven-dependency-for-servlet-3-0-api&quot;&gt;Stack Overflow&lt;/a&gt; for the tip).
&lt;/p&gt;&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.glassfish&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;javax.servlet&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;3.0&amp;lt;/version&amp;gt;
    &amp;lt;scope&amp;gt;provided&amp;lt;/scope&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Since Servlet 3.0 doesn&apos;t appear to be in Maven Central, I had to add the GlassFish Repository to my pom.xml&apos;s &amp;lt;repositories&amp;gt; element.
&lt;/p&gt;
&lt;pre class=&quot;brush: xml; auto-links: false&quot;&gt;
&amp;lt;repository&amp;gt;
    &amp;lt;id&amp;gt;glassfish-repo&amp;lt;/id&amp;gt;
    &amp;lt;url&amp;gt;http://download.java.net/maven/glassfish&amp;lt;/url&amp;gt;
&amp;lt;/repository&amp;gt;
&lt;/pre&gt;
&lt;p&gt;After that, it was easy to implement the LoginService interface with a JavaEELoginService class:&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
package org.appfuse.examples.webapp.security;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;

@Service(&quot;javaeeLoginService&quot;)
public class JavaEELoginService implements LoginService {
    private Log log = LogFactory.getLog(JavaEELoginService.class);

    @Autowired
    HttpServletRequest request;

    public LoginStatus getStatus() {
        if (request.getRemoteUser() != null) {
            return new LoginStatus(true, request.getRemoteUser());
        } else {
            return new LoginStatus(false, null);
        }
    }

    @Override
    public LoginStatus login(String username, String password) {
        try {
            if (request.getRemoteUser() == null) {
                request.login(username, password);
                log.debug(&quot;Login succeeded!&quot;);
            }
            return new LoginStatus(true, request.getRemoteUser());
        } catch (ServletException e) {
            e.printStackTrace();
            return new LoginStatus(false, null);
        }
    }
}
&lt;/pre&gt;
&lt;p&gt;I tried to use this with &quot;mvn jetty:run&quot; (with version 8.0.0.M2 of the jetty-maven-plugin), but I got the following error:&lt;/p&gt;
&lt;pre&gt;
javax.servlet.ServletException
        at org.eclipse.jetty.server.Request.login(Request.java:1927)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler.invoke(AutowireUtils.java:178)
        at $Proxy52.login(Unknown Source)
        at org.appfuse.examples.webapp.security.JavaEELoginService.login(JavaEELoginService.java:30)
&lt;/pre&gt;
&lt;p&gt;This lead me to believe that Servlet 3 is not quite implemented, so I tried it with Tomcat 7.0.8. To support SSL and container-managed authentication, I had to &lt;a href=&quot;http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html&quot;&gt;create a certificate keystore and uncomment the SSL Connector in $CATALINA_HOME/conf/server.xml&lt;/a&gt;. I also had to add an &quot;admin&quot; user with roles=&quot;ROLE_ADMIN&quot; to $CATALINA_HOME/conf/tomcat-users.xml.&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;user username=&quot;admin&quot; password=&quot;admin&quot; roles=&quot;ROLE_ADMIN&quot;/&gt;
&lt;/pre&gt;
&lt;p&gt;With Tomcat 7, I was able to login successfully, proven by the following logging.&lt;/p&gt;
&lt;pre&gt;
DEBUG - JavaEELoginService.login(31) | Login succeeded!
&lt;/pre&gt;
&lt;p&gt;However, in the UI, I still got a &quot;Login failed, please try again.&quot; message. Recalling that I had some issues with ports previous, I configured Apache to proxy the default http/https ports to 8080/8443 and tried again. This time it worked!&lt;/p&gt;
&lt;p id=&quot;springsecurity-loginservice&quot;&gt;&lt;strong&gt;Spring Security LoginService&lt;/strong&gt;&lt;br/&gt;
Spring Security offers a programmatic API and I was able to implement its LoginService as follows:&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
package org.appfuse.examples.webapp.security;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.appfuse.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

@Service(&quot;springLoginService&quot;)
public class SpringSecurityLoginService implements LoginService {
    private Log log = LogFactory.getLog(SpringSecurityLoginService.class);

    @Autowired(required = false)
    @Qualifier(&quot;authenticationManager&quot;)
    AuthenticationManager authenticationManager;

    public LoginStatus getStatus() {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        if (auth != null &amp;&amp; !auth.getName().equals(&quot;anonymousUser&quot;) &amp;&amp; auth.isAuthenticated()) {
            return new LoginStatus(true, auth.getName());
        } else {
            return new LoginStatus(false, null);
        }
    }

    public LoginStatus login(String username, String password) {
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
        User details = new User(username);
        token.setDetails(details);

        try {
            Authentication auth = authenticationManager.authenticate(token);
            log.debug(&quot;Login succeeded!&quot;);
            SecurityContextHolder.getContext().setAuthentication(auth);
            return new LoginStatus(auth.isAuthenticated(), auth.getName());
        } catch (BadCredentialsException e) {
            return new LoginStatus(false, null);
        }
    }
}
&lt;/pre&gt;
&lt;p&gt;I then modified the LoginService dependency in LoginController so this implementation would be used.
&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
@Autowired
@Qualifier(&quot;springLoginService&quot;)
LoginService loginService;
&lt;/pre&gt;
&lt;p&gt;Since Spring&apos;s API doesn&apos;t depend on Servlet 3, I tried it in Jetty using &quot;mvn jetty:run&quot;. Of course, I modified my web.xml accordingly for Spring Security before doing so. Interestingly enough, I found that the my SpringSecurityLoginService seemed to work:
&lt;/p&gt;
&lt;pre&gt;DEBUG - SpringSecurityLoginService.login(39) | Login succeeded!&lt;/pre&gt;
&lt;p&gt;But in the UI, the login failed with a &quot;Login failed, please try again.&quot; message. Using the standard ports with Apache in front of Jetty solved this issue.
&lt;p id=&quot;apacheshiro-loginservice&quot;&gt;&lt;strong&gt;Apache Shiro LoginService&lt;/strong&gt;&lt;br/&gt;
Apache Shiro is nice enough to offer a programmatic API as well. I was able to implement a ShiroLoginService as follows:&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
package org.appfuse.examples.webapp.security;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Service;

@Service(&quot;shiroLoginService&quot;)
public class ShiroLoginService implements LoginService {
    private Log log = LogFactory.getLog(ShiroLoginService.class);

    public LoginStatus getStatus() {
        Subject currentUser = SecurityUtils.getSubject();
        if (currentUser.isAuthenticated()) {
            return new LoginStatus(true, currentUser.getPrincipal().toString());
        } else {
            return new LoginStatus(false, null);
        }
    }

    public LoginStatus login(String username, String password) {
        if (!getStatus().isLoggedIn()) {
            UsernamePasswordToken token = new UsernamePasswordToken(username, password);
            Subject currentUser = SecurityUtils.getSubject();
            try {
                currentUser.login(token);
                log.debug(&quot;Login succeeded!&quot;);
                return new LoginStatus(currentUser.isAuthenticated(),
                        currentUser.getPrincipal().toString());
            } catch (AuthenticationException e) {
                return new LoginStatus(false, null);
            }

        } else {
            return getStatus();
        }
    }
}
&lt;/pre&gt;
&lt;p&gt;Then I modified the LoginService dependency in LoginController so this implementation would be used.
&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
@Autowired
@Qualifier(&quot;shiroLoginService&quot;)
LoginService loginService;
&lt;/pre&gt;&lt;p&gt;Next, I modified my web.xml for Apache Shiro and tried &quot;mvn jetty:run&quot;. Again, the login appeared to succeed (based on log messages) on the server, but failed in the UI. When using http://localhost instead of http://localhost:8080, everything worked fine.&lt;/p&gt;
&lt;p id=&quot;summary&quot;&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;br/&gt;
This article has shown you how you can programmatically login using Java EE 6, Spring Security and Apache Shiro. Before Java EE 6 (and Servlet 3), there was no API to programmatically login, so this is a welcome addition. The fact that my Ajax login example didn&apos;t work when ports differed is because of browsers&apos; &lt;a href=&quot;http://en.wikipedia.org/wiki/Same_origin_policy&quot;&gt;same origin policy&lt;/a&gt;, which specifies the ports have to be the same. Specifying no ports (the defaults), seems to be the loophole.
&lt;/p&gt;
&lt;p&gt;On a related note, I&apos;ve discovered some interesting articles recently from the &lt;a href=&quot;http://software-security.sans.org/blog&quot;&gt;AppSec Blog&lt;/a&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://software-security.sans.org/blog/2010/08/11/security-misconfigurations-java-webxml-files&quot;&gt;Seven Security (Mis)Configurations in Java web.xml Files&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://software-security.sans.org/blog/2011/06/06/safer-software-through-secure-frameworks&quot;&gt;Safer Software through Secure Frameworks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The 2nd article has an interesting paragraph:
&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;... there&apos;s &lt;a href=&quot;http://shiro.apache.org/&quot;&gt;Apache Shiro&lt;/a&gt; (FKA &lt;a href=&quot;http://www.jsecurity.org/&quot;&gt;JSecurity&lt;/a&gt; and then later as Apache &lt;a href=&quot;http://incubator.apache.org/ki/&quot;&gt;Ki&lt;/a&gt;), another secure framework for Java apps. Although it looks &lt;a href=&quot;http://shiro.apache.org/10-minute-tutorial.html&quot;&gt;simpler to use and understand&lt;/a&gt; than ESAPI and covers most of the main security bases (authentication, authorization, session management and encryption), it doesn&apos;t help take care of important functions like input validation and output encoding. And Spring users have &lt;a href=&quot;http://static.springsource.org/spring-security/site/&quot;&gt;Spring Security&lt;/a&gt; (Acegi) a comprehensive, but heavyweight authorization and authentication framework.
&lt;/p&gt;So according to this blog, the security frameworks discussed here aren&apos;t the best.&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
The most comprehensive, up-to-date choice for Java developers is OWASP&apos;s &lt;a href=&quot;https://www.owasp.org/index.php/Category:OWASP_Enterprise_Security_API&quot;&gt;ESAPI Enterprise Security API&lt;/a&gt; especially now that the 2.0 release has just come out. 
&lt;/p&gt;
&lt;p&gt;I haven&apos;t heard of many organizations adopting ESAPI over Java EE 6, Spring Security or Apache Shiro, but maybe I&apos;m wrong. Is ESAPI something that&apos;s being used out there by companies?
</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/java_web_application_security_part2</id>
        <title type="html">Java Web Application Security - Part III: Apache Shiro Login Demo</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/java_web_application_security_part2"/>
        <published>2011-05-26T16:43:22-06:00</published>
        <updated>2015-07-07T01:37:14-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="java" scheme="http://roller.apache.org/ns/tags/" />
        <category term="presentation" scheme="http://roller.apache.org/ns/tags/" />
        <category term="web" scheme="http://roller.apache.org/ns/tags/" />
        <category term="security" scheme="http://roller.apache.org/ns/tags/" />
        <category term="apacheshiro" scheme="http://roller.apache.org/ns/tags/" />
        <category term="ujug" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">A couple weeks ago, I wrote &lt;a href=&quot;http://raibledesigns.com/rd/entry/java_web_application_security_part1&quot;&gt;a tutorial on how to implement security with Spring Security&lt;/a&gt;. The week prior, I wrote a &lt;a href=&quot;http://raibledesigns.com/rd/entry/java_web_application_security_part&quot;&gt;similar tutorial for Java EE 6&lt;/a&gt;. This week, I&apos;d like to show you how to implement the same features using &lt;a href=&quot;http://shiro.apache.org&quot;&gt;Apache Shiro&lt;/a&gt;. As I mentioned in previous articles, I&apos;m writing this because I told the audience at April&apos;s &lt;a href=&quot;http://ujug.org&quot;&gt;UJUG&lt;/a&gt; that I would publish screencasts of the demos.
&lt;/p&gt;
&lt;p&gt;
Today, I&apos;ve finished the third screencast showing how to implement security with Apache Shiro. Below is the presentation (with the screencast embedded on slide 22) as well as a step-by-step tutorial.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;iframe src=&quot;//www.slideshare.net/slideshow/embed_code/key/o4wwswiZck6bKS&quot; width=&quot;510&quot; height=&quot;420&quot; frameborder=&quot;0&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; scrolling=&quot;no&quot; style=&quot;border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%;&quot; allowfullscreen&gt; &lt;/iframe&gt;
&lt;br/&gt;
&lt;div style=&quot;font-size: .9em; text-align: left&quot;&gt;* You can also &lt;a href=&quot;http://www.youtube.com/watch?v=YJByiDvOhsc&quot;&gt;watch the screencast on YouTube&lt;/a&gt; or &lt;a href=&quot;http://static.raibledesigns.com/repository/presentations/Java_Web_Application_Security_UJUG2011.pdf&quot;&gt;download the presentation PDF&lt;/a&gt;.&lt;/div&gt;
&lt;/p&gt;

&lt;p id=&quot;apacheshiro-login-tutorial&quot;&gt;&lt;strong&gt;Apache Shiro Login Tutorial&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#download-run&quot;&gt;Download and Run the Application&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#secure-basic&quot;&gt;Implement Basic Authentication&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#ssl&quot;&gt;Force SSL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#form-authentication&quot;&gt;Implement Form-based Authentication&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#jdbc&quot;&gt;Store Users in a Database&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#summary&quot;&gt;Summary&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;download-run&quot;&gt;&lt;strong&gt;Download and Run the Application&lt;/strong&gt;&lt;br/&gt;

To begin, &lt;a href=&quot;http://static.raibledesigns.com/downloads/ajax-login-apacheshiro-tutorial-1.0.zip&quot;&gt;download the application&lt;/a&gt; you&apos;ll be implementing security in. This app is a stripped-down version of the Ajax Login application I wrote for my article on &lt;a href=&quot;http://raibledesigns.com/rd/entry/implementing_ajax_authentication_using_jquery&quot;&gt;Implementing Ajax Authentication using jQuery, Spring Security and HTTPS&lt;/a&gt;. You&apos;ll need Java 6 and Maven installed to run the app. Run it using &lt;strong&gt;mvn jetty:run&lt;/strong&gt; and open &lt;a href=&quot;http://localhost:8080&quot;&gt;http://localhost:8080&lt;/a&gt; in your browser. You&apos;ll see it&apos;s a simple CRUD application for users and there&apos;s no login required to add or delete users.&lt;/p&gt;

&lt;p id=&quot;secure-basic&quot;&gt;&lt;strong&gt;Implement Basic Authentication&lt;/strong&gt;&lt;br/&gt;
  
The first step is to protect the list screen so people have to login to view users. To do this, you&apos;ll need to create a shiro.ini file Shiro&apos;s configuration. Create &lt;em&gt;src/main/resources/shiro.ini&lt;/em&gt; and populate it with the contents below:
&lt;/p&gt;
&lt;pre&gt;
[main]

[users]
admin = admin, ROLE_ADMIN

[roles]
ROLE_ADMIN = *

[urls]
/app/users = authcBasic
&lt;/pre&gt;
&lt;p&gt;You can see this file has four sections and is pretty simple to read and understand. For more information about what each section is for, check out &lt;a href=&quot;http://shiro.apache.org/configuration.html#Configuration-INISections&quot;&gt;Shiro&apos;s configuration documentation&lt;/a&gt;.
&lt;p&gt;Next, open &lt;em&gt;src/main/webapp/WEB-INF/web.xml&lt;/em&gt; and add Shiro&apos;s IniShiroFilter:&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;filter&amp;gt;
    &amp;lt;filter-name&amp;gt;securityFilter&amp;lt;/filter-name&amp;gt;
    &amp;lt;filter-class&amp;gt;org.apache.shiro.web.servlet.IniShiroFilter&amp;lt;/filter-class&amp;gt;
    &amp;lt;!-- no init-param means load the INI config from classpath:shiro.ini --&amp;gt;
&amp;lt;/filter&amp;gt;
&lt;/pre&gt;
&lt;p&gt;And add its filter-mapping just after the rewriteFilter in the filter-mappings section (order is important!):
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;filter-mapping&amp;gt;
    &amp;lt;filter-name&amp;gt;rewriteFilter&amp;lt;/filter-name&amp;gt;
    &amp;lt;url-pattern&amp;gt;/*&amp;lt;/url-pattern&amp;gt;
&amp;lt;/filter-mapping&amp;gt;
&amp;lt;filter-mapping&amp;gt;
    &amp;lt;filter-name&amp;gt;securityFilter&amp;lt;/filter-name&amp;gt;
    &amp;lt;url-pattern&amp;gt;/*&amp;lt;/url-pattern&amp;gt;
    &amp;lt;dispatcher&amp;gt;REQUEST&amp;lt;/dispatcher&amp;gt;
    &amp;lt;dispatcher&amp;gt;FORWARD&amp;lt;/dispatcher&amp;gt;
    &amp;lt;dispatcher&amp;gt;INCLUDE&amp;lt;/dispatcher&amp;gt;
&amp;lt;/filter-mapping&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
Then add Shiro&apos;s &lt;em&gt;core&lt;/em&gt; and &lt;em&gt;web&lt;/em&gt; dependencies to your pom.xml:
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.apache.shiro&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;shiro-core&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;1.1.0&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.apache.shiro&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;shiro-web&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;1.1.0&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
At this point, if you restart Jetty (Ctrl+C and jetty:run again), you should be prompted to login when you click on the &quot;Users&quot; tab. Enter admin/admin to login. Apache Shiro is easier to configure than Spring Security out-of-the-box, mostly because it doesn&apos;t require XML.
&lt;/p&gt;
&lt;p&gt;After logging in, you can try to logout by clicking the &quot;Logout&quot; link in the top-right corner. This calls a LogoutController with the following code that logs the user out.
&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
public void logout(HttpServletResponse response) throws ServletException, IOException {
    request.getSession().invalidate();
    response.sendRedirect(request.getContextPath()); 
}
&lt;/pre&gt; 
&lt;p class=&quot;quote&quot; style=&quot;font-style: italic; color: #666&quot;&gt;NOTE: Shiro doesn&apos;t currently have a way to logout with its API. However, &lt;a href=&quot;https://issues.apache.org/jira/browse/SHIRO-284&quot;&gt;it will be added in the 1.2 release&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You&apos;ll notice that clicking this link doesn&apos;t log you out, even though the session is invalidated. The only way to logout with basic authentication is to close the browser. In order to get the ability to logout, as well as to have more control over the look-and-feel of the login, you can implement form-based authentication.
  Before you implement form-based authentication, I&apos;d like to show you how easy it is to force SSL with Apache Shiro.
&lt;/p&gt;
&lt;p id=&quot;ssl&quot;&gt;&lt;strong&gt;Force SSL&lt;/strong&gt;&lt;br/&gt;
Apache Shiro allows you to force SSL on a URL by simply adding &quot;ssl[&lt;em&gt;port&lt;/em&gt;]&quot; to a URL in the [urls] section. If you don&apos;t specify the port, it will use the default port (443). I&apos;m not sure if it allows you to switch back to http like Spring Security&apos;s &lt;em&gt;requires-channel&lt;/em&gt;, but I don&apos;t think it does. Modify the URLs section of your &lt;em&gt;shiro.ini&lt;/em&gt; to have the following:
&lt;/p&gt;
&lt;pre&gt;[urls]
/app/users = ssl[8443],authc
&lt;/pre&gt;
&lt;p&gt;In order for this to work, you have to configure Jetty to listen on an SSL port. Add the following just after the jetty-maven-plugin&apos;s &amp;lt;/webAppConfig&amp;gt; element in your pom.xml:
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;connectors&amp;gt;
    &amp;lt;connector implementation=&quot;org.eclipse.jetty.server.nio.SelectChannelConnector&quot;&amp;gt;
        &amp;lt;forwarded&amp;gt;true&amp;lt;/forwarded&amp;gt;
        &amp;lt;port&amp;gt;8080&amp;lt;/port&amp;gt;
    &amp;lt;/connector&amp;gt;
    &amp;lt;connector implementation=&quot;org.eclipse.jetty.server.ssl.SslSelectChannelConnector&quot;&amp;gt;
        &amp;lt;forwarded&amp;gt;true&amp;lt;/forwarded&amp;gt;
        &amp;lt;port&amp;gt;8443&amp;lt;/port&amp;gt;
        &amp;lt;maxIdleTime&amp;gt;60000&amp;lt;/maxIdleTime&amp;gt;
        &amp;lt;keystore&amp;gt;${project.build.directory}/ssl.keystore&amp;lt;/keystore&amp;gt;
        &amp;lt;password&amp;gt;appfuse&amp;lt;/password&amp;gt;
        &amp;lt;keyPassword&amp;gt;appfuse&amp;lt;/keyPassword&amp;gt;
    &amp;lt;/connector&amp;gt;
&amp;lt;/connectors&amp;gt;
&lt;/pre&gt;
&lt;p&gt;The keystore must be generated for Jetty to start successfully, so add the keytool-maven-plugin just above the jetty-maven-plugin in pom.xml.
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;plugin&amp;gt;
    &amp;lt;groupId&amp;gt;org.codehaus.mojo&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;keytool-maven-plugin&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;1.0&amp;lt;/version&amp;gt;
    &amp;lt;executions&amp;gt;
        &amp;lt;execution&amp;gt;
            &amp;lt;phase&amp;gt;generate-resources&amp;lt;/phase&amp;gt;
            &amp;lt;id&amp;gt;clean&amp;lt;/id&amp;gt;
            &amp;lt;goals&amp;gt;
                &amp;lt;goal&amp;gt;clean&amp;lt;/goal&amp;gt;
            &amp;lt;/goals&amp;gt;
        &amp;lt;/execution&amp;gt;
        &amp;lt;execution&amp;gt;
            &amp;lt;phase&amp;gt;generate-resources&amp;lt;/phase&amp;gt;
            &amp;lt;id&amp;gt;genkey&amp;lt;/id&amp;gt;
            &amp;lt;goals&amp;gt;
                &amp;lt;goal&amp;gt;genkey&amp;lt;/goal&amp;gt;
            &amp;lt;/goals&amp;gt;
        &amp;lt;/execution&amp;gt;
    &amp;lt;/executions&amp;gt;
    &amp;lt;configuration&amp;gt;
        &amp;lt;keystore&amp;gt;${project.build.directory}/ssl.keystore&amp;lt;/keystore&amp;gt;
        &amp;lt;dname&amp;gt;cn=localhost&amp;lt;/dname&amp;gt;
        &amp;lt;keypass&amp;gt;appfuse&amp;lt;/keypass&amp;gt;
        &amp;lt;storepass&amp;gt;appfuse&amp;lt;/storepass&amp;gt;
        &amp;lt;alias&amp;gt;appfuse&amp;lt;/alias&amp;gt;
        &amp;lt;keyalg&amp;gt;RSA&amp;lt;/keyalg&amp;gt;
    &amp;lt;/configuration&amp;gt;
&amp;lt;/plugin&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Now if you restart Jetty, go to &lt;a href=&quot;http://localhost:8080&quot;&gt;http://localhost:8080&lt;/a&gt; and click on the &quot;Users&quot; tab, you&apos;ll be prompted to accept the Untrusted Certificate and then redirected to https://localhost:8443/users after logging in. 
&lt;/p&gt;
&lt;p&gt;Now let&apos;s look at how to have more control over the look-and-feel of the login screen, as well as how to make logout work with form-based authentication.&lt;/p&gt;
&lt;p id=&quot;form-authentication&quot;&gt;&lt;strong&gt;Implement Form-based Authentication&lt;/strong&gt;&lt;br/&gt;
To change from basic to form-based authentication, you simply have to add a few lines to shiro.ini. First of all, since I&apos;d rather not change the name of the input elements in login.jsp, override the default names in the [main] section:
&lt;/p&gt;
&lt;pre&gt;
# name of request parameter with username; if not present filter assumes &apos;username&apos;
authc.usernameParam = j_username
# name of request parameter with password; if not present filter assumes &apos;password&apos;
authc.passwordParam = j_password
authc.failureKeyAttribute = shiroLoginFailure
&lt;/pre&gt;
&lt;p&gt;Then change the [urls] section to filter on login.jsp and use &quot;authc&quot; instead of &quot;authcBasic&quot;:
&lt;pre&gt;[urls]
# The /login.jsp is not restricted to authenticated users (otherwise no one could log in!), but
# the &apos;authc&apos; filter must still be specified for it so it can process that url&apos;s
# login submissions. It is &apos;smart&apos; enough to allow those requests through as specified by the
# shiro.loginUrl above.
/login.jsp = authc
/app/users = ssl[8443],authc
&lt;/pre&gt;
&lt;p&gt;Then change login.jsp so the form&apos;s action is blank (causing it to submit to itself) instead of j_security_check:&lt;/p&gt;
&lt;pre class=&quot;brush: xml; toolbar: false&quot;&gt;
&amp;lt;form action=&quot;&quot; id=&quot;loginForm&quot; method=&quot;post&quot;&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Now, restart Jetty and you should be prompted to login with this JSP instead of the basic authentication dialog.
&lt;/p&gt;
&lt;p id=&quot;jdbc&quot;&gt;&lt;strong&gt;Store Users in a Database&lt;/strong&gt;&lt;br/&gt;
To store your users in a database instead of file, you&apos;ll need to add a few settings to shiro.ini to define your database and tables to use. Open &lt;em&gt;src/main/resources/shiro.ini&lt;/em&gt; and add the following lines under the [main] section.
&lt;/p&gt;
&lt;pre&gt;
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
#jdbcRealm.permissionsLookupEnabled=false
# If not filled, subclasses of JdbcRealm assume &quot;select password from users where username = ?&quot;
jdbcRealm.authenticationQuery = select user_pass from users where user_name = ?
# If not filled, subclasses of JdbcRealm assume &quot;select role_name from user_roles where username = ?&quot;
jdbcRealm.userRolesQuery = select role_name from users_roles where user_name = ?

ds = com.mysql.jdbc.jdbc2.optional.MysqlDataSource
ds.serverName = localhost
ds.user = root
ds.databaseName = appfuse
jdbcRealm.dataSource = $ds
&lt;/pre&gt;
&lt;p class=&quot;quote&quot; style=&quot;margin-left: 0; color: #666&quot;&gt;This configuration is similar to what I did with the Java EE 6 tutorial where I&apos;m pointing to a database other than the H2 instance that&apos;s used by the application. I believe Shiro can talk to a DAO like Spring Security, but I have yet to explore that option.
&lt;/p&gt;
&lt;p&gt;While you&apos;re at it, add the following lines to enable password encryption.&lt;/p&gt;
&lt;pre&gt;
sha256Matcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher
jdbcRealm.credentialsMatcher = $sha256Matcher
&lt;/pre&gt;
&lt;/p&gt;
&lt;p&gt;You&apos;ll need to &lt;a href=&quot;http://dev.mysql.com/downloads/mysql/5.5.html&quot;&gt;install MySQL&lt;/a&gt; for this to work. After installing it, you should be able to create an &quot;appfuse&quot; database using the following command:
&lt;/p&gt;
&lt;pre&gt;
mysql -u root -p -e &apos;create database appfuse&apos;
&lt;/pre&gt;
&lt;p&gt;Then create the tables necessary and populate it with an &apos;admin&apos; user. Login using &quot;mysql -u root -p appfuse&quot; and execute the following SQL statements:&lt;/p&gt;
&lt;pre&gt;
create table users (
  user_name         varchar(30) not null primary key,
  user_pass         varchar(100) not null
);

create table user_roles (
  user_name         varchar(30) not null,
  role_name         varchar(30) not null,
  primary key (user_name, role_name)
);

insert into users values (&apos;admin&apos;, &apos;22f256eca1f336a97eef2b260773cb0d81d900c208ff26e94410d292d605fed8&apos;);
insert into user_roles values (&apos;admin&apos;, &apos;ROLE_ADMIN&apos;);
&lt;/pre&gt;
&lt;p&gt;Now if you restart Jetty, you should be able to login with admin/adminjdbc and view the list of users.
&lt;/p&gt;
&lt;p id=&quot;summary&quot;&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;br/&gt;
In this tutorial, you learned how to implement authentication using Apache Shiro 1.1.0. I don&apos;t have a lot of experience with Apache Shiro, but I was able to get the basics working without too much effort. This tutorial doesn&apos;t show how to do Remember Me because I couldn&apos;t figure it out in 5 minutes, which means I have 5 more minutes before it fails the 10-minute test. &lt;img src=&quot;https://raibledesigns.com/images/smileys/wink.gif&quot; class=&quot;smiley&quot; alt=&quot;;)&quot; title=&quot;;)&quot; /&gt;
&lt;/p&gt;
&lt;p&gt;
Shiro was formerly named &lt;a href=&quot;http://www.jsecurity.org/&quot;&gt;JSecurity&lt;/a&gt; and has been an Apache project for less than a year. It seems to be more targeted towards non-web use, so its certainly something to look at if you&apos;re more interested in cryptography or non-web apps. I think there&apos;s a good chance this project will continue to grow and be used more as more developers learn about it. The Apache brand certainly doesn&apos;t hurt.
&lt;/p&gt;
&lt;p&gt;
I didn&apos;t include a slide about the limitations I found with Shiro, mostly because I haven&apos;t used it much. I&apos;ve used Java EE and Spring Security for several years. The main limitation I found was the lack of documentation, but I&apos;ve heard it&apos;s improving rapidly.
&lt;/p&gt;
  &lt;p&gt;In the next couple weeks, I&apos;ll post a &lt;a href=&quot;http://raibledesigns.com/rd/entry/java_web_application_security_part3&quot;&gt;Part IV&lt;/a&gt; on implementing programmatic login using the APIs of Java EE 6, Spring Security and Apache Shiro. I&apos;ll be presenting this topic at &lt;a href=&quot;http://jazoon.com&quot;&gt;Jazoon&lt;/a&gt; as well as the long-form version (with hacking) at &lt;a href=&quot;http://uberconf.com/conference/denver/2011/07/home&quot;&gt;&#220;berConf&lt;/a&gt;. Hopefully I&apos;ll see you at one of those conferences.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; Thanks to help from &lt;a href=&quot;http://leshazlewood.com&quot;&gt;Les Hazlewood&lt;/a&gt;, I&apos;ve figured out how to implement Remember Me with Apache Shiro. In the [urls] section of shiro.ini, the second url (shown below) says to Shiro &quot;In order to visit the /app/users URL, you must be connecting via SSL on port 8443 and you must also be authenticated.&quot;
&lt;/p&gt;
&lt;pre&gt;
/app/users = ssl[8443],authc
&lt;/pre&gt;
&lt;p&gt;
Remembered users are not authenticated because their identity hasn&apos;t been proven during the current session.  What I want Shiro to say is &quot;In order to visit the /app/users URL, you must be connecting via SSL on 8443 and you must also be a known user.  If you&apos;re not, you should login first.&quot; Where a &lt;em&gt;known user&lt;/em&gt; is someone who has a recognized identity and has either authenticated during the current session or is known via RememberMe from a previous session.  The &lt;a href=&quot;http://shiro.apache.org/authentication.html#Authentication-Rememberedvs.Authenticated&quot;&gt;documentation&lt;/a&gt; gives a good example with Amazon.com for why Shiro makes this distinction.  It allows more control (usually necessary), but you can relax the control as you see fit.
&lt;p&gt;
So, to relax my configuration a bit to match what I want (&lt;em&gt;known users&lt;/em&gt;), I updated shiro.ini&apos;s [urls] section to be as follows:&lt;/p&gt;
&lt;pre&gt;
/app/users = ssl[8443],user
&lt;/pre&gt;
&lt;p&gt;
The key is that the /app/users url is now protected with the more relaxed &lt;a href=&quot;http://shiro.apache.org/static/current/apidocs/org/apache/shiro/web/filter/authc/UserFilter.html&quot;&gt;&lt;em&gt;user&lt;/em&gt; filter&lt;/a&gt; instead of the &lt;em&gt;authc&lt;/em&gt; filter.  However, you would typically want an account profile page (or credit card information page, or similar) protected with the authc filter instead to guarantee proof of identity for those sensitive operations.</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/java_web_application_security_part1</id>
        <title type="html">Java Web Application Security - Part II: Spring Security Login Demo</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/java_web_application_security_part1"/>
        <published>2011-05-13T09:20:51-06:00</published>
        <updated>2015-07-07T01:36:15-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="presentation" scheme="http://roller.apache.org/ns/tags/" />
        <category term="springsecurity" scheme="http://roller.apache.org/ns/tags/" />
        <category term="java" scheme="http://roller.apache.org/ns/tags/" />
        <category term="security" scheme="http://roller.apache.org/ns/tags/" />
        <category term="ujug" scheme="http://roller.apache.org/ns/tags/" />
        <category term="web" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">Last week, I wrote &lt;a href=&quot;http://raibledesigns.com/rd/entry/java_web_application_security_part&quot;&gt;a tutorial on how to implement Security in Java EE 6&lt;/a&gt;. This week, I&apos;d like to show you how to implement the same features using &lt;a href=&quot;http://static.springsource.org/spring-security/site/&quot;&gt;Spring Security&lt;/a&gt;. Before I begin, I&apos;d like to explain my reason for writing this article.
&lt;/p&gt;
&lt;p&gt;Last month, I presented a talk on Java Web Application Security at the &lt;a href=&quot;http://ujug.org&quot;&gt;Utah JUG&lt;/a&gt; (UJUG). As part of that presentation, I did a number of demos about how to implement security with Java EE 6, Spring Security and Apache Shiro. I told the audience that I would post the presentation and was planning on recording screencasts of the various demos so the online version of the presentation would make more sense. 
&lt;/p&gt;
&lt;p&gt;
Today, I&apos;ve finished the second screencast showing how to implement security with Spring Security. Below is the presentation (with the screencast embedded on slide 16) as well as a step-by-step tutorial.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;iframe src=&quot;//www.slideshare.net/slideshow/embed_code/key/o4wwswiZck6bKS&quot; width=&quot;510&quot; height=&quot;420&quot; frameborder=&quot;0&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; scrolling=&quot;no&quot; style=&quot;border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%;&quot; allowfullscreen&gt; &lt;/iframe&gt;
&lt;br/&gt;
&lt;div style=&quot;font-size: .9em; text-align: left&quot;&gt;* You can also &lt;a href=&quot;http://www.youtube.com/watch?v=K5Hf-2bKYu8&quot;&gt;watch the screencast on YouTube&lt;/a&gt; or &lt;a href=&quot;http://static.raibledesigns.com/repository/presentations/Java_Web_Application_Security_UJUG2011.pdf&quot;&gt;download the presentation PDF&lt;/a&gt;.&lt;/div&gt;
&lt;/p&gt;

&lt;p id=&quot;springsecurity-login-tutorial&quot;&gt;&lt;strong&gt;Spring Security Login Tutorial&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#download-run&quot;&gt;Download and Run the Application&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#secure-basic&quot;&gt;Implement Basic Authentication&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#ssl&quot;&gt;Force SSL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#form-authentication&quot;&gt;Implement Form-based Authentication&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#remember-me&quot;&gt;Add Remember Me&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#jdbc&quot;&gt;Store Users in a Database&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#summary&quot;&gt;Summary&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;download-run&quot;&gt;&lt;strong&gt;Download and Run the Application&lt;/strong&gt;&lt;br/&gt;

To begin, &lt;a href=&quot;http://static.raibledesigns.com/downloads/ajax-login-springsecurity-tutorial-1.0.zip&quot;&gt;download the application&lt;/a&gt; you&apos;ll be implementing security in. This app is a stripped-down version of the Ajax Login application I wrote for my article on &lt;a href=&quot;http://raibledesigns.com/rd/entry/implementing_ajax_authentication_using_jquery&quot;&gt;Implementing Ajax Authentication using jQuery, Spring Security and HTTPS&lt;/a&gt;. You&apos;ll need Java 6 and Maven installed to run the app. Run it using &lt;strong&gt;mvn jetty:run&lt;/strong&gt; and open &lt;a href=&quot;http://localhost:8080&quot;&gt;http://localhost:8080&lt;/a&gt; in your browser. You&apos;ll see it&apos;s a simple CRUD application for users and there&apos;s no login required to add or delete users.&lt;/p&gt;

&lt;p id=&quot;secure-basic&quot;&gt;&lt;strong&gt;Implement Basic Authentication&lt;/strong&gt;&lt;br/&gt;
  
The first step is to protect the list screen so people have to login to view users. To do this, you&apos;ll need to create a Spring context file that contains Spring Security&apos;s configuration. Create &lt;em&gt;src/main/webapp/WEB-INF/security.xml&lt;/em&gt; and populate it with the contents below:
&lt;/p&gt;
&lt;pre class=&quot;brush: xml; auto-links: false&quot;&gt;
  &amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
  &amp;lt;beans:beans xmlns=&quot;http://www.springframework.org/schema/security&quot;
               xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
               xmlns:beans=&quot;http://www.springframework.org/schema/beans&quot;
               xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd&quot;&amp;gt;

      &amp;lt;!-- New in Spring Security 3.1 --&amp;gt;
      &amp;lt;!-- &amp;lt;http pattern=&quot;/css/**&quot; security=&quot;none&quot;/&amp;gt; --&amp;gt;

      &amp;lt;http auto-config=&quot;true&quot;&amp;gt;
          &amp;lt;intercept-url pattern=&quot;/app/users&quot; access=&quot;ROLE_USER,ROLE_ADMIN&quot;/&amp;gt;
          &amp;lt;http-basic/&amp;gt;
      &amp;lt;/http&amp;gt;

      &amp;lt;authentication-manager alias=&quot;authenticationManager&quot;&amp;gt;
          &amp;lt;authentication-provider&amp;gt;
              &amp;lt;password-encoder hash=&quot;sha&quot;/&amp;gt;
              &amp;lt;user-service&amp;gt;
                  &amp;lt;user name=&quot;user&quot; password=&quot;12dea96fec20593566ab75692c9949596833adc9&quot; authorities=&quot;ROLE_USER&quot;/&amp;gt;
                  &amp;lt;user name=&quot;admin&quot; password=&quot;d033e22ae348aeb5660fc2140aec35850c4da997&quot; authorities=&quot;ROLE_ADMIN&quot;/&amp;gt;
              &amp;lt;/user-service&amp;gt;
          &amp;lt;/authentication-provider&amp;gt;
      &amp;lt;/authentication-manager&amp;gt;

      &amp;lt;!-- Override userSecurityAdvice bean in appfuse-service to allow any role to update a user. --&amp;gt;
      &amp;lt;beans:bean id=&quot;userSecurityAdvice&quot; class=&quot;org.appfuse.examples.webapp.security.UserSecurityAdvice&quot;/&amp;gt;
  &amp;lt;/beans:beans&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
The last bean, userSecurityAdvice, is an aspect that&apos;s needed to override some behavior in &lt;a href=&quot;http://appfuse.org&quot;&gt;AppFuse&lt;/a&gt;. You won&apos;t need this normally when implementing Spring Security. 
&lt;/p&gt;
&lt;p&gt;Next, open &lt;em&gt;src/main/webapp/WEB-INF/web.xml&lt;/em&gt; and add Spring&apos;s DelegatingFilterProxy:&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;filter&amp;gt;
    &amp;lt;filter-name&amp;gt;securityFilter&amp;lt;/filter-name&amp;gt;
    &amp;lt;filter-class&amp;gt;org.springframework.web.filter.DelegatingFilterProxy&amp;lt;/filter-class&amp;gt;
    &amp;lt;init-param&amp;gt;
        &amp;lt;param-name&amp;gt;targetBeanName&amp;lt;/param-name&amp;gt;
        &amp;lt;param-value&amp;gt;springSecurityFilterChain&amp;lt;/param-value&amp;gt;
    &amp;lt;/init-param&amp;gt;
&amp;lt;/filter&amp;gt;
&lt;/pre&gt;
&lt;p&gt;And add its filter-mapping just after the rewriteFilter in the filter-mappings section (order is important!):
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;filter-mapping&amp;gt;
    &amp;lt;filter-name&amp;gt;rewriteFilter&amp;lt;/filter-name&amp;gt;
    &amp;lt;url-pattern&amp;gt;/*&amp;lt;/url-pattern&amp;gt;
&amp;lt;/filter-mapping&amp;gt;
&amp;lt;filter-mapping&amp;gt;
    &amp;lt;filter-name&amp;gt;securityFilter&amp;lt;/filter-name&amp;gt;
    &amp;lt;url-pattern&amp;gt;/*&amp;lt;/url-pattern&amp;gt;
    &amp;lt;dispatcher&amp;gt;REQUEST&amp;lt;/dispatcher&amp;gt;
    &amp;lt;dispatcher&amp;gt;FORWARD&amp;lt;/dispatcher&amp;gt;
    &amp;lt;dispatcher&amp;gt;INCLUDE&amp;lt;/dispatcher&amp;gt;
&amp;lt;/filter-mapping&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
You don&apos;t need to add any dependencies in your pom.xml is because this project depends on AppFuse, which already contains these dependencies.
&lt;/p&gt;
&lt;p&gt;
At this point, if you restart Jetty (Ctrl+C and jetty:run again), you should be prompted to login when you click on the &quot;Users&quot; tab. Enter admin/admin to login. Spring Security is a bit easier to configure than Java EE 6 out-of-the-box, mostly because it doesn&apos;t require you to configure your container.
&lt;/p&gt;
&lt;p&gt;After logging in, you can try to logout by clicking the &quot;Logout&quot; link in the top-right corner. This calls a LogoutController with the following code that logs the user out.
&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
public void logout(HttpServletResponse response) throws ServletException, IOException {
    request.getSession().invalidate();
    response.sendRedirect(request.getContextPath()); 
}
&lt;/pre&gt; 
&lt;p class=&quot;quote&quot; style=&quot;font-style: italic; color: #666&quot;&gt;NOTE: Spring Security has a way to configure &quot;logout&quot; to match a URL and get rid of a class like LogoutController. Since it was already in the project, I don&apos;t cover that in this tutorial.&lt;/p&gt;
&lt;p&gt;You&apos;ll notice that clicking this link doesn&apos;t log you out, even though the session is invalidated. The only way to logout with basic authentication is to close the browser. In order to get the ability to logout, as well as to have more control over the look-and-feel of the login, you can implement form-based authentication.
  Before you implement form-based authentication, I&apos;d like to show you how easy it is to force SSL with Spring Security.
&lt;/p&gt;
&lt;p id=&quot;ssl&quot;&gt;&lt;strong&gt;Force SSL&lt;/strong&gt;&lt;br/&gt;
Spring Security allows you to switch between secure (https) and non-secure (http) protocols using a simple &lt;em&gt;requires-channel&lt;/em&gt; attribute on the &amp;lt;intercept-url&amp;gt; element. Possible values are &quot;http&quot;, &quot;https&quot; and &quot;any&quot;. Add &lt;em&gt;requires-channel=&quot;https&quot;&lt;/em&gt; to your security.xml file:
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;intercept-url pattern=&quot;/app/users&quot; access=&quot;ROLE_USER,ROLE_ADMIN&quot; requires-channel=&quot;https&quot;/&amp;gt;
&lt;/pre&gt;
&lt;p&gt;In order for this to work, you have to configure Jetty to listen on an SSL port. Add the following just after the jetty-maven-plugin&apos;s &amp;lt;/webAppConfig&amp;gt; element in your pom.xml:
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;connectors&amp;gt;
    &amp;lt;connector implementation=&quot;org.eclipse.jetty.server.nio.SelectChannelConnector&quot;&amp;gt;
        &amp;lt;forwarded&amp;gt;true&amp;lt;/forwarded&amp;gt;
        &amp;lt;port&amp;gt;8080&amp;lt;/port&amp;gt;
    &amp;lt;/connector&amp;gt;
    &amp;lt;connector implementation=&quot;org.eclipse.jetty.server.ssl.SslSelectChannelConnector&quot;&amp;gt;
        &amp;lt;forwarded&amp;gt;true&amp;lt;/forwarded&amp;gt;
        &amp;lt;port&amp;gt;8443&amp;lt;/port&amp;gt;
        &amp;lt;maxIdleTime&amp;gt;60000&amp;lt;/maxIdleTime&amp;gt;
        &amp;lt;keystore&amp;gt;${project.build.directory}/ssl.keystore&amp;lt;/keystore&amp;gt;
        &amp;lt;password&amp;gt;appfuse&amp;lt;/password&amp;gt;
        &amp;lt;keyPassword&amp;gt;appfuse&amp;lt;/keyPassword&amp;gt;
    &amp;lt;/connector&amp;gt;
&amp;lt;/connectors&amp;gt;
&lt;/pre&gt;
&lt;p&gt;The keystore must be generated for Jetty to start successfully, so add the keytool-maven-plugin just above the jetty-maven-plugin in pom.xml.
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;plugin&amp;gt;
    &amp;lt;groupId&amp;gt;org.codehaus.mojo&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;keytool-maven-plugin&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;1.0&amp;lt;/version&amp;gt;
    &amp;lt;executions&amp;gt;
        &amp;lt;execution&amp;gt;
            &amp;lt;phase&amp;gt;generate-resources&amp;lt;/phase&amp;gt;
            &amp;lt;id&amp;gt;clean&amp;lt;/id&amp;gt;
            &amp;lt;goals&amp;gt;
                &amp;lt;goal&amp;gt;clean&amp;lt;/goal&amp;gt;
            &amp;lt;/goals&amp;gt;
        &amp;lt;/execution&amp;gt;
        &amp;lt;execution&amp;gt;
            &amp;lt;phase&amp;gt;generate-resources&amp;lt;/phase&amp;gt;
            &amp;lt;id&amp;gt;genkey&amp;lt;/id&amp;gt;
            &amp;lt;goals&amp;gt;
                &amp;lt;goal&amp;gt;genkey&amp;lt;/goal&amp;gt;
            &amp;lt;/goals&amp;gt;
        &amp;lt;/execution&amp;gt;
    &amp;lt;/executions&amp;gt;
    &amp;lt;configuration&amp;gt;
        &amp;lt;keystore&amp;gt;${project.build.directory}/ssl.keystore&amp;lt;/keystore&amp;gt;
        &amp;lt;dname&amp;gt;cn=localhost&amp;lt;/dname&amp;gt;
        &amp;lt;keypass&amp;gt;appfuse&amp;lt;/keypass&amp;gt;
        &amp;lt;storepass&amp;gt;appfuse&amp;lt;/storepass&amp;gt;
        &amp;lt;alias&amp;gt;appfuse&amp;lt;/alias&amp;gt;
        &amp;lt;keyalg&amp;gt;RSA&amp;lt;/keyalg&amp;gt;
    &amp;lt;/configuration&amp;gt;
&amp;lt;/plugin&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Now if you restart Jetty, go to &lt;a href=&quot;http://localhost:8080&quot;&gt;http://localhost:8080&lt;/a&gt; and click on the &quot;Users&quot; tab, you&apos;ll be prompted to accept the Untrusted Certificate and then redirected to https://localhost:8443/users after logging in. This is an 
  improvement on Java EE&apos;s user-data-constraint for two reasons:
  &lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;You can switch between http and https protocols. With Java EE, you can only force https. You have to write a custom filter to switch back to http.&lt;/li&gt;
    &lt;li&gt;Redirecting to https actually works. With Java EE (on Jetty at least), a 403 is returned instead of redirecting the request.&lt;/li&gt;
  &lt;/ul&gt;
&lt;/p&gt;
&lt;p&gt;Now let&apos;s look at how to have more control over the look-and-feel of the login screen, as well as how to make logout work with form-based authentication.&lt;/p&gt;
&lt;p id=&quot;form-authentication&quot;&gt;&lt;strong&gt;Implement Form-based Authentication&lt;/strong&gt;&lt;br/&gt;
To change from basic to form-based authentication, you simply have to add a &amp;lt;form-login&amp;gt; element in security.xml&apos;s &amp;lt;http&amp;gt; element:
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;http auto-config=&quot;true&quot;&amp;gt;
    &amp;lt;intercept-url pattern=&quot;/app/users&quot; access=&quot;ROLE_USER,ROLE_ADMIN&quot; requires-channel=&quot;https&quot;/&amp;gt;
    &amp;lt;form-login login-page=&quot;/login&quot; authentication-failure-url=&quot;/login?error=true&quot;
                login-processing-url=&quot;/j_security_check&quot;/&amp;gt;
    &amp;lt;http-basic/&amp;gt;
&amp;lt;/http&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
  You can leave the &amp;lt;http-basic&amp;gt; element since Spring Security is smart enough to serve up the form for browsers and use Basic Authentication for clients such as web services.
  
  The login.jsp page (that /login forwards to) already exists in the project, in the &lt;em&gt;src/main/webapp&lt;/em&gt; directory. The forwarding is done by the &lt;a href=&quot;http://www.tuckey.org/urlrewrite/&quot;&gt;UrlRewriteFilter&lt;/a&gt; with the following configuration in &lt;em&gt;src/main/webapp/WEB-INF/urlrewrite.xml&lt;/em&gt;. 
  
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;rule&amp;gt;
    &amp;lt;from&amp;gt;/login&amp;lt;/from&amp;gt;
    &amp;lt;to&amp;gt;/login.jsp&amp;lt;/to&amp;gt;
&amp;lt;/rule&amp;gt;
&lt;/pre&gt;  
&lt;p&gt;
  This JSP has 3 important elements: 1) a form that submits to &quot;/j_security_check&quot;, 2) an input element named &quot;j_username&quot; and 3) an input element named &quot;j_password&quot;. If you restart Jetty, you&apos;ll now be prompted to login with this JSP instead of the basic authentication dialog.
&lt;/p&gt;
&lt;p id=&quot;remember-me&quot;&gt;&lt;strong&gt;Add Remember Me&lt;/strong&gt;&lt;br/&gt;
Remember Me is a feature you see in many web applications today. It&apos;s usually a checkbox on the login form that allows you to auto-login the next time you visit a site. This feature doesn&apos;t exist in Java EE security, but it does exist in Spring Security. To enable it, add the following just below &amp;lt;form-login&amp;gt; in security.xml:
&lt;/p&gt;
&lt;pre class=&quot;brush: xml; toolbar: false&quot;&gt;
&amp;lt;remember-me user-service-ref=&quot;userDao&quot; key=&quot;e37f4b31-0c45-11dd-bd0b-0800200c9a66&quot;/&gt;
&lt;/pre&gt;
&lt;p&gt;Next, open &lt;em&gt;src/main/webapp/login.jsp&lt;/em&gt; and change the name of the &quot;remember me&quot; checkbox to be &lt;strong&gt;_spring_security_remember_me&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class=&quot;brush: xml; toolbar: false&quot;&gt;
&amp;lt;input type=&quot;checkbox&quot; name=&quot;_spring_security_remember_me&quot; id=&quot;rememberMe&quot;/&gt;
&lt;/pre&gt;
&lt;p&gt;After making these changes, you should be able to restart Jetty, go to &lt;a href=&quot;http://localhost:8080/users&quot;&gt;http://localhost:8080/users&lt;/a&gt;, enter admin/adminjdbc, check the Remember Me checkbox and login. Then close your browser, and repeat the process. This time, you won&apos;t be prompted to login. For more information on this feature, see &lt;a href=&quot;http://static.springsource.org/spring-security/site/docs/3.0.x/reference/remember-me.html&quot;&gt;Spring Security&apos;s Remember Me documentation&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
While storing usernames and passwords in a file is convenient for demos, it&apos;s not very real-world-ish. The next section shows you how to configure Spring Security to use a database for its user store.
&lt;/p&gt;
&lt;p id=&quot;jdbc&quot;&gt;&lt;strong&gt;Store Users in a Database&lt;/strong&gt;&lt;br/&gt;
To store your users in a database instead of file, you&apos;ll need to add a &lt;em&gt;user-service-ref&lt;/em&gt; attribute to the &amp;lt;authentication-provider&amp;gt; element. You can also delete the &amp;lt;user-service&amp;gt; element. 
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;authentication-manager alias=&quot;authenticationManager&quot;&amp;gt;
    &amp;lt;authentication-provider user-service-ref=&quot;userDao&quot;&amp;gt;
        &amp;lt;password-encoder hash=&quot;sha&quot;/&amp;gt;
    &amp;lt;/authentication-provider&amp;gt;
&amp;lt;/authentication-manager&amp;gt;
&lt;/pre&gt;
&lt;p&gt;The &quot;userDao&quot; bean is provided by AppFuse and its &lt;a href=&quot;http://static.appfuse.org/appfuse-data/appfuse-hibernate/xref/org/appfuse/dao/hibernate/UserDaoHibernate.html&quot;&gt;UserDaoHibernate.java&lt;/a&gt; class. This 
  class implements Spring Security&apos;s &lt;a href=&quot;http://static.springsource.org/spring-security/site/docs/3.0.x/apidocs/org/springframework/security/core/userdetails/UserDetailsService.html&quot;&gt;UserDetailsService&lt;/a&gt; interface.
  With Java EE, I had to configure a database connection and make sure the JDBC Driver was in my container&apos;s classpath. With Spring Security, you can talk to the database you already have configured in your application.
&lt;/p&gt;
&lt;div class=&quot;quote&quot; style=&quot;margin-left: 0; color: #666&quot;&gt;Of course, you could do this with Java EE too. One thing I neglected to show in my last tutorial was that 1) the app uses H2 and 2) I had to configure Java EE&apos;s database to be MySQL. This was because when I tried to access my H2 instance, I got an error about two threads trying to access it at once.
&lt;pre style=&quot;margin-top: 10px; margin-bottom: 0&quot;&gt;
2011-05-13 08:47:29.081:WARN::UserRealm Java EE Login could not connect to database; will try later
org.h2.jdbc.JdbcSQLException: Database may be already in use: &quot;Locked by another process&quot;. 
        Possible solutions: close all other connection(s); use the server mode [90020-154]
	at org.h2.message.DbException.getJdbcSQLException(DbException.java:327)
	at org.h2.message.DbException.get(DbException.java:167)
	at org.h2.message.DbException.get(DbException.java:144)
	at org.h2.store.FileLock.getExceptionAlreadyInUse(FileLock.java:443)
	at org.h2.store.FileLock.lockFile(FileLock.java:338)
	at org.h2.store.FileLock.lock(FileLock.java:134)
	at org.h2.engine.Database.open(Database.java:535)
	at org.h2.engine.Database.openDatabase(Database.java:218)
&lt;/pre&gt;
&lt;/div&gt;
&lt;/p&gt;
&lt;!-- Figure out exact error from H2 --&gt;
&lt;p&gt;The password for the &quot;admin&quot; user is configured in &lt;em&gt;src/test/resources/sample-data.xml&lt;/em&gt; and it&apos;s loaded by &lt;a href=&quot;http://dbunit.sourceforge.net/&quot;&gt;DbUnit&lt;/a&gt; before the application starts. 
  You can view your pom.xml and the dbunit-maven-plugin&apos;s configuration if you&apos;re interested in learning how this is done. The password is currently configured to &quot;adminjdbc&quot;, but you can reset it by
  &lt;a href=&quot;http://darrenfauth.com/generators/sha1&quot;&gt;generating a new password&lt;/a&gt; and modifying sample-data.xml.
&lt;/p&gt;
&lt;p&gt;Now if you restart Jetty, you should be able to login with admin/adminjdbc and view the list of users.
&lt;/p&gt;
&lt;p id=&quot;summary&quot;&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;br/&gt;
In this tutorial, you learned how to implement authentication using Spring Security 3.0.5. In addition to the basic XML configuration, Spring Security also provides a AOP support and annotations you can use to secure methods. It also has many more features than standard Java EE Security. In my opinion, it&apos;s the most mature security framework we have in Java today. Currently, I think its &lt;a href=&quot;http://static.springsource.org/spring-security/site/docs/3.0.x/reference/springsecurity.html&quot;&gt;reference documentation&lt;/a&gt; is the best place to learn more.
&lt;/p&gt;
There are a few limitations I found with Spring Security:
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;The authentication mechanism (file, database, ldap, etc.) is contained in the WAR&lt;/li&gt;
&lt;li&gt;Securing methods only works on Spring beans&lt;/li&gt;
&lt;li&gt;Remember Me doesn&apos;t work in my screencast (because I forgot to rename the checkbox in login.jsp)&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;&lt;p&gt;Of course, you can configure Spring to load its configuration from outside the WAR (e.g. a file or JNDI), but it&apos;s not as easy as including the configuration in your app.
  &lt;/p&gt;
  &lt;p&gt;In the next couple weeks, I&apos;ll post &lt;a href=&quot;http://raibledesigns.com/rd/entry/java_web_application_security_part2&quot;&gt;Part III&lt;/a&gt; of this series, where I&apos;ll show you how to implement this same set of features using Apache Shiro. In the meantime, please let me know if you have any questions.
&lt;/p&gt;
&lt;p style=&quot;border-top: 1px dotted silver; padding-top: 5px; color: #666&quot;&gt;
I created the screencasts with &lt;a href=&quot;http://www.techsmith.com/camtasia/&quot; style=&quot;color: #666&quot;&gt;Camtasia&lt;/a&gt;. For small screens, and embedding in the presentation, I created it at 50% and used the SmartFocus feature to zoom in and out during the demo. For larger screens, I published &lt;a href=&quot;http://www.youtube.com/watch?v=poc5dyImbig&quot; style=&quot;color: #666&quot;&gt;another screencast at 100%, in HD&lt;/a&gt;. If you have a preference for which screencast is better, I&apos;d love to hear about it.&lt;/p&gt;
&lt;p&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/java_web_application_security_part</id>
        <title type="html">Java Web Application Security - Part I: Java EE 6 Login Demo</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/java_web_application_security_part"/>
        <published>2011-05-05T16:58:00-06:00</published>
        <updated>2022-08-22T14:56:40-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="ujug" scheme="http://roller.apache.org/ns/tags/" />
        <category term="security" scheme="http://roller.apache.org/ns/tags/" />
        <category term="javaee" scheme="http://roller.apache.org/ns/tags/" />
        <category term="web" scheme="http://roller.apache.org/ns/tags/" />
        <category term="java" scheme="http://roller.apache.org/ns/tags/" />
        <category term="presentation" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">&lt;p&gt;Back in February, I &lt;a href=&quot;http://raibledesigns.com/rd/entry/upcoming_conferences_tssjs_in_las&quot;&gt;wrote about my upcoming conferences&lt;/a&gt;:&lt;/p&gt;
&lt;p style=&quot;color: #666; border-top: 1px dotted silver; padding-top: 10px; margin-top: -5px&quot;&gt;
In addition to Vegas and Poland, there&apos;s a couple other events I might speak at in the next few months: the &lt;a href=&quot;http://www.ujug.org/&quot;&gt;Utah Java Users Group&lt;/a&gt; (possibly in April), &lt;a href=&quot;http://jazoon.com/&quot;&gt;Jazoon&lt;/a&gt; and &lt;a href=&quot;http://uberconf.com/conference/denver/2011/07/home&quot;&gt;&#220;berConf&lt;/a&gt; (if my proposals are accepted). For these events, I&apos;m hoping to present the following talk:
&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
&lt;strong&gt;Webapp Security: Develop. Penetrate. Protect. Relax.&lt;/strong&gt;&lt;br&gt;
In this session, you&apos;ll learn how to implement authentication in your Java web applications using Spring Security, Apache Shiro and good ol&apos; Java EE Container Managed Authentication. You&apos;ll also learn how to secure your REST API with OAuth and lock it down with SSL.
&lt;br&gt;&lt;br&gt;
After learning how to develop authentication, I&apos;ll introduce you to OWASP, the OWASP Top 10, its Testing Guide and its Code Review Guide. From there, I&apos;ll discuss using WebGoat to verify your app is secure and commercial tools like webapp firewalls and accelerators.&lt;/p&gt;
&lt;p&gt;Fast forward a couple months and I&apos;m happy to say that I&apos;ve completed my talk at the Utah JUG and it&apos;s been accepted at Jazoon and &#220;ber Conf. For this talk, I created a presentation that primarily consists of demos implementing basic, form and Ajax authentication using &lt;a href=&quot;http://download.oracle.com/javaee/6/tutorial/doc/gkbaa.html&quot;&gt;Java EE 6&lt;/a&gt;, &lt;a href=&quot;http://static.springsource.org/spring-security/site/&quot;&gt;Spring Security&lt;/a&gt; and &lt;a href=&quot;http://shiro.apache.org/&quot;&gt;Apache Shiro&lt;/a&gt;. In the process of creating the demos, I learned (or re-educated myself) how to do a number of things in all 3 frameworks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Implement Basic Authentication&lt;/li&gt;
&lt;li&gt;Implement Form-based Authentication&lt;/li&gt;
&lt;li&gt;Implement Ajax HTTP -&gt; HTTPS Authentication (with programmatic APIs)&lt;/li&gt;
&lt;li&gt;Force SSL for certain URLs&lt;/li&gt;
&lt;li&gt;Implement a file-based store of users and passwords (in Jetty/Maven and Tomcat standalone)&lt;/li&gt;
&lt;li&gt;Implement a database store of users and passwords (in Jetty/Maven and Tomcat standalone)&lt;/li&gt;
&lt;li&gt;Encrypt Passwords&lt;/li&gt;
&lt;li&gt;Secure methods with annotations&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For the demos, I showed the audience how to do almost all of these, but skipped Tomcat standalone and securing methods in the interest of time. In July, when I do this talk at &#220;berConf, I plan on adding 1) hacking the app (to show security holes) and 2) fixing it to protect it against vulnerabilities. 
&lt;/p&gt;
&lt;p&gt;I told the audience at UJUG that I would post the presentation and was planning on recording screencasts of the various demos so the online version of the presentation would make more sense. Today, I&apos;ve finished the first screencast showing how to implement security with Java EE 6. Below is the presentation (with the screencast embedded on slide 10) as well as a step-by-step tutorial.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;object id=&quot;__sse7850034&quot; width=&quot;510&quot; height=&quot;426&quot;&gt; &lt;param name=&quot;movie&quot; value=&quot;http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=javawebapplicationsecurityujug2011-110505121131-phpapp02&amp;stripped_title=java-web-application-security-utah-jug-2011&amp;userName=mraible&quot; /&gt; &lt;param name=&quot;allowFullScreen&quot; value=&quot;true&quot;/&gt; &lt;param name=&quot;allowScriptAccess&quot; value=&quot;always&quot;/&gt; &lt;embed name=&quot;__sse7850034&quot; src=&quot;//static.slidesharecdn.com/swf/ssplayer2.swf?doc=javawebapplicationsecurityujug2011-110505121131-phpapp02&amp;stripped_title=java-web-application-security-utah-jug-2011&amp;userName=mraible&quot; type=&quot;application/x-shockwave-flash&quot; allowscriptaccess=&quot;always&quot; allowfullscreen=&quot;true&quot; width=&quot;510&quot; height=&quot;426&quot;&gt;&lt;/embed&gt; &lt;/object&gt;
&lt;br/&gt;
&lt;div style=&quot;font-size: .9em; text-align: left&quot;&gt;* You can also &lt;a href=&quot;http://www.youtube.com/watch?v=4LD4mF5ex2U&quot;&gt;watch the screencast on YouTube&lt;/a&gt; or &lt;a href=&quot;http://static.raibledesigns.com/repository/presentations/Java_Web_Application_Security_UJUG2011.pdf&quot;&gt;download the presentation PDF&lt;/a&gt;.&lt;/div&gt;
&lt;/p&gt;
&lt;p id=&quot;javaee6-login-tutorial&quot;&gt;&lt;strong&gt;Java EE 6 Login Tutorial&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://raibledesigns.com/rd/entry/java_web_application_security_part#download-run&quot;&gt;Download and Run the Application&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://raibledesigns.com/rd/entry/java_web_application_security_part#secure-basic&quot;&gt;Implement Basic Authentication&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://raibledesigns.com/rd/entry/java_web_application_security_part#form-authentication&quot;&gt;Implement Form-based Authentication&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://raibledesigns.com/rd/entry/java_web_application_security_part#ssl&quot;&gt;Force SSL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://raibledesigns.com/rd/entry/java_web_application_security_part#jdbc&quot;&gt;Store Users in a Database&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://raibledesigns.com/rd/entry/java_web_application_security_part#summary&quot;&gt;Summary&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;download-run&quot;&gt;&lt;strong&gt;Download and Run the Application&lt;/strong&gt;&lt;br/&gt;

To begin, &lt;a href=&quot;http://static.raibledesigns.com/downloads/ajax-login-javaee-tutorial-1.0.zip&quot;&gt;download the application&lt;/a&gt; you&apos;ll be implementing security in. This app is a stripped-down version of the Ajax Login application I wrote for my article on &lt;a href=&quot;http://raibledesigns.com/rd/entry/implementing_ajax_authentication_using_jquery&quot;&gt;Implementing Ajax Authentication using jQuery, Spring Security and HTTPS&lt;/a&gt;. You&apos;ll need Java 6 and Maven installed to run the app. Run it using &lt;strong&gt;mvn jetty:run&lt;/strong&gt; and open &lt;a href=&quot;http://localhost:8080&quot;&gt;http://localhost:8080&lt;/a&gt; in your browser. You&apos;ll see it&apos;s a simple CRUD application for users and there&apos;s no login required to add or delete users.&lt;/p&gt;

&lt;p id=&quot;secure-basic&quot;&gt;&lt;strong&gt;Implement Basic Authentication&lt;/strong&gt;&lt;br/&gt;
  
The first step is to protect the list screen so people have to login to view users. To do this, add the following to the bottom of &lt;em&gt;src/main/webapp/WEB-INF/web.xml&lt;/em&gt;:
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;security-constraint&amp;gt;
    &amp;lt;web-resource-collection&amp;gt;
        &amp;lt;web-resource-name&amp;gt;users&amp;lt;/web-resource-name&amp;gt;
        &amp;lt;url-pattern&amp;gt;/users&amp;lt;/url-pattern&amp;gt;
        &amp;lt;http-method&amp;gt;GET&amp;lt;/http-method&amp;gt;
        &amp;lt;http-method&amp;gt;POST&amp;lt;/http-method&amp;gt;
    &amp;lt;/web-resource-collection&amp;gt;
    &amp;lt;auth-constraint&amp;gt;
        &amp;lt;role-name&amp;gt;ROLE_ADMIN&amp;lt;/role-name&amp;gt;
    &amp;lt;/auth-constraint&amp;gt;
&amp;lt;/security-constraint&amp;gt;

&amp;lt;login-config&amp;gt;
    &amp;lt;auth-method&amp;gt;BASIC&amp;lt;/auth-method&amp;gt;
    &amp;lt;realm-name&amp;gt;Java EE Login&amp;lt;/realm-name&amp;gt;
&amp;lt;/login-config&amp;gt;

&amp;lt;security-role&amp;gt;
    &amp;lt;role-name&amp;gt;ROLE_ADMIN&amp;lt;/role-name&amp;gt;
&amp;lt;/security-role&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
At this point, if you restart Jetty (Ctrl+C and jetty:run again), you&apos;ll get an error about a missing LoginService. This happens because Jetty doesn&apos;t know where the &quot;Java EE Login&quot; realm is located. Add the following to &lt;em&gt;pom.xml&lt;/em&gt;, just after &amp;lt;/webAppConfig&gt; in the Jetty plugin&apos;s configuration.
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;loginServices&amp;gt;
    &amp;lt;loginService implementation=&quot;org.eclipse.jetty.security.HashLoginService&quot;&amp;gt;
        &amp;lt;name&amp;gt;Java EE Login&amp;lt;/name&amp;gt;
        &amp;lt;config&amp;gt;${basedir}/src/test/resources/realm.properties&amp;lt;/config&amp;gt;
    &amp;lt;/loginService&amp;gt;
&amp;lt;/loginServices&amp;gt;
&lt;/pre&gt;
&lt;p&gt;The realm.properties file already exists in the project and contains user names and passwords. Start the app again using &lt;strong&gt;mvn jetty:run&lt;/strong&gt; and you should be prompted to login when you click on the &quot;Users&quot; tab. Enter admin/admin to login.
&lt;/p&gt;
&lt;p&gt;After logging in, you can try to logout by clicking the &quot;Logout&quot; link in the top-right corner. This calls a LogoutController with the following code that logs the user out.
&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
public void logout(HttpServletResponse response) throws ServletException, IOException {
    request.getSession().invalidate();
    response.sendRedirect(request.getContextPath());
}
&lt;/pre&gt; 
&lt;p&gt;You&apos;ll notice that clicking this link doesn&apos;t log you out, even though the session is invalidated. The only way to logout with basic authentication is to close the browser. In order to get the ability to logout, as well as to have more control over the look-and-feel of the login, you can implement form-based authentication.
&lt;/p&gt;
&lt;p id=&quot;form-authentication&quot;&gt;&lt;strong&gt;Implement Form-based Authentication&lt;/strong&gt;&lt;br/&gt;
To change from basic to form-based authentication, you simply have to replace the &amp;lt;login-config&amp;gt; in your web.xml with the following:
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;login-config&amp;gt;
    &amp;lt;auth-method&amp;gt;FORM&amp;lt;/auth-method&amp;gt;
    &amp;lt;form-login-config&amp;gt;
        &amp;lt;form-login-page&amp;gt;/login.jsp&amp;lt;/form-login-page&amp;gt;
        &amp;lt;form-error-page&amp;gt;/login.jsp?error=true&amp;lt;/form-error-page&amp;gt;
    &amp;lt;/form-login-config&amp;gt;
&amp;lt;/login-config&amp;gt;
&lt;/pre&gt;
&lt;p&gt;The login.jsp page already exists in the project, in the &lt;em&gt;src/main/webapp&lt;/em&gt; directory. This JSP has 3 important elements: 1) a form that submits to &quot;${contextPath}/j_security_check&quot;, 2) an input element named &quot;j_username&quot; and 3) an input element named &quot;j_password&quot;. If you restart Jetty, you&apos;ll now be prompted to login with this JSP instead of the basic authentication dialog.
&lt;/p&gt;
&lt;p id=&quot;ssl&quot;&gt;&lt;strong&gt;Force SSL&lt;/strong&gt;&lt;br/&gt;
Another thing you might want to implement to secure your application is forcing SSL for certain URLs. To do this on the same &amp;lt;security-constraint&amp;gt; you already have in web.xml, add the following after &amp;lt;/auth-constraint&gt;:
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;user-data-constraint&amp;gt;
    &amp;lt;transport-guarantee&amp;gt;CONFIDENTIAL&amp;lt;/transport-guarantee&amp;gt;
&amp;lt;/user-data-constraint&amp;gt;
&lt;/pre&gt;
&lt;p&gt;To configure Jetty to listen on an SSL port, add the following just after &amp;lt;/loginServices&amp;gt; in your pom.xml:
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;connectors&amp;gt;
    &amp;lt;connector implementation=&quot;org.eclipse.jetty.server.nio.SelectChannelConnector&quot;&amp;gt;
        &amp;lt;forwarded&amp;gt;true&amp;lt;/forwarded&amp;gt;
        &amp;lt;port&amp;gt;8080&amp;lt;/port&amp;gt;
    &amp;lt;/connector&amp;gt;
    &amp;lt;connector implementation=&quot;org.eclipse.jetty.server.ssl.SslSelectChannelConnector&quot;&amp;gt;
        &amp;lt;forwarded&amp;gt;true&amp;lt;/forwarded&amp;gt;
        &amp;lt;port&amp;gt;8443&amp;lt;/port&amp;gt;
        &amp;lt;maxIdleTime&amp;gt;60000&amp;lt;/maxIdleTime&amp;gt;
        &amp;lt;keystore&amp;gt;${project.build.directory}/ssl.keystore&amp;lt;/keystore&amp;gt;
        &amp;lt;password&amp;gt;appfuse&amp;lt;/password&amp;gt;
        &amp;lt;keyPassword&amp;gt;appfuse&amp;lt;/keyPassword&amp;gt;
    &amp;lt;/connector&amp;gt;
&amp;lt;/connectors&amp;gt;
&lt;/pre&gt;
&lt;p&gt;The keystore must be generated for Jetty to start successfully, so add the keytool-maven-plugin just above the jetty-maven-plugin in pom.xml.
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;plugin&amp;gt;
    &amp;lt;groupId&amp;gt;org.codehaus.mojo&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;keytool-maven-plugin&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;1.0&amp;lt;/version&amp;gt;
    &amp;lt;executions&amp;gt;
        &amp;lt;execution&amp;gt;
            &amp;lt;phase&amp;gt;generate-resources&amp;lt;/phase&amp;gt;
            &amp;lt;id&amp;gt;clean&amp;lt;/id&amp;gt;
            &amp;lt;goals&amp;gt;
                &amp;lt;goal&amp;gt;clean&amp;lt;/goal&amp;gt;
            &amp;lt;/goals&amp;gt;
        &amp;lt;/execution&amp;gt;
        &amp;lt;execution&amp;gt;
            &amp;lt;phase&amp;gt;generate-resources&amp;lt;/phase&amp;gt;
            &amp;lt;id&amp;gt;genkey&amp;lt;/id&amp;gt;
            &amp;lt;goals&amp;gt;
                &amp;lt;goal&amp;gt;genkey&amp;lt;/goal&amp;gt;
            &amp;lt;/goals&amp;gt;
        &amp;lt;/execution&amp;gt;
    &amp;lt;/executions&amp;gt;
    &amp;lt;configuration&amp;gt;
        &amp;lt;keystore&amp;gt;${project.build.directory}/ssl.keystore&amp;lt;/keystore&amp;gt;
        &amp;lt;dname&amp;gt;cn=localhost&amp;lt;/dname&amp;gt;
        &amp;lt;keypass&amp;gt;appfuse&amp;lt;/keypass&amp;gt;
        &amp;lt;storepass&amp;gt;appfuse&amp;lt;/storepass&amp;gt;
        &amp;lt;alias&amp;gt;appfuse&amp;lt;/alias&amp;gt;
        &amp;lt;keyalg&amp;gt;RSA&amp;lt;/keyalg&amp;gt;
    &amp;lt;/configuration&amp;gt;
&amp;lt;/plugin&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Now if you restart Jetty, go to http://localhost:8080 and click on the &quot;Users&quot; tab, you&apos;ll get a 403. What the heck?! When this first happened to me, it took me a while to figure out. It turns out that Jetty doesn&apos;t redirect to HTTPS when using Java EE authentication, so you have to manually type in &lt;a href=&quot;https://localhost:8443/&quot;&gt;https://localhost:8443/&lt;/a&gt; (or add a filter to redirect for you). If you deployed this same application on Tomcat (after &lt;a href=&quot;http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html&quot;&gt;enabling SSL&lt;/a&gt;), it would redirect for you.
&lt;/p&gt;
&lt;p id=&quot;jdbc&quot;&gt;&lt;strong&gt;Store Users in a Database&lt;/strong&gt;&lt;br/&gt;
Finally, to store your users in a database instead of file, you&apos;ll need to change the &amp;lt;loginService&amp;gt; in the Jetty plugin&apos;s configuration. Replace the existing &amp;lt;loginService&amp;gt; element with the following:
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;loginServices&amp;gt;
    &amp;lt;loginService implementation=&quot;org.eclipse.jetty.security.JDBCLoginService&quot;&amp;gt;
        &amp;lt;name&amp;gt;Java EE Login&amp;lt;/name&amp;gt;
        &amp;lt;config&amp;gt;${basedir}/src/test/resources/jdbc-realm.properties&amp;lt;/config&amp;gt;
    &amp;lt;/loginService&amp;gt;
&amp;lt;/loginServices&amp;gt;
&lt;/pre&gt;
&lt;p&gt;The jdbc-realm.properties file already exists in the project and contains the database settings and table/column names for the user and role information.
&lt;/p&gt;
&lt;pre&gt;
jdbcdriver = com.mysql.jdbc.Driver
url = jdbc:mysql://localhost/appfuse
username = root
password =
usertable = app_user
usertablekey = id
usertableuserfield = username
usertablepasswordfield = password
roletable = role
roletablekey = id
roletablerolefield = name
userroletable = user_role
userroletableuserkey = user_id
userroletablerolekey = role_id
cachetime = 300
&lt;/pre&gt;
&lt;p&gt;Of course, you&apos;ll need to &lt;a href=&quot;http://dev.mysql.com/downloads/mysql/5.5.html&quot;&gt;install MySQL&lt;/a&gt; for this to work. After installing it, you should be able to create an &quot;appfuse&quot; database and populate it using the following commands:
&lt;/p&gt;
&lt;pre&gt;
mysql -u root -p -e &apos;create database appfuse&apos;
curl https://gist.github.com/raw/958091/ceecb4a6ae31c31429d5639d0d1e6bfd93e2ea42/create-appfuse.sql &gt; create-appfuse.sql
mysql -u root -p appfuse &amp;lt; create-appfuse.sql
&lt;/pre&gt;
&lt;p&gt;Next you&apos;ll need to configure Jetty so it has MySQL&apos;s JDBC Driver in its classpath. To do this, add the following dependency just after the &amp;lt;configuration&amp;gt; element (before &amp;lt;executions&amp;gt;) in pom.xml:
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;dependencies&amp;gt;
    &amp;lt;!-- MySQL for JDBC Realm --&amp;gt;
    &amp;lt;dependency&amp;gt;
        &amp;lt;groupId&amp;gt;mysql&amp;lt;/groupId&amp;gt;
        &amp;lt;artifactId&amp;gt;mysql-connector-java&amp;lt;/artifactId&amp;gt;
        &amp;lt;version&amp;gt;5.1.14&amp;lt;/version&amp;gt;
    &amp;lt;/dependency&amp;gt;
&amp;lt;/dependencies&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Now run the &lt;em&gt;jetty-password.sh&lt;/em&gt; file in the root directory of the project to generate a password of your choosing. For example:&lt;/p&gt;
&lt;pre&gt;
$ sh jetty-password.sh javaeelogin
javaeelogin
OBF:1vuj1t2v1wum1u9d1ugo1t331uh21ua51wts1t3b1vur
MD5:53b176e6ce1b5183bc970ef1ebaffd44
&lt;/pre&gt;&lt;p&gt;The last two lines are obfuscated and MD5 versions of the password. Update the &lt;strong&gt;admin&lt;/strong&gt; user&apos;s password to this new value. You can do this with the following SQL statement.
&lt;/p&gt;
&lt;pre&gt;
UPDATE app_user SET password=&apos;MD5:53b176e6ce1b5183bc970ef1ebaffd44&apos; WHERE username = &apos;admin&apos;;
&lt;/pre&gt;
&lt;p&gt;Now if you restart Jetty, you should be able to login with admin/javaeelogin and view the list of users.
&lt;/p&gt;
&lt;p id=&quot;summary&quot;&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;br/&gt;
In this tutorial, you learned how to implement authentication using standard Java EE 6. In addition to the basic XML configuration, there&apos;s also some new methods in HttpServletRequest for Java EE 6 and Servlet 3.0:
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;authenticate(response)&lt;/li&gt;
&lt;li&gt;login(user, pass)&lt;/li&gt;
&lt;li&gt;logout()&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;This tutorial doesn&apos;t show you how to use them, but I did play with them a bit as part of my UJUG demo when implementing Ajax authentication. I found that login() did work, but it didn&apos;t persist the authentication for the users session. I also found that after calling logout(), I still needed to invalidate the session to completely logout the user. There are some additional limitations I found with Java EE authentication, namely:
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;No error messages for failed logins&lt;/li&gt;
&lt;li&gt;No Remember Me&lt;/li&gt;
&lt;li&gt;No auto-redirect from HTTP to HTTPS&lt;/li&gt;
&lt;li&gt;Container has to be configured&lt;/li&gt;
&lt;li&gt;Doesn&#8217;t support regular expressions for URLs&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;Of course, no error messages indicating why login failed is probably a good thing (you don&apos;t want to tell users why their credentials failed). However, when you&apos;re trying to figure out if your container is configured properly, the lack of container logging can be a pain.
  &lt;/p&gt;
  &lt;p&gt;In the next couple weeks, I&apos;ll post &lt;a href=&quot;http://raibledesigns.com/rd/entry/java_web_application_security_part1&quot;&gt;Part II&lt;/a&gt; of this series, where I&apos;ll show you how to implement this same set of features using Spring Security. In the meantime, please let me know if you have any questions.</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/upgraded_to_apache_roller_5</id>
        <title type="html">Upgraded to Apache Roller 5.0, RC4</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/upgraded_to_apache_roller_5"/>
        <published>2011-03-03T11:39:37-07:00</published>
        <updated>2011-03-21T17:38:23-06:00</updated> 
        <category term="/Roller" label="Roller" />
        <category term="apache" scheme="http://roller.apache.org/ns/tags/" />
        <category term="roller" scheme="http://roller.apache.org/ns/tags/" />
        <category term="apacheroller" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">Last Sunday, &lt;a href=&quot;http://rollerweblogger.org/roller&quot;&gt;Dave Johnson&lt;/a&gt; released &lt;a href=&quot;http://markmail.org/message/5pm2iixwoma64n26&quot;&gt;Apache Roller 5.0, RC4&lt;/a&gt;. Since I&apos;m an Apache Roller committer, and I&apos;ve been seeing issues with comments throwing exceptions on this site, I decided to upgrade. In doing so, I discovered a number of issues. Hopefully by documenting them here, you&apos;ll be able to upgrade from Roller 4 to Roller 5 without any issues.
&lt;/p&gt;
&lt;p&gt;To upgrade, I took a snapshot of my existing site and database and copied them locally. After getting everything setup locally (importing existing database and upgrading it), I started Tomcat and began solving problems.
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Database settings - JNDI vs. Properties&lt;/strong&gt;&lt;br/&gt;
With Roller 4, I configured by database settings in a ROOT.xml so they&apos;d be read using JNDI. With Roller 5, I got the following error when I tried to do this.
&lt;/p&gt;
&lt;pre&gt;
&amp;lt;openjpa-2.0.1-r422266:989424 fatal user error&gt; org.apache.openjpa.persistence.ArgumentException:
A JDBC Driver or DataSource class name must be specified in the ConnectionDriverName property.
       at org.apache.openjpa.jdbc.schema.DataSourceFactory.newDataSource(DataSourceFactory.java:76)
&lt;/pre&gt;
&lt;p&gt;I was able to get around this issue by adding the following to my roller-custom.properties:&lt;/p&gt;
&lt;pre&gt;
database.configurationType=jdbc
database.jdbc.driverClass=com.mysql.jdbc.Driver
database.jdbc.connectionURL=jdbc:mysql://localhost/rollerdb
database.jdbc.username=root
database.jdbc.password=
mail.configurationType=properties
mail.hostname=localhost
&lt;/pre&gt;
&lt;p&gt;After making this change, I received an error when Planet tried to startup:&lt;/p&gt;
&lt;pre&gt;
ERROR 2011-03-02 09:56:08,502 DatabaseProvider:errorMessage - ERROR: unable to obtain database connection. 
Likely problem: bad connection parameters or database unavailable.
FATAL 2011-03-02 09:56:08,502 RollerContext:contextInitialized - Roller Planet startup failed during app preparation
org.apache.roller.planet.business.startup.StartupException: ERROR: unable to obtain database connection. 
Likely problem: bad connection parameters or database unavailable.
&lt;/pre&gt;
&lt;p&gt;I don&apos;t remember why I enabled planet, but turning it off in roller-custom.properties seemed to solve the problem.&lt;/p&gt;
&lt;pre&gt;
planet.aggregator.enabled=false
&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Password Encyrption&lt;/strong&gt;&lt;br/&gt;
The next thing I tried to do was login. When this didn&apos;t work, I figured it must be related to password encryption. With Roller 4, I had to have &quot;passwds.encryption.enabled=true&quot; in roller-custom.properties. In Roller 5, I also had to add the encryption algorithm.&lt;/p&gt;
&lt;pre&gt;
passwds.encryption.algorithm=SHA
&lt;/pre&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;GZip Compression&lt;/strong&gt;&lt;br/&gt;
In November 2009, I optimized this site and used &lt;a href=&quot;http://raibledesigns.com/rd/entry/javascript_and_css_concatenation&quot;&gt;Roller&apos;s CompressionFilter and wro4j&lt;/a&gt; to gzip and concatenate JavaScript and CSS. With Roller 4, I used the CompressionFilter to compress *.css and *.js instead of using Wro4J&apos;s built-in gzip compression. The Roller 5 CompressionFilter seems to have issues with wro4j, so I had to disable it for *.css and *.js and use wro4j instead.
&lt;/p&gt;
&lt;p&gt;At this point, I figured I was good to go, so I zipped up my local WAR and scp&apos;ed it to raibledesigns.com. I stopped Tomcat and attempted to upgrade my production MySQL database (version 3.23.56). Below is the error I received.
&lt;/p&gt;
&lt;pre&gt;
$ mysql -u raible -p raible &lt; 400-to-500-migration.sql
Enter password:
ERROR 1064 (00000) at line 42: You have an error in your SQL syntax near &apos;as w set
   lastmodified = lastmodified,
   datecreated = datecreated,
   cr&apos; at line 1
&lt;/pre&gt;
&lt;p&gt;At this point, I figured my database might be slightly hosed, but since it was simply creating tables, I was probably OK. I restarted Tomcat and left the old version in place while I waited for a MySQL 5 database instance from my hosting provider, &lt;a href=&quot;http://kgbinternet.com&quot;&gt;KGB Internet&lt;/a&gt;. Once I got the new instance, I imported my backed-up database, ran the upgrade script and everything worked just peachy.&lt;/p&gt;
&lt;p&gt;I generally upgrade Roller by coping the new codebase over my old one. This is because I have a lot of symlinks and other files in my &quot;ROOT&quot; directory and like to keep those. In doing this, I found I had to do a couple things after copying everything over:
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Delete WEB-INF/lib and recopy from RC4&apos;s WEB-INF/lib.&lt;/li&gt;
&lt;li&gt;Delete WEB-INF/classes and recopy from RC4&apos;s WEB-INF/classes.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I then experienced some issues with JARs not being present for Roller&apos;s &lt;a href=&quot;http://wiki.java.net/bin/view/Javanet/JSPWikiPlugin&quot;&gt;JSPWikiPlugin&lt;/a&gt;. I enabled this long ago, but don&apos;t use it anymore. However, to keep old posts still working, I wanted to enable it. The downloads for the plugin seem to be gone, but luckily I found a copy and put all the JARs into my WEB-INF/lib directory.&lt;/p&gt;
&lt;p&gt;After starting Tomcat and browsing around a bit, I discovered two more issues:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Search doesn&apos;t seem to work. For example, there are &lt;a href=&quot;http://raibledesigns.com/rd/search?q=jQuery&quot;&gt;no results for jQuery&lt;/a&gt;.
&lt;li&gt;My &lt;a href=&quot;http://raibledesigns.com/rd/page/archives&quot;&gt;Archives&lt;/a&gt; page&apos;s calendar didn&apos;t work. It showed the following:
&lt;pre style=&quot;margin-top: 5px; margin-bottom: 0&quot;&gt;$calendarModel.showWeblogEntryCalendarBig($weblog, $cat)&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I was able to fix issue #2 by changing #showBigWeblogCalendar() to the following.
&lt;/p&gt;
&lt;pre&gt;#showWeblogEntryCalendarBig($model.weblog &quot;nil&quot;)&lt;/pre&gt;
&lt;p&gt;The first issue with search seems to remain.
&lt;/p&gt;
&lt;p&gt;If you notice any other issues on this site, please let me know. I&apos;ll try to get them fixed asap.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; I entered an &lt;a href=&quot;https://issues.apache.org/jira/browse/ROL-1910&quot;&gt;issue for my search problem&lt;/a&gt; in Roller&apos;s JIRA. I also managed to figure out that the problem is due to the old version of oscache that&apos;s needed by the JSPWiki plugin. Hopefully we can get the plugin upgraded to avoid this issue for other users.
</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/implementing_ajax_authentication_using_jquery</id>
        <title type="html">Implementing Ajax Authentication using jQuery, Spring Security and HTTPS</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/implementing_ajax_authentication_using_jquery"/>
        <published>2011-02-23T16:55:55-07:00</published>
        <updated>2012-11-11T02:00:40-07:00</updated> 
        <category term="/Java" label="Java" />
        <category term="springsecurity" scheme="http://roller.apache.org/ns/tags/" />
        <category term="jquery" scheme="http://roller.apache.org/ns/tags/" />
        <category term="spring" scheme="http://roller.apache.org/ns/tags/" />
        <category term="owasp" scheme="http://roller.apache.org/ns/tags/" />
        <category term="authentication" scheme="http://roller.apache.org/ns/tags/" />
        <category term="overstock.com" scheme="http://roller.apache.org/ns/tags/" />
        <category term="springmvc" scheme="http://roller.apache.org/ns/tags/" />
        <category term="ajax" scheme="http://roller.apache.org/ns/tags/" />
        <category term="appfuse-light" scheme="http://roller.apache.org/ns/tags/" />
        <category term="https" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">I&apos;ve always had a keen interest in implementing security in webapps. I implemented container-managed authentication (CMA) in &lt;a href=&quot;http://appfuse.org&quot;&gt;AppFuse&lt;/a&gt; in 2002, watched &lt;a href=&quot;http://raibledesigns.com/rd/entry/container_managed_authentication_enhancements_in&quot;&gt;Tomcat improve it&apos;s implementation in 2003&lt;/a&gt; and &lt;a href=&quot;http://raibledesigns.com/rd/entry/appfuse_refactorings_part_iii_remember&quot;&gt;implemented Remember Me with CMA&lt;/a&gt; in 2004. In 2005, I &lt;a href=&quot;http://raibledesigns.com/rd/entry/ann_appfuse_1_8_released&quot;&gt;switched from CMA to Acegi Security&lt;/a&gt; (now &lt;a href=&quot;http://static.springsource.org/spring-security/site/&quot;&gt;Spring Security&lt;/a&gt;) and never looked back. I&apos;ve been very happy with Spring Security over the years, but also hope to learn more about &lt;a href=&quot;http://shiro.apache.org/&quot;&gt;Apache Shiro&lt;/a&gt; and &lt;a href=&quot;http://www.quora.com/Is-OAuth-the-best-way-to-implement-security-for-a-JavaScript-API&quot;&gt;implementing OAuth to protect JavaScript APIs&lt;/a&gt; in the near future.
&lt;/p&gt;
&lt;p&gt;I was recently re-inspired to learn more about security when working on a new feature at &lt;a href=&quot;http://www.overstock.com&quot;&gt;Overstock.com&lt;/a&gt;. The feature hasn&apos;t been released yet, but basically boils down to allowing users to login without leaving a page. For example, if they want to leave a review on a product, they would click a link, be prompted to login, enter their credentials, then continue to leave their review. The login prompt and subsequent review would likely be implemented using a lightbox. While lightboxes are often seen in webapps these days because they look good, it&apos;s also possible &lt;a href=&quot;http://uxexchange.com/questions/1877/the-usability-of-lightbox-uis&quot;&gt;Lightbox UIs provide a poor user experience&lt;/a&gt;. User experience aside, I think it&apos;s interesting to see what&apos;s required to implement such a feature.
&lt;/p&gt;
&lt;p&gt;To demonstrate how we did it, I whipped up an example using AppFuse Light, jQuery and Spring Security. The source is available in my &lt;a href=&quot;https://github.com/mraible/ajax-login&quot;&gt;ajax-login&lt;/a&gt; project on GitHub. To begin, I wanted to accomplish a number of things to replicate the Overstock environment:
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Force HTTPS for authentication.&lt;/li&gt;
&lt;li&gt;Allow testing HTTPS without installing a certificate locally.&lt;/li&gt;
&lt;li&gt;Implement a RESTful LoginService that allows users to login.&lt;/li&gt;
&lt;li&gt;Implement login with Ajax, with the request coming from an insecure page.&lt;/li&gt;
&lt;/ol&gt;
&lt;p id=&quot;force-https&quot;&gt;&lt;strong&gt;Forcing HTTPS with Spring Security&lt;/strong&gt;&lt;br/&gt;
The first feature was fairly easy to implement thanks to Spring Security. Its configuration supports a &lt;a href=&quot;http://static.springsource.org/spring-security/site/docs/3.0.x/reference/ns-config.html#ns-requires-channel&quot;&gt;requires-channel&lt;/a&gt; attribute that can be used for this. I used this to force HTTPS on the &quot;users&quot; page and it subsequently causes the login to be secure.
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;intercept-url pattern=&quot;/app/users&quot; access=&quot;ROLE_ADMIN&quot; requires-channel=&quot;https&quot;/&amp;gt;
&lt;/pre&gt;
&lt;p id=&quot;testing-https&quot;&gt;&lt;strong&gt;Testing HTTPS without adding a certificate locally&lt;/strong&gt;&lt;br/&gt;
After making the above change in &lt;a href=&quot;https://github.com/mraible/ajax-login/blob/master/src/main/webapp/WEB-INF/security.xml&quot;&gt;security.xml&lt;/a&gt;, I had to modify my &lt;a href=&quot;https://github.com/mraible/ajax-login/blob/master/src/test/java/org/appfuse/examples/web/UserWebTest.java&quot;&gt;jWebUnit test&lt;/a&gt; to work with SSL. In reality, I didn&apos;t have to modify the test, I just had to modify the configuration that ran the test. In my &lt;a href=&quot;http://raibledesigns.com/rd/entry/integration_testing_with_http_https&quot;&gt;last post&lt;/a&gt;, I wrote about &lt;a href=&quot;http://blogs.sun.com/gc/entry/unable_to_find_valid_certification&quot;&gt;adding my &apos;untrusted&apos; cert to my JVM keystore&lt;/a&gt;. For some reason, this works for HttpClient, but not for jWebUnit/HtmlUnit. The good news is I figured out an easier solution - adding the trustStore and trustStore password as system properties to the maven-failsafe-plugin configuration.
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;artifactId&amp;gt;maven-failsafe-plugin&amp;lt;/artifactId&amp;gt;
&amp;lt;version&amp;gt;2.7.2&amp;lt;/version&amp;gt;
&amp;lt;configuration&amp;gt;
    &amp;lt;includes&amp;gt;
        &amp;lt;include&amp;gt;**/*WebTest.java&amp;lt;/include&amp;gt;
    &amp;lt;/includes&amp;gt;
    &amp;lt;systemPropertyVariables&amp;gt;
      &amp;lt;javax.net.ssl.trustStore&amp;gt;${project.build.directory}/ssl.keystore&amp;lt;/javax.net.ssl.trustStore&amp;gt;
      &amp;lt;javax.net.ssl.trustStorePassword&amp;gt;appfuse&amp;lt;/javax.net.ssl.trustStorePassword&amp;gt;
    &amp;lt;/systemPropertyVariables&amp;gt;
&amp;lt;/configuration&amp;gt;
&lt;/pre&gt;
&lt;p&gt;The disadvantage to doing things this way is you&apos;ll have to pass these in as arguments when running unit tests in your IDE.&lt;/p&gt;
&lt;p id=&quot;login-service&quot;&gt;&lt;strong&gt;Implementing a LoginService&lt;/strong&gt;&lt;br/&gt;
Next, I set about implementing a LoginService as a Spring MVC Controller that returns JSON thanks to the &lt;a href=&quot;http://blog.springsource.com/2010/01/25/ajax-simplifications-in-spring-3-0/&quot;&gt;@ResponseBody annotation and Jackson&lt;/a&gt;.
&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
package org.appfuse.examples.web;

import org.appfuse.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping(&quot;/api/login.json&quot;)
public class LoginService {

  @Autowired
  @Qualifier(&quot;authenticationManager&quot;)
  AuthenticationManager authenticationManager;

  @RequestMapping(method = RequestMethod.GET)
  @ResponseBody
  public LoginStatus getStatus() {
    Authentication auth = SecurityContextHolder.getContext().getAuthentication();
    if (auth != null &amp;&amp; !auth.getName().equals(&quot;anonymousUser&quot;) &amp;&amp; auth.isAuthenticated()) {
      return new LoginStatus(true, auth.getName());
    } else {
      return new LoginStatus(false, null);
    }
  }

  @RequestMapping(method = RequestMethod.POST)
  @ResponseBody
  public LoginStatus login(@RequestParam(&quot;j_username&quot;) String username,
                           @RequestParam(&quot;j_password&quot;) String password) {

    UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
    User details = new User(username);
    token.setDetails(details);

    try {
      Authentication auth = authenticationManager.authenticate(token);
      SecurityContextHolder.getContext().setAuthentication(auth);
      return new LoginStatus(auth.isAuthenticated(), auth.getName());
    } catch (BadCredentialsException e) {
      return new LoginStatus(false, null);
    }
  }

  public class LoginStatus {

    private final boolean loggedIn;
    private final String username;

    public LoginStatus(boolean loggedIn, String username) {
      this.loggedIn = loggedIn;
      this.username = username;
    }

    public boolean isLoggedIn() {
      return loggedIn;
    }

    public String getUsername() {
      return username;
    }
  }
}
&lt;/pre&gt;
&lt;p&gt;To verify this class worked as expected, I wrote a unit test using JUnit and &lt;a href=&quot;http://mockito.org/&quot;&gt;Mockito&lt;/a&gt;. I used Mockito because Overstock is transitioning to it from EasyMock and I&apos;ve found it very simple to use.&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
package org.appfuse.examples.web;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Matchers;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextImpl;

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

public class LoginServiceTest {

  LoginService loginService;
  AuthenticationManager authenticationManager;

  @Before
  public void before() {
    loginService = new LoginService();
    authenticationManager = mock(AuthenticationManager.class);
    loginService.authenticationManager = authenticationManager;
  }

  @After
  public void after() {
    SecurityContextHolder.clearContext();
  }

  @Test
  public void testLoginStatusSuccess() {
    Authentication auth = new TestingAuthenticationToken(&quot;foo&quot;, &quot;bar&quot;);
    auth.setAuthenticated(true);
    SecurityContext context = new SecurityContextImpl();
    context.setAuthentication(auth);
    SecurityContextHolder.setContext(context);

    LoginService.LoginStatus status = loginService.getStatus();
    assertTrue(status.isLoggedIn());
  }

  @Test
  public void testLoginStatusFailure() {
    LoginService.LoginStatus status = loginService.getStatus();
    assertFalse(status.isLoggedIn());
  }

  @Test
  public void testGoodLogin() {
    Authentication auth = new TestingAuthenticationToken(&quot;foo&quot;, &quot;bar&quot;);
    auth.setAuthenticated(true);
    when(authenticationManager.authenticate(Matchers.&amp;lt;Authentication&amp;gt;anyObject())).thenReturn(auth);
    LoginService.LoginStatus status = loginService.login(&quot;foo&quot;, &quot;bar&quot;);
    assertTrue(status.isLoggedIn());
    assertEquals(&quot;foo&quot;, status.getUsername());
  }

  @Test
  public void testBadLogin() {
    Authentication auth = new TestingAuthenticationToken(&quot;foo&quot;, &quot;bar&quot;);
    auth.setAuthenticated(false);
    when(authenticationManager.authenticate(Matchers.&lt;Authentication&gt;anyObject()))
        .thenThrow(new BadCredentialsException(&quot;Bad Credentials&quot;));
    LoginService.LoginStatus status = loginService.login(&quot;foo&quot;, &quot;bar&quot;);
    assertFalse(status.isLoggedIn());
    assertEquals(null, status.getUsername());
  }
}
&lt;/pre&gt;
&lt;p id=&quot;ajax-login&quot;&gt;&lt;strong&gt;Implement login with Ajax&lt;/strong&gt;&lt;br/&gt;
The last feature was the hardest to implement and still isn&apos;t fully working as I&apos;d hoped. I used jQuery and jQuery UI to implement a dialog that opens the login page on the same page rather than redirecting to the login page. The &quot;#demo&quot; locator refers to a button in the page.
&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
Passing in the &quot;ajax=true&quot; parameter disables SiteMesh decoration on the login page, something that&apos;s described in my &lt;a href=&quot;http://raibledesigns.com/rd/entry/ajaxified_body&quot;&gt;Ajaxified Body&lt;/a&gt; article.
&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
var dialog = $(&apos;&amp;lt;div&gt;&amp;lt;/div&gt;&apos;);

$(document).ready(function() {
    $.get(&apos;/login?ajax=true&apos;, function(data) {
        dialog.html(data);
        dialog.dialog({
            autoOpen: false,
	       title: &apos;Authentication Required&apos;
        });
    });

    $(&apos;#demo&apos;).click(function() {
      dialog.dialog(&apos;open&apos;);
      // prevent the default action, e.g., following a link
      return false;
    });
});
&lt;/pre&gt;
&lt;p class=&quot;quote&quot;&gt;
Instead of adding a click handler to a specific id, it&apos;s probably better to use a CSS class that indicates authentication is required for a link, or -- even better -- use Ajax to see if the link is secured.
&lt;/p&gt;
&lt;p&gt;The login page then has the following JavaScript to add a click handler to the &quot;login&quot; button that submits the request securely to the LoginService.
&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
var getHost = function() {
    var port = (window.location.port == &quot;8080&quot;) ? &quot;:8443&quot; : &quot;&quot;;
    return ((secure) ? &apos;https://&apos; : &apos;http://&apos;) + window.location.hostname + port;
};

var loginFailed = function(data, status) {
    $(&quot;.error&quot;).remove();
    $(&apos;#username-label&apos;).before(&apos;&amp;lt;div class=&quot;error&quot;&gt;Login failed, please try again.&amp;lt;/div&gt;&apos;);
};

$(&quot;#login&quot;).live(&apos;click&apos;, function(e) {
    e.preventDefault();
    $.ajax({url: getHost() + &quot;/api/login.json&quot;,
        type: &quot;POST&quot;,
        data: $(&quot;#loginForm&quot;).serialize(),
        success: function(data, status) {
            if (data.loggedIn) {
                // success
                dialog.dialog(&apos;close&apos;);
                location.href= getHost() + &apos;/users&apos;;
            } else {
                loginFailed(data);
            }
        },
        error: loginFailed
    });
});
&lt;/pre&gt;
&lt;p&gt;The biggest secret to making this all work (the HTTP -&gt; HTTPS communication, which is considered cross-domain), is the &lt;a href=&quot;http://www.sitepen.com/blog/2008/07/22/windowname-transport/&quot;&gt;window.name Transport&lt;/a&gt; and the &lt;a href=&quot;http://friedcellcollective.net/outbreak/jsjquerywindownameplugin/&quot;&gt;jQuery plugin&lt;/a&gt; that implements it. To make this plugin work with Firefox 3.6, I had to implement a Filter that adds Access-Control headers. &lt;a href=&quot;http://stackoverflow.com/questions/1099787/jquery-ajax-post-sending-options-as-request-method-in-firefox&quot;&gt;A question on Stackoverflow&lt;/a&gt; helped me figure this out.
&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
public class OptionsHeadersFilter implements Filter {

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) res;

        response.setHeader(&quot;Access-Control-Allow-Origin&quot;, &quot;*&quot;);
        response.setHeader(&quot;Access-Control-Allow-Methods&quot;, &quot;GET,POST&quot;);
        response.setHeader(&quot;Access-Control-Max-Age&quot;, &quot;360&quot;);
        response.setHeader(&quot;Access-Control-Allow-Headers&quot;, &quot;x-requested-with&quot;);

        chain.doFilter(req, res);
    }

    public void init(FilterConfig filterConfig) {
    }

    public void destroy() {
    }
}
&lt;/pre&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Issues&lt;/strong&gt;&lt;br/&gt;
I encountered a number of issues when implementing this in the &lt;a href=&quot;https://github.com/mraible/ajax-login&quot;&gt;ajax-login&lt;/a&gt; project. 
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you try to run this with ports (e.g. 8080 and 8443) in your URLs, you&apos;ll get a 501 (Not Implemented) response. Removing the ports by fronting with &lt;a href=&quot;http://raibledesigns.com/rd/entry/apache_2_on_os_x&quot;&gt;Apache and mod_proxy&lt;/a&gt; solves this problem.
&lt;li&gt;If you haven&apos;t accepted the certificate in your browser, the Ajax request will fail. In the example, I solved this by clicking on the &quot;Users&quot; tab to make a secure request, then going back to the homepage to try and login.&lt;/li&gt;
&lt;li&gt;The jQuery window.name version 0.9.1 doesn&apos;t work with jQuery 1.5.0. The error is &quot;$.httpSuccess function not found.&quot;&lt;/li&gt;
&lt;li&gt;Finally, even though I was able to authenticate successfully, I was &lt;a href=&quot;http://stackoverflow.com/questions/5087137/is-it-possible-to-programmatically-authenticate-with-spring-security-and-make-it&quot;&gt;unable to make the authentication persist&lt;/a&gt;.  I tried adding the following to persist the updated SecurityContext to the session, but it doesn&apos;t work. I expect the solution is to create a secure JSESSIONID cookie somehow.
&lt;pre class=&quot;brush: java&quot;&gt;
@Autowired
SecurityContextRepository repository;

@RequestMapping(method = RequestMethod.POST)
@ResponseBody
public LoginStatus login(@RequestParam(&quot;j_username&quot;) String username,
                         @RequestParam(&quot;j_password&quot;) String password,
                         HttpServletRequest request, HttpServletResponse response) {

    UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
    ...

    try {
        Authentication auth = authenticationManager.authenticate(token);
        SecurityContextHolder.getContext().setAuthentication(auth);
        // save the updated context to the session
        repository.saveContext(SecurityContextHolder.getContext(), request, response);
        return new LoginStatus(auth.isAuthenticated(), auth.getName());
    } catch (BadCredentialsException e) {
        return new LoginStatus(false, null);
    }
}
&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;br/&gt;
This article has shown you how to force HTTPS for login, how to do integration testing with a self-generated certificate, how to implement a LoginService with Spring MVC and Spring Security, as well as how to use jQuery to talk to a service cross-domain with the window.name Transport. While I don&apos;t have everything working as much as I&apos;d like, I hope this helps you implement a similar feature in your applications. 
&lt;/p&gt;
&lt;p&gt;
One thing to be aware of is with lightbox/dialog logins and HTTP -&gt; HTTPS is that users won&apos;t see a secure icon in their address bar. If your app has sensitive data, you might want to force https for your entire app. OWASP&apos;s &lt;a href=&quot;http://www.owasp.org/index.php/SSL_Best_Practices#Secure_Login_Pages&quot;&gt;Secure Login Pages&lt;/a&gt; has a lot of good tips in this area.
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; I&apos;ve posted a &lt;a href=&quot;http://demo.raibledesigns.com/ajax-login/&quot;&gt;demo of the ajax-login webapp&lt;/a&gt;. Thanks to &lt;a href=&quot;http://www.contegix.com/&quot;&gt;Contegix&lt;/a&gt; for hosting the demo and helping obtain/install an SSL certificate so quickly.
</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/2010_a_year_in_review</id>
        <title type="html">2010 - A Year in Review</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/2010_a_year_in_review"/>
        <published>2011-01-10T11:42:01-07:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/Roller" label="Roller" />
        <category term="yearinreview" scheme="http://roller.apache.org/ns/tags/" />
        <category term="newyear" scheme="http://roller.apache.org/ns/tags/" />
        <category term="blogging" scheme="http://roller.apache.org/ns/tags/" />
        <category term="2010" scheme="http://roller.apache.org/ns/tags/" />
        <category term="roller" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">2010 was a heckuva year, possibly one of my all-time favorites. It started with lots of &lt;a href=&quot;http://raibledesigns.com/rd/entry/2009_a_year_in_review&quot;&gt;anticipated fun&lt;/a&gt; and ended with lots of travel, skiing and relaxing and I&apos;m only just now finding time to write this post. I had a lot of goals when I started 2010; the top two being my favorite:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Happiness&lt;/li&gt;
&lt;li&gt;Girl&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The rest of my goals involved running, skiing, mountain biking and finishing The Bus. I also had some professional goals that involved open source, conferences, publishing articles and learning new technologies. I accomplished about 75% of my personal goals and 50% of my professional goals. I&apos;m pretty happy with these percentages considering how much fun I had last year.
&lt;/p&gt;
&lt;p&gt;In December 2009, I started working with &lt;a href=&quot;http://www.timewarnercable.com&quot;&gt;Time Warner Cable&lt;/a&gt; as their Chief Architect of Web Development. I was hired to help them build a team of hard-working developers that could quickly build their online video presence. In January, we started getting our ducks in a row with some &lt;a href=&quot;http://properosolutions.com/services/&quot;&gt;Agile Training&lt;/a&gt; in Virginia, followed by hiring some Agile Coaches. The only technical post I had during this process was about &lt;a href=&quot;http://raibledesigns.com/rd/entry/what_s_your_preferred_development&quot;&gt;development infrastructure stacks&lt;/a&gt;. Both at TWC and my current gig, I&apos;ve found myself enjoying the following stack:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Source Control: &lt;strong&gt;SVN&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Source Viewer: &lt;strong&gt;FishEye&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Wiki: &lt;strong&gt;Confluence&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Bug Tracker: &lt;strong&gt;JIRA&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Continuous Integration: &lt;strong&gt;Hudson&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We tried Git for a couple months at TWC, but ended up reverting to SVN after we had &quot;code deleted&quot; issues during one of our most intense development cycles. When the kids and I weren&apos;t skiing, we worked on documenting &lt;a href=&quot;http://raibledesigns.com/rd/entry/how_to_be_a_super&quot;&gt;How to be a Super Hero&lt;/a&gt; and &lt;a href=&quot;http://raibledesigns.com/rd/entry/upside_down_man_saves_the&quot;&gt;The Adventures of Upside Down Man&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In February, I &lt;a href=&quot;http://raibledesigns.com/rd/entry/reviews_for_grails_a_quick&quot;&gt;refreshed my Grails knowledge&lt;/a&gt;, later learning that it was tough to teach Grails to developers that didn&apos;t know Java. First of all, a lot of Grails and Groovy books are targeted at Java Developers. Secondly, the developers I was trying to sell it to had more interest in learning Java. Since I failed to sell Grails, we ended up using Spring + jQuery to build our app. I don&apos;t think was a bad decision as most of our development ended up being UI: ActionScript/Flex, Objective-C and jQuery/HTML.&lt;/p&gt;
&lt;p&gt;My &lt;a href=&quot;http://raibledesigns.com/rd/entry/17_macbook_pro_stolen_from&quot;&gt;laptop was stolen from my living room&lt;/a&gt; in early February. It worked out nicely in the end since I didn&apos;t lose any data (thanks to good backups) and my business insurance covered the loss. My parents came out and helped me finish &lt;a href=&quot;http://raibledesigns.com/rd/entry/my_guest_room_remodel_is&quot;&gt;remodeling my guest room&lt;/a&gt;. I posted about &lt;a href=&quot;http://raibledesigns.com/rd/entry/my_future_of_web_frameworks&quot;&gt;My Future of Web Frameworks Presentations&lt;/a&gt; and became a proud father when &quot;&lt;a href=&quot;http://raibledesigns.com/rd/entry/jack_s_skiing_like_a&quot;&gt;Jack was flying past both Abbie and I and giggling while doing it&lt;/a&gt;&quot;.&lt;/p&gt;
&lt;p&gt;By the beginning of March, we&apos;d &lt;a href=&quot;http://raibledesigns.com/rd/entry/how_we_hired_a_team&quot;&gt;hired a team of 10 at TWC&lt;/a&gt; and I &lt;a href=&quot;http://raibledesigns.com/rd/entry/fantastic_fun_in_jackson_hole&quot;&gt;took a trip to Jackson Hole&lt;/a&gt; to celebrate.&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
The thing I enjoyed the most about this trip was how well the group &lt;em&gt;jelled&lt;/em&gt;. Kudos to Chris for assembling such an awesome group and putting such a spectacular trip together. Can&apos;t wait for next year. 
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://farm5.static.flickr.com/4015/4414627855_f2b8c7c794.jpg&quot; title=&quot;Top o&apos; Jackson Hole&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm5.static.flickr.com/4015/4414627855_f2b8c7c794_m.jpg&quot; width=&quot;240&quot; height=&quot;180&quot; alt=&quot;Top o&apos; Jackson Hole&quot; style=&quot;border: 1px solid black&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;The next week, I flew to Las Vegas for The ServerSide Java Symposium and enjoyed sessions on &lt;a href=&quot;http://raibledesigns.com/rd/entry/the_cloud_computing_continuum_with&quot;&gt;Cloud&lt;/a&gt;, &lt;a href=&quot;http://raibledesigns.com/rd/entry/developing_rich_web_service_apis&quot;&gt;Web Service APIs&lt;/a&gt;, &lt;a href=&quot;http://raibledesigns.com/rd/entry/highly_interactive_software_with_java&quot;&gt;Flex&lt;/a&gt;, &lt;a href=&quot;http://raibledesigns.com/rd/entry/what_s_new_in_spring&quot;&gt;Spring 3&lt;/a&gt; and Cameron&apos;s &lt;a href=&quot;http://raibledesigns.com/rd/entry/c_java_and_net_lessons&quot;&gt;Lessons Learned from the Internet Age&lt;/a&gt;. I posted my &lt;a href=&quot;http://raibledesigns.com/rd/entry/tssjs_2010_presentations_and_summary&quot;&gt;Future of Web Frameworks and GWT vs. Flex Smackdown presentations&lt;/a&gt; after the conference.&lt;/p&gt;
&lt;p&gt;I ended March with &lt;a href=&quot;http://raibledesigns.com/rd/entry/the_trifecta_3_resorts_in&quot;&gt;The Trifecta&lt;/a&gt;.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://farm3.static.flickr.com/2728/4472155910_2a87f5e811.jpg&quot; title=&quot;You are about to experience Copper&apos;s High Alpine&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm3.static.flickr.com/2728/4472155910_2a87f5e811_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;You are about to experience Copper&apos;s High Alpine&quot; style=&quot;border: 1px solid black&quot;/&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm5.static.flickr.com/4061/4472157278_0ed25a10fc.jpg&quot; title=&quot;Nice ride up Storm King&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm5.static.flickr.com/4061/4472157278_0ed25a10fc_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Nice ride up Storm King&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot;/&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm3.static.flickr.com/2772/4472157540_503893a227.jpg&quot; title=&quot;Spaulding Bowl&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm3.static.flickr.com/2772/4472157540_503893a227_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Spaulding Bowl&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot;/&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm3.static.flickr.com/2732/4472157796_ef3a358c8f.jpg&quot; title=&quot;View from Enchanted Forest&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm3.static.flickr.com/2732/4472157796_ef3a358c8f_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;View from Enchanted Forest&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot;/&gt;&lt;/a&gt;

&lt;/p&gt;&lt;p&gt;For Easter, I purchased an iPad and &lt;a href=&quot;http://raibledesigns.com/rd/entry/my_ipad_review&quot;&gt;reviewed it a few days later&lt;/a&gt;. Seven months later, I&apos;m not enthralled with the iPad, but I do think it&apos;s a heckuva lot better than the Galaxy Tab. I don&apos;t use it much, except for movies when traveling. My kids are its primary users, mostly using it for games and Netflix. 
&lt;/p&gt;
&lt;p&gt;We visited my parents in Oregon and &lt;a href=&quot;http://raibledesigns.com/rd/entry/celebrating_my_mom_s_retirement&quot;&gt;celebrated my Mom&apos;s retirement&lt;/a&gt;.

&lt;p style=&quot;text-align: center&quot;&gt;

&lt;a href=&quot;http://farm5.static.flickr.com/4039/4583410595_074720572f.jpg&quot; title=&quot;Huckleberry Aficionado&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm5.static.flickr.com/4039/4583410595_074720572f_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Huckleberry Aficionado&quot; style=&quot;border: 1px solid black&quot; /&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm4.static.flickr.com/3327/4584043110_78188e39cf.jpg&quot; title=&quot;Oregon Garden Brewfest&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm4.static.flickr.com/3327/4584043110_78188e39cf_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Oregon Garden Brewfest&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot; /&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm5.static.flickr.com/4002/4584043518_074d446180.jpg&quot; title=&quot;Happy Beer Drinkers&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm5.static.flickr.com/4002/4584043518_074d446180_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Happy Beer Drinkers&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot;/&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm5.static.flickr.com/4052/4583418495_8703f271f0.jpg&quot; title=&quot;Old Friends&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm5.static.flickr.com/4052/4583418495_8703f271f0_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Old Friends&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot;/&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;The picture I took of my Mom and Abbie that weekend is one of my all-time favorites.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://farm5.static.flickr.com/4009/4584047006_4323c67012.jpg&quot; title=&quot;Abbie and Mimi&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm5.static.flickr.com/4009/4584047006_4323c67012_m.jpg&quot; width=&quot;240&quot; height=&quot;180&quot; alt=&quot;Abbie and Mimi&quot; style=&quot;border: 1px solid black&quot; /&gt;&lt;/a&gt;

&lt;/p&gt; 
&lt;p&gt;
At the end of April, I said &lt;a href=&quot;http://raibledesigns.com/rd/entry/farewell_to_the_2009_2010&quot;&gt;farewell to the ski season&lt;/a&gt;, getting in 25 days; a personal best. I&apos;m happy to report I have 10 days in so far this year, so my goal of 30 days looks to be well on track. No helicopter attempts yet, but hopefully soon.&lt;/p&gt;
&lt;p&gt;I ended the month with a work trip to Seattle and &lt;a href=&quot;http://raibledesigns.com/rd/entry/thanks_to_seattle_for_a&quot;&gt;painting the town red with my sister and Mya&lt;/a&gt;. &lt;/p&gt;

&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://farm5.static.flickr.com/4048/4585489353_cfe91a8aec.jpg&quot; title=&quot;Sweet Seats at the Mariner&apos;s Game&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm5.static.flickr.com/4048/4585489353_cfe91a8aec_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Sweet Seats at the Mariner&apos;s Game&quot; style=&quot;border: 1px solid black&quot; /&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm5.static.flickr.com/4044/4585489443_6d290c0a7b.jpg&quot; title=&quot;Rally Caps!&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm5.static.flickr.com/4044/4585489443_6d290c0a7b_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Rally Caps!&quot; style=&quot;border: 1px solid black;margin-left: 10px&quot;/&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm5.static.flickr.com/4012/4585491717_0de23a17b7.jpg&quot; title=&quot;Midnight&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm5.static.flickr.com/4012/4585491717_0de23a17b7_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Midnight&quot; style=&quot;border: 1px solid black;margin-left: 10px&quot;/&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm4.static.flickr.com/3319/4586117300_9bf3d4c6c8.jpg&quot; title=&quot;Wheeeeee!!&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm4.static.flickr.com/3319/4586117300_9bf3d4c6c8_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Wheeeeee!!&quot; style=&quot;border: 1px solid black;margin-left: 10px&quot;/&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;I returned to Denver and &lt;a href=&quot;http://twitter.com/#!/mraible/status/13172187335&quot;&gt;turned off my TV for a month&lt;/a&gt;. I &lt;a href=&quot;http://raibledesigns.com/rd/entry/life_without_tv&quot;&gt;wrote about my experience&lt;/a&gt; in early June. For Memorial Day Weekend, I went &lt;a href=&quot;http://raibledesigns.com/rd/entry/mountain_biking_in_moab&quot;&gt;mountain biking in Moab&lt;/a&gt; and had a blast at the &lt;a href=&quot;http://www.desertrocks.org/&quot;&gt;Desert Rocks Music Festival&lt;/a&gt;. The Porcupine Rim ride took us 4.5 hours and we tracked 26.75 miles. The several points in the trail with &quot;death on the right&quot; were truly epic.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;

&lt;a href=&quot;http://farm5.static.flickr.com/4046/4662670544_478cb254c5.jpg&quot; title=&quot;Hazard County Trail&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm5.static.flickr.com/4046/4662670544_478cb254c5_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Hazard County Trail&quot; style=&quot;border: 1px solid black&quot; /&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm5.static.flickr.com/4068/4662049049_9761af8d03.jpg&quot; title=&quot;Close to The Edge&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm5.static.flickr.com/4068/4662049049_9761af8d03_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Close to The Edge&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot; /&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm5.static.flickr.com/4038/4662049861_02a837094d.jpg&quot; title=&quot;Awesome Singletrack&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm5.static.flickr.com/4038/4662049861_02a837094d_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Awesome Singletrack&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot; /&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm5.static.flickr.com/4027/4662050007_80627bc86a.jpg&quot; title=&quot;Sweet View&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm5.static.flickr.com/4027/4662050007_80627bc86a_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Sweet View&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;June started with our annual &lt;a href=&quot;http://raibledesigns.com/rd/entry/a_nice_riding_weekend_before&quot;&gt;Ride to Red Rocks&lt;/a&gt;. I slept six hours and did the 25-mile off-road &lt;a href=&quot;http://www.elephantrockride.com/&quot;&gt;Elephant Rock&lt;/a&gt; ride the next morning. The next morning I hopped on a plane to Ireland for the &lt;a href=&quot;http://raibledesigns.com/rd/entry/presentations_from_the_irish_software&quot;&gt;Irish Software Show&lt;/a&gt;. My sister joined me and we had an &lt;a href=&quot;http://raibledesigns.com/rd/entry/my_incredible_trip_to_ireland&quot;&gt;incredible time&lt;/a&gt; with &lt;a href=&quot;http://www.jamievandyke.com/&quot;&gt;Jamie&lt;/a&gt;, Rob, &lt;a href=&quot;http://www.johnmwillis.com/&quot;&gt;John&lt;/a&gt; and &lt;a href=&quot;http://www.joshlong.com/&quot;&gt;Josh&lt;/a&gt;.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;

&lt;a href=&quot;http://farm5.static.flickr.com/4006/4699508311_77a05c609a.jpg&quot; title=&quot;Straight to the top!&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm5.static.flickr.com/4006/4699508311_77a05c609a_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Straight to the top!&quot; style=&quot;border: 1px solid black&quot;/&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm5.static.flickr.com/4015/4699508737_612d1b1b42.jpg&quot; title=&quot;Mmmmm, Guinness&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm5.static.flickr.com/4015/4699508737_612d1b1b42_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Mmmmm, Guinness&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot;/&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm5.static.flickr.com/4052/4700141820_3943b0dea6.jpg&quot; title=&quot;The Storehouse is shaped like a pint glass&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm5.static.flickr.com/4052/4700141820_3943b0dea6_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;The Storehouse is shaped like a pint glass&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot;/&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm2.static.flickr.com/1285/4700143032_120cd3d789.jpg&quot; title=&quot;Brainwave&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm2.static.flickr.com/1285/4700143032_120cd3d789_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Brainwave&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot;/&gt;&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;Upon returning to work, I got to have an awkward conversation with my client about all the vacation I was taking.
&lt;/p&gt;
&lt;p&gt;
&lt;a href=&quot;http://farm2.static.flickr.com/1359/4723660690_9313a26d78.jpg&quot; title=&quot;Fire in Background, 100 foot tall flames&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm2.static.flickr.com/1359/4723660690_9313a26d78_m.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Fire in Background, 100 foot tall flames&quot; style=&quot;border: 1px solid black&quot; class=&quot;picture&quot; /&gt;&lt;/a&gt;
For Father&apos;s Day, my parents drove to Denver and we packed up the camping gear for a &lt;a href=&quot;http://raibledesigns.com/rd/entry/another_fun_father_s_day&quot;&gt;fun weekend at The Great Sand Dunes&lt;/a&gt;.
After The Dunes, my parents and kids drove to The Cabin, camping out and touring Yellowstone along the way. That Thursday, June 24th, I attended my good friend &lt;a href=&quot;http://jasonjbarton.com/&quot;&gt;Jason&lt;/a&gt;&apos;s birthday party in Lodo.&lt;/p&gt;
&lt;p&gt;That&apos;s the night I met Trish. &lt;/p&gt;
&lt;p&gt;I saw her switch from a Martini to a Guinness and I knew I had to talk to her. I introduced myself and quickly found myself conflicted with having a soon-to-expire iPhone 4 reservation at the Apple Store. I rushed out to grab the phone, returning because there was something special about Trish. We talked for a couple hours that night ... me mesmerized, her smiling a lot. I was dumbfounded to find such a cool person existed in the world. At the end of the night, I got her number and drove home with a feeling that my life was about to change. 
&lt;/p&gt;
&lt;p&gt;The next day, I began a road trip to The Last Best Place for a &lt;a href=&quot;http://raibledesigns.com/rd/entry/my_summer_vacation_in_montana1&quot;&gt;Montana Summer Vacation&lt;/a&gt;. 

&lt;p style=&quot;text-align: center&quot;&gt;

&lt;a href=&quot;http://farm5.static.flickr.com/4096/4788758981_ea7346ab25.jpg&quot; rel=&quot;lightbox[yearinreview2010]&quot; title=&quot;Big Sky Country&quot;&gt;&lt;img src=&quot;//farm5.static.flickr.com/4096/4788758981_ea7346ab25_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Big Sky Country&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm5.static.flickr.com/4077/4788762471_187425438b.jpg&quot; title=&quot;Ready for the Parade&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm5.static.flickr.com/4077/4788762471_187425438b_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Ready for the Parade&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot;&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm5.static.flickr.com/4098/4788767939_d273d4ddf5.jpg&quot; title=&quot;Chris Auchenbach&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm5.static.flickr.com/4098/4788767939_d273d4ddf5_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Chris Auchenbach&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot;&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm5.static.flickr.com/4143/4789398916_a70dd2077e.jpg&quot; title=&quot;Meadow Lake Golf Course in Columbia Falls&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm5.static.flickr.com/4143/4789398916_a70dd2077e_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Meadow Lake Golf Course in Columbia Falls&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot;&gt;&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;During that trip, Trish and I exchanged occasional text messages and I told many friends about the kick-ass girl I met. It was another great Montana vacation.
&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
My favorite part of this year&apos;s trip to The Cabin was seeing it as a home again. My Mom retired in April and my parents moved back to Montana shortly after. Seeing how happy they are there is truly magical. I especially enjoy the thought of visiting them and all the wonderful folks in the Swan Valley many, many times in the future. 
&lt;/p&gt;
&lt;p&gt;The kids and I drove like bats out of hell and made it back to Denver (950 miles) in 14.25 hours. I quickly scheduled a first date with Trish that Wednesday, and went back to work at TWC with a renewed energy. After our first date, I formed a sort of perma-grin. That Friday was my birthday. I&apos;ve been having a blast with Trish ever since.
&lt;/p&gt;
&lt;p&gt;July ended with a trip to the Lost Coast for &lt;a href=&quot;http://raibledesigns.com/rd/entry/jess_and_lili_s_legendary&quot;&gt;Jess and Lili&apos;s Wedding&lt;/a&gt;. &lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;The reception afterwards was a truly spectacular party that lasted well into the evening. Clint and I vowed to go to bed early, but we ended up having so much fun we closed the place down. Jess and Lili were an instrumental part in creating a spectacular night, especially with their &lt;a href=&quot;http://www.youtube.com/watch?v=zVNYDwQXggI&quot;&gt;wedding dance&lt;/a&gt; and infectious happiness.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://farm5.static.flickr.com/4128/4836504101_199c9821ba.jpg&quot; title=&quot;Lili and Jess&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm5.static.flickr.com/4128/4836504101_199c9821ba_m.jpg&quot; width=&quot;240&quot; height=&quot;180&quot; alt=&quot;Lili and Jess&quot; style=&quot;border: 1px solid black&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;In August, I celebrated this blog&apos;s &lt;a href=&quot;http://raibledesigns.com/rd/entry/happy_8th_birthday_to_this&quot;&gt;8th birthday&lt;/a&gt; and we started our &quot;60-Day Push&quot; at work. The goal of our 60-Day Push was to re-write our Video Portal, iPad and Sony Blu-ray apps from scratch, without politics dictating their features. We hired &lt;a href=&quot;http://method.com/&quot;&gt;Method&lt;/a&gt; for design, chose our own features and went to work. I &lt;a href=&quot;http://raibledesigns.com/rd/entry/how_s_the_ol_team&quot;&gt;wrote about the success of this effort&lt;/a&gt; in October.
&lt;/p&gt;
&lt;p&gt;
I neglected to write about the Denver Cruisers &lt;a href=&quot;http://www.examiner.com/nightlife-photography-in-denver/denver-cruisers-saints-and-sinners&quot;&gt;Saints and Sinners&lt;/a&gt; ride or how much fun we had listening to &lt;a href=&quot;http://www.redrocksonline.com/CalendarEventDisplay.aspx?id=41820&quot;&gt;B.B. King at Red Rocks&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;
Abbie and Jack&apos;s &lt;a href=&quot;http://raibledesigns.com/rd/entry/the_first_day_of_school1&quot;&gt;first day of school&lt;/a&gt; was on August 16th.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://farm5.static.flickr.com/4121/4899887269_9754511cec.jpg&quot; title=&quot;Pretend like you&apos;re playing Wii Jack!&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm5.static.flickr.com/4121/4899887269_9754511cec_m.jpg&quot; width=&quot;240&quot; height=&quot;180&quot; alt=&quot;Pretend like you&apos;re playing Wii Jack!&quot; style=&quot;border: 1px solid black&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;At the end of August, we celebrated &lt;a href=&quot;http://www.flickr.com/photos/mraible/sets/72157624769760851/&quot;&gt;Jack&apos;s 6th Birthday&lt;/a&gt; and attended my &lt;a href=&quot;http://www.flickr.com/photos/mraible/sets/72157624894607518/&quot;&gt;Cousin Amy&apos;s Wedding&lt;/a&gt; in Missouri. 
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;

&lt;a href=&quot;http://farm5.static.flickr.com/4084/4965213510_20c8e36f87.jpg&quot; rel=&quot;lightbox[yearinreview2010]&quot; title=&quot;Wheee!&quot;&gt;&lt;img src=&quot;//farm5.static.flickr.com/4084/4965213510_20c8e36f87_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Wheee!&quot; style=&quot;border: 1px solid black&quot;/&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm5.static.flickr.com/4152/4964612385_06ac5a0ef4.jpg&quot; rel=&quot;lightbox[yearinreview2010]&quot; title=&quot;Super Mario Bros. Cake&quot;&gt;&lt;img src=&quot;//farm5.static.flickr.com/4152/4964612385_06ac5a0ef4_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Super Mario Bros. Cake&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot;/&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm5.static.flickr.com/4132/4965217230_2cd7bc5e37.jpg&quot; rel=&quot;lightbox[yearinreview2010]&quot; title=&quot;Tebow Fan&quot;&gt;&lt;img src=&quot;//farm5.static.flickr.com/4132/4965217230_2cd7bc5e37_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Tebow Fan&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot;/&gt;&lt;/a&gt;

&lt;br/&gt;&lt;br/&gt;

&lt;a href=&quot;http://farm5.static.flickr.com/4085/4964754995_cec227df27.jpg&quot; rel=&quot;lightbox[yearinreview2010]&quot; title=&quot;The Happy Couple&quot;&gt;&lt;img src=&quot;//farm5.static.flickr.com/4085/4964754995_cec227df27_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;The Happy Couple&quot; style=&quot;border: 1px solid black&quot;/&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm5.static.flickr.com/4086/4964743889_4f813aa9ee.jpg&quot; rel=&quot;lightbox[yearinreview2010]&quot; title=&quot;Jack and Abbie&quot;&gt;&lt;img src=&quot;//farm5.static.flickr.com/4086/4964743889_4f813aa9ee_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Jack and Abbie&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot;/&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm5.static.flickr.com/4100/4964746601_ee8cbbf3b9.jpg&quot; rel=&quot;lightbox[yearinreview2010]&quot; title=&quot;The Cousins&quot;&gt;&lt;img src=&quot;//farm5.static.flickr.com/4100/4964746601_ee8cbbf3b9_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;The Cousins&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot;/&gt;&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;September rolled in, we finished the majority of the work in our 60-Day Push and Trish and I drove up to Estes Park for an &lt;a href=&quot;http://raibledesigns.com/rd/entry/an_epic_weekend_in_estes&quot;&gt;epic weekend&lt;/a&gt; at the &lt;a href=&quot;http://www.scotfest.com/Scottish_Festival/Home.html&quot;&gt;Scottish-Irish Festival&lt;/a&gt;. We rode our bikes in the parade (by accident), enjoyed a few pints and even did a bit of fly-fishing near our riverside accommodations. &lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;

&lt;a href=&quot;http://farm5.static.flickr.com/4090/4988535019_34fc7a67d7.jpg&quot; title=&quot;Beautiful View&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm5.static.flickr.com/4090/4988535019_34fc7a67d7_s.jpg&quot; width=&quot;75&quot; height=&quot;75&quot; alt=&quot;Beautiful View&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot; /&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm5.static.flickr.com/4103/4988536455_db7ccf0b07.jpg&quot; title=&quot;Plane in Sky&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm5.static.flickr.com/4103/4988536455_db7ccf0b07_s.jpg&quot; width=&quot;75&quot; height=&quot;75&quot; alt=&quot;Plane in Sky&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot; /&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm5.static.flickr.com/4133/4989142114_f1e7b43e99.jpg&quot; title=&quot;Ride to Stanley Hotel&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm5.static.flickr.com/4133/4989142114_f1e7b43e99_s.jpg&quot; width=&quot;75&quot; height=&quot;75&quot; alt=&quot;Ride to Stanley Hotel&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot; /&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm5.static.flickr.com/4154/4989143134_68fb3b7801.jpg&quot; title=&quot;View from The Stanley Hotel&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm5.static.flickr.com/4154/4989143134_68fb3b7801_s.jpg&quot; width=&quot;75&quot; height=&quot;75&quot; alt=&quot;View from The Stanley Hotel&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;We listened to the 1st 2010 Broncos game while driving back from Estes and slipped into a Rockies game shortly after losing. Our sadness over the Broncos loss was erased within a couple hours as we celebrated &lt;a href=&quot;http://en.wikipedia.org/wiki/Jason_Giambi&quot;&gt;Jason Giambi&apos;s&lt;/a&gt; walk-off home run. Unfortunately, the Rockies didn&apos;t make it to the post-season, but the Broncos looked good at their &lt;a href=&quot;http://www.flickr.com/photos/mraible/sets/72157624879516331/&quot;&gt;home opener&lt;/a&gt;. 
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://farm5.static.flickr.com/4109/5011958664_88c02e1dfb.jpg&quot; title=&quot;Fighter Jets&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm5.static.flickr.com/4109/5011958664_88c02e1dfb_m.jpg&quot; width=&quot;240&quot; height=&quot;179&quot; alt=&quot;Fighter Jets&quot; style=&quot;border: 1px solid black;&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;October came quickly and I wrote &lt;a href=&quot;http://raibledesigns.com/rd/entry/how_s_the_ol_team&quot;&gt;How&apos;s the ol&apos; Team Doing&lt;/a&gt; and &lt;a href=&quot;http://raibledesigns.com/rd/entry/re_moving_from_spring_to&quot;&gt;defended the Age of Frameworks&lt;/a&gt;. At the end of October, we moved into &lt;a href=&quot;http://www.flickr.com/photos/mraible/sets/72157625130347727/&quot;&gt;TWC&apos;s Lodo Office&lt;/a&gt; on Wynkoop. That weekend, we dressed up as superheros for &lt;a href=&quot;http://www.flickr.com/photos/mraible/sets/72157625285301110/&quot;&gt;Halloween&lt;/a&gt;. 
&lt;/p&gt;
&lt;p&gt;November started off with &lt;a href=&quot;http://raibledesigns.com/rd/entry/abbie_s_8th_birthday_and&quot;&gt;Abbie&apos;s birthday&lt;/a&gt; and Trish got to meet the kids for the first time. From there, we went into traveling-like-madmen mode. We enjoyed &lt;a href=&quot;http://www.flickr.com/photos/mraible/sets/72157625463274916/&quot;&gt;suite seats&lt;/a&gt; at the Broncos vs. Kansas City game, then flew to Amsterdam for &lt;a href=&quot;http://devoxx.com/display/Devoxx2K10/Home&quot;&gt;Devoxx&lt;/a&gt; the next morning (performing an &lt;a href=&quot;http://raibledesigns.com/rd/entry/appfuse_2_1_milestone_2&quot;&gt;AppFuse release&lt;/a&gt; along the way). I presented on Thursday and posted my &lt;a href=&quot;http://raibledesigns.com/rd/entry/my_comparing_jvm_web_frameworks&quot;&gt;Comparing JVM Web Frameworks presentation&lt;/a&gt; shortly after. I &lt;a href=&quot;http://raibledesigns.com/rd/entry/an_awesome_trip_to_amsterdam&quot;&gt;wrote about our trip&lt;/a&gt; while &lt;a href=&quot;http://www.flickr.com/photos/mraible/sets/72157625547334972/&quot;&gt;at The Cabin for Thanksgiving&lt;/a&gt;. &lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;

&lt;a href=&quot;http://farm5.static.flickr.com/4111/5205300493_0fa88e937a.jpg&quot; title=&quot;Sunset in Amsterdam&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm5.static.flickr.com/4111/5205300493_0fa88e937a_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Sunset in Amsterdam&quot; style=&quot;border: 1px solid black&quot; /&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm6.static.flickr.com/5165/5205310971_8908fc905f.jpg&quot; title=&quot;Waffles at D&#233;sir&#233; de Lille&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm6.static.flickr.com/5165/5205310971_8908fc905f_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Waffles at D&#233;sir&#233; de Lille&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot; /&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm6.static.flickr.com/5047/5205310525_35786d0758.jpg&quot; title=&quot;Partying with the Adobe Folks&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm6.static.flickr.com/5047/5205310525_35786d0758_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Partying with the Adobe Crew&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot; /&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm5.static.flickr.com/4149/5205911660_718ab0bcd4.jpg&quot; title=&quot;Ghent&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm5.static.flickr.com/4149/5205911660_718ab0bcd4_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Ghent&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot;/&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;I flew back from Montana, stopped in at the Lodo office for a couple days, then hopped on a plane to the Rich Web Experience with Trish. My &lt;a href=&quot;http://raibledesigns.com/rd/entry/my_everything_you_ever_wanted&quot;&gt;presentations&lt;/a&gt; went well, sparked some &lt;a href=&quot;http://raibledesigns.com/rd/entry/how_i_calculated_ratings_for&quot;&gt;controversy&lt;/a&gt;, and we &lt;a href=&quot;http://raibledesigns.com/rd/entry/a_fun_week_in_florida&quot;&gt;raced to Key West&lt;/a&gt; to celebrate the end of the conference season. 
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://farm6.static.flickr.com/5288/5240091611_6af06fa815.jpg&quot; title=&quot;Dreams do come true.&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm6.static.flickr.com/5288/5240091611_6af06fa815_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Dreams do come true.&quot; style=&quot;border: 1px solid black&quot; /&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm6.static.flickr.com/5166/5240688766_45ed6a544e.jpg&quot; title=&quot;Sunset in Key Largo&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm6.static.flickr.com/5166/5240688766_45ed6a544e_t.jpg&quot; width=&quot;100&quot; height=&quot;74&quot; alt=&quot;Sunset in Key Largo&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot; /&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm6.static.flickr.com/5247/5240688882_dceac2f8a5.jpg&quot; title=&quot;Pi&#241;a Coladas in Key Largo&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm6.static.flickr.com/5247/5240688882_dceac2f8a5_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Pi&#241;a Coladas in Key Largo&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot; /&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm6.static.flickr.com/5202/5240092461_5658cb18ed.jpg&quot; title=&quot;Key Largo Sunset&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm6.static.flickr.com/5202/5240092461_5658cb18ed_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Key Largo Sunset&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
I returned to Denver for my last week at TWC, enjoyed a couple days of skiing, then headed to Utah for an interview with &lt;a href=&quot;http://overstock.com&quot;&gt;Overstock.com&lt;/a&gt;. While I enjoyed my time at TWC, my contract duration was up and being a full-time employee didn&apos;t give me the vacation time I tend to enjoy. My interview with Overstock.com was two days, with the 2nd day &lt;a href=&quot;http://www.flickr.com/photos/mraible/sets/72157625716396790/&quot;&gt;on the slopes at Snowbird&lt;/a&gt;. I was very impressed by the company, people and interview process and agreed to work there on the way to the airport.&lt;/p&gt;
&lt;p&gt;
I returned to Denver for &lt;a href=&quot;http://www.flickr.com/photos/mraible/sets/72157625590980727/&quot;&gt;Trish&apos;s Birthday Weekend at Breckenridge&lt;/a&gt;, then flew down to Naples on Wednesday to spend &lt;a href=&quot;http://www.flickr.com/photos/mraible/sets/72157625591245697/&quot;&gt;Christmas with her family&lt;/a&gt;. It was my first time meeting her parents, but that didn&apos;t stop us from having a great time talking, beach-going, golfing and relaxing. 
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://farm6.static.flickr.com/5003/5311035104_99de319abb.jpg&quot; title=&quot;Trish and her awesome parents&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm6.static.flickr.com/5003/5311035104_99de319abb_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Trish and her awesome parents&quot; style=&quot;border: 1px solid black&quot;/&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm6.static.flickr.com/5163/5310446881_87f57d5506.jpg&quot; title=&quot;Hot Santa&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm6.static.flickr.com/5163/5310446881_87f57d5506_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Hot Santa&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot;/&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm6.static.flickr.com/5207/5311037298_1415347e60.jpg&quot; title=&quot;Scotch: Making White Men Dance since 1494.&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm6.static.flickr.com/5207/5311037298_1415347e60_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Scotch: Making White Men Dance since 1494.&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot;/&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm6.static.flickr.com/5048/5311037698_a5b6b29f8c.jpg&quot; title=&quot;Sunset in Naples&quot; rel=&quot;lightbox[yearinreview2010]&quot;&gt;&lt;img src=&quot;//farm6.static.flickr.com/5048/5311037698_a5b6b29f8c_t.jpg&quot; width=&quot;100&quot; height=&quot;75&quot; alt=&quot;Sunset in Naples&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot;/&gt;&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;
We returned to Denver, I &lt;a href=&quot;http://twitter.com/#!/mraible/status/20247873694203904&quot; title=&quot;Made good progress on fixing AppFuse issues for 2.1.0, but not enough for a release. Will have to wait until next year.&quot;&gt;tried to get AppFuse 2.1 finished&lt;/a&gt; and then we celebrated New Years with friends in Steamboat. 
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://farm6.static.flickr.com/5281/5343664200_4d11c44451.jpg&quot; rel=&quot;lightbox[yearinreview2010]&quot; title=&quot;Good Morning from Steamboat!&quot;&gt;&lt;img src=&quot;//farm6.static.flickr.com/5281/5343664200_4d11c44451_m.jpg&quot; width=&quot;240&quot; height=&quot;180&quot; alt=&quot;Good Morning from Steamboat!&quot; style=&quot;border: 1px solid black&quot;/&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm6.static.flickr.com/5006/5343055387_8e6cd3e2e3.jpg&quot; rel=&quot;lightbox[yearinreview2010]&quot; title=&quot;Sunrise over Steamboat&quot;&gt;&lt;img src=&quot;//farm6.static.flickr.com/5006/5343055387_8e6cd3e2e3_m.jpg&quot; width=&quot;240&quot; height=&quot;179&quot; alt=&quot;Sunrise over Steamboat&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot;/&gt;&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;As I reminisce about last year, I can&apos;t help but smile. While I&apos;ve been a happy person for a while, having someone to share your life with is a special thing. I feel like I dreamed up Trish a couple years ago. I was looking for someone that liked to do my favorite activities: mountain biking, skiing, traveling the world and enjoying good beer. I found that and more in Trish and couldn&apos;t be happier. 
&lt;/p&gt;
&lt;p&gt;
I didn&apos;t write as many technical posts on this blog as I&apos;d like to, but I attribute that mostly to the lack of learning new things at TWC. At Overstock.com, I expect that to change and hope to have more technical articles in the coming year. 
&lt;/p&gt;
&lt;p&gt;
At the end of last year&apos;s &lt;a href=&quot;http://raibledesigns.com/rd/entry/2009_a_year_in_review&quot;&gt;Year in Review&lt;/a&gt;, I wrote:&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
I hope to speak at (or attend) 3 conferences, finish up The Bus and do a whole bunch of skiing and mountain biking.
&lt;/p&gt;
&lt;p&gt;I accomplished all but one goal: finishing The Bus. In 2011, I plan on doing two main things: keep rockin&apos; it with Trish and finishing The Bus. Everything else is gravy. &lt;img src=&quot;https://raibledesigns.com/images/smileys/wink.gif&quot; class=&quot;smiley&quot; alt=&quot;;-)&quot; title=&quot;;-)&quot; /&gt;
&lt;/p&gt;
&lt;p&gt;It&apos;s gonna be a spectacular year.  
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://farm6.static.flickr.com/5249/5310864430_3630ce2f5a.jpg&quot; rel=&quot;lightbox[yearinreview2010]&quot; title=&quot;Double Rainbow&quot;&gt;&lt;img src=&quot;//farm6.static.flickr.com/5249/5310864430_3630ce2f5a_m.jpg&quot; width=&quot;240&quot; height=&quot;180&quot; alt=&quot;Double Rainbow&quot; style=&quot;border: 1px solid black&quot;/&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/how_s_the_ol_team</id>
        <title type="html">How&apos;s the ol&apos; Team Doing?</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/how_s_the_ol_team"/>
        <published>2010-10-21T09:00:41-06:00</published>
        <updated>2012-11-11T02:00:40-07:00</updated> 
        <category term="/Java" label="Java" />
        <category term="recruiting" scheme="http://roller.apache.org/ns/tags/" />
        <category term="video" scheme="http://roller.apache.org/ns/tags/" />
        <category term="java" scheme="http://roller.apache.org/ns/tags/" />
        <category term="ipad" scheme="http://roller.apache.org/ns/tags/" />
        <category term="hiring" scheme="http://roller.apache.org/ns/tags/" />
        <category term="team" scheme="http://roller.apache.org/ns/tags/" />
        <category term="rest" scheme="http://roller.apache.org/ns/tags/" />
        <category term="android" scheme="http://roller.apache.org/ns/tags/" />
        <category term="developers" scheme="http://roller.apache.org/ns/tags/" />
        <category term="html5" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">Back in March, I wrote about &lt;a href=&quot;http://raibledesigns.com/rd/entry/how_we_hired_a_team&quot;&gt;How We Hired a Team of 10 in 2 Months&lt;/a&gt;: &lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
This week, we on-boarded 3 of our final 4 developers. I breathed a big sigh of relief that the hiring was over and we could get back to slinging code and making things happen. As luck would have it, I received an e-mail from my boss on Tuesday that the hiring engine is starting up again and we need to hire 6 more developers. While I&apos;m not anxious to start the Hiring Engine again, I am glad to know it works well and it &lt;em&gt;has&lt;/em&gt; helped us build a great team.
&lt;/p&gt;
&lt;p&gt;We never ended up hiring those additional 6 developers, but we&apos;ve had quite a ride since March. One of the first commenters on my original post wrote:&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;I hope that whirlwind of hiring works out for you Matt. But, don&apos;t you think it&apos;s a bit early to be declaring success? 
&lt;/p&gt;
&lt;p&gt;At that time, we still had a lot of work to do to become a successful team. However, by the end of March, we&apos;d finished our first deliverable - converting a somewhat slow ColdFusion/jQuery/Video webapp into a fast Java/jQuery/Video webapp. The slowness wasn&apos;t due to ColdFusion, but mostly performance, caching and YSlow-type optimizations. At that time, we surprised the folks that were in charge of our app. They didn&apos;t think we&apos;d finish so fast and it took them awhile to decide what to do with our work. 
&lt;/p&gt;
&lt;p&gt;
While we were waiting for the product folks to launch our app in April/May, we decided to experiment with developing some new clients. So we wrote an iPhone app, an Android App, an iPhone/Android/iPad/HTML5 version of our webapp and a Blu-ray client. All of these applications used our webapp&apos;s backend RESTful services and we were able to learn the SDKs and implement all the apps in a matter of weeks. In May, we demoed all our clients and got rave reviews from executives. We celebrated that afternoon with a big sigh of relief.
&lt;/p&gt;
&lt;p&gt;The glowing from our many-clients demo was short-lived. A week later, we were asked to enhance our iPad app to include TV-Remote type features, namely channel-changing and DVR functionality. After freaking out and trying to figure out how to deliver such an app in a week, the demo was rescheduled and we were afforded 2 weeks to build the app.
After much frantic development, we were able to complete the app in time and the &lt;a href=&quot;http://gizmodo.com/5611578/time-warner-cable-ipad-app-to-deliver-on+demand-tv-streaming&quot;&gt;demo was published to YouTube&lt;/a&gt; a couple months later. 
&lt;/p&gt;
&lt;p&gt;In June, if you asked me if we were a successful team, I would&apos;ve definitely said &quot;Yes!&quot; We&apos;d been asked to develop apps for 3 different demos and we delivered on-time. 
&lt;/p&gt;
&lt;p&gt;
The remainder of June and July we slipped into a bit of limbo where we weren&apos;t asked to develop anything, but simply maintain and enhance the stuff we&apos;d already developed. After a few weeks of doing this, several of us began to wonder if the apps we&apos;d developed would ever see the light of day. We expressed this concern to our VP and a new idea was hatched: The 60-Day Push.
&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;60-Day Push&lt;/em&gt; was designed to eliminate politics and meetings and allow us to develop a 3-screen (PC, iPad, TV) experience for our video content. We decided to aim high and try to complete most of the work in 30-days, so we could do executive demos of our progress. We started all our applications from scratch, split into Services, Portal, TV and iPad teams and worked with &lt;a href=&quot;http://method.com/&quot;&gt;Method&lt;/a&gt; to implement a slick design for all our apps. I&apos;m proud to say we delivered yet again and there were many proud moments as we demoed to the top executives of the company.
&lt;/p&gt;
&lt;p&gt;In early September, after doing several demos, we were approved to launch and we&apos;ve been working towards that goal ever since. We feel we have several weeks of work to coax our DemoWare into RealWorldWare, but the momentum is there and the end of the tunnel is in sight. &lt;/p&gt;
&lt;p&gt;As a further sign of our success, we&apos;re moving into a new office in LoDo next week. This also means the End of an Era, where the Raible Designs&apos; office across from Forest Room 5 will cease to exist. We (Goodwill, Scotty, Country Bry and I) first moved into this office while working for Evite in April 2009. It&apos;s been a fantastic location, a facilitator of extensive collaboration and host to many dart games, FACs, bird-dog spottings and way too many Mom Jokes.&lt;/p&gt;
&lt;p&gt;As part of our transition, I&apos;ll be looking for renters to fill out the rest of my lease (through March 2010). If you&apos;re looking for a sweet location for 5-6 people in &lt;a href=&quot;http://maps.google.com/maps?q=2525+15th+Street,+Denver&quot;&gt;Denver&apos;s Highlands&lt;/a&gt; (near the REI store downtown), please &lt;a href=&quot;http://raibledesigns.com/contact.jsp&quot;&gt;let me know&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
Also, there&apos;s a good chance my team will continue to grow as we move our apps into production and start ramping up for millions of users. If you&apos;re a strong Web or Java developer with social skills and a &lt;a href=&quot;http://www.jroller.com/wj/entry/a_tale_of_two_programmers&quot;&gt;Ditchdigger&lt;/a&gt; attitude, I&apos;d love to hear from you. We probably won&apos;t be hiring until January, but that doesn&apos;t mean we can&apos;t start talking now.</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/presentations_from_the_irish_software</id>
        <title type="html">My Presentations from The Irish Software Show 2010</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/presentations_from_the_irish_software"/>
        <published>2010-06-10T07:11:35-06:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="webframeworks" scheme="http://roller.apache.org/ns/tags/" />
        <category term="grails" scheme="http://roller.apache.org/ns/tags/" />
        <category term="iss2010" scheme="http://roller.apache.org/ns/tags/" />
        <category term="rubyonrails" scheme="http://roller.apache.org/ns/tags/" />
        <category term="flex" scheme="http://roller.apache.org/ns/tags/" />
        <category term="rails" scheme="http://roller.apache.org/ns/tags/" />
        <category term="gwt" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">This week I&apos;ve been enjoying Dublin, Ireland thanks to the 2nd Annual &lt;a href=&quot;http://epicenter.ie/2010.html&quot;&gt;Irish Software Show&lt;/a&gt;. On Wednesday night, I spoke about &lt;a href=&quot;http://epicenter.ie/2010.html?zone_id=20&amp;amp;mode=agenda&amp;amp;session=152#session&quot;&gt;The Future of Web Frameworks&lt;/a&gt; and  participated in a panel with Grails, Rails, ASP.NET MVC and Seaside developers. It was a fun night with lots of lively discussion. Below is my presentation from this event.&lt;/p&gt;

&lt;p style=&quot;text-align: center&quot;&gt;&lt;object id=&quot;__sse3271151&quot; width=&quot;425&quot; height=&quot;355&quot;&gt;&lt;param name=&quot;movie&quot; value=&quot;http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=thefutureofwebframeworks-100225012146-phpapp02&amp;amp;stripped_title=the-future-of-web-frameworks&quot; /&gt;&lt;param name=&quot;allowFullScreen&quot; value=&quot;true&quot;/&gt;&lt;param name=&quot;allowScriptAccess&quot; value=&quot;always&quot;/&gt;&lt;embed name=&quot;__sse3271151&quot; src=&quot;//static.slidesharecdn.com/swf/ssplayer2.swf?doc=thefutureofwebframeworks-100225012146-phpapp02&amp;amp;stripped_title=the-future-of-web-frameworks&quot; type=&quot;application/x-shockwave-flash&quot; allowscriptaccess=&quot;always&quot; allowfullscreen=&quot;true&quot; width=&quot;425&quot; height=&quot;355&quot;&gt;&lt;/embed&gt;&lt;/object&gt;
&lt;/p&gt;
&lt;p&gt;This morning, I delivered my &lt;a href=&quot;http://epicenter.ie/2010.html?zone_id=20&amp;amp;mode=agenda&amp;amp;session=151#session&quot;&gt;Comparing Kick-Ass Web Frameworks&lt;/a&gt; talk. This presentation contains updated statistics for various metrics comparing Rails vs. Grails and Flex vs. GWT. 
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;object id=&quot;__sse2644393&quot; width=&quot;425&quot; height=&quot;355&quot;&gt;&lt;param name=&quot;movie&quot; value=&quot;http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=comparingkickasswebframeworks-091203145644-phpapp02&amp;stripped_title=comparing-kick-ass-web-frameworks&quot; /&gt;&lt;param name=&quot;allowFullScreen&quot; value=&quot;true&quot;/&gt;&lt;param name=&quot;allowScriptAccess&quot; value=&quot;always&quot;/&gt;&lt;embed name=&quot;__sse2644393&quot; src=&quot;//static.slidesharecdn.com/swf/ssplayer2.swf?doc=comparingkickasswebframeworks-091203145644-phpapp02&amp;stripped_title=comparing-kick-ass-web-frameworks&quot; type=&quot;application/x-shockwave-flash&quot; allowscriptaccess=&quot;always&quot; allowfullscreen=&quot;true&quot; width=&quot;425&quot; height=&quot;355&quot;&gt;&lt;/embed&gt;&lt;/object&gt;
&lt;/p&gt;
&lt;p&gt;
Thanks to all who attended my talks this week!
&lt;/p&gt;
&lt;p style=&quot;border-top: 1px dotted silver; padding-top: 5px; color: #666&quot;&gt;
P.S. I believe audio was recorded on Wednesday night, but I&apos;m unsure how it turned out. I&apos;m pretty sure no recordings were done on this morning&apos;s session. 
&lt;/p&gt;&lt;p&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/javascript_and_css_concatenation</id>
        <title type="html">JavaScript and CSS Concatenation with wro4j</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/javascript_and_css_concatenation"/>
        <published>2009-11-09T10:44:44-07:00</published>
        <updated>2009-11-23T18:17:42-07:00</updated> 
        <category term="/Roller" label="Roller" />
        <category term="roller" scheme="http://roller.apache.org/ns/tags/" />
        <category term="compression" scheme="http://roller.apache.org/ns/tags/" />
        <category term="yuicompressor" scheme="http://roller.apache.org/ns/tags/" />
        <category term="minification" scheme="http://roller.apache.org/ns/tags/" />
        <category term="javascript" scheme="http://roller.apache.org/ns/tags/" />
        <category term="wro4j" scheme="http://roller.apache.org/ns/tags/" />
        <category term="appfuse" scheme="http://roller.apache.org/ns/tags/" />
        <category term="css" scheme="http://roller.apache.org/ns/tags/" />
        <category term="yslow" scheme="http://roller.apache.org/ns/tags/" />
        <category term="jawr" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">This past weekend, I decided it was about time to fix my &lt;a href=&quot;http://developer.yahoo.com/yslow/&quot;&gt;YSlow&lt;/a&gt; score on this site. I did the easiest thing first by moving all my JavaScript files to the bottom of each page. Then I turned on GZip compression using Roller&apos;s built-in CompressionFilter. These changes helped, but the most glaring problem continued to be &lt;em&gt;too many requests&lt;/em&gt;. To solve this, I turned to &lt;a href=&quot;http://wro4j.googlecode.com&quot;&gt;wro4j&lt;/a&gt; (as &lt;a href=&quot;http://twitter.com/alexobjelean/status/5481711667&quot;&gt;recommended on Twitter&lt;/a&gt;) to concatenate my JS and CSS files into one.
&lt;/p&gt;
&lt;p&gt;I have to say, I&apos;m very happy with the results. I&apos;m now sitting at a YSlow (V2) score of 75; 90 if I use the &quot;Small Site or Blog&quot; ruleset. I believe I can improve this by adding expires headers to my images, js and css. More than anything, I&apos;m impressed with wro4j, its &lt;a href=&quot;http://groups.google.com/group/wro4j/browse_thread/thread/33b85936c8f1e8b3?hl=en&quot;&gt;great support&lt;/a&gt; and &lt;a href=&quot;http://code.google.com/p/wro4j/wiki/GettingStarted&quot;&gt;easy setup&lt;/a&gt;. I was looking for a runtime solution (b/c I didn&apos;t want to have to rebuild Roller) and it seems to be perfect for the job. Furthermore, wro4j minifies everything on the fly and they&apos;ll have an &lt;a href=&quot;http://groups.google.com/group/wro4j/msg/b85b4aba0f9500ba?hl=en&quot;&gt;expires header filter in the next release&lt;/a&gt;. 
&lt;/p&gt;
&lt;p&gt;
&lt;a href=&quot;http://jawr.dev.java.net&quot;&gt;JAWR&lt;/a&gt; and the &lt;a href=&quot;http://developer.yahoo.com/yui/compressor/&quot;&gt;YUI Compressor&lt;/a&gt; are other alternatives to this filter, but I&apos;m currently sold on wro4j. First of all, it passed the 10-minute test. Secondly, it didn&apos;t require me to modify Roller&apos;s build system.  
&lt;/p&gt;
&lt;p&gt;At this point, if I&apos;m going to implement JS/CSS concatenation and minification in &lt;a href=&quot;http://appfuse.org&quot;&gt;AppFuse&lt;/a&gt; and &lt;a href=&quot;http://roller.apache.org&quot;&gt;Roller&lt;/a&gt;, wro4j seems like the best option. If you disagree, I&apos;d love to hear your reasoning.&lt;/p&gt;
&lt;p style=&quot;font-style: italic; color: #666; border-top: 1px dotted silver&quot;&gt;
&lt;strong&gt;TIP:&lt;/strong&gt; See &lt;a href=&quot;http://www.sonatype.com/people/2009/11/javascript-compression-in-nexus/&quot;&gt;Javascript Compression in Nexus&lt;/a&gt; for information on using YUI Compressor with Maven.
&lt;/p&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/consulting_sofea_grails_and_gwt</id>
        <title type="html">Consulting, SOFEA, Grails and GWT at next week&apos;s Denver JUG</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/consulting_sofea_grails_and_gwt"/>
        <published>2009-11-05T22:52:37-07:00</published>
        <updated>2009-11-06T06:00:15-07:00</updated> 
        <category term="/Java" label="Java" />
        <category term="consulting" scheme="http://roller.apache.org/ns/tags/" />
        <category term="java" scheme="http://roller.apache.org/ns/tags/" />
        <category term="denver" scheme="http://roller.apache.org/ns/tags/" />
        <category term="gwt" scheme="http://roller.apache.org/ns/tags/" />
        <category term="sofea" scheme="http://roller.apache.org/ns/tags/" />
        <category term="grails" scheme="http://roller.apache.org/ns/tags/" />
        <category term="denverjug" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">Next Wednesday, I&apos;ll be at Denver&apos;s JUG meeting to talk about Independent Consulting and Building SOFEA Applications with Grails and GWT. The first talk will be a a panel discussion among local independent consultants, including &lt;a href=&quot;http://www.jroller.com/JamesGoodwill/&quot;&gt;James Goodwill&lt;/a&gt;, &lt;a href=&quot;http://www.ambientideas.com/blog/&quot;&gt;Matthew McCullough&lt;/a&gt;, &lt;a href=&quot;http://www.augusttechgroup.com/tim/blog/&quot;&gt;Tim Berglund&lt;/a&gt; and myself.
&lt;/p&gt;
&lt;p class=&quot;smokey&quot;&gt;
This session explores the trials and tribulations of an independent
consultant. How do you find contracts? Should you setup an LLC, an
S-Corp or just be a sole proprietorship? What about health insurance
and benefits? Are recruiters helpful or hurtful? Learn lots of tips
and tricks to get your dream job and your ideal lifestyle.
&lt;/p&gt;
&lt;p&gt;
The Grails and GWT talk is a preview of a talk I&apos;ll be doing at the &lt;a href=&quot;http://www.therichwebexperience.com/conference/speaker/topic_view?topicId=2104&quot;&gt;Rich Web Experience&lt;/a&gt; in December. Below is a rewrite of the abstract in first-person.
&lt;/p&gt;
&lt;p class=&quot;smokey&quot;&gt;Earlier this year, I participated in a major enhancement of a high-traffic well-known internet site. The company wanted us to quickly re-architect their site and use a modern Ajax framework to do it with. An Ajax Framework evaluation was done to help the team choose the best framework for their skillset. The application was built with a SOFEA architecture using GWT on the frontend and Grails/REST on the backend.
&lt;br/&gt;&lt;br/&gt;
This talk will cover how &lt;a href=&quot;http://www.linkedin.com/in/bryannoll&quot;&gt;Bryan Noll&lt;/a&gt;, &lt;a href=&quot;http://www.linkedin.com/in/scottthomasnicholls&quot;&gt;Scott Nicholls&lt;/a&gt;, &lt;a href=&quot;http://www.linkedin.com/in/jgoodwill&quot;&gt;James Goodwill&lt;/a&gt; and I came to choose GWT and Grails, as well as stumbling blocks we encountered along the way. In addition, we&apos;ll explore many topics such as raw GWT vs. GXT/SmartGWT, the Maven GWT Plugin, modularizing your code, multiple EntryPoints, MVP, integration testing and JSON parsing with Overlay Types. 
&lt;/p&gt;
&lt;p&gt;If you&apos;re in Denver next Wednesday night (November 11th), you should stop by the &lt;a href=&quot;http://www.denverjug.org/&quot;&gt;Denver JUG&lt;/a&gt; meeting. It&apos;ll be a fun night and there&apos;s sure to be a few beers afterward. &lt;img src=&quot;https://raibledesigns.com/images/smileys/wink.gif&quot; class=&quot;smiley&quot; alt=&quot;;-)&quot; title=&quot;;-)&quot; /&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/what_would_you_like_to</id>
        <title type="html">What would you like to see at TSSJS 2010?</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/what_would_you_like_to"/>
        <published>2009-10-12T11:28:21-06:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="tssjs" scheme="http://roller.apache.org/ns/tags/" />
        <category term="vegas" scheme="http://roller.apache.org/ns/tags/" />
        <category term="java" scheme="http://roller.apache.org/ns/tags/" />
        <category term="gwt" scheme="http://roller.apache.org/ns/tags/" />
        <category term="jvm" scheme="http://roller.apache.org/ns/tags/" />
        <category term="theserverside" scheme="http://roller.apache.org/ns/tags/" />
        <category term="conference" scheme="http://roller.apache.org/ns/tags/" />
        <category term="flex" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">&lt;a href=&quot;http://static.raibledesigns.com/repository/images/thevenetian.jpg&quot; rel=&quot;lightbox&quot; title=&quot;The Venetian&quot;&gt;&lt;img src=&quot;//static.raibledesigns.com/repository/images/thevenetian_sm.jpg&quot; class=&quot;picture&quot; alt=&quot;The Venetian&quot; height=&quot;98&quot; width=&quot;130&quot;&gt;&lt;/a&gt;
A couple months ago, I was asked by &lt;a href=&quot;http://www.theserverside.com/&quot;&gt;TheServerSide&lt;/a&gt; to speak at next year&apos;s &lt;a href=&quot;http://javasymposium.techtarget.com/&quot;&gt;TheServerSide Java Symposium&lt;/a&gt; in Las Vegas. In addition, they asked me to help them evaluate presentation proposals and suggest topics/speakers. 
&lt;/p&gt;
&lt;p&gt;
First of all, I think the biggest thing that TSSJS could do to improve is to host more networking events. With the &lt;a href=&quot;http://java.dzone.com/news/fate-javaone-community-round&quot;&gt;JavaOne Party being over&lt;/a&gt;, I think there&apos;s a tremendous opportunity to fill a gap in the networking needs of the Java Community. When I &lt;a href=&quot;http://raibledesigns.com/rd/entry/tssjs_bof_web_framework_sweet&quot;&gt;first attended TSSJS in 2006&lt;/a&gt;, there were a fair amount of parties and everyone got to interact quite a bit. &lt;a href=&quot;http://raibledesigns.com/rd/entry/java_web_framework_smackdown_at&quot;&gt;In 2008&lt;/a&gt;, there were no networking events. I believe having a strong networking story would attract a lot more attendees, companies and sponsors.
&lt;/p&gt;
&lt;p&gt;Secondly, I think it&apos;s possible that TSSJS has too many server-side related sessions. IMO, the server-side (and middleware in general) isn&apos;t that exciting. TechTarget appears to own &lt;a href=&quot;http://theclientside.com&quot;&gt;TheClientSide&lt;/a&gt;, so why not add some more client-side stuff to the mix? For example, I&apos;d love to see a Struts 1 app-makeover using different technologies (for example, Flex, GWT and jQuery). I think HTML5 and Google Wave&apos;s Architecture sessions would be interesting too. If adding client-side sessions is too far away from TheServerSide, maybe it should be renamed to TheServerSide JVM Symposium and there can be all kinds of sessions on JVM languages (e.g. Scala, JRuby, Groovy) and all the great things those languages can accomplish.
&lt;/p&gt;
&lt;p&gt;Lastly, I&apos;ve been asked to send a couple session proposals. Currently, I&apos;m thinking about a doing GWT vs. Flex Smackdown with &lt;a href=&quot;http://www.jamesward.com/blog/&quot;&gt;James Ward&lt;/a&gt;, but I&apos;m open to other ideas. It&apos;s been quite awhile since I did a &quot;Comparing Web Frameworks&quot; talk. Maybe &quot;Hot Web Frameworks for 2010&quot; is more appropriate? I also think it&apos;d be interesting to do a somewhat philosophical talk on &quot;The State of Web Frameworks&quot; and where we&apos;re headed in the next year.&lt;/p&gt;
&lt;p&gt;What would make &lt;em&gt;you&lt;/em&gt; want to attend TSSJS next year? Let me know your thoughts and I&apos;ll do my best to make them a reality.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update October 22, 2009:&lt;/strong&gt; Whoo hoo! It looks like &lt;a href=&quot;http://www.theserverside.com/news/thread.tss?thread_id=58300&quot;&gt;TheClientSide&lt;/a&gt; &lt;em&gt;will&lt;/em&gt; be a part of TSSJS Vegas next year. Should be a great show.</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/creating_a_facebook_style_autocomplete</id>
        <title type="html">Creating a Facebook-style Autocomplete with GWT</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/creating_a_facebook_style_autocomplete"/>
        <published>2009-06-05T07:05:10-06:00</published>
        <updated>2016-05-31T03:48:04-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="autocomplete" scheme="http://roller.apache.org/ns/tags/" />
        <category term="facebook" scheme="http://roller.apache.org/ns/tags/" />
        <category term="linkedin" scheme="http://roller.apache.org/ns/tags/" />
        <category term="jquery" scheme="http://roller.apache.org/ns/tags/" />
        <category term="gwt-autocomplete" scheme="http://roller.apache.org/ns/tags/" />
        <category term="gwt" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">Have you used the &quot;To:&quot; widget on on Facebook or LinkedIn when composing a message? It&apos;s an autocompleter that looks up contact names and displays them as you type. It looks like a normal textbox (a.k.a. &amp;lt;input type=&quot;text&quot;&amp;gt;), but wraps the contact name to allow you to easily delete it. Here&apos;s a screenshot of what Facebook&apos;s widget looks like.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;img src=&quot;//farm3.static.flickr.com/2468/3595186177_5334fff971.jpg&quot; width=&quot;500&quot; height=&quot;233&quot; alt=&quot;Facebook Autocomplete&quot; /&gt;
&lt;/p&gt;
&lt;p&gt;Last week, I was asked to create a similar widget with GWT. After searching the web and &lt;a href=&quot;
http://groups.google.com/group/Google-Web-Toolkit/browse_thread/thread/627687a4b607ce02&quot;&gt;not finding much&lt;/a&gt;, I decided to try writing my own. The best example I found on how to create this widget was from James Smith&apos;s &lt;a href=&quot;http://loopj.com/2009/04/25/jquery-plugin-tokenizing-autocomplete-text-entry/&quot;&gt;Tokenizing Autocomplete jQuery Plugin&lt;/a&gt;. I used its &lt;a href=&quot;http://loopj.com/tokeninput/demo.html&quot;&gt;demo&lt;/a&gt; to help me learn how the DOM changed after you selected a contact. 
&lt;/p&gt;
&lt;p&gt;GWT&apos;s &lt;a href=&quot;http://gwt.google.com/samples/Showcase/Showcase.html#CwSuggestBox&quot;&gt;SelectBox&lt;/a&gt; allows you to easily create an autocompleter. However, &lt;a href=&quot;http://code.google.com/p/google-web-toolkit/issues/detail?id=3044&amp;amp;can=5&quot;&gt;it doesn&apos;t have support for multiple values&lt;/a&gt; (for example, a comma-delimited list). The good news is it&apos;s not difficult to add this functionality using &lt;a href=&quot;http://ljvjonok.blogspot.com/2008/10/gwt-suggestbox-how-to-make-multiple.html&quot;&gt;Viktor Zaprudnev&apos;s HowTo&lt;/a&gt;. Another feature you might want in a SelectBox is to populate it with POJOs. &lt;a href=&quot;http://eggsylife.co.uk/2008/08/25/gwt-suggestbox-backed-by-dto-model/&quot;&gt;GWT SuggestBox backed by DTO Model&lt;/a&gt; is a good blog post that shows how to do this.&lt;/p&gt;
&lt;p&gt;Back to the Facebook Autocompleter. To demonstrate how to create this widget in GWT, I put together a simple application. You can &lt;a href=&quot;http://demo.raibledesigns.com/gwt-autocomplete&quot;&gt;view the demo&lt;/a&gt; or &lt;a href=&quot;http://static.raibledesigns.com/downloads/gwt-autocomplete-1.0.zip&quot;&gt;download it&lt;/a&gt;. The meat of this example is in an InputListWidget. After looking at the jQuery example, I learned the widget was a &amp;lt;div&amp;gt; with a unordered list (&amp;lt;ul&amp;gt;). It starts out looking like this:&lt;/p&gt;
&lt;pre class=&quot;brush:xml&quot;&gt;
&amp;lt;ul class=&quot;token-input-list-facebook&quot;&amp;gt;
    &amp;lt;li class=&quot;token-input-input-token-facebook&quot;&amp;gt;
        &amp;lt;input type=&quot;text&quot; style=&quot;outline-color: -moz-use-text-color; outline-style: none; outline-width: medium;&quot;/&amp;gt;
    &amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&lt;/pre&gt;
&lt;p&gt;I did this in GWT using custom BulletList and ListItem widgets (contained in the download).&lt;/p&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
final BulletList list = new BulletList();
list.setStyleName(&quot;token-input-list-facebook&quot;);

final ListItem item = new ListItem();
item.setStyleName(&quot;token-input-input-token-facebook&quot;);

final TextBox itemBox = new TextBox();
itemBox.getElement().setAttribute(&quot;style&quot;, 
        &quot;outline-color: -moz-use-text-color; outline-style: none; outline-width: medium;&quot;);

final SuggestBox box = new SuggestBox(getSuggestions(), itemBox);
box.getElement().setId(&quot;suggestion_box&quot;);

item.add(box);
list.add(item);
&lt;/pre&gt;
&lt;p&gt;After tabbing off the input, I noticed that it was removed and replaced with a &amp;lt;p&amp;gt; around the value and a &amp;lt;span&amp;gt; to show the &quot;x&quot; to delete it. After adding a couple items, the HTML is as follows:&lt;/p&gt;
&lt;pre class=&quot;brush:xml&quot;&gt;
&amp;lt;ul class=&quot;token-input-list-facebook&quot;&amp;gt;
    &amp;lt;li class=&quot;token-input-token-facebook&quot;&amp;gt;
        &amp;lt;p&amp;gt;What&apos;s New Scooby-Doo?&amp;lt;/p&amp;gt;
        &amp;lt;span class=&quot;token-input-delete-token-facebook&quot;&amp;gt;x&amp;lt;/span&amp;gt;
    &amp;lt;/li&amp;gt;
    &amp;lt;li class=&quot;token-input-token-facebook&quot;&amp;gt;
        &amp;lt;p&amp;gt;Fear Factor&amp;lt;/p&amp;gt;
        &amp;lt;span class=&quot;token-input-delete-token-facebook&quot;&amp;gt;x&amp;lt;/span&amp;gt;
     &amp;lt;/li&amp;gt;
     &amp;lt;li class=&quot;token-input-input-token-facebook&quot;&amp;gt;
         &amp;lt;input type=&quot;text&quot; style=&quot;outline-color: -moz-use-text-color; outline-style: none; outline-width: medium;&quot;/&amp;gt;
     &amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&lt;/pre&gt;
&lt;p&gt;To do this, I created a &lt;code&gt;deselectItem()&lt;/code&gt; method that triggers the DOM transformation.
&lt;/p&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
private void deselectItem(final TextBox itemBox, final BulletList list) {
    if (itemBox.getValue() != null &amp;&amp; !&quot;&quot;.equals(itemBox.getValue().trim())) {
        /** Change to the following structure:
         * &amp;lt;li class=&quot;token-input-token-facebook&quot;&amp;gt;
         * &amp;lt;p&amp;gt;What&apos;s New Scooby-Doo?&amp;lt;/p&amp;gt;
         * &amp;lt;span class=&quot;token-input-delete-token-facebook&quot;&amp;gt;x&amp;lt;/span&amp;gt;
         * &amp;lt;/li&amp;gt;
         */

        final ListItem displayItem = new ListItem();
        displayItem.setStyleName(&quot;token-input-token-facebook&quot;);
        Paragraph p = new Paragraph(itemBox.getValue());

        displayItem.addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent clickEvent) {
                displayItem.addStyleName(&quot;token-input-selected-token-facebook&quot;);
            }
        });

        Span span = new Span(&quot;x&quot;);
        span.addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent clickEvent) {
                list.remove(displayItem);
            }
        });

        displayItem.add(p);
        displayItem.add(span);
        
        list.insert(displayItem, list.getWidgetCount() - 1);
        itemBox.setValue(&quot;&quot;);
        itemBox.setFocus(true);
    }
}
&lt;/pre&gt;
&lt;p&gt;This method is called after selecting a new item from the SuggestBox:&lt;/p&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
box.addSelectionHandler(new SelectionHandler&amp;lt;SuggestOracle.Suggestion&amp;gt;() {
    public void onSelection(SelectionEvent selectionEvent) {
        deselectItem(itemBox, list);
    }
});
&lt;/pre&gt;
&lt;p&gt;I also added the ability for you to type in an e-mail address manually and to delete the previous item when you backspace from the input field. Here&apos;s the handler that calls &lt;code&gt;deselectItem()&lt;/code&gt; and allows deleting with backspace:&lt;/p&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
// this needs to be on the itemBox rather than box, or backspace will get executed twice
itemBox.addKeyDownHandler(new KeyDownHandler() {
    public void onKeyDown(KeyDownEvent event) {
        if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
            // only allow manual entries with @ signs (assumed email addresses)
            if (itemBox.getValue().contains(&quot;@&quot;))
                deselectItem(itemBox, list);
        }
        // handle backspace
        if (event.getNativeKeyCode() == KeyCodes.KEY_BACKSPACE) {
            if (&quot;&quot;.equals(itemBox.getValue().trim())) {
                ListItem li = (ListItem) list.getWidget(list.getWidgetCount() - 2);
                Paragraph p = (Paragraph) li.getWidget(0);

                list.remove(li);
                itemBox.setFocus(true);
            }
        }
    }
});
&lt;/pre&gt;
&lt;p&gt;I&apos;m happy with the results, and grateful for the &lt;a href=&quot;http://loopj.com/tokeninput/token-input-facebook.css&quot;&gt;jQuery plugin&apos;s CSS&lt;/a&gt;. However, it still has one issue that I haven&apos;t been able to solve: I&apos;m unable to click on a list item (to select it) and then delete it (with the backspace key). I believe this is because I&apos;m unable to give focus to the list item. Here&apos;s the code that highlights the item and you can see the commented-out code that doesn&apos;t work.
&lt;/p&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
displayItem.addClickHandler(new ClickHandler() {
    public void onClick(ClickEvent clickEvent) {
        displayItem.addStyleName(&quot;token-input-selected-token-facebook&quot;);
    }
});

/** TODO: Figure out how to select item and allow deleting with backspace key
displayItem.addKeyDownHandler(new KeyDownHandler() {
    public void onKeyDown(KeyDownEvent event) {
        if (event.getNativeKeyCode() == KeyCodes.KEY_BACKSPACE) {
            list.remove(displayItem);
        }
    }
});
displayItem.addBlurHandler(new BlurHandler() {
    public void onBlur(BlurEvent blurEvent) {
        displayItem.removeStyleName(&quot;token-input-selected-token-facebook&quot;);
    }
});
*/
&lt;/pre&gt;
&lt;p&gt;If you know of a solution to this issue, please let me know. Feel free to use this widget and improve it as you see fit. I&apos;d love to see this as a native widget in GWT. In the meantime, here&apos;s the &lt;a href=&quot;http://demo.raibledesigns.com/gwt-autocomplete&quot;&gt;GWT Facebook-style Autocomplete demo&lt;/a&gt; and &lt;a href=&quot;http://static.raibledesigns.com/downloads/gwt-autocomplete-1.0.zip&quot;&gt;code&lt;/a&gt;.</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/ajax_framework_analysis_results</id>
        <title type="html">Ajax Framework Analysis Results</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/ajax_framework_analysis_results"/>
        <published>2009-04-23T20:34:44-06:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="ajax" scheme="http://roller.apache.org/ns/tags/" />
        <category term="gxt" scheme="http://roller.apache.org/ns/tags/" />
        <category term="jquery" scheme="http://roller.apache.org/ns/tags/" />
        <category term="smartgwt" scheme="http://roller.apache.org/ns/tags/" />
        <category term="dojo" scheme="http://roller.apache.org/ns/tags/" />
        <category term="yui" scheme="http://roller.apache.org/ns/tags/" />
        <category term="java" scheme="http://roller.apache.org/ns/tags/" />
        <category term="gwt" scheme="http://roller.apache.org/ns/tags/" />
        <category term="extjs" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">Way back in January, I wrote about how my colleagues and I were &lt;a href=&quot;http://raibledesigns.com/rd/entry/choosing_an_ajax_framework&quot;&gt;evaluating Ajax frameworks&lt;/a&gt; to build a SOFEA-style architecture. To make our choice, we used the following process:
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Choose a short list of frameworks to prototype with.&lt;/li&gt;
&lt;li&gt;Create an application prototype with each framework.&lt;/li&gt;
&lt;li&gt;Document findings and create a matrix with important criteria.&lt;/li&gt;
&lt;li&gt;Create presentation to summarize document.&lt;/li&gt;
&lt;li&gt;Deliver document, presentation and recommendation.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When I wrote that entry, we had just finished step 2 and were starting step 3. I first wrote this blog post a week later, when we delivered step 5. Here is the comparison and conclusion sections of the analysis document we composed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Framework Comparison&lt;/strong&gt;&lt;br/&gt;
In order to evaluate the different frameworks against important criteria, we created a matrix with weights and ranks for each framework. This matrix shows how our weighting and rankings lead us to the winner for our project.  You can &lt;a href=&quot;http://spreadsheets.google.com/ccc?key=p2SLd279MTmShLQdCjfi0OQ&amp;hl=en&quot;&gt;view this matrix online&lt;/a&gt; or see below for a summary.
&lt;/p&gt;

&lt;iframe width=&apos;520&apos; height=&apos;500&apos; frameborder=&apos;0&apos; src=&apos;http://spreadsheets.google.com/pub?key=p2SLd279MTmShLQdCjfi0OQ&amp;output=html&amp;gid=0&amp;single=true&amp;widget=true&apos; style=&apos;margin: 0 auto&apos;&gt;&lt;/iframe&gt;

&lt;p style=&quot;text-align: left&quot;&gt;
&lt;strong&gt;Note:&lt;/strong&gt; Criteria whose values were identical across all candidates were weighted at zero. 
Charting capability was weighted at zero b/c we decided to use Flash for this.
&lt;/p&gt;

&lt;p&gt;This matrix indicates that &lt;strong&gt;GWT&lt;/strong&gt; is the best candidate for our team to develop SOFEA-style applications with.
In addition to the matrix, below are graphs that illustrate interesting (and possibly meaningless) statistics about each project. 
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;img src=&quot;//spreadsheets.google.com/pub?key=p2SLd279MTmShLQdCjfi0OQ&amp;amp;oid=1&amp;amp;output=image&quot; width=&quot;320&quot; alt=&quot;Number of Committers&quot;/&gt;
&lt;br/&gt;&lt;br/&gt;
&lt;!--img src=&quot;//spreadsheets.google.com/pub?key=p2SLd279MTmShLQdCjfi0OQ&amp;amp;oid=2&amp;amp;output=image&quot; width=&quot;320&quot; alt=&quot;Mailing List Traffic&quot;/&gt;
&lt;br/&gt;&lt;br/--&gt;
&lt;img src=&quot;//spreadsheets.google.com/pub?key=p2SLd279MTmShLQdCjfi0OQ&amp;amp;oid=3&amp;amp;output=image&quot; width=&quot;320&quot; alt=&quot;Books on Amazon&quot;/&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;br/&gt;

After working with the various frameworks, we believe that all the frameworks were very good and could be used to write applications with. If all weights are equal, these frameworks were almost even when compared against our evaluation criteria. The graph below illustrates this. 
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;img src=&quot;//spreadsheets.google.com/pub?key=p2SLd279MTmShLQdCjfi0OQ&amp;amp;oid=4&amp;amp;output=image&quot; width=&quot;320&quot; alt=&quot;Ranking with equal criteria weights&quot;/&gt;
&lt;/p&gt;
&lt;p&gt;Even after applying the weighted criteria, the evenness doesn&apos;t change a whole lot. &lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;img src=&quot;//spreadsheets.google.com/pub?key=p2SLd279MTmShLQdCjfi0OQ&amp;amp;oid=5&amp;amp;output=image&quot; width=&quot;320&quot; alt=&quot;Ranking with weighted criteria&quot; /&gt;
&lt;/p&gt;
&lt;p&gt;
Without considering the even or weighted criteria, we believe the decision all comes down to what the developers on the project feel they will be most comfortable with. If you&apos;re developing with Dojo or YUI, chances are you&apos;re &lt;em&gt;dressing up&lt;/em&gt; existing HTML and possibly using &lt;a href=&quot;http://www.alistapart.com/articles/understandingprogressiveenhancement&quot;&gt;progressive enhancement&lt;/a&gt; to add more rich functionality. On the other hand, Ext JS and GWT are similar to Swing programming where you build the UI with code (JavaScript for Ext JS, Java for GWT).
&lt;/p&gt;
&lt;p&gt;
The tools available for JavaScript development have gotten increasingly better in recent years. IntelliJ IDEA has a &lt;a href=&quot;http://www.jetbrains.com/idea/features/javascript_editor.html&quot;&gt;JavaScript Editor&lt;/a&gt; that provides many of the same features as its Java editor. &lt;a href=&quot;http://www.aptana.com/studio&quot;&gt;Aptana Studio&lt;/a&gt; also has excellent support for authoring and debugging JavaScript. However, we believe the Java debugging and authoring support in IDEs is much better. Furthermore, we are more familiar with organizing code in Java projects and feel more comfortable in this development environment. 
&lt;/p&gt;
&lt;p&gt;
Based on this evaluation, we believe that GWT is the best framework for our team to develop SOFEA-style applications with. 

&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Flash Forward to Today...&lt;/strong&gt;&lt;br/&gt;
The core GWT library from Google doesn&apos;t have a whole lot of widgets, nor do they look
good out-of-the-box.  So early on, we experimented with two alternative implementations
that continue to leverage GWT concepts and tools:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://extjs.com/products/gxt&quot;&gt;GXT&lt;/a&gt;: a GWT version of Ext JS&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://code.google.com/p/smartgwt&quot;&gt;SmartGWT&lt;/a&gt;: a GWT version of SmartClient&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
Unfortunately, over the past few months, we&apos;ve found that both of these implementations are too heavy for our requirements, mostly because of the file size of the generated JavaScript code. For example, a feature I wrote generated a 275K *.cache.html file using GXT. After determining that was too slow to give users the initial &quot;pop&quot;, I re-wrote it &lt;em&gt;without&lt;/em&gt; GXT. After a day, we had an application with *.cache.html files of 133K. Yes, that&apos;s over a 50% reduction in size!&lt;a href=&quot;http://raibledesigns.com/rd/entry/ajax_framework_analysis_results#footnote-gxtmvc&quot;&gt;*&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;
Because of these findings, we are proceeding with the core GWT library from Google and adding in new components as needed.
It is cool to know you can make a UI &quot;pop&quot; with GWT, as long as you stick to the core - close-to-the-metal - components. For those applications that can afford an initial &quot;loading...&quot; state, I&apos;d definitely recommend looking at GXT and SmartGWT.
&lt;/p&gt;
&lt;p style=&quot;font-size: 90%&quot;&gt;&lt;a name=&quot;footnote-gxtmvc&quot;&gt;*&lt;/a&gt; To make refactoring easier, I copied &lt;a href=&quot;http://raibledesigns.com/rd/entry/gxt_s_mvc_framework&quot;&gt;GXT MVC&lt;/a&gt; into our source tree and modified all imports.&lt;/p&gt;
&lt;p&gt;















</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/my_drunk_on_software_interview</id>
        <title type="html">My Drunk on Software Interview</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/my_drunk_on_software_interview"/>
        <published>2009-04-05T22:23:57-06:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="sofea" scheme="http://roller.apache.org/ns/tags/" />
        <category term="java" scheme="http://roller.apache.org/ns/tags/" />
        <category term="drunkonsoftware" scheme="http://roller.apache.org/ns/tags/" />
        <category term="ajax" scheme="http://roller.apache.org/ns/tags/" />
        <category term="flex" scheme="http://roller.apache.org/ns/tags/" />
        <category term="interview" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">Back in February, I met up with &lt;a href=&quot;http://www.jamesward.com/blog/&quot;&gt;James Ward&lt;/a&gt; and &lt;a href=&quot;http://ectropic.com/wordpress/&quot;&gt;Jon Rose&lt;/a&gt; for a &lt;a href=&quot;http://www.drunkonsoftware.com/2009/04/05/episode-11-matt-raible/&quot;&gt;Drunk on Software interview&lt;/a&gt;. We enjoyed some &lt;a href=&quot;http://www.gordonbiersch.com/restaurants/index.php?pg=beer&quot;&gt;good beer&lt;/a&gt; and had a great conversation about SOFEA, open source and RIA. See larger video &lt;a href=&quot;http://www.drunkonsoftware.com/2009/04/05/episode-11-matt-raible/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;embed src=&quot;//blip.tv/play/AfbqQpHGOw&quot; type=&quot;application/x-shockwave-flash&quot; width=&quot;500&quot; height=&quot;281.25&quot; allowscriptaccess=&quot;always&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/embed&gt; 
&lt;/p&gt;&lt;p&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/optimizing_a_gwt_application_with</id>
        <title type="html">Optimizing a GWT Application with Multiple EntryPoints</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/optimizing_a_gwt_application_with"/>
        <published>2009-03-25T16:00:37-06:00</published>
        <updated>2009-03-25T22:00:52-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="gzip" scheme="http://roller.apache.org/ns/tags/" />
        <category term="webframeworks" scheme="http://roller.apache.org/ns/tags/" />
        <category term="gwt-maven" scheme="http://roller.apache.org/ns/tags/" />
        <category term="optimization" scheme="http://roller.apache.org/ns/tags/" />
        <category term="gwt" scheme="http://roller.apache.org/ns/tags/" />
        <category term="entrypoint" scheme="http://roller.apache.org/ns/tags/" />
        <category term="maven" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">Building a GWT application is an easy way for Java Developers to write Ajax applications. However, it can be difficult to release a GWT application to production before it&apos;s finished. One of the most important things I&apos;ve learned in Software Development is to get a new application into production as soon as possible. Not only does getting it from dev &amp;rarr; qa &amp;rarr; prod verify your process works, it also can do a lot to test the viability of the new application.
&lt;/p&gt;
&lt;p&gt;One of the biggest issues with GWT applications is size. The project I&apos;m working on compiles Java to JavaScript and creates ~570K *.cache.html files (one for each modern browser). These files end up being around 180K gzipped. I believe this is an OK size for an entire application. However, if you&apos;re going to &lt;em&gt;release early, release often&lt;/em&gt; with GWT, chances are you&apos;ll just want to release one feature at a time. &lt;/p&gt;
&lt;p&gt;When the first feature was completed on my project, the *.cache.html files were around 300K. Rather than using branches to release to QA and UAT, bug fixes and new features were developed on trunk. Unfortunately, the QA and UAT process took several weeks longer than expected so by the time the feature was ready to release, the *.cache.html files had grown to around ~570K. The reason the file had grown so much was because it included all of the other features.&lt;/p&gt;
&lt;p&gt;Earlier this week, while running to a dentist appointment, I thought of a solution to this problem. The basic idea was to optimize the compilation process so only the to-be-released feature was included. Even better, the solution didn&apos;t require &lt;a href=&quot;http://raibledesigns.com/rd/entry/modularizing_gwt_applications_with_gwt&quot;&gt;more modularization&lt;/a&gt;. The results:&lt;/p&gt;
&lt;pre&gt;
Before: *.cache.html -&gt; 569K, gzipped 175K
After: *.cache.html -&gt; 314K, gzipped 100K
&lt;/pre&gt;
&lt;p&gt;According to my calculations, that&apos;s a 56% reduction in size. How did I do it?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Created a new &lt;code&gt;FeatureName.java&lt;/code&gt; EntryPoint with only the to-be-released features imported. &lt;/li&gt;
&lt;li&gt;Created a new &lt;code&gt;FeatureName.gwt.xml&lt;/code&gt; that references the new EntryPoint.&lt;/li&gt;
&lt;li&gt;Copied old (kitchen-sink) EntryPoint.html to &lt;code&gt;FeatureName.html&lt;/code&gt; and changed the reference to the nocache.js file.&lt;/li&gt;
&lt;li&gt;Created a Maven profile that allows using -PFeatureName to build a FeatureName-only module.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;One downside to doing things this way is it&apos;s possible to create a WAR that has the same name and different features. Surely the Maven Overlords would frown upon this. Since this is just a temporary solution to release features incrementally, I&apos;m not too worried about it. A possible workaround is to create different WAR names when a feature&apos;s profile is activated. I believe the true &quot;Maven way&quot; would be to make the &quot;kitchen sink&quot; application into a JAR and have several WAR modules with the different EntryPoints. Seems a bit complicated to me.
&lt;/p&gt;
&lt;p&gt;Other than this Maven publishing issue, the only other issue I can foresee is keeping the two EntryPoints and HTML files in synch. Then again, the separate files allow a feature to be customized for the release and can be deleted when its no longer needed.
&lt;/p&gt;
&lt;p&gt;What do you think? Do you know of a better way to compile a GWT application so it only contains certain features?</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/ajax_the_state_of_the</id>
        <title type="html">Ajax: The State of the Art with Dion and Ben</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/ajax_the_state_of_the"/>
        <published>2009-02-05T11:03:10-07:00</published>
        <updated>2012-11-11T02:00:40-07:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="frameworks" scheme="http://roller.apache.org/ns/tags/" />
        <category term="firefox" scheme="http://roller.apache.org/ns/tags/" />
        <category term="ben" scheme="http://roller.apache.org/ns/tags/" />
        <category term="bespin" scheme="http://roller.apache.org/ns/tags/" />
        <category term="dion" scheme="http://roller.apache.org/ns/tags/" />
        <category term="canvas" scheme="http://roller.apache.org/ns/tags/" />
        <category term="ajax" scheme="http://roller.apache.org/ns/tags/" />
        <category term="html5" scheme="http://roller.apache.org/ns/tags/" />
        <category term="ajaxian" scheme="http://roller.apache.org/ns/tags/" />
        <category term="opera" scheme="http://roller.apache.org/ns/tags/" />
        <category term="thunderhead" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">This morning, I added Dion and Ben&apos;s talk titled &lt;a href=&quot;http://north.webdirections.org/program#ajax-the-state-of-the-art&quot;&gt;Ajax: The State of the Art&lt;/a&gt;. Below are my notes from the event.
&lt;/p&gt;
&lt;p&gt;
Ajax started out as a bunch of hacks. It showed that we could take our web interfaces and do a lot more with them. A hack isn&apos;t necessarily a bad thing. Often, they turn into something much more elegant over time. The new browsers have many amazing capabilities that we haven&apos;t taken advantage of yet. We&apos;ve seen discussions on Ajax go from how to do XHR to frameworks and how rich and mature they are. Dojo is great for Enterprise Development (packing system, namespaces). jQuery is well-suited for lightweight developers (PHP). Prototype is fantastic for people who do a lot of JavaScript development and take it very seriously. 
&lt;/p&gt;
&lt;p&gt;
Today&apos;s Ajax landscape is mature, really rich, and really exciting. Today, Dion and Ben are going to talk about technologies they&apos;re really excited about for the future. 
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Canvas&lt;/strong&gt;&lt;br/&gt;
The building blocks of the web are text, boxes and images. With canvas, it really makes a lot more things possible. You can do bitmap rendering and image manipulation. They&apos;re showing a slide with Doom and Mario Kart running. Canvas 3D does true 3D rendering. Firefox and Opera have done prototypes of this. Can you do canvas-type things today in a browser? Yes, if you use Flash or Curl. Dion and Ben are excited about canvas over plugins for the following reasons:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No start-up delay&lt;/li&gt;
&lt;li&gt;Available on mobile devices today&lt;/li&gt;
&lt;li&gt;Rendering fidelity with browser (especially important for typography)&lt;/li&gt;
&lt;li&gt;No bridges necessary (no marshalling/unmarshalling)&lt;/li&gt;
&lt;li&gt;Not a plug-in&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &amp;lt;canvas&amp;gt; tag originally came from Apple&apos;s Dashboard. Dashboard&apos;s programming model was in HTML and JavaScript. Dashboard is using WebKit under the covers. Today, canvas support exists in every major browser except for IE. The good news is there are Flash and Silverlight bridges to add support to IE. There&apos;s also an ActiveX component that wraps the Firefox implementation and allows it to run in IE.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SVG&lt;/strong&gt;&lt;br/&gt;
Dion and Ben aren&apos;t that excited about SVG because it&apos;s such a huge spec. We&apos;ve been struggling with the HTML standard for the last 10 years and the thought of another huge spec for the next 10 years isn&apos;t that appealing.
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fast JavaScript&lt;/strong&gt;&lt;br/&gt;
Almost all major browsers have a Fast JavaScript implementation. Chrome has V8, Safari has SquirrelFish Extreme, Firefox has TraceMonkey and Opera has Carakan. This is exciting because of industry trends and how companies are trying to reduce computation cycles in data centers. The more computing that can be put on the client, the better. IE doesn&apos;t have anything, but Dion and Ben believe they are working on something.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Web Workers&lt;/strong&gt;&lt;br/&gt;
Interface latency is awful for applications. Jakob Nielsen once said:
&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
0.1 second is about the limit for having the user feel that the system is reacting instantaneously. 1.0 second is about the limit for the user&apos;s flow of thought to stay uninterrupted, even though the user will notice the delay.&lt;/p&gt;
&lt;p&gt;
Anything that takes longer than a tenth of a second should be pushed to a background thread. Unfortunately, there are no threads in the web. Maybe we can add threads to JavaScript? Brendan Eich has said that &quot;Threads suck&quot; and there&apos;s very little chance for threads getting into JavaScript. Gears brought Worker Pools and this is going into HTML 5 as Web Workers. You could also use Java applets to do this. With the latest Java Plugin, many of applets&apos; long-standing issues have been solved.
&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;Desktop Integration&lt;/strong&gt;&lt;br/&gt;
The ability to build desktop apps as web apps is very exciting. There&apos;s a few technologies that demonstrate this: Fluid, Mozilla Prism, Adobe AIR, Appcelerator Titanium and Gears. The Palm Pre demonstrates the logical extension of this. The Palm Pre uses the web stack as its developer SDK. It&apos;s very cool that web developers don&apos;t have to learn anything new to become a Palm developer. Desktop integration is exciting especially if we can access desktop applications like email and address book. 
&lt;/p&gt;
&lt;p&gt;
The Ajax frameworks that are out there have done a lot to make web development simpler. However, there&apos;s still a lot of pain with CSS and cross-browser issues. What if you took canvas and combined it with a sophisticated grid-based layout in JavaScript? 
&lt;/p&gt;
&lt;p&gt;
There&apos;s a lot of platforms out there: Microsoft Silverlight, Adobe Flash, Apple Cocoa and Sun&apos;s JavaFX. The web often isn&apos;t considered a platform. Dion and Ben believe there should be an &lt;em&gt;Open Web Platform&lt;/em&gt;. The problem right now is there is no central location to find out how to get stuff done. You have to search and find resources from many different locations. Mozilla is putting it&apos;s resources into creating an Open Web Platform. This site will consist of 4 different areas:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Home&lt;/li&gt;
&lt;li&gt;Documentation (for different frameworks, browsers, quirks)&lt;/li&gt;
&lt;li&gt;Dashboard (state of the open web)&lt;/li&gt;
&lt;li&gt;Roadmap (what&apos;s going on)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
This is not just Mozilla, it&apos;s very much a community effort. This is something that Ben and Dion have been working on. But there&apos;s something else they&apos;ve been working on too. They&apos;ve been talking about all these cool things, but what about an interesting application to test all these technologies?
&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;Bespin&lt;/strong&gt;&lt;br/&gt;
As they looked at code editors, most of them provide awful user experiences. Bespin is the Editor of Your Dreams and contains the following features:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Accessible from anywhere - any device in any location&lt;/li&gt;
&lt;li&gt;Simple to use, like Textmate (not heavyweight like Eclipse) - an editor, not an IDE&lt;/li&gt;
&lt;li&gt;Wicked Fast - performance, performance, performance&lt;/li&gt;
&lt;li&gt;Rock-solid real-time collaboration, like SubEthaEdit - it just works&lt;/li&gt;
&lt;li&gt;Integrated command-line, like vi - Fun like Quicksilver, social like Ubiquity&lt;/li&gt;
&lt;li&gt;&quot;Self-hosted&quot; environment, like Emacs - For extreme extensibility, but with JavaScript!&lt;/li&gt;
&lt;/ul&gt;
Dion and Ben are showed a screen shot of Bespin and now they&apos;re doing a demo. The core editor has what you&apos;d expect with syntax highlighting and line numbers. Canvas doesn&apos;t have text-selection by default, so they had to write it from scratch. The command line allows you to get help, run core command and also to subscribe to commands that others write. You can change your keybindings to emacs or vi as well as many other settings. Much of Bespin is event-driven, so you can easily plugin new behavior for different events. 
&lt;/p&gt;
&lt;p&gt;
For viewing files, they couldn&apos;t bring themselves to use a tree. Instead, they developed a file-browsing interface that looks very much like Apple&apos;s Finder. Personally, I like Finder, but wish it had Windows Explorer&apos;s path bar that allows you to simply type in the path without mouse clicks. Back to the command line. They&apos;ve done a lot to make things more discoverable so users can easily find the power of the editor.
&lt;/p&gt;
&lt;p&gt;
Bespin could be used to engage developers more with open source projects. Checking out projects, modifying code and creating patches can be a real pain. Bespin could be used to interface with open source projects in the cloud. You could login, modify code and easily patch/build with the click of a button. One other thing they want to do is to have the server do code-analysis as you&apos;re developing.
&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
Is it OK to love a software tool? You &lt;em&gt;must&lt;/em&gt; love your software tools. What we do as Software Developers is one of the most difficult jobs on the planet. Programmers, like poets, start with a blank slate and create something from nothing. If you don&apos;t love your tools, you&apos;ll start resenting what you do. If you don&apos;t love your tools, it shows in your work. &lt;em&gt;-- Dave Thomas at RubyConf08&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;Thunderhead&lt;/strong&gt;&lt;br/&gt;
A GUI Toolkit written with canvas and JavaScript. Allows you to do layouts with very little thought. It&apos;s a lab experiment that&apos;s in progress, stay tuned for more information.
&lt;p&gt;
All users care about is the user interface. Dion and Ben believe there&apos;s a key to creating compelling user experiences. It all has to do with &lt;em&gt;managing expectations&lt;/em&gt;. 
It&apos;s not that different from how you manage relationships in your life. Expectations for movies and games have changes drastically over the years. What used to be the web (animated gifs and awful web pages) has also changed drastically (video of Apple&apos;s online store). What was cool with MapQuest got changed drastically with Google Maps. What we have today isn&apos;t the end of the game - expectations will continue to change. However, users have different expectations for software. 
&lt;/p&gt;
&lt;p&gt;
Alan Cooper has done some interesting work in this area. The software designer needs to focus in on a user&apos;s goals. There are basic things you can apply to all users, for instance &quot;sex sells&quot;. An example of this is Delicious Library. This application allows you to keep track of things in your home such as books, movies, music and games. They made $500K in 3 months and made $54K the first day, with no advertising. 
&lt;/p&gt;
&lt;p&gt;
The quality of any software is determined by the interaction. If the interaction isn&apos;t good, it will poison the entire experience. Donald Norman has a good quote: &quot;Attractive things work better&quot;. In society, this is often called &quot;Dress for Success&quot;. 
&lt;/p&gt;
&lt;p&gt;
The Open Web is hear to stay because it has:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An Easy Programming Model&lt;/li&gt;
&lt;li&gt;Easy Remoting&lt;/li&gt;
&lt;li&gt;Extensive Customization Vectors (e.g. GreaseMonkey)&lt;/li&gt;
&lt;li&gt;Easy Deployment&lt;/li&gt;
&lt;li&gt;Great Widgets&lt;/li&gt;
&lt;li&gt;Great Visual Effects&lt;/li&gt;
&lt;li&gt;Great Mobile Story&lt;/li&gt;
&lt;li&gt;Desktop Integration&lt;/li&gt;
&lt;li&gt;State-of-the-Art Plug-ins&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
Bespin is a tech preview that they hope to release next week. Thunderhead will be released at the same time.
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;br/&gt;
This was a great talk and easily the most inspiring of the conference. Dion and Ben always do a great job and the sexiness of their presentation made it all the more appealing. </content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/the_state_of_the_web</id>
        <title type="html">The State of the Web 2009</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/the_state_of_the_web"/>
        <published>2009-02-04T18:05:38-07:00</published>
        <updated>2009-02-05T01:11:52-07:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="web" scheme="http://roller.apache.org/ns/tags/" />
        <category term="chriswilson" scheme="http://roller.apache.org/ns/tags/" />
        <category term="webdirectionsnorth" scheme="http://roller.apache.org/ns/tags/" />
        <category term="w3c" scheme="http://roller.apache.org/ns/tags/" />
        <category term="wdn09" scheme="http://roller.apache.org/ns/tags/" />
        <category term="opera" scheme="http://roller.apache.org/ns/tags/" />
        <category term="danconnolly" scheme="http://roller.apache.org/ns/tags/" />
        <category term="johnallsopp" scheme="http://roller.apache.org/ns/tags/" />
        <category term="microsoft" scheme="http://roller.apache.org/ns/tags/" />
        <category term="adobe" scheme="http://roller.apache.org/ns/tags/" />
        <category term="larserikbolstad" scheme="http://roller.apache.org/ns/tags/" />
        <category term="scottgegette" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">This afternoon, I attended &lt;a href=&quot;http://north.webdirections.org/program#the-state-of-the-web-2009&quot;&gt;The State of the Web 2009&lt;/a&gt; at &lt;a href=&quot;http://north.webdirections.org&quot;&gt;Web Directions North&lt;/a&gt;. Below are my notes from this session.
&lt;/p&gt;
&lt;p&gt;
This panel has quite the list of superstars:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://north.webdirections.org/speakers/#chris-wilson&quot;&gt;Chris Wilson&lt;/a&gt; (Microsoft)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://north.webdirections.org/speakers/#dan-connolly&quot;&gt;Dan Connolly&lt;/a&gt; (W3C HTML Working Group)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://north.webdirections.org/speakers#michael-tm-smith&quot;&gt;Mike (TM) Smith&lt;/a&gt; (W3C HTML Working Group)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://north.webdirections.org/speakers/#lars-erik-bolstad&quot;&gt;Lars Erik Bolstad&lt;/a&gt; (Opera)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://north.webdirections.org/speakers/#scott-fegette&quot;&gt;Scott Fegette&lt;/a&gt; (Adobe)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://north.webdirections.org/speakers/#john-allsopp&quot;&gt;John Allsopp&lt;/a&gt; (Organizer and &lt;a href=&quot;http://westciv.com/style_master/index.html&quot;&gt;Style Master&lt;/a&gt; Developer)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
John is moderating this session and is starting by asking each panelist to speak about what they believe the state of the web is. 
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Chris Wilson&lt;/strong&gt;&lt;br/&gt;
The future of the web may be as ubiquitous as electricity. Chris has a desktop, two laptops (one 10&quot; NetBook, one is a 13&quot; MacBook) and an iPhone. There&apos;s a lot of difference between these devices, especially when it comes to screen size. Chris uses a number of different browsers throughout the day. The web isn&apos;t just one browser, it isn&apos;t just one platform. He&apos;s showing a slide with a browser market share graph from &lt;a href=&quot;http://www.netapplications.com&quot;&gt;http://www.netapplications.com&lt;/a&gt;. 
&lt;/p&gt;
&lt;p&gt;Many different browsers are a reality. Many different devices are a reality. Web builders need to learn to write scalable applications that run across multiple browsers, devices and environments. They need to use progressive functionality and learn the tools they have in CSS and HTML. Semantic structuring helps. 
&lt;/p&gt;
&lt;p&gt;
Developers need to learn to live with multiple browsers. Cross-browser interoperability will get better, but it&apos;s likely to be an issue forever. Test suites with new specifications are helping. Developers should build for browsers of today &lt;em&gt;and&lt;/em&gt; tomorrow. 
&lt;/p&gt;
&lt;p&gt;You should build the applications you want to build and then figure out how how to make them degrade gracefully on the web.
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Lars Erik Bolstad&lt;/strong&gt;&lt;br/&gt;
Opera Software has 600 employees in 10 countries. They&apos;ve been developing Opera and other browsers since 1995. The bulk of what Opera does is based on commercial browser deliveries to OEMs around the world. 
&lt;/p&gt;
&lt;p&gt;Devices, platforms and networks: they come in all shapes and sizes. Only one thing unifies them: &lt;strong&gt;The Web&lt;/strong&gt;. The browser is becoming more and more important on these devices. Users are not satisfied with WAP-based content anymore, they want the same content no matter which device they&apos;re using.&lt;/p&gt;
&lt;p&gt;Opera Mini does its processing on the server-side. This allows Opera to gather statistics. These stats show that users around the world hit the same top sites on their mobile devices as they do on their desktops. It&apos;s a one-to-one match. Opera is seeing tremendous growth in the usage of Opera Mini, both in developed countries and emerging markets. 
&lt;/p&gt;
&lt;p&gt;The point: don&apos;t just develop for desktop browsers. The mobile market seems to be growing much faster. The problem is actually more in the hands of browser developers since they have to satisfy the user&apos;s desire to see the same content on mobile vs. desktop. 
&lt;/p&gt;
&lt;p&gt;Opera is focusing on advancing core browser technology in three areas:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Web standards&lt;/strong&gt;: CSS (webfonts, backgrounds/borders, transitions, transforms), HTML 5 (video/audio, persistent storage, drag and drop) and W3C Geolocation API and &quot;Mobile DOM&quot; API (access to camera, address book, calendars).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Performance&lt;/strong&gt;: VEGA (vector graphics-based rendering, hardware acceleration) and CARAKAN (new JavaScript engine, native compilation). For more details, see &lt;a href=&quot;http://my.opera.com/core&quot;&gt;http://my.opera.com/core&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Web applications&lt;/strong&gt;: Standalone web apps, RIAs, Widgets. Gears support and &lt;a href=&quot;http://www.opera.com/dragonfly&quot;&gt;Developer Tools&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Dan Connolly&lt;/strong&gt;&lt;br/&gt;
The web is kinda important these days. It&apos;s a big deal. Make a mistake and 300 million dollars go away (see end of last entry about United news). One of the beauties of the web is you can easily participate as an individual. You can report bugs, write articles and be a part of many web standards groups. Most of the other systems in the world don&apos;t provide this kind of access.
&lt;/p&gt;
&lt;p&gt;Dan has been under a rock for the last 5 years working on Semantic Web stuff. Now that he&apos;s back in the game, it&apos;s incredible how much stuff is going on. He&apos;s glad there&apos;s JavaScript frameworks so he doesn&apos;t have to learn everything. The default security policies in browsers are a little rickety at this point. They allow you to download and run JavaScript from virtually any site. &lt;a href=&quot;http://code.google.com/p/google-caja/&quot;&gt;Caja&lt;/a&gt; might help to solve this. Dan believes that security will become more important and stricter to protect web users.
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Scott Fegette&lt;/strong&gt;&lt;br/&gt;
Scott is a Product Manager in the Web Group of Adobe. At the beginning of each year, they do heavy user research. Adobe wants people that develop content for the web to be as expressive as possible. Scott is going to give us a peak into the conversations he&apos;s had with the web community.
&lt;/p&gt;
&lt;p&gt;One of the biggest topics on people&apos;s minds is The Economy, but it&apos;s not negative as you might think. Small web designers are actually getting more business in the downturn, likely because companies are polishing their presence on the web. People are working much more distributed these days. There&apos;s a few areas that Adobe generally asks about: CSS, JavaScript, HTML (both statically and dynamically).
&lt;/p&gt;
&lt;p&gt;Frameworks are becoming more important to developers, as well as with clients. They&apos;ve even seen some clients demand certain frameworks. Two years ago, when Adobe talked to small design shops and agencies, most web sites were built statically. Now they&apos;re developing with frameworks like WordPress. Out of 60 folks they talked to, only 2 were using static systems and not CMSes.
&lt;/p&gt;
&lt;p&gt;In the JavaScript frameworks arena, jQuery is the dominant leader. Shops are starting to use CSS frameworks as well. The only one Scott mentioned was Reset. Design is becoming a technical discipline and Adobe is calling this &lt;em&gt;Stateful Design&lt;/em&gt;. WYSIWIG is definitely dying and designers aren&apos;t developing with visual tools.
&lt;/p&gt;
&lt;p&gt;The kind of projects that people are working on has changed a lot. Many shops are being asked to do work on mobile development. The iPhone has done wonders for the industry in raising the awareness of what a mobile device can do.&lt;/p&gt;
&lt;p&gt;The other big investments for Adobe is RIAs and AIR. Ajax has matured enough that it can now compete with proprietary plugins like Flash. The reason for AIR is to allow web developers to use their skills to develop desktop applications. Flash and Flex are often overkill for browser-based applications, but they do often handle video and audio better than Ajax applications.
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Mike (TM) Smith&lt;/strong&gt;&lt;br/&gt;
Mike is also known as the &quot;W3C HTML jackass&quot;. Mike thinks the state of the web is that it&apos;s a mess in a lot of ways. If you don&apos;t believe him, ask Doug Crockford. Most of this stuff is going to remain a mess for the next 20 years, unless another genius like Tim Berners-Lee comes along and invents something new. However, the good part about it being a mess is that we all have jobs.
&lt;/p&gt;
&lt;p&gt;
One of the biggest things they&apos;re trying to do with HTML 5 is not breaking backward compatibility. Other working groups at the W3C don&apos;t share this philosophy, hence the reason they don&apos;t have browser vendors participating. Many of the ideas for HTML 5 game from Gears and Ajax Framework developers like &lt;a href=&quot;http://ejohn.org&quot;&gt;John Resig&lt;/a&gt;. All this will make things less messy, especially with the help of browser vendors.
&lt;/p&gt;
&lt;p&gt;Developers like the ubiquitous web and are pushing the mobile web. Mike thinks everyone just needs to get a life (big applause). For mobile, SVG has already been a big success. You will see significant great things with SVN happen in major browsers by next fall. If you&apos;re a web developer, you should spend some time experiment with SVG. It will payoff for you. If it doesn&apos;t pay off for you and you see Mike next year at Web Directions North, you can punch him in the face.
&lt;/p&gt;
&lt;p&gt;Location-aware applications will be big as well. Browser vendors are implementing the Geo Location API. It&apos;s implemented in Opera, Firefox, WebKit and Gears. Video on the web will be significant as well. The SVG working group pioneered video support into standards, before HTML 5. Many of the problems they face are related to video codecs. The only way to solve the problems with video on the web is with money and lawyers. Very specifically, there&apos;s no royalty-free codec for video. This is nothing that standards bodies can solve. The most promising is that Sun Microsystems is developing an open codec and spending money to make sure they&apos;re not infringing on patents. &lt;/p&gt;
&lt;p&gt;Not only is the HTML Working Group improving markup in HTML, they&apos;re also working on coming up with new APIs that give you access to features. If you have ideas that aren&apos;t included in HTML 5, the group is definitely interested in hearing about them. 
&lt;/p&gt;
&lt;p&gt;
After each panelist talked, John asked them questions about what&apos;s the biggest thing they&apos;d like to see implemented by everyone (open video codec, geo location api were the winners). Mike also did some complaining about XML and how broken it is because there&apos;s no failure mechanism. There was some audience banter with Chris about SVG in IE. 
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;br/&gt;
This was a very interesting session, especially to hear from the people who are building/supporting the future of the web. I liked Scott&apos;s talk on what Adobe&apos;s hearing from their users. I also liked hearing Mike (TM)&apos;s opinionated thoughts on XML and his non-marketing approach to most everything related to the web. Lars from Opera had a marketing-ish presentation, but it was nevertheless interesting to hear what Opera&apos;s working on. Good stuff.</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/choosing_an_ajax_framework</id>
        <title type="html">Choosing an Ajax Framework</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/choosing_an_ajax_framework"/>
        <published>2009-01-08T21:36:22-07:00</published>
        <updated>2009-01-09T04:42:59-07:00</updated> 
        <category term="/Java" label="Java" />
        <category term="comparison" scheme="http://roller.apache.org/ns/tags/" />
        <category term="extjs" scheme="http://roller.apache.org/ns/tags/" />
        <category term="gwt" scheme="http://roller.apache.org/ns/tags/" />
        <category term="webframeworks" scheme="http://roller.apache.org/ns/tags/" />
        <category term="dojo" scheme="http://roller.apache.org/ns/tags/" />
        <category term="ajax" scheme="http://roller.apache.org/ns/tags/" />
        <category term="yui" scheme="http://roller.apache.org/ns/tags/" />
        <category term="frameworks" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">This past week, my colleagues and I have been researching Ajax Frameworks. We&apos;re working on a project that&apos;s following SOFEA-style architecture principles and we want the best framework for our needs. I&apos;m writing this post to see 1) if you, the community, agree with our selection process and 2) to learn about your experiences with the frameworks we&apos;re evaluating. Below is the process we&apos;re following to make our choice.
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Choose a short list of frameworks to prototype with.&lt;/li&gt;
&lt;li&gt;Create an application prototype with each framework.&lt;/li&gt;
&lt;li&gt;Document findings and create a matrix with important criteria.&lt;/li&gt;
&lt;li&gt;Create presentation to summarize document.&lt;/li&gt;
&lt;li&gt;Deliver document, presentation (with demos) and recommendation.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For #1, we chose  &lt;a href=&quot;http://extjs.com/products/extjs/&quot;&gt;Ext JS&lt;/a&gt;, &lt;a href=&quot;http://dojotoolkit.org/&quot;&gt;Dojo&lt;/a&gt;, &lt;a href=&quot;http://developer.yahoo.com/yui/&quot;&gt;YUI&lt;/a&gt; and &lt;a href=&quot;http://code.google.com/intl/nl/webtoolkit/&quot;&gt;GWT&lt;/a&gt; because we feel these Ajax libraries offer the most UI widgets. We also considered Prototype/Scriptaculous, jQuery and MooTools, but decided against them because of their lack of UI widgets.&lt;/p&gt;
&lt;p&gt;For #2, we time-boxed ourselves to 3 days of development. In addition to basic functionality, we added several features (i.e. edit in place, drag and drop, calendar widgets, transitions, charts, grid) that might be used in the production application. We all were able to complete most of the functionality of the application. Of course, there&apos;s still some code cleanup as well as styling to make each app look good for the demo. The nice thing about doing this is we&apos;re able to look at each others code and see how the same thing is done in each framework. None of us are experts in any of the frameworks, so it&apos;s possible we could do things better. However, I think it&apos;s good we all started somewhat green because it shows what&apos;s possible for someone relatively new to the frameworks.&lt;/p&gt;
&lt;p&gt;For #3, we&apos;re creating a document with the following outline:&lt;/p&gt;
&lt;pre style=&quot;font-family: inherit; background: #CDFFCC; border: 1px solid #54FF52; width: 250px; padding-left: 20px&quot;&gt;
Introduction

Ajax Framework Candidates
(intro and explanation)

  Project Information
  (history)
  (license / cost)
  (number of committers)
  (support options)
  (mailing list traffic (nov/dec 2008))

Matrix and Notes

Conclusion
&lt;/pre&gt;
&lt;p&gt;For the Matrix referenced in the outline above, we&apos;re using a table with weights and ranks:&lt;/p&gt;

&lt;table class=&quot;comparison&quot; style=&quot;width: 500px&quot;&gt;
    &lt;thead&gt;
    &lt;tr&gt;
        &lt;th&gt;Weight&lt;/th&gt;
        &lt;th&gt;Criteria&lt;/th&gt;
        &lt;th&gt;Dojo&lt;/th&gt;
        &lt;th&gt;YUI&lt;/th&gt;
        &lt;th&gt;GWT&lt;/th&gt;
        &lt;th style=&quot;white-space: nowrap&quot;&gt;Ext JS&lt;/th&gt;
        &lt;th&gt;Notes&lt;/th&gt;
    &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
    &lt;tr&gt;
        &lt;td&gt;#&lt;/td&gt;
        &lt;td&gt;Important Criteria for Customer&lt;/td&gt;
        &lt;td&gt;0..1&lt;/td&gt;
        &lt;td&gt;0..1&lt;/td&gt;
        &lt;td&gt;0..1&lt;/td&gt;
        &lt;td&gt;0..1&lt;/td&gt;
        &lt;td&gt;Notes about rankings&lt;/td&gt;
    &lt;/tr&gt;
    &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Our strategy for filling in this matrix:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Customer adjusts the weight for each criteria (removing/adding as needed) so all weights add up to 1.&lt;/li&gt;
&lt;li&gt;We rank each framework with 0, .5 or 1 where 0 = doesn&apos;t satisfy criteria, .5 = partially satisfies, 1 = satisfies.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The list of criteria provided to us by our client is as follows (in no particular order).&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;Quality of Documentation/Tutorials/Self Help&lt;/li&gt;
&lt;li&gt;Browser support (most important browsers/versions based on web stats)&lt;/li&gt;
&lt;li&gt;Testability (esp. Selenium compatibility)&lt;/li&gt;
&lt;li&gt;Licensing&lt;/li&gt;
&lt;li&gt;Project health/adoption&lt;/li&gt;
&lt;li&gt;Performance&lt;/li&gt;
&lt;li&gt;Scalability&lt;/li&gt;
&lt;li&gt;Flexibility/extensibility&lt;/li&gt;
&lt;li&gt;Productivity (app dev, web dev)&lt;/li&gt;
&lt;li&gt;Richness of widget/component library&lt;/li&gt;
&lt;li&gt;Charting capability&lt;/li&gt;
&lt;li&gt;Ability to create new widgets&lt;/li&gt;
&lt;li&gt;Match to existing Java team skill-set&lt;/li&gt;
&lt;li&gt;Ease of deployment (on Ops, QA, Users)&lt;/li&gt;
&lt;li&gt;Degree of risk generally&lt;/li&gt;
&lt;li&gt;Ability to integrate with existing site (which includes Prototype)&lt;/li&gt;
&lt;li&gt;Easy to style with CSS&lt;/li&gt;
&lt;li&gt;Validation (esp. marking form elements invalid)&lt;/li&gt;
&lt;li&gt;Component Theme-ing/Decoration&lt;/li&gt;
&lt;li&gt;CDN Availability (i.e. Google&apos;s Ajax Libraries API or Ext CDN)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What do you think? How could this process be improved? Of course, if you have framework answers (0, .5 or 1) for our matrix, we&apos;d love to hear your opinions.</content>
    </entry>
</feed>

