<?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 [form] in weblog rd</title>
    <subtitle type="html">Search results for [form] within weblog Raible Designs</subtitle>
    <id>https://raibledesigns.com/rd/feed/entries/atom?q=form</id>

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

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

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

    <link rel="first" type="application/atom+xml" href="https://raibledesigns.com/rd/feed/entries/atom?q=form" />
    <link rel="next" type="application/atom+xml" href="https://raibledesigns.com/rd/feed/entries/atom?q=form&amp;page=1" />
    <updated>2026-04-30T03:43:46-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/getting_started_with_angular_cli</id>
        <title type="html">Getting Started + Testing with Angular CLI and Angular 2 (RC5)</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/getting_started_with_angular_cli"/>
        <published>2016-08-23T17:18:41-06:00</published>
        <updated>2017-04-28T20:38:39-06:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="npm" scheme="http://roller.apache.org/ns/tags/" />
        <category term="jasmine" scheme="http://roller.apache.org/ns/tags/" />
        <category term="angular2" scheme="http://roller.apache.org/ns/tags/" />
        <category term="karma" scheme="http://roller.apache.org/ns/tags/" />
        <category term="javascript" scheme="http://roller.apache.org/ns/tags/" />
        <category term="typescript" scheme="http://roller.apache.org/ns/tags/" />
        <category term="node" scheme="http://roller.apache.org/ns/tags/" />
        <category term="angular-cli" scheme="http://roller.apache.org/ns/tags/" />
        <category term="protractor" scheme="http://roller.apache.org/ns/tags/" />
        <category term="asciidoctor" scheme="http://roller.apache.org/ns/tags/" />
        <summary type="html">&lt;p&gt;I started creating Angular 2 applications when it was in beta (&lt;a
    href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_2&quot;&gt;back in March&lt;/a&gt;). To keep up with Angular 2&apos;s
    changes,
    I wrote a &lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_2_rc1&quot;&gt;tutorial about developing with
        RC1&lt;/a&gt; in June. Earlier this month, RC5 was released
    and &lt;a href=&quot;https://angular.io/docs/ts/latest/cookbook/rc4-to-rc5.html&quot;&gt;many things changed once again&lt;/a&gt;. I think
    Scott Davis sums it up nicely in a tweet.&lt;/p&gt;

&lt;div style=&quot;margin: 0 auto; max-width: 500px&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;They keep saying &quot;Release Candidate&quot;, but I
        don&apos;t think it means what they think it means...&lt;br&gt;&lt;br&gt;/cc &lt;a
            href=&quot;https://twitter.com/hashtag/angular2?src=hash&quot;&gt;#angular2&lt;/a&gt; &lt;a
            href=&quot;https://twitter.com/hashtag/rc5?src=hash&quot;&gt;#rc5&lt;/a&gt; &lt;a href=&quot;https://t.co/WmNalTYgTN&quot;&gt;https://t.co/WmNalTYgTN&lt;/a&gt;
    &lt;/p&gt;&amp;mdash; Scott Davis (@scottdavis99) &lt;a href=&quot;https://twitter.com/scottdavis99/status/763399251229417472&quot;&gt;August
        10, 2016&lt;/a&gt;&lt;/blockquote&gt;
    &lt;script async src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;/div&gt;

&lt;p&gt;To keep up with the rapid pace of change in Angular 2, I decided to write another tutorial, this time using &lt;a
    href=&quot;https://github.com/angular/angular-cli&quot;&gt;Angular CLI&lt;/a&gt;. The biggest change I found since
    writing the last tutorial is testing infrastructure changes. Since &lt;a
        href=&quot;https://angular.io/docs/ts/latest/testing/&quot;&gt;Angular&apos;s Testing documentation&lt;/a&gt; hasn&apos;t been updated
    recently, hopefully this tutorial will help.&lt;/p&gt;</summary>
        <content type="html">&lt;p&gt;I started creating Angular 2 applications when it was in beta (&lt;a
    href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_2&quot;&gt;back in March&lt;/a&gt;). To keep up with Angular 2&apos;s
    changes,
    I wrote a &lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_2_rc1&quot;&gt;tutorial about developing with
        RC1&lt;/a&gt; in June. Earlier this month, RC5 was released
    and &lt;a href=&quot;https://angular.io/docs/ts/latest/cookbook/rc4-to-rc5.html&quot;&gt;many things changed once again&lt;/a&gt;. I think
    Scott Davis sums it up nicely in a tweet.&lt;/p&gt;

&lt;div style=&quot;margin: 0 auto; max-width: 500px&quot;&gt;
    &lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;They keep saying &quot;Release Candidate&quot;, but I
        don&apos;t think it means what they think it means...&lt;br&gt;&lt;br&gt;/cc &lt;a
            href=&quot;https://twitter.com/hashtag/angular2?src=hash&quot;&gt;#angular2&lt;/a&gt; &lt;a
            href=&quot;https://twitter.com/hashtag/rc5?src=hash&quot;&gt;#rc5&lt;/a&gt; &lt;a href=&quot;https://t.co/WmNalTYgTN&quot;&gt;https://t.co/WmNalTYgTN&lt;/a&gt;
    &lt;/p&gt;&amp;mdash; Scott Davis (@scottdavis99) &lt;a href=&quot;https://twitter.com/scottdavis99/status/763399251229417472&quot;&gt;August
        10, 2016&lt;/a&gt;&lt;/blockquote&gt;
    &lt;script async src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;/div&gt;

&lt;p&gt;To keep up with the rapid pace of change in Angular 2, I decided to write another tutorial, this time using &lt;a
    href=&quot;https://github.com/angular/angular-cli&quot;&gt;Angular CLI&lt;/a&gt;. The biggest change I found since
    writing the last tutorial is testing infrastructure changes. Since &lt;a
        href=&quot;https://angular.io/docs/ts/latest/testing/&quot;&gt;Angular&apos;s Testing documentation&lt;/a&gt; hasn&apos;t been updated
    recently, hopefully this tutorial will help.&lt;/p&gt;
&lt;p&gt;Below is a table of contents in case you want to skip right to a particular section.&lt;/p&gt;

&lt;div id=&quot;toc&quot; class=&quot;toc&quot;&gt;
    &lt;ul&gt;
        &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_cli#_what_you_ll_build&quot;&gt;What you&apos;ll build&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_cli#_what_you_ll_need&quot;&gt;What you&apos;ll need&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_cli#_create_your_project&quot;&gt;Create your project&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_cli#_run_the_application&quot;&gt;Run the application&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_cli#_add_a_search_feature&quot;&gt;Add a search feature&lt;/a&gt;
            &lt;ul class=&quot;sectlevel2&quot;&gt;
                &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_cli#_the_basics&quot;&gt;The Basics&lt;/a&gt;&lt;/li&gt;
                &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_cli#_the_backend&quot;&gt;The Backend&lt;/a&gt;&lt;/li&gt;
            &lt;/ul&gt;
        &lt;/li&gt;
        &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_cli#_add_an_edit_feature&quot;&gt;Add an edit feature&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_cli#_testing&quot;&gt;Testing&lt;/a&gt;
            &lt;ul class=&quot;sectlevel2&quot;&gt;
                &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_cli#_unit_test_the_searchservice&quot;&gt;Unit test the SearchService&lt;/a&gt;&lt;/li&gt;
                &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_cli#_unit_test_the_searchcomponent&quot;&gt;Unit test the SearchComponent&lt;/a&gt;&lt;/li&gt;
                &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_cli#_integration_test_the_search_ui&quot;&gt;Integration test the search UI&lt;/a&gt;&lt;/li&gt;
                &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_cli#_testing_the_search_feature&quot;&gt;Testing the search feature&lt;/a&gt;&lt;/li&gt;
                &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_cli#_testing_the_edit_feature&quot;&gt;Testing the edit feature&lt;/a&gt;&lt;/li&gt;
            &lt;/ul&gt;
        &lt;/li&gt;
        &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_cli#_continuous_integration&quot;&gt;Continuous Integration&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_cli#_source_code&quot;&gt;Source code&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_cli#_summary&quot;&gt;Summary&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
&lt;/div&gt;

&lt;h3 id=&quot;_what_you_ll_build&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#_what_you_ll_build&quot;&gt;&lt;/a&gt;What you&apos;ll build&lt;/h3&gt;
&lt;p&gt;You&apos;ll build a simple web application with Angular CLI, a new tool for Angular 2 development. You&apos;ll
    create an application with search and edit features.&lt;/p&gt;

&lt;h3 id=&quot;_what_you_ll_need&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#_what_you_ll_need&quot;&gt;&lt;/a&gt;What you&apos;ll need&lt;/h3&gt;

&lt;ul&gt;
    &lt;li&gt;About 30-40 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; and its
        &lt;a href=&quot;https://plugins.jetbrains.com/plugin/8395?pr=idea&quot;&gt;Angular 2 TypeScript Live
            Templates
            plugin&lt;/a&gt;.
    &lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;http://nodejs.org/&quot;&gt;Node.js&lt;/a&gt; and npm installed. I recommend using &lt;a
        href=&quot;https://github.com/creationix/nvm&quot;&gt;nvm&lt;/a&gt;.
    &lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;https://github.com/angular/angular-cli&quot;&gt;Angular CLI&lt;/a&gt; installed. If you don&apos;t have
        Angular CLI installed, install it using &lt;code&gt;npm install -g angular-cli@latest&lt;/code&gt;.
    &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The latest release of Angular CLI (beta 10) uses Angular 2 RC4. Because of this, I used
    the master branch of Angular CLI to create this tutorial. To do this, clone &lt;a
        href=&quot;https://github.com/angular/angular-cli&quot;&gt;angular-cli&lt;/a&gt; and
    run &lt;code&gt;npm link&lt;/code&gt; in the directory you cloned it into. If you have issues,
    see &lt;a href=&quot;https://github.com/angular/angular-cli/issues/1773&quot;&gt;#1733&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;
    &lt;a href=&quot;https://augury.angular.io/&quot;&gt;Angular Augury&lt;/a&gt; is a Google Chrome Dev Tools
    extension for debugging Angular 2 applications.
    I haven&apos;t needed it much myself, but I can see how it might come in handy.&lt;/p&gt;

&lt;h3 id=&quot;_create_your_project&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#_create_your_project&quot;&gt;&lt;/a&gt;Create your project&lt;/h3&gt;
&lt;p&gt;Create a new project using the &lt;code&gt;ng new&lt;/code&gt; command:&lt;/p&gt;

&lt;pre&gt;ng new ng2-demo&lt;/pre&gt;

&lt;p&gt;This will create a &lt;code&gt;ng2-demo&lt;/code&gt; project and run &lt;code&gt;npm install&lt;/code&gt; in it. It takes
    about a minute to complete, but will vary based on your internet connection speed.&lt;/p&gt;

&lt;pre&gt;[mraible:~/dev] 45s $ ng new ng2-demo
installing ng2
  create .editorconfig
  create README.md
  create src/app/app.component.css
  create src/app/app.component.html
  create src/app/app.component.spec.ts
  create src/app/app.component.ts
  create src/app/environment.ts
  create src/app/index.ts
  create src/app/shared/index.ts
  create src/favicon.ico
  create src/index.html
  create src/main.ts
  create src/system-config.ts
  create src/tsconfig.json
  create src/typings.d.ts
  create angular-cli-build.js
  create angular-cli.json
  create config/environment.dev.ts
  create config/environment.js
  create config/environment.prod.ts
  create config/karma-test-shim.js
  create config/karma.conf.js
  create config/protractor.conf.js
  create e2e/app.e2e-spec.ts
  create e2e/app.po.ts
  create e2e/tsconfig.json
  create e2e/typings.d.ts
  create .gitignore
  create package.json
  create public/.npmignore
  create tslint.json
  create typings.json
Successfully initialized git.
- Installing packages for tooling via npm
  -- es6-shim (global)
  -- angular-protractor (global dev)
  -- jasmine (global dev)
  -- selenium-webdriver (global dev)

Installed packages for tooling via npm.
[mraible:~/dev] 1m5s $&lt;/pre&gt;

&lt;p&gt;You can see the what version of Angular CLI you&apos;re using with &lt;code&gt;ng --version&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;$ ng --version
angular-cli: local (v1.0.0-beta.11-webpack.2, branch: master)
node: 4.4.7
os: darwin x64&lt;/pre&gt;

&lt;h3 id=&quot;_run_the_application&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#_run_the_application&quot;&gt;&lt;/a&gt;Run the application&lt;/h3&gt;

&lt;p&gt;The project is configured with a simple web server for development. To start it, run:&lt;/p&gt;

&lt;pre&gt;ng serve&lt;/pre&gt;

&lt;p&gt;You should see a screen like the one below at &lt;a href=&quot;http://localhost:4200&quot;&gt;http://localhost:4200&lt;/a&gt;.&lt;/p&gt;

&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;img src=&quot;https://raw.githubusercontent.com/mraible/ng2-demo/master/src/assets/images/default-homepage.png&quot;
         alt=&quot;Default Homepage&quot; width=&quot;600&quot;&gt;
&lt;/p&gt;
&lt;p&gt;You can make sure your new project&apos;s tests pass, run &lt;code&gt;ng test&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;$ ng test
Built project successfully. Stored in &quot;dist/&quot;.
...
Chrome 52.0.2743 (Mac OS X 10.11.6): Executed 2 of 2 SUCCESS (0.039 secs / 0.012 secs)&lt;/pre&gt;

&lt;h3 id=&quot;_add_a_search_feature&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#_add_a_search_feature&quot;&gt;&lt;/a&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 &lt;code&gt;ng2-demo&lt;/code&gt; directory.&lt;/p&gt;

&lt;h3 id=&quot;_the_basics&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#_the_basics&quot;&gt;&lt;/a&gt;The Basics&lt;/h3&gt;
&lt;p&gt;In a terminal window, cd into your project&apos;s directory and run the following command. This will
    create a search component.&lt;/p&gt;
&lt;pre&gt;$ ng g component search
installing component
  create src/app/search/search.component.css
  create src/app/search/search.component.html
  create src/app/search/search.component.spec.ts
  create src/app/search/search.component.ts
  create src/app/search/index.ts&lt;/pre&gt;

&lt;div class=&quot;quote&quot;&gt;
    &lt;p style=&quot;margin-top: 0&quot;&gt;&lt;strong&gt;Adding a Search Route&lt;/strong&gt;&lt;br&gt;

        In previous versions of CLI, you could generate a route
        &lt;strong&gt;and&lt;/strong&gt; a component. However, since beta 8, route generation has been
        disabled. This will likely be re-enabled in a future release.&lt;/p&gt;

    &lt;p&gt;The &lt;a href=&quot;https://angular.io/docs/ts/latest/guide/router.html&quot;&gt;Router
        documentation&lt;/a&gt;
        for Angular 2 RC5 provides the information you need to
        setup a route to the &lt;code&gt;SearchComponent&lt;/code&gt; you just generated. Here&apos;s a quick
        summary:&lt;/p&gt;

    &lt;p&gt;Create &lt;code&gt;src/app/app.routing.ts&lt;/code&gt; to define your routes.&lt;/p&gt;

    &lt;pre class=&quot;brush: js&quot;&gt;
import { Routes, RouterModule } from &apos;@angular/router&apos;;
import { SearchComponent } from &apos;./search/index&apos;;

const appRoutes: Routes = [
  { path: &apos;search&apos;, component: SearchComponent },
  { path: &apos;&apos;, redirectTo: &apos;/search&apos;, pathMatch: &apos;full&apos; }
];

export const appRoutingProviders: any[] = [];

export const routing = RouterModule.forRoot(appRoutes);
&lt;/pre&gt;
    &lt;p class=&quot;alert alert-warning&quot;&gt;
        Without the last path to redirect, there&apos;s a &lt;a
        href=&quot;http://stackoverflow.com/questions/38998085/error-cannot-match-any-routes-in-angular-2-rc5-with-angular-cli&quot;&gt;Cannot
        match any routes: &apos;&apos;&lt;/a&gt; console error.
    &lt;/p&gt;
    &lt;p&gt;In &lt;code&gt;src/app/app.module.ts&lt;/code&gt;, import the two constants you exported and
        configure them in &lt;code&gt;@NgModule&lt;/code&gt;:&lt;/p&gt;

    &lt;pre class=&quot;brush: js&quot;&gt;
import { routing, appRoutingProviders } from &apos;./app.routing&apos;;

import { SearchComponent } from &apos;./search/search.component&apos;;

@NgModule({
  ...
  imports: [
    ...
    routing
  ],
  providers: [appRoutingProviders],
  ...
})
export class AppModule { }
&lt;/pre&gt;
    &lt;p&gt;In &lt;code&gt;src/app/app.component.html&lt;/code&gt;, add a &lt;code&gt;RouterOutlet&lt;/code&gt; to display routes.&lt;/p&gt;

    &lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;!-- Routed views go here --&amp;gt;
&amp;lt;router-outlet&amp;gt;&amp;lt;/router-outlet&amp;gt;
&lt;/pre&gt;
    &lt;p style=&quot;margin-bottom: 0&quot;&gt;Now that you have routing setup, you can continue writing the search feature.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;To allow navigation to the &lt;code&gt;SearchComponent&lt;/code&gt;, you can add a link in &lt;code&gt;src/app/app.component.html&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;nav&amp;gt;
  &amp;lt;a routerLink=&quot;/search&quot; routerLinkActive=&quot;active&quot;&amp;gt;Search&amp;lt;/a&amp;gt;
&amp;lt;/nav&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Open &lt;code&gt;src/app/search/search.component.html&lt;/code&gt; and replace its default HTML with the following:&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;h2&amp;gt;Search&amp;lt;/h2&amp;gt;
&amp;lt;form&amp;gt;
  &amp;lt;input type=&quot;search&quot; name=&quot;query&quot; &amp;#91;(ngModel)&amp;#93;=&quot;query&quot; (keyup.enter)=&quot;search()&quot;&amp;gt;
  &amp;lt;button type=&quot;button&quot; (click)=&quot;search()&quot;&amp;gt;Search&amp;lt;/button&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;pre&amp;gt;{{searchResults | json}}&amp;lt;/pre&amp;gt;
&lt;/pre&gt;
&lt;p&gt;If you still have &lt;code&gt;ng serve&lt;/code&gt; running, your browser should refresh automatically.
    If not, navigate to &lt;a href=&quot;http://localhost:4200&quot;&gt;http://localhost:4200&lt;/a&gt;, and you should see
    the search form.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;img src=&quot;https://raw.githubusercontent.com/mraible/ng2-demo/master/src/assets/images/search-without-css.png&quot;
         alt=&quot;Search component&quot; width=&quot;600&quot;&gt;
&lt;/p&gt;
&lt;p&gt;If you want to add CSS for this components, open &lt;code&gt;src/app/search/search.component.css&lt;/code&gt; and
    add some CSS. For example:&lt;/p&gt;
&lt;pre class=&quot;brush: css&quot;&gt;
:host {
  display: block;
  padding: 0 20px;
}
&lt;/pre&gt;
&lt;p&gt;This section has shown you how to generate a new component to a basic Angular 2 application
    with Angular CLI. The next section shows you how to create a use a JSON file and &lt;code&gt;localStorage&lt;/code&gt; to
    create a fake API.&lt;/p&gt;

&lt;h3 id=&quot;_the_backend&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#_the_backend&quot;&gt;&lt;/a&gt;The Backend&lt;/h3&gt;
&lt;p&gt;To get search results, create a &lt;code&gt;SearchService&lt;/code&gt; that makes HTTP requests to a JSON
    file. Start by generating a new service.&lt;/p&gt;
&lt;pre&gt;ng g service search&lt;/pre&gt;
&lt;p&gt;Move the generated &lt;code&gt;search.service.ts&lt;/code&gt; and its test to &lt;code&gt;app/shared/search&lt;/code&gt;. You
    will likely need to create this directory.&lt;/p&gt;
&lt;p&gt;Then, create &lt;code&gt;src/app/shared/search/data/people.json&lt;/code&gt; to hold your data.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
[
  {
    &quot;id&quot;: 1,
    &quot;name&quot;: &quot;Peyton Manning&quot;,
    &quot;phone&quot;: &quot;(303) 567-8910&quot;,
    &quot;address&quot;: {
      &quot;street&quot;: &quot;1234 Main Street&quot;,
      &quot;city&quot;: &quot;Greenwood Village&quot;,
      &quot;state&quot;: &quot;CO&quot;,
      &quot;zip&quot;: &quot;80111&quot;
    }
  },
  {
    &quot;id&quot;: 2,
    &quot;name&quot;: &quot;Demaryius Thomas&quot;,
    &quot;phone&quot;: &quot;(720) 213-9876&quot;,
    &quot;address&quot;: {
      &quot;street&quot;: &quot;5555 Marion Street&quot;,
      &quot;city&quot;: &quot;Denver&quot;,
      &quot;state&quot;: &quot;CO&quot;,
      &quot;zip&quot;: &quot;80202&quot;
    }
  },
  {
    &quot;id&quot;: 3,
    &quot;name&quot;: &quot;Von Miller&quot;,
    &quot;phone&quot;: &quot;(917) 323-2333&quot;,
    &quot;address&quot;: {
      &quot;street&quot;: &quot;14 Mountain Way&quot;,
      &quot;city&quot;: &quot;Vail&quot;,
      &quot;state&quot;: &quot;CO&quot;,
      &quot;zip&quot;: &quot;81657&quot;
    }
  }
]
&lt;/pre&gt;
&lt;p&gt;Modify &lt;code&gt;src/app/shared/search/search.service.ts&lt;/code&gt; and provide &lt;code&gt;Http&lt;/code&gt; as
    a dependency in its constructor. In this same file, create a &lt;code&gt;getAll()&lt;/code&gt; method to gather all the people. Also,
    define the &lt;code&gt;Address&lt;/code&gt; and &lt;code&gt;Person&lt;/code&gt; classes that JSON will be marshalled to.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import { Injectable } from &apos;@angular/core&apos;;
import { Http, Response } from &apos;@angular/http&apos;;

@Injectable()
export class SearchService {
  constructor(private http: Http) {}

  getAll() {
    return this.http.get(&apos;app/shared/search/data/people.json&apos;).map((res: Response) =&gt; res.json());
  }
}

export class Address {
  street: string;
  city: string;
  state: string;
  zip: string;

  constructor(obj?: any) {
    this.street = obj &amp;&amp; obj.street || null;
    this.city = obj &amp;&amp; obj.city || null;
    this.state = obj &amp;&amp; obj.state || null;
    this.zip = obj &amp;&amp; obj.zip || null;
  }
}

export class Person {
  id: number;
  name: string;
  phone: string;
  address: Address;

  constructor(obj?: any) {
    this.id = obj &amp;&amp; Number(obj.id) || null;
    this.name = obj &amp;&amp; obj.name || null;
    this.phone = obj &amp;&amp; obj.phone || null;
    this.address = obj &amp;&amp; obj.address || null;
  }
}
&lt;/pre&gt;
&lt;p&gt;To make these classes available for consumption by your components, edit &lt;code&gt;src/app/shared/index.ts&lt;/code&gt;
    and add the following:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;export * from &apos;./search/search.service&apos;;&lt;/pre&gt;
&lt;p&gt;In &lt;code&gt;search.component.ts&lt;/code&gt;, add imports for these classes.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;import { Person, SearchService } from &apos;../shared/index&apos;;&lt;/pre&gt;
&lt;p&gt;You can now add &lt;code&gt;query&lt;/code&gt; and &lt;code&gt;searchResults&lt;/code&gt; variables. While you&apos;re
    there, modify the constructor to inject the &lt;code&gt;SearchService&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
export class SearchComponent implements OnInit {
  query: string;
  searchResults: Array&amp;lt;Person&gt;;

  constructor(private searchService: SearchService) {}
&lt;/pre&gt;
&lt;p&gt;Then implement the &lt;code&gt;search()&lt;/code&gt; method to call the service&apos;s &lt;code&gt;getAll()&lt;/code&gt; method.&lt;/p&gt;

&lt;pre class=&quot;brush: js&quot;&gt;
search(): void {
  this.searchService.getAll().subscribe(
    data =&gt; { this.searchResults = data; },
    error =&gt; console.log(error)
  );
}
&lt;/pre&gt;
&lt;p&gt;At this point, you&apos;ll likely see the following message in your browser&apos;s console.&lt;/p&gt;
&lt;pre&gt;ORIGINAL EXCEPTION: No provider for SearchService!&lt;/pre&gt;
&lt;p&gt;To fix the &quot;No provider&quot; error from above, update &lt;code&gt;app.component.ts&lt;/code&gt; to import the &lt;code&gt;SearchService&lt;/code&gt;
    and add the service to the list of providers.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import { SearchService } from &apos;./shared/index&apos;;

@Component({
  ...
  styleUrls: [&apos;app.component.css&apos;],
  viewProviders: [SearchService]
})
&lt;/pre&gt;
&lt;p&gt;Now clicking the search button should work. To make the results look better, remove the
    &lt;code&gt;&amp;lt;pre&amp;gt;&lt;/code&gt; tag and replace it with a &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;table *ngIf=&quot;searchResults&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 *ngFor=&quot;let person of searchResults; let i=index&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;p&gt;Then add some additional CSS to improve its table layout.&lt;/p&gt;
&lt;pre class=&quot;brush: css&quot;&gt;
table {
  margin-top: 10px;
  border-collapse: collapse;
}

th {
  text-align: left;
  border-bottom: 2px solid #ddd;
  padding: 8px;
}

td {
  border-top: 1px solid #ddd;
  padding: 8px;
}
&lt;/pre&gt;
&lt;p&gt;Now the search results look better.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;img
        src=&quot;https://raw.githubusercontent.com/mraible/ng2-demo/master/src/assets/images/search-results.png&quot;
        alt=&quot;Search Results&quot; width=&quot;600&quot;&gt;
&lt;/p&gt;
&lt;p&gt;But wait, we still don&apos;t have search functionality! To add a search feature, add a
    &lt;code&gt;search()&lt;/code&gt; method to &lt;code&gt;SearchService&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
search(q: string) {
  if (!q || q === &apos;*&apos;) {
    q = &apos;&apos;;
  } else {
    q = q.toLowerCase();
  }
  return this.getAll().map(data =&gt; {
    let results: any = [];
    data.map(item =&gt; {
      if (JSON.stringify(item).toLowerCase().includes(q)) {
        results.push(item);
      }
    });
    return results;
  });
}
&lt;/pre&gt;
&lt;p&gt;Then refactor &lt;code&gt;SearchComponent&lt;/code&gt; to call this method with its &lt;code&gt;query&lt;/code&gt; variable.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
search(): void {
  this.searchService.search(this.query).subscribe(
    data =&gt; { this.searchResults = data; },
    error =&gt; console.log(error)
  );
}
&lt;/pre&gt;
&lt;p&gt;Now search results will be filtered by the query value you type in.&lt;/p&gt;
&lt;p&gt;This section showed 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 id=&quot;_add_an_edit_feature&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#_add_an_edit_feature&quot;&gt;&lt;/a&gt;Add an edit feature&lt;/h3&gt;

&lt;p&gt;Modify &lt;code&gt;search.component.html&lt;/code&gt; to add a click handler for editing a person.&lt;/p&gt;

&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;td&amp;gt;&amp;lt;a (click)=&quot;onSelect(person)&quot;&amp;gt;{{person.name}}&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&lt;/pre&gt;

&lt;div class=&quot;alert alert-warning&quot;&gt;
    &lt;p&gt;In previous versions of Angular 2, you could embed a link with parameters directly into the HTML.
        For example:&lt;/p&gt;
    &lt;pre&gt;&amp;lt;a &amp;#91;routerLink&amp;#93;=&quot;&amp;#91;&apos;/edit&apos;, person.id&amp;#93;&quot;&amp;gt;&lt;/pre&gt;

    &lt;p&gt;Unfortunately, this doesn&apos;t work with RC5. Another issue is adding &lt;code&gt;href=&quot;&quot;&lt;/code&gt; causes
        the page to refresh. Without &lt;code&gt;href&lt;/code&gt;, the link doesn&apos;t look like a link.
        If you know of a solution to this problem, please send me a pull request.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Then add &lt;code&gt;onSelect(person)&lt;/code&gt; to &lt;code&gt;search.component.ts&lt;/code&gt;. You&apos;ll need to import
    &lt;code&gt;Router&lt;/code&gt; and set it as a local variable to make this work.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import { Router } from &apos;@angular/router&apos;;
...
export class SearchComponent implements OnInit {
  ...

  constructor(private searchService: SearchService, private router: Router) { }

  ...

  onSelect(person: Person) {
    this.router.navigate([&apos;/edit&apos;, person.id]);
  }
}
&lt;/pre&gt;
&lt;p&gt;Run the following command to generate an &lt;code&gt;EditComponent&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;$ ng g component edit
installing component
  create src/app/edit/edit.component.css
  create src/app/edit/edit.component.html
  create src/app/edit/edit.component.spec.ts
  create src/app/edit/edit.component.ts
  create src/app/edit/index.ts&lt;/pre&gt;
&lt;p&gt;Add a route for this component in &lt;code&gt;app.routing.ts&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import { EditComponent } from &apos;./edit/index&apos;;

const appRoutes: Routes = [
  { path: &apos;search&apos;, component: SearchComponent },
  { path: &apos;edit/:id&apos;, component: EditComponent },
  { path: &apos;&apos;, redirectTo: &apos;/search&apos;, pathMatch: &apos;full&apos; }
];
&lt;/pre&gt;
&lt;p&gt;Update &lt;code&gt;src/app/edit/edit.component.html&lt;/code&gt; to display an editable form. You might notice
    I&apos;ve added &lt;code&gt;id&lt;/code&gt; attributes to most elements. This is to
    make things easier when writing integration tests with Protractor.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
&amp;lt;div *ngIf=&quot;person&quot;&amp;gt;
  &amp;lt;h3&amp;gt;{{editName}}&amp;lt;/h3&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;label&amp;gt;Id:&amp;lt;/label&amp;gt;
    {{person.id}}
  &amp;lt;/div&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;label&amp;gt;Name:&amp;lt;/label&amp;gt;
    &amp;lt;input &amp;#91;(ngModel)&amp;#93;=&quot;editName&quot; name=&quot;name&quot; id=&quot;name&quot; placeholder=&quot;name&quot;/&amp;gt;
  &amp;lt;/div&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;label&amp;gt;Phone:&amp;lt;/label&amp;gt;
    &amp;lt;input &amp;#91;(ngModel)&amp;#93;=&quot;editPhone&quot; name=&quot;phone&quot; id=&quot;phone&quot; placeholder=&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&amp;gt;
      &amp;lt;input &amp;#91;(ngModel)&amp;#93;=&quot;editAddress.street&quot; id=&quot;street&quot;&amp;gt;&amp;lt;br/&amp;gt;
      &amp;lt;input &amp;#91;(ngModel)&amp;#93;=&quot;editAddress.city&quot; id=&quot;city&quot;&amp;gt;,
      &amp;lt;input &amp;#91;(ngModel)&amp;#93;=&quot;editAddress.state&quot; id=&quot;state&quot; size=&quot;2&quot;&amp;gt;
      &amp;lt;input &amp;#91;(ngModel)&amp;#93;=&quot;editAddress.zip&quot; id=&quot;zip&quot; size=&quot;5&quot;&amp;gt;
    &amp;lt;/address&amp;gt;
  &amp;lt;/fieldset&amp;gt;
  &amp;lt;button (click)=&quot;save()&quot; id=&quot;save&quot;&amp;gt;Save&amp;lt;/button&amp;gt;
  &amp;lt;button (click)=&quot;cancel()&quot; id=&quot;cancel&quot;&amp;gt;Cancel&amp;lt;/button&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Modify &lt;code&gt;EditComponent&lt;/code&gt; to import model and service classes and to use the
    &lt;code&gt;SearchService&lt;/code&gt; to get data.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import { Component, OnInit, OnDestroy } from &apos;@angular/core&apos;;
import { Address, Person, SearchService } from &apos;../shared/index&apos;;
import { Subscription } from &apos;rxjs&apos;;
import { ActivatedRoute, Router } from &apos;@angular/router&apos;;

@Component({
  selector: &apos;app-edit&apos;,
  templateUrl: &apos;edit.component.html&apos;,
  styleUrls: [&apos;edit.component.css&apos;]
})
export class EditComponent implements OnInit, OnDestroy {
  person: Person;
  editName: string;
  editPhone: string;
  editAddress: Address;

  sub: Subscription;

  constructor(private route: ActivatedRoute,
              private router: Router,
              private service: SearchService) {
  }

  ngOnInit() {
    this.sub = this.route.params.subscribe(params =&gt; {
      let id = + params[&apos;id&apos;]; // (+) converts string &apos;id&apos; to a number
      this.service.get(id).subscribe(person =&gt; {
        if (person) {
          this.editName = person.name;
          this.editPhone = person.phone;
          this.editAddress = person.address;
          this.person = person;
        } else {
          this.gotoList();
        }
      });
    });
  }

  ngOnDestroy() {
    this.sub.unsubscribe();
  }

  cancel() {
    this.router.navigate([&apos;/search&apos;]);
  }

  save() {
    this.person.name = this.editName;
    this.person.phone = this.editPhone;
    this.person.address = this.editAddress;
    this.service.save(this.person);
    this.gotoList();
  }

  gotoList() {
    if (this.person) {
      this.router.navigate([&apos;/search&apos;, {term: this.person.name} ]);
    } else {
      this.router.navigate([&apos;/search&apos;]);
    }
  }
}
&lt;/pre&gt;
&lt;p&gt;Modify &lt;code&gt;SearchService&lt;/code&gt; to contain functions for finding a person by their id, and saving
    them. While you&apos;re in there, modify the &lt;code&gt;search()&lt;/code&gt; method to
    be aware of updated objects in &lt;code&gt;localStorage&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
search(q: string) {
  if (!q || q === &apos;*&apos;) {
    q = &apos;&apos;;
  } else {
    q = q.toLowerCase();
  }
  return this.getAll().map(data =&gt; {
    let results: any = [];
    data.map(item =&gt; {
      // check for item in localStorage
      if (localStorage[&apos;person&apos; + item.id]) {
        item = JSON.parse(localStorage[&apos;person&apos; + item.id]);
      }
      if (JSON.stringify(item).toLowerCase().includes(q)) {
        results.push(item);
      }
    });
    return results;
  });
}

get(id: number) {
  return this.getAll().map(all =&gt; {
    if (localStorage[&apos;person&apos; + id]) {
      return JSON.parse(localStorage[&apos;person&apos; + id]);
    }
    return all.find(e =&gt; e.id === id);
  });
}

save(person: Person) {
  localStorage[&apos;person&apos; + person.id] = JSON.stringify(person);
}
&lt;/pre&gt;
&lt;p&gt;You can add CSS to &lt;code&gt;src/app/edit/edit.component.css&lt;/code&gt; if you want to make the form look a
    bit better.&lt;/p&gt;
&lt;pre class=&quot;brush: css&quot;&gt;
:host {
  display: block;
  padding: 0 20px;
}

button {
  margin-top: 10px;
}
&lt;/pre&gt;
&lt;p&gt;At this point, you should be able to search for a person and update their information.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;img src=&quot;https://rawgit.com/mraible/ng2-demo/master//src/assets/images/edit-form.png&quot;
         alt=&quot;Edit form&quot;
         width=&quot;600&quot;&gt;
&lt;/p&gt;
&lt;p&gt;The &amp;lt;form&amp;gt; in &lt;code&gt;src/app/edit/edit.component.html&lt;/code&gt; calls a &lt;code&gt;save()&lt;/code&gt;
    function to update a person&apos;s data. You already implemented this above.
    The function calls a &lt;code&gt;gotoList()&lt;/code&gt; function that appends the person&apos;s name to the URL when
    sending the user back to the search screen.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
gotoList() {
  if (this.person) {
    this.router.navigate([&apos;/search&apos;, {term: this.person.name} ]);
  } else {
    this.router.navigate([&apos;/search&apos;]);
  }
}
&lt;/pre&gt;
&lt;p&gt;Since the &lt;code&gt;SearchComponent&lt;/code&gt; doesn&apos;t execute a search automatically when you execute this
    URL, add the following logic to do so in its constructor.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import { Router, ActivatedRoute } from &apos;@angular/router&apos;;
import { Subscription } from &apos;rxjs&apos;;
...
  sub: Subscription;

  constructor(private searchService: SearchService, private router: Router, private route: ActivatedRoute) {
    this.sub = this.route.params.subscribe(params =&gt; {
      if (params[&apos;term&apos;]) {
        this.query = decodeURIComponent(params[&apos;term&apos;]);
        this.search();
      }
    });
  }
&lt;/pre&gt;
&lt;p&gt;You&apos;ll want to implement &lt;code&gt;OnDestroy&lt;/code&gt; and define the &lt;code&gt;ngOnDestroy&lt;/code&gt; method to
    clean up this subscription.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import { Component, OnInit, OnDestroy } from &apos;@angular/core&apos;;

export class SearchComponent implements OnInit, OnDestroy {
...
  ngOnDestroy() {
    this.sub.unsubscribe();
  }
}
&lt;/pre&gt;
&lt;p&gt;After making all these changes, you should be able to search/edit/update a person&apos;s information. If
    it works - nice job!&lt;/p&gt;

&lt;h3 id=&quot;_testing&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#_testing&quot;&gt;&lt;/a&gt;Testing&lt;/h3&gt;

&lt;p&gt;Now that you&apos;ve built an application, it&apos;s important to test it to ensure it works. The best reason for
    writing tests is to automate your testing. Without tests, you&apos;ll likely be testing manually. This manual testing will
    take longer and longer as your application grows.&lt;/p&gt;
&lt;p&gt;In this section, you&apos;ll learn to use &lt;a href=&quot;http://jasmine.github.io/&quot;&gt;Jasmine&lt;/a&gt; for unit testing
    controllers and &lt;a href=&quot;https://angular.github.io/protractor/&quot;&gt;Protractor&lt;/a&gt; for
    integration testing. Angular&apos;s testing documentation lists &lt;a
        href=&quot;https://angular.io/docs/ts/latest/guide/testing.html&quot;&gt;good reasons to test&lt;/a&gt;, but doesn&apos;t currently have many examples.&lt;/p&gt;

&lt;h3 id=&quot;_unit_test_the_searchservice&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#_unit_test_the_searchservice&quot;&gt;&lt;/a&gt;Unit test
    the SearchService&lt;/h3&gt;
&lt;p&gt;Modify &lt;code&gt;src/app/shared/search/search.service.spec.ts&lt;/code&gt; and setup the test&apos;s infrastructure
    using &lt;a href=&quot;https://angular.io/docs/js/latest/api/http/testing/MockBackend-class.html&quot;&gt;MockBackend&lt;/a&gt;
    and &lt;a href=&quot;https://angular.io/docs/ts/latest/api/http/index/BaseRequestOptions-class.html&quot;&gt;BaseRequestOptions&lt;/a&gt;.
&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import { MockBackend } from &apos;@angular/http/testing&apos;;
import { Http, ConnectionBackend, BaseRequestOptions, Response, ResponseOptions } from &apos;@angular/http&apos;;
import { SearchService } from &apos;./search.service&apos;;
import { tick, fakeAsync } from &apos;@angular/core/testing/fake_async&apos;;
import { inject, TestBed } from &apos;@angular/core/testing/test_bed&apos;;

describe(&apos;SearchService&apos;, () =&gt; {
  beforeEach(() =&gt; {

    TestBed.configureTestingModule({
      providers: [
        {
          provide: Http, useFactory: (backend: ConnectionBackend, defaultOptions: BaseRequestOptions) =&gt; {
          return new Http(backend, defaultOptions);
        }, deps: [MockBackend, BaseRequestOptions]
        },
        {provide: SearchService, useClass: SearchService},
        {provide: MockBackend, useClass: MockBackend},
        {provide: BaseRequestOptions, useClass: BaseRequestOptions}
      ]
    });
  });
});
&lt;/pre&gt;
&lt;p&gt;If you run &lt;code&gt;ng test&lt;/code&gt;, you will likely see some errors about the test stubs that Angular
    CLI created for you. You can ignore these for now.&lt;/p&gt;
&lt;pre&gt;ERROR in [default] /Users/mraible/ng2-demo/src/app/edit/edit.component.spec.ts:10:20
Supplied parameters do not match any signature of call target.

ERROR in [default] /Users/mraible/ng2-demo/src/app/search/search.component.spec.ts:10:20
Supplied parameters do not match any signature of call target.&lt;/pre&gt;
&lt;p&gt;Add the first test of &lt;code&gt;getAll()&lt;/code&gt; to &lt;code&gt;search.service.spec.ts&lt;/code&gt;. This test shows
    how &lt;code&gt;MockBackend&lt;/code&gt; can be used to mock results and set the response.&lt;/p&gt;
&lt;p class=&quot;alert alert-info&quot;&gt;
    &lt;strong&gt;TIP:&lt;/strong&gt;
    When you are testing code that returns either a Promise or an RxJS Observable, you can use
    the &lt;code&gt;fakeAsync&lt;/code&gt; helper to test that code as if it were synchronous.
    Promises are be fulfilled and Observables are notified immediately after you call &lt;code&gt;tick()&lt;/code&gt;.
&lt;/p&gt;
&lt;p&gt;The test below should be on the same level as &lt;code&gt;beforeEach&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
it(&apos;should retrieve all search results&apos;,
  inject([SearchService, MockBackend], fakeAsync((searchService: SearchService, mockBackend: MockBackend) =&gt; {
    let res: Response;
    mockBackend.connections.subscribe(c =&gt; {
      expect(c.request.url).toBe(&apos;app/shared/search/data/people.json&apos;);
      let response = new ResponseOptions({body: &apos;[{&quot;name&quot;: &quot;John Elway&quot;}, {&quot;name&quot;: &quot;Gary Kubiak&quot;}]&apos;});
      c.mockRespond(new Response(response));
    });
    searchService.getAll().subscribe((response) =&gt; {
      res = response;
    });
    tick();
    expect(res[0].name).toBe(&apos;John Elway&apos;);
  }))
);
&lt;/pre&gt;
&lt;p&gt;Notice that tests continually run as you add them when using &lt;code&gt;ng test&lt;/code&gt;. You can run tests
    once by using &lt;code&gt;ng test --watch=false&lt;/code&gt;.
    You will likely see &quot;Executed 5 of 5 &lt;span style=&quot;color: red&quot;&gt;(1 FAILED)&lt;/span&gt;&quot; in your terminal.
    Add a couple more tests for filtering by search term and fetching by id.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
it(&apos;should filter by search term&apos;,
  inject([SearchService, MockBackend], fakeAsync((searchService: SearchService, mockBackend: MockBackend) =&gt; {
    let res;
    mockBackend.connections.subscribe(c =&gt; {
      expect(c.request.url).toBe(&apos;app/shared/search/data/people.json&apos;);
      let response = new ResponseOptions({body: &apos;[{&quot;name&quot;: &quot;John Elway&quot;}, {&quot;name&quot;: &quot;Gary Kubiak&quot;}]&apos;});
      c.mockRespond(new Response(response));
    });
    searchService.search(&apos;john&apos;).subscribe((response) =&gt; {
      res = response;
    });
    tick();
    expect(res[0].name).toBe(&apos;John Elway&apos;);
  }))
);

it(&apos;should fetch by id&apos;,
  inject([SearchService, MockBackend], fakeAsync((searchService: SearchService, mockBackend: MockBackend) =&gt; {
    let res;
    mockBackend.connections.subscribe(c =&gt; {
      expect(c.request.url).toBe(&apos;app/shared/search/data/people.json&apos;);
      let response = new ResponseOptions({body: &apos;[{&quot;id&quot;: 1, &quot;name&quot;: &quot;John Elway&quot;}, {&quot;id&quot;: 2, &quot;name&quot;: &quot;Gary Kubiak&quot;}]&apos;});
      c.mockRespond(new Response(response));
    });
    searchService.search(&apos;2&apos;).subscribe((response) =&gt; {
      res = response;
    });
    tick();
    expect(res[0].name).toBe(&apos;Gary Kubiak&apos;);
  }))
);
&lt;/pre&gt;
&lt;h3 id=&quot;_unit_test_the_searchcomponent&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#_unit_test_the_searchcomponent&quot;&gt;&lt;/a&gt;Unit
    test the SearchComponent&lt;/h3&gt;
&lt;p&gt;To unit test the &lt;code&gt;SearchComponent&lt;/code&gt;, create a &lt;code&gt;MockSearchProvider&lt;/code&gt; that has
    &lt;a  href=&quot;http://angular-tips.com/blog/2014/03/introduction-to-unit-test-spies/&quot;&gt;spies&lt;/a&gt;.
    These allow you to &lt;em&gt;spy&lt;/em&gt; on functions to check if they were called.&lt;/p&gt;
&lt;p&gt;Create &lt;code&gt;src/app/shared/search/mocks/search.service.ts&lt;/code&gt; and populate it with spies for each
    method, as well as methods to set the response and subscribe to results.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import { SpyObject } from &apos;./helper&apos;;
import { SearchService } from &apos;../search.service&apos;;
import Spy = jasmine.Spy;

export class MockSearchService extends SpyObject {
  getAllSpy: Spy;
  getByIdSpy: Spy;
  searchSpy: Spy;
  saveSpy: Spy;
  fakeResponse: any;

  constructor() {
    super( SearchService );

    this.fakeResponse = null;
    this.getAllSpy = this.spy(&apos;getAll&apos;).andReturn(this);
    this.getByIdSpy = this.spy(&apos;get&apos;).andReturn(this);
    this.searchSpy = this.spy(&apos;search&apos;).andReturn(this);
    this.saveSpy = this.spy(&apos;save&apos;).andReturn(this);
  }

  subscribe(callback: any) {
    callback(this.fakeResponse);
  }

  setResponse(json: any): void {
    this.fakeResponse = json;
  }
}
&lt;/pre&gt;
&lt;p&gt;In this same directory, create a &lt;code&gt;helper.ts&lt;/code&gt; class to implement the
    &lt;code&gt;SpyObject&lt;/code&gt; that &lt;code&gt;MockSearchService&lt;/code&gt; extends.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import {StringMapWrapper} from &apos;@angular/core/src/facade/collection&apos;;

export interface GuinessCompatibleSpy extends jasmine.Spy {
  /** By chaining the spy with and.returnValue, all calls to the function will return a specific
   * value. */
  andReturn(val: any): void;
  /** By chaining the spy with and.callFake, all calls to the spy will delegate to the supplied
   * function. */
  andCallFake(fn: Function): GuinessCompatibleSpy;
  /** removes all recorded calls */
  reset();
}

export class SpyObject {
  static stub(object = null, config = null, overrides = null) {
    if (!(object instanceof SpyObject)) {
      overrides = config;
      config = object;
      object = new SpyObject();
    }

    let m = StringMapWrapper.merge(config, overrides);
    StringMapWrapper.forEach(m, (value, key) =&amp;gt; { object.spy(key).andReturn(value); });
    return object;
  }

  constructor(type = null) {
    if (type) {
      for (let prop in type.prototype) {
        let m = null;
        try {
          m = type.prototype&amp;#91;prop&amp;#93;;
        } catch (e) {
          // As we are creating spys for abstract classes,
          // these classes might have getters that throw when they are accessed.
          // As we are only auto creating spys for methods, this
          // should not matter.
        }
        if (typeof m === &apos;function&apos;) {
          this.spy(prop);
        }
      }
    }
  }

  spy(name) {
    if (!this&amp;#91;name&amp;#93;) {
      this&amp;#91;name&amp;#93; = this._createGuinnessCompatibleSpy(name);
    }
    return this&amp;#91;name&amp;#93;;
  }

  prop(name, value) { this&amp;#91;name&amp;#93; = value; }

  /** @internal */
  _createGuinnessCompatibleSpy(name): GuinessCompatibleSpy {
    let newSpy: GuinessCompatibleSpy = &amp;lt;any&amp;gt;jasmine.createSpy(name);
    newSpy.andCallFake = &amp;lt;any&amp;gt;newSpy.and.callFake;
    newSpy.andReturn = &amp;lt;any&amp;gt;newSpy.and.returnValue;
    newSpy.reset = &amp;lt;any&amp;gt;newSpy.calls.reset;
    // revisit return null here (previously needed for rtts_assert).
    newSpy.and.returnValue(null);
    return newSpy;
  }
}
&lt;/pre&gt;
&lt;p&gt;Alongside, create &lt;code&gt;routes.ts&lt;/code&gt; to mock Angular&apos;s &lt;code&gt;Router&lt;/code&gt; and &lt;code&gt;ActivatedRoute&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import { ActivatedRoute, Params } from &apos;@angular/router&apos;;
import { Observable } from &apos;rxjs&apos;;

export class MockActivatedRoute extends ActivatedRoute {
  params: Observable&amp;lt;Params&amp;gt;

  constructor(parameters?: { [key: string]: any; }) {
    super();
    this.params = Observable.of(parameters);
  }
}

export class MockRouter {
  navigate = jasmine.createSpy(&apos;navigate&apos;);
}
&lt;/pre&gt;
&lt;p&gt;With mocks in place, you can &lt;code&gt;TestBed.configureTestingModule()&lt;/code&gt; to setup &lt;code&gt;SearchComponent&lt;/code&gt;
    to use these as providers.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import { ActivatedRoute, Router } from &apos;@angular/router&apos;;
import { MockActivatedRoute, MockRouter } from &apos;../shared/search/mocks/routes&apos;;
import { MockSearchService } from &apos;../shared/search/mocks/search.service&apos;;
import { SearchComponent } from &apos;./search.component&apos;;
import { TestBed } from &apos;@angular/core/testing/test_bed&apos;;
import { FormsModule } from &apos;@angular/forms&apos;;
import { SearchService } from &apos;../shared/search/search.service&apos;;

describe(&apos;Component: Search&apos;, () =&gt; {
  let mockSearchService: MockSearchService;
  let mockActivatedRoute: MockActivatedRoute;
  let mockRouter: MockRouter;

  beforeEach(() =&gt; {
    mockSearchService = new MockSearchService();
    mockActivatedRoute = new MockActivatedRoute({&apos;term&apos;: &apos;peyton&apos;});
    mockRouter = new MockRouter();

    TestBed.configureTestingModule({
      declarations: [SearchComponent],
      providers: [
        {provide: SearchService, useValue: mockSearchService},
        {provide: ActivatedRoute, useValue: mockActivatedRoute},
        {provide: Router, useValue: mockRouter}
      ],
      imports: [FormsModule]
    });
  });
});
&lt;/pre&gt;
&lt;p&gt;Add two tests, one to verify a search term is used when it&apos;s set on the component, and a second
    to verify search is called when a term is passed in as a route parameter.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
it(&apos;should search when a term is set and search() is called&apos;, () =&gt; {
  let fixture = TestBed.createComponent(SearchComponent);
  let searchComponent = fixture.debugElement.componentInstance;
  searchComponent.query = &apos;M&apos;;
  searchComponent.search();
  expect(mockSearchService.searchSpy).toHaveBeenCalledWith(&apos;M&apos;);
});

it(&apos;should search automatically when a term is on the URL&apos;, () =&gt; {
  let fixture = TestBed.createComponent(SearchComponent);
  fixture.detectChanges();
  expect(mockSearchService.searchSpy).toHaveBeenCalledWith(&apos;peyton&apos;);
});
&lt;/pre&gt;

&lt;p&gt;After adding these tests, you should see the first instance of all tests passing (Executed 8 of 8
    &lt;span style=&quot;color: green&quot;&gt;SUCCESS&lt;/span&gt;).&lt;/p&gt;
&lt;p&gt;Update the test for &lt;code&gt;EditComponent&lt;/code&gt;, verifying fetching a single record works. Notice
    how you can access the component directly with &lt;code&gt;fixture.debugElement.componentInstance&lt;/code&gt;, or
    its rendered version with &lt;code&gt;fixture.debugElement.nativeElement&lt;/code&gt;.
&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import { MockSearchService } from &apos;../shared/search/mocks/search.service&apos;;
import { EditComponent } from &apos;./edit.component&apos;;
import { TestBed } from &apos;@angular/core/testing/test_bed&apos;;
import { SearchService } from &apos;../shared/search/search.service&apos;;
import { MockRouter, MockActivatedRoute } from &apos;../shared/search/mocks/routes&apos;;
import { ActivatedRoute, Router } from &apos;@angular/router&apos;;
import { FormsModule } from &apos;@angular/forms&apos;;

describe(&apos;Component: Edit&apos;, () =&gt; {
  let mockSearchService: MockSearchService;
  let mockActivatedRoute: MockActivatedRoute;
  let mockRouter: MockRouter;

  beforeEach(() =&gt; {
    mockSearchService = new MockSearchService();
    mockActivatedRoute = new MockActivatedRoute({&apos;id&apos;: 1});
    mockRouter = new MockRouter();

    TestBed.configureTestingModule({
      declarations: [EditComponent],
      providers: [
        {provide: SearchService, useValue: mockSearchService},
        {provide: ActivatedRoute, useValue: mockActivatedRoute},
        {provide: Router, useValue: mockRouter}
      ],
      imports: [FormsModule]
    });
  });

  it(&apos;should fetch a single record&apos;, () =&gt; {
    const fixture = TestBed.createComponent(EditComponent);

    let person = {name: &apos;Emmanuel Sanders&apos;, address: {city: &apos;Denver&apos;}};
    mockSearchService.setResponse(person);

    fixture.detectChanges();
    // verify service was called
    expect(mockSearchService.getByIdSpy).toHaveBeenCalledWith(1);

    // verify data was set on component when initialized
    let editComponent = fixture.debugElement.componentInstance;
    expect(editComponent.editAddress.city).toBe(&apos;Denver&apos;);

    // verify HTML renders as expected
    let compiled = fixture.debugElement.nativeElement;
    expect(compiled.querySelector(&apos;h3&apos;).innerHTML).toBe(&apos;Emmanuel Sanders&apos;);
  });
});
&lt;/pre&gt;
&lt;p&gt;You should see &quot;Executed 8 of 8 &lt;span style=&quot;color: green&quot;&gt;SUCCESS&lt;/span&gt; (0.238 secs / 0.259
    secs)&quot; in the shell window that&apos;s running &lt;code&gt;ng test&lt;/code&gt;. If you don&apos;t, try cancelling the command and
    restarting.&lt;/p&gt;
&lt;h3 id=&quot;_integration_test_the_search_ui&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#_integration_test_the_search_ui&quot;&gt;&lt;/a&gt;Integration test the search UI&lt;/h3&gt;
&lt;p&gt;To test if the application works end-to-end, you can write tests with &lt;a
    href=&quot;http://angular.github.io/protractor&quot;&gt;Protractor&lt;/a&gt;. These are also known as integration
    tests, since they test the &lt;em&gt;integration&lt;/em&gt; between all layers of your application.&lt;/p&gt;
&lt;p&gt;To verify end-to-end tests work in the project before you begin, run the following commands in three
    different console windows.&lt;/p&gt;
&lt;pre&gt;ng serve
ng e2e&lt;/pre&gt;
&lt;p&gt;All tests should pass.&lt;/p&gt;
&lt;pre&gt;$ ng e2e

&amp;gt; ng2-demo@0.0.0 pree2e /Users/mraible/dev/ng2-demo
&amp;gt; webdriver-manager update

Updating selenium standalone to version 2.52.0
downloading https://selenium-release.storage.googleapis.com/2.52/selenium-server-standalone-2.52.0.jar...
Updating chromedriver to version 2.21
downloading https://chromedriver.storage.googleapis.com/2.21/chromedriver_mac32.zip...
chromedriver_2.21mac32.zip downloaded to /Users/mraible/dev/ng2-demo/node_modules/protractor/selenium/chromedriver_2.21mac32.zip
selenium-server-standalone-2.52.0.jar downloaded to /Users/mraible/dev/ng2-demo/node_modules/protractor/selenium/selenium-server-standalone-2.52.0.jar

&amp;gt; ng2-demo@0.0.0 e2e /Users/mraible/dev/ng2-demo
&amp;gt; protractor &quot;config/protractor.conf.js&quot;

[00:01:07] I/direct - Using ChromeDriver directly...
[00:01:07] I/launcher - Running 1 instances of WebDriver
Spec started

  ng2-demo App
    &amp;#10004; should display message saying app works

Executed 1 of 1 spec SUCCESS in 0.684 sec.
[00:01:09] I/launcher - 0 instance(s) of WebDriver still running
[00:01:09] I/launcher - chrome #01 passed

All end-to-end tests pass.&lt;/pre&gt;
&lt;h3 id=&quot;_testing_the_search_feature&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#_testing_the_search_feature&quot;&gt;&lt;/a&gt;Testing the
    search feature&lt;/h3&gt;
&lt;p&gt;Create end-to-end tests in &lt;code&gt;e2e/search.e2e-spec.ts&lt;/code&gt; to verify the search feature works.
    Populate it with the following code:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
describe(&apos;Search&apos;, () =&gt; {

  beforeEach(() =&gt; {
    browser.get(&apos;/search&apos;);
    element(by.linkText(&apos;Search&apos;)).click();
  });

  it(&apos;should have an input and search button&apos;, () =&gt; {
    expect(element(by.css(&apos;app-root app-search form input&apos;)).isPresent()).toEqual(true);
    expect(element(by.css(&apos;app-root app-search form button&apos;)).isPresent()).toEqual(true);
  });

  it(&apos;should allow searching&apos;, () =&gt; {
    let searchButton = element(by.css(&apos;button&apos;));
    let searchBox = element(by.css(&apos;input&apos;));
    searchBox.sendKeys(&apos;M&apos;);
    searchButton.click().then(() =&gt; {
      var list = element.all(by.css(&apos;app-search table tbody tr&apos;));
      expect(list.count()).toBe(3);
    });
  });
});
&lt;/pre&gt;
&lt;h3 id=&quot;_testing_the_edit_feature&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#_testing_the_edit_feature&quot;&gt;&lt;/a&gt;Testing the edit feature&lt;/h3&gt;
&lt;p&gt;Create a &lt;code&gt;e2e/edit.e2e-spec.ts&lt;/code&gt; test to verify the &lt;code&gt;EditComponent&lt;/code&gt; renders a
    person&apos;s information and that their information can be updated.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
describe(&apos;Edit&apos;, () =&gt; {

  beforeEach(() =&gt; {
    browser.get(&apos;/edit/1&apos;);
  });

  let name = element(by.id(&apos;name&apos;));
  let street = element(by.id(&apos;street&apos;));
  let city = element(by.id(&apos;city&apos;));

  it(&apos;should allow viewing a person&apos;, () =&gt; {
    expect(element(by.css(&apos;h3&apos;)).getText()).toEqual(&apos;Peyton Manning&apos;);
    expect(name.getAttribute(&apos;value&apos;)).toEqual(&apos;Peyton Manning&apos;);
    expect(street.getAttribute(&apos;value&apos;)).toEqual(&apos;1234 Main Street&apos;);
    expect(city.getAttribute(&apos;value&apos;)).toEqual(&apos;Greenwood Village&apos;);
  });

  it(&apos;should allow updating a name&apos;, function () {
    let save = element(by.id(&apos;save&apos;));
    // send individual characters since sendKeys passes partial values sometimes
    // https://github.com/angular/protractor/issues/698
    &apos; Won!&apos;.split(&apos;&apos;).forEach((c) =&gt; name.sendKeys(c));
    save.click();
    // verify one element matched this change
    var list = element.all(by.css(&apos;app-search table tbody tr&apos;));
    expect(list.count()).toBe(1);
  });
});
&lt;/pre&gt;
&lt;p&gt;Run &lt;code&gt;ng e2e&lt;/code&gt; to verify all your end-to-end tests pass. You should see a success message
    similar to the one below in your terminal window.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;img  src=&quot;https://raw.githubusercontent.com/mraible/ng2-demo/master/src/assets/images/protractor-success.png&quot;
          alt=&quot;Protractor success&quot; width=&quot;600&quot;&gt;
&lt;/p&gt;
&lt;p&gt;If you made it this far and have all your specs passing - congratulations! You&apos;re well on your way to
    writing quality code with Angular 2 and verifying it works.&lt;/p&gt;
&lt;p&gt;You can see the test coverage of your project by opening &lt;code&gt;coverage/index.html&lt;/code&gt; in your
    browser. You might notice that the new components and service could use some additional coverage. If you feel
    the need to improve this coverage, please send me a pull request!&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;img
        src=&quot;https://raw.githubusercontent.com/mraible/ng2-demo/master/src/assets/images/test-coverage.png&quot;
        alt=&quot;Test coverage&quot; width=&quot;600&quot;&gt;
&lt;/p&gt;
&lt;h3 id=&quot;_continuous_integration&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#_continuous_integration&quot;&gt;&lt;/a&gt;Continuous Integration&lt;/h3&gt;
&lt;p&gt;At the time of this writing, Angular CLI did not have any continuous integration support. However, it&apos;s
    easy to add with &lt;a href=&quot;https://travis-ci.org/&quot;&gt;Travis CI&lt;/a&gt;. If you&apos;ve checked in your project to GitHub,
    you can easily use Travis CI.
    Simply login and enable builds for the GitHub repo you created the project in. Then add the following
    &lt;code&gt;.travis.yml&lt;/code&gt;
    in your root directory and &lt;code&gt;git push&lt;/code&gt;. This will trigger the first build.&lt;/p&gt;
&lt;pre&gt;
language: node_js
sudo: true

cache:
  directories:
    - node
    - node_modules

dist: trusty

node_js:
  - &apos;5.6.0&apos;

branches:
  only:
  - master

before_install:
 - npm install -g angular-cli
 - export CHROME_BIN=/usr/bin/google-chrome
 - export DISPLAY=:99.0
 - sh -e /etc/init.d/xvfb start
 - sudo apt-get update
 - sudo apt-get install -y libappindicator1 fonts-liberation
 - wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
 - sudo dpkg -i google-chrome*.deb

script:
 - ng test --watch false # http://mseemann.de/frontend/2016/05/31/setup-angular-2-app-part-1.html
 - ng serve &amp;
 - ng e2e

notifications:
  webhooks:
    on_success: change  # options: [always|never|change] default: always
    on_failure: always  # options: [always|never|change] default: always
    on_start: false     # default: false
&lt;/pre&gt;
&lt;div class=&quot;paragraph&quot;&gt;
    &lt;p&gt;&lt;a href=&quot;https://travis-ci.org/mraible/ng2-demo/builds/154182594&quot;&gt;Here&lt;/a&gt; is a build showing all unit
        and integration tests passing.&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;_source_code&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#_source_code&quot;&gt;&lt;/a&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/ng2-demo&quot;&gt;https://github.com/mraible/ng2-demo&lt;/a&gt;.
    If you have ideas for improvements, please leave a comment or send a pull request.
&lt;/p&gt;
&lt;p&gt;
    This tutorial was originally written using &lt;a href=&quot;http://asciidoctor.org/&quot;&gt;Asciidoctor&lt;/a&gt;.
    This means you can &lt;a href=&quot;http://gist.asciidoctor.org/?github-mraible%2Fng2-demo%2F%2FREADME.adoc&quot;&gt;read it using DocGist&lt;/a&gt; if you like.
&lt;/p&gt;
&lt;h3 id=&quot;_summary&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#_summary&quot;&gt;&lt;/a&gt;Summary&lt;/h3&gt;
&lt;p&gt;I hope you&apos;ve enjoyed this in-depth tutorial on how to get started with Angular 2 and Angular CLI.
    Angular CLI takes much of the pain out of setting up an Angular 2 project and using Typescript. I expect great
    things from Angular CLI, mostly because the Angular 2 setup process can be tedious and CLI greatly simplifies things. &lt;/p&gt;
</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/testing_angular_2_0_rc1</id>
        <title type="html">Testing Angular 2.0 RC1 Applications</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/testing_angular_2_0_rc1"/>
        <published>2016-06-06T09:57:13-06:00</published>
        <updated>2016-06-21T05:06:24-06:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="protractor" scheme="http://roller.apache.org/ns/tags/" />
        <category term="asciidoctor" scheme="http://roller.apache.org/ns/tags/" />
        <category term="npm" scheme="http://roller.apache.org/ns/tags/" />
        <category term="angular2" scheme="http://roller.apache.org/ns/tags/" />
        <category term="javascript" scheme="http://roller.apache.org/ns/tags/" />
        <category term="karma" scheme="http://roller.apache.org/ns/tags/" />
        <category term="git" scheme="http://roller.apache.org/ns/tags/" />
        <category term="jasmine" scheme="http://roller.apache.org/ns/tags/" />
        <category term="node" scheme="http://roller.apache.org/ns/tags/" />
        <summary type="html">&lt;p&gt;As mentioned &lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_2_rc1&quot;&gt;on Friday&lt;/a&gt;, there&apos;s been
    quite a bit that&apos;s changed with Angular 2 between its
    Beta 9 and RC 1 releases. This article is an update to the
    &lt;a href=&quot;//raibledesigns.com/rd/entry/testing_angular_2_applications&quot;&gt;Testing Angular 2 Applications&lt;/a&gt; I wrote in
    March.
    That tutorial was based on Angular 2.0 Beta 9. Rather than simply updating that
    tutorial and blog post for 2.0 RC1, I decided to create a new version for posterity&apos;s sake. The 2.0 Beta 9 version
    will
    remain on my blog and I&apos;ve &lt;a href=&quot;https://github.com/mraible/angular2-tutorial/releases/tag/2.0.0-beta.9&quot;&gt;tagged
        the source on GitHub&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;If you&apos;ve already read the first version of &lt;a href=&quot;//raibledesigns.com/rd/entry/testing_angular_2_applications&quot;&gt;Testing
    Angular 2 Applications&lt;/a&gt;,
    checkout the &lt;a
        href=&quot;https://github.com/mraible/angular2-tutorial/pull/2/files#diff-c5cd85f8ff52aad4b08a3dd38575dddf&quot;&gt;
        diff of the Asciidoctor version&lt;/a&gt; to see what&apos;s changed.&lt;/p&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;what_you_ll_build&quot;&gt;What you&apos;ll build&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;You&apos;ll learn to use &lt;a href=&quot;http://jasmine.github.io/&quot;&gt;Jasmine&lt;/a&gt; for unit testing controllers and &lt;a
                href=&quot;http://www.protractortest.org/&quot;&gt;Protractor&lt;/a&gt; for
                integration testing. See Angular 2&apos;s &lt;a href=&quot;https://angular.io/docs/ts/latest/guide/testing.html&quot;&gt;guide
                    to unit testing&lt;/a&gt;
                if you&apos;d like more information on testing and why it&apos;s important.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;The best reason for writing tests is to automate your testing. Without tests, you&apos;ll likely be testing
                manually.
                This manual testing will take longer and longer as your codebase grows.&lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;what_you_ll_need&quot;&gt;What you&apos;ll need&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;ulist&quot;&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. I recommend using &lt;a
                    href=&quot;https://github.com/creationix/nvm&quot;&gt;nvm&lt;/a&gt;.
                &lt;/li&gt;
            &lt;/ul&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;get_the_tutorial_project&quot;&gt;Get the tutorial project&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Clone the &lt;a href=&quot;https://github.com/mraible/angular2-tutorial&quot;&gt;angular2-tutorial repository&lt;/a&gt;, checkout the &lt;code&gt;testing-start&lt;/code&gt; branch, and install its dependencies.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre&gt;git clone https://github.com/mraible/angular2-tutorial.git
cd angular2-tutorial
git checkout testing-start
npm install&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;If you haven&apos;t completed the &lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_2_rc1&quot;&gt;Getting
                Started with Angular 2.0 RC1&lt;/a&gt; tutorial,
                you should peruse it so you understand how this application works.
                You can also simply start the app with &lt;code&gt;npm start&lt;/code&gt; and view it in your browser at &lt;a
                    href=&quot;http://localhost:5555/&quot; class=&quot;bare&quot;&gt;http://localhost:5555/&lt;/a&gt;.
            &lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;</summary>
        <content type="html">&lt;p&gt;As mentioned &lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_2_rc1&quot;&gt;on Friday&lt;/a&gt;, there&apos;s been
    quite a bit that&apos;s changed with Angular 2 between its
    Beta 9 and RC 1 releases. This article is an update to the
    &lt;a href=&quot;//raibledesigns.com/rd/entry/testing_angular_2_applications&quot;&gt;Testing Angular 2 Applications&lt;/a&gt; I wrote in
    March.
    That tutorial was based on Angular 2.0 Beta 9. Rather than simply updating that
    tutorial and blog post for 2.0 RC1, I decided to create a new version for posterity&apos;s sake. The 2.0 Beta 9 version
    will
    remain on my blog and I&apos;ve &lt;a href=&quot;https://github.com/mraible/angular2-tutorial/releases/tag/2.0.0-beta.9&quot;&gt;tagged
        the source on GitHub&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;If you&apos;ve already read the first version of &lt;a href=&quot;//raibledesigns.com/rd/entry/testing_angular_2_applications&quot;&gt;Testing
    Angular 2 Applications&lt;/a&gt;,
    checkout the &lt;a
        href=&quot;https://github.com/mraible/angular2-tutorial/pull/2/files#diff-c5cd85f8ff52aad4b08a3dd38575dddf&quot;&gt;
        diff of the Asciidoctor version&lt;/a&gt; to see what&apos;s changed.&lt;/p&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;what_you_ll_build&quot;&gt;What you&apos;ll build&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;You&apos;ll learn to use &lt;a href=&quot;http://jasmine.github.io/&quot;&gt;Jasmine&lt;/a&gt; for unit testing controllers and &lt;a
                href=&quot;http://www.protractortest.org/&quot;&gt;Protractor&lt;/a&gt; for
                integration testing. See Angular 2&apos;s &lt;a href=&quot;https://angular.io/docs/ts/latest/guide/testing.html&quot;&gt;guide
                    to unit testing&lt;/a&gt;
                if you&apos;d like more information on testing and why it&apos;s important.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;The best reason for writing tests is to automate your testing. Without tests, you&apos;ll likely be testing
                manually.
                This manual testing will take longer and longer as your codebase grows.&lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;what_you_ll_need&quot;&gt;What you&apos;ll need&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;ulist&quot;&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. I recommend using &lt;a
                    href=&quot;https://github.com/creationix/nvm&quot;&gt;nvm&lt;/a&gt;.
                &lt;/li&gt;
            &lt;/ul&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;get_the_tutorial_project&quot;&gt;Get the tutorial project&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Clone the &lt;a href=&quot;https://github.com/mraible/angular2-tutorial&quot;&gt;angular2-tutorial repository&lt;/a&gt;, checkout the &lt;code&gt;testing-start&lt;/code&gt; branch, and install its dependencies.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre&gt;git clone https://github.com/mraible/angular2-tutorial.git
cd angular2-tutorial
git checkout testing-start
npm install&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;If you haven&apos;t completed the &lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_2_rc1&quot;&gt;Getting
                Started with Angular 2.0 RC1&lt;/a&gt; tutorial,
                you should peruse it so you understand how this application works.
                You can also simply start the app with &lt;code&gt;npm start&lt;/code&gt; and view it in your browser at &lt;a
                    href=&quot;http://localhost:5555/&quot; class=&quot;bare&quot;&gt;http://localhost:5555/&lt;/a&gt;.
            &lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;unit_test_the_searchservice&quot;&gt;Unit test the SearchService&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Create &lt;code&gt;src/client/app/shared/search/search.service.spec.ts&lt;/code&gt; and setup the test&apos;s
                infrastructure using &lt;a
                    href=&quot;https://angular.io/docs/js/latest/api/http/testing/MockBackend-class.html&quot;&gt;MockBackend&lt;/a&gt;
                and &lt;a href=&quot;https://angular.io/docs/js/latest/api/http/BaseRequestOptions-class.html&quot;&gt;BaseRequestOptions&lt;/a&gt;.
            &lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import {
  beforeEachProviders,
  it,
  describe,
  expect,
  inject,
  fakeAsync,
  tick
} from &apos;@angular/core/testing&apos;;
import { MockBackend } from &apos;@angular/http/testing&apos;;
import { provide } from &apos;@angular/core&apos;;
import { Http, ConnectionBackend, BaseRequestOptions, Response, ResponseOptions } from &apos;@angular/http&apos;;
import { SearchService } from &apos;./search.service&apos;;

export function main() {
  describe(&apos;Search Service&apos;, () =&gt; {
    beforeEachProviders(() =&gt; {
      return [BaseRequestOptions, MockBackend, SearchService,
        provide(Http, {
          useFactory: (backend:ConnectionBackend, defaultOptions:BaseRequestOptions) =&gt; {
            return new Http(backend, defaultOptions);
          }, deps: [MockBackend, BaseRequestOptions]
        }),
      ];
    });
  });
}

&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;If you run &lt;code&gt;npm test&lt;/code&gt;, all tests will pass, but you don&apos;t see &quot;Search Service&quot; as a listed
                test. You can fix this by adding the first test of &lt;code&gt;getAll()&lt;/code&gt;.
                This test shows how &lt;code&gt;MockBackend&lt;/code&gt; can be used to mock results and set the response.&lt;/p&gt;
        &lt;/div&gt;
        &lt;p class=&quot;alert alert-success&quot;&gt;&lt;b&gt;TIP&lt;/b&gt;: When you are testing code that returns either a Promise or an RxJS
            Observable, you can use the &lt;code&gt;fakeAsync&lt;/code&gt; helper to test that code as
            if it were synchronous.
            Promises are be fulfilled and Observables are notified immediately after you call &lt;code&gt;tick()&lt;/code&gt;.&lt;/p&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;The test below should be on the same level as &lt;code&gt;beforeEachProviders&lt;/code&gt;.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;it(&apos;should retrieve all search results&apos;,
  inject([SearchService, MockBackend], fakeAsync((searchService:SearchService, mockBackend:MockBackend) =&amp;gt; {
    var res:Response;
    mockBackend.connections.subscribe(c =&amp;gt; {
      expect(c.request.url).toBe(&apos;app/shared/search/data/people.json&apos;);
      let response = new ResponseOptions({body: &apos;[{&quot;name&quot;: &quot;John Elway&quot;}, {&quot;name&quot;: &quot;Gary Kubiak&quot;}]&apos;});
      c.mockRespond(new Response(response));
    });
    searchService.getAll().subscribe((response) =&amp;gt; {
      res = response;
    });
    tick();
    expect(res[0].name).toBe(&apos;John Elway&apos;);
  }))
);
&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Running &lt;code&gt;npm test&lt;/code&gt; should result in &quot;12 tests completed&quot;. Add a couple more tests for
                filtering by search term and fetching by id.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
it(&apos;should filter by search term&apos;,
  inject([SearchService, MockBackend], fakeAsync((searchService:SearchService, mockBackend:MockBackend) =&gt; {
    var res;
    mockBackend.connections.subscribe(c =&gt; {
      expect(c.request.url).toBe(&apos;app/shared/search/data/people.json&apos;);
      let response = new ResponseOptions({body: &apos;[{&quot;name&quot;: &quot;John Elway&quot;}, {&quot;name&quot;: &quot;Gary Kubiak&quot;}]&apos;});
      c.mockRespond(new Response(response));
    });
    searchService.search(&apos;john&apos;).subscribe((response) =&gt; {
      res = response;
    });
    tick();
    expect(res[0].name).toBe(&apos;John Elway&apos;);
  }))
);

it(&apos;should fetch by id&apos;,
  inject([SearchService, MockBackend], fakeAsync((searchService:SearchService, mockBackend:MockBackend) =&gt; {
    var res;
    mockBackend.connections.subscribe(c =&gt; {
      expect(c.request.url).toBe(&apos;app/shared/search/data/people.json&apos;);
      let response = new ResponseOptions({body: &apos;[{&quot;id&quot;: 1, &quot;name&quot;: &quot;John Elway&quot;}, {&quot;id&quot;: 2, &quot;name&quot;: &quot;Gary Kubiak&quot;}]&apos;});
      c.mockRespond(new Response(response));
    });
    searchService.search(&apos;2&apos;).subscribe((response) =&gt; {
      res = response;
    });
    tick();
    expect(res[0].name).toBe(&apos;Gary Kubiak&apos;);
  }))
);
&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;If you want to have tests continually run as you add them, you can run the following commands in separate
                shell windows.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre&gt;npm run build.test.watch
npm run karma.start&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;p&gt;NOTE: See &lt;a
            href=&quot;https://www.jetbrains.com/help/idea/15.0/running-unit-tests-on-karma.html?origin=old_help&quot;&gt;Running
            Unit Tests on Karma&lt;/a&gt; to
            learn how to run your tests from IntelliJ IDEA.&lt;/p&gt;

    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;unit_test_the_searchcomponent&quot;&gt;Unit test the SearchComponent&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;To unit test the &lt;code&gt;SearchComponent&lt;/code&gt;, create a &lt;code&gt;MockSearchProvider&lt;/code&gt; that has &lt;a
                href=&quot;http://angular-tips.com/blog/2014/03/introduction-to-unit-test-spies/&quot;&gt;spies&lt;/a&gt;.
                These allow you to &lt;em&gt;spy&lt;/em&gt; on functions to check if they were called.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Create &lt;code&gt;src/client/app/shared/search/mocks/search.service.ts&lt;/code&gt; and populate it with spies for
                each method, as well as methods to set the response and subscribe to results.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import { provide } from &apos;@angular/core&apos;;
import { SpyObject } from &apos;./helper&apos;;

import { SearchService } from &apos;../search.service&apos;;
import Spy = jasmine.Spy;

export class MockSearchService extends SpyObject {
  getAllSpy:Spy;
  getByIdSpy:Spy;
  searchSpy:Spy;
  saveSpy:Spy;
  fakeResponse:any;

  constructor() {
    super(SearchService);

    this.fakeResponse = null;
    this.getAllSpy = this.spy(&apos;getAll&apos;).andReturn(this);
    this.getByIdSpy = this.spy(&apos;get&apos;).andReturn(this);
    this.searchSpy = this.spy(&apos;search&apos;).andReturn(this);
    this.saveSpy = this.spy(&apos;save&apos;).andReturn(this);
  }

  subscribe(callback:any) {
    callback(this.fakeResponse);
  }

  setResponse(json:any):void {
    this.fakeResponse = json;
  }

  getProviders():Array&amp;lt;any&amp;gt; {
    return [provide(SearchService, {useValue: this})];
  }
}
&lt;/pre&gt;
                &lt;p&gt;
                    In this same directory, create a &lt;code&gt;helper.ts&lt;/code&gt; class to implement the
                    &lt;code&gt;SpyObject&lt;/code&gt;
                    that &lt;code&gt;MockSearchService&lt;/code&gt; extends.
                &lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import {StringMapWrapper} from &apos;@angular/core/src/facade/collection&apos;;

export interface GuinessCompatibleSpy extends jasmine.Spy {
  /** By chaining the spy with and.returnValue, all calls to the function will return a specific
   * value. */
  andReturn(val: any): void;
  /** By chaining the spy with and.callFake, all calls to the spy will delegate to the supplied
   * function. */
  andCallFake(fn: Function): GuinessCompatibleSpy;
  /** removes all recorded calls */
  reset();
}

export class SpyObject {
  static stub(object = null, config = null, overrides = null) {
    if (!(object instanceof SpyObject)) {
      overrides = config;
      config = object;
      object = new SpyObject();
    }

    var m = StringMapWrapper.merge(config, overrides);
    StringMapWrapper.forEach(m, (value, key) =&amp;gt; { object.spy(key).andReturn(value); });
    return object;
  }

  constructor(type = null) {
    if (type) {
      for (var prop in type.prototype) {
        var m = null;
        try {
          m = type.prototype&amp;#91;prop&amp;#93;;
        } catch (e) {
          // As we are creating spys for abstract classes,
          // these classes might have getters that throw when they are accessed.
          // As we are only auto creating spys for methods, this
          // should not matter.
        }
        if (typeof m === &apos;function&apos;) {
          this.spy(prop);
        }
      }
    }
  }

  spy(name) {
    if (!this&amp;#91;name&amp;#93;) {
      this&amp;#91;name&amp;#93; = this._createGuinnessCompatibleSpy(name);
    }
    return this&amp;#91;name&amp;#93;;
  }

  prop(name, value) { this&amp;#91;name&amp;#93; = value; }

  /** @internal */
  _createGuinnessCompatibleSpy(name): GuinessCompatibleSpy {
    var newSpy: GuinessCompatibleSpy = &amp;lt;any&amp;gt;jasmine.createSpy(name);
    newSpy.andCallFake = &amp;lt;any&amp;gt;newSpy.and.callFake;
    newSpy.andReturn = &amp;lt;any&amp;gt;newSpy.and.returnValue;
    newSpy.reset = &amp;lt;any&amp;gt;newSpy.calls.reset;
    // revisit return null here (previously needed for rtts_assert).
    newSpy.and.returnValue(null);
    return newSpy;
  }
}
&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Alongside, create &lt;code&gt;routes.ts&lt;/code&gt; to mock Angular&apos;s &lt;code&gt;RouteSegment&lt;/code&gt; and passing
                parameters between components.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import { RouteSegment } from &apos;@angular/router&apos;;

export class MockRouteSegment implements RouteSegment {
  urlSegments:any;
  parameters:any;
  outlet:string;
  _type:any;
  _componentFactory:any;
  type:any;
  stringifiedUrlSegments:string;

  constructor(parameters?:{ [key:string]:any; }) {
    this.parameters = parameters;
  }

  getParam(param:string) {
    return this.parameters[param];
  }
}
&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;With mocks in place, you can create a spec for &lt;code&gt;SearchComponent&lt;/code&gt; that uses these as providers.
                Create a file at &lt;code&gt;src/search/components/search.component.spec.ts&lt;/code&gt; and populate it with the
                following code.
            &lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import { provide } from &apos;@angular/core&apos;;
import { TestComponentBuilder } from &apos;@angular/compiler/testing&apos;;
import {
  it,
  describe,
  expect,
  inject,
  beforeEachProviders,
} from &apos;@angular/core/testing&apos;;

import { RouteSegment } from &apos;@angular/router&apos;;
import { MockRouteSegment } from &apos;../shared/search/mocks/routes&apos;;
import { MockSearchService } from &apos;../shared/search/mocks/search.service&apos;;

import { SearchComponent } from &apos;./search.component&apos;;

export function main() {
  describe(&apos;Search component&apos;, () =&gt; {
    var mockSearchService:MockSearchService;

    beforeEachProviders(() =&gt; {
      mockSearchService = new MockSearchService();

      return [
        mockSearchService.getProviders(),
        provide(RouteSegment, { useValue: new MockRouteSegment({ &apos;term&apos;: &apos;peyton&apos; }) })
      ];
    });
  });
}
&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Add two tests, one to verify a search term is used when it&apos;s set on the component and a second to verify
                search is called when a term is passed in as a route
                parameter.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
it(&apos;should search when a term is set and search() is called&apos;, inject([TestComponentBuilder], (tcb:TestComponentBuilder) =&gt; {
  return tcb.createAsync(SearchComponent).then((fixture) =&gt; {
    let searchComponent = fixture.debugElement.componentInstance;
    searchComponent.query = &apos;M&apos;;
    searchComponent.search();
    expect(mockSearchService.searchSpy).toHaveBeenCalledWith(&apos;M&apos;);
  });
}));

it(&apos;should search automatically when a term is on the URL&apos;, inject([TestComponentBuilder], (tcb:TestComponentBuilder) =&gt; {
  return tcb.createAsync(SearchComponent).then((fixture) =&gt; {
    fixture.detectChanges();
    expect(mockSearchService.searchSpy).toHaveBeenCalledWith(&apos;peyton&apos;);
  });
}));
&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Add a spec for the &lt;code&gt;EditComponent&lt;/code&gt; as well, verifying fetching a single record works. Notice
                how you can access the component directly with
                &lt;code&gt;fixture.debugElement.componentInstance&lt;/code&gt;, or its rendered version with &lt;code&gt;fixture.debugElement.nativeElement&lt;/code&gt;.
                Create a file at &lt;code&gt;src/search/components/edit.component.spec.ts&lt;/code&gt; and populate it with the code
                below.
            &lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import { provide } from &apos;@angular/core&apos;;
import { TestComponentBuilder } from &apos;@angular/compiler/testing&apos;;
import {
  it,
  describe,
  expect,
  inject,
  beforeEachProviders,
} from &apos;@angular/core/testing&apos;;

import { RouteSegment } from &apos;@angular/router&apos;;
import { ROUTER_FAKE_PROVIDERS } from &apos;@angular/router/testing&apos;;
import { MockRouteSegment } from &apos;../shared/search/mocks/routes&apos;;
import { MockSearchService } from &apos;../shared/search/mocks/search.service&apos;;

import { EditComponent } from &apos;./edit.component&apos;;

export function main() {
  describe(&apos;Edit component&apos;, () =&gt; {
    var mockSearchService:MockSearchService;

    beforeEachProviders(() =&gt; {
      mockSearchService = new MockSearchService();

      return [
        mockSearchService.getProviders(),
        ROUTER_FAKE_PROVIDERS,
        provide(RouteSegment, { useValue: new MockRouteSegment({ &apos;id&apos;: &apos;1&apos; }) })
      ];
    });

    it(&apos;should fetch a single record&apos;, inject([TestComponentBuilder], (tcb:TestComponentBuilder) =&gt; {
      return tcb.createAsync(EditComponent).then((fixture) =&gt; {
        let person = {name: &apos;Emmanuel Sanders&apos;, address: {city: &apos;Denver&apos;}};
        mockSearchService.setResponse(person);

        fixture.detectChanges();
        // verify service was called
        expect(mockSearchService.getByIdSpy).toHaveBeenCalledWith(1);

        // verify data was set on component when initialized
        let editComponent = fixture.debugElement.componentInstance;
        expect(editComponent.editAddress.city).toBe(&apos;Denver&apos;);

        // verify HTML renders as expected
        var compiled = fixture.debugElement.nativeElement;
        expect(compiled.querySelector(&apos;h3&apos;)).toHaveText(&apos;Emmanuel Sanders&apos;);
      });
    }));
  });
}
&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;You should see &quot;&lt;span style=&quot;color: green&quot;&gt;&amp;#10004; 22 tests completed&lt;/span&gt;&quot; in the shell window that&apos;s
                running &lt;code&gt;npm run karma.start&lt;/code&gt;. If you don&apos;t, try cancelling the command and restarting.&lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;integration_test_the_search_ui&quot;&gt;Integration test the search UI&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;To test if the application works end-to-end, you can write tests with &lt;a
                href=&quot;http://angular.github.io/protractor&quot;&gt;Protractor&lt;/a&gt;. These are also known as integration tests,
                since they test the &lt;em&gt;integration&lt;/em&gt; between all layers of your application.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;To verify end-to-end tests work in the project before you begin, run the following commands in three
                different console windows.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre&gt;# npm run webdriver-update &amp;lt;- You will need to run this the first time
npm run webdriver-start
npm run serve.e2e
npm run e2e&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;You should receive an error stating that the &quot;nav text for About&quot; is incorrect.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div id=&quot;protractor-nav-error&quot; class=&quot;imageblock&quot;&gt;
            &lt;div style=&quot;text-align: center&quot;&gt;

                &lt;a href=&quot;https://c6.staticflickr.com/8/7393/27377971101_9a4bdfee36_c.jpg&quot;
                   title=&quot;Protractor nav test error&quot; rel=&quot;lightbox[testing-angular2.0rc1]&quot;
                   data-href=&quot;https://www.flickr.com/photos/mraible/27377971101/in/datetaken-public/&quot;&gt;&lt;img
                    src=&quot;https://c6.staticflickr.com/8/7393/27377971101_9a4bdfee36_z.jpg&quot; width=&quot;640&quot;
                    alt=&quot;Protractor nav test error&quot;&gt;&lt;/a&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;This happens because we added a Search link to the navbar and didn&apos;t update the test (in &lt;code&gt;app.component.e2e.ts&lt;/code&gt;)
                that looks for the last child. &lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;it(&apos;should have correct nav text for About&apos;, () =&amp;gt; {
    expect(element(by.css(&apos;sd-app sd-navbar nav a:last-child&apos;)).getText()).toEqual(&apos;ABOUT&apos;);
});
&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Replace this test with the one below, and add a new one to verify the Search link is last.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;it(&apos;should have correct nav text for About&apos;, () =&amp;gt; {
  expect(element(by.css(&apos;sd-app sd-navbar nav a:nth-child(2)&apos;)).getText()).toEqual(&apos;ABOUT&apos;);
});

it(&apos;should have correct nav text for Search&apos;, () =&amp;gt; {
  expect(element(by.css(&apos;sd-app sd-navbar nav a:last-child&apos;)).getText()).toEqual(&apos;SEARCH&apos;);
});
&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Now when you run &lt;code&gt;npm run e2e&lt;/code&gt;, all specs should pass.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;sect2&quot;&gt;
            &lt;h3 id=&quot;_testing_the_search_feature&quot;&gt;Testing the search feature&lt;/h3&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Create a new &lt;code&gt;search.component.e2e-spec.ts&lt;/code&gt; spec in the same directory as your &lt;code&gt;SearchComponent&lt;/code&gt;.
                    Add tests to verify elements are rendered correctly and
                    search works. At the time of this writing, Protractor&apos;s &lt;code&gt;by.model&lt;/code&gt; and
                    &lt;code&gt;by.repeater&lt;/code&gt; don&apos;t work with Angular 2. For this reason, I used &lt;code&gt;by.css&lt;/code&gt; to
                    verify the HTML renders as expected.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
describe(&apos;Search&apos;, () =&gt; {

  beforeEach(() =&gt; {
    browser.get(&apos;/search&apos;);
  });

  it(&apos;should have an input and search button&apos;, () =&gt; {
    expect(element(by.css(&apos;sd-app sd-search form input&apos;)).isPresent()).toEqual(true);
    expect(element(by.css(&apos;sd-app sd-search form button&apos;)).isPresent()).toEqual(true);
  });

  it(&apos;should allow searching&apos;, () =&gt; {
    let searchButton = element(by.css(&apos;button&apos;));
    let searchBox = element(by.css(&apos;input&apos;));
    searchBox.sendKeys(&apos;M&apos;);
    searchButton.click().then(() =&gt; {
      // doesn&apos;t work as expected - results in 0
      //expect(element.all(by.repeater(&apos;person of searchResults&apos;)).count()).toEqual(3);
      var list = element.all(by.css(&apos;sd-search table tbody tr&apos;));
      expect(list.count()).toBe(3);
    });
  });
});
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;sect2&quot;&gt;
            &lt;h3 id=&quot;_testing_the_edit_feature&quot;&gt;Testing the edit feature&lt;/h3&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Create a &lt;code&gt;edit.component.e2e-spec.ts&lt;/code&gt; spec to verify the &lt;code&gt;EditComponent&lt;/code&gt;
                    renders a person&apos;s information and that you can update their information.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
describe(&apos;Edit&apos;, () =&gt; {

  beforeEach(() =&gt; {
    browser.get(&apos;/edit/1&apos;);
  });

  let name = element(by.id(&apos;name&apos;));
  let street = element(by.id(&apos;street&apos;));
  let city = element(by.id(&apos;city&apos;));

  it(&apos;should allow viewing a person&apos;, () =&gt; {
    expect(element(by.css(&apos;h3&apos;)).getText()).toEqual(&apos;Peyton Manning&apos;);
    expect(name.getAttribute(&apos;value&apos;)).toEqual(&apos;Peyton Manning&apos;);
    expect(street.getAttribute(&apos;value&apos;)).toEqual(&apos;1234 Main Street&apos;);
    expect(city.getAttribute(&apos;value&apos;)).toEqual(&apos;Greenwood Village&apos;);
  });

  it(&apos;should allow updating a name&apos;, function () {
    let save = element(by.id(&apos;save&apos;));
    // send individual characters since sendKeys passes partial values sometimes
    // https://github.com/angular/protractor/issues/698
    &apos; Won!&apos;.split(&apos;&apos;).forEach((c) =&gt; name.sendKeys(c));
    save.click();
    // verify one element matched this change
    var list = element.all(by.css(&apos;sd-search table tbody tr&apos;));
    expect(list.count()).toBe(1);
  });
});
&lt;/pre&gt;
                    &lt;p&gt;
                        Run &lt;code&gt;npm run e2e&lt;/code&gt; to verify all your end-to-end tests pass. You might receive a
                        failure for the &quot;Home&quot; test.
                    &lt;/p&gt;
                    &lt;div style=&quot;text-align: center&quot;&gt;
                        &lt;a href=&quot;https://c1.staticflickr.com/8/7227/27173055360_80b1055f07_c.jpg&quot;
                           data-href=&quot;https://www.flickr.com/photos/mraible/27173055360/in/datetaken-public/&quot;
                           title=&quot;Protractor home error&quot; rel=&quot;lightbox[testing-angular2.0rc1]&quot;&gt;&lt;img
                            src=&quot;https://c1.staticflickr.com/8/7227/27173055360_80b1055f07_z.jpg&quot; width=&quot;640&quot;
                            alt=&quot;Protractor home error&quot;&gt;&lt;/a&gt;
                    &lt;/div&gt;
                    &lt;p&gt;If you do, open &lt;code&gt;src/client/app/+home/home.component.e2e-spec.ts&lt;/code&gt; and change line 17
                        from this:&lt;/p&gt;
&lt;pre class=&quot;brush: css&quot;&gt;
element(by.css(&apos;sd-home form input&apos;)).sendKeys(&apos;Tim Berners-Lee&apos;);
&lt;/pre&gt;
                    &lt;p&gt;To this:&lt;/p&gt;
&lt;pre class=&quot;brush: css&quot;&gt;
let input = element(by.css(&apos;sd-home form input&apos;));
&apos;Tim Berners-Lee&apos;.split(&apos;&apos;).forEach((c) =&gt; input.sendKeys(c));
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Run &lt;code&gt;npm run e2e&lt;/code&gt; again. You should see a success message similar to the one below in your
                    terminal window.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div id=&quot;protractor-success&quot; class=&quot;imageblock&quot;&gt;
                &lt;div style=&quot;text-align: center&quot;&gt;
                    &lt;a href=&quot;https://c1.staticflickr.com/8/7345/26840174984_01e6906f98_c.jpg&quot;
                       title=&quot;Protractor success&quot; rel=&quot;lightbox[testing-angular2.0rc1]&quot;
                       data-href=&quot;https://www.flickr.com/photos/mraible/26840174984/in/datetaken-public/&quot;&gt;&lt;img
                        src=&quot;https://c1.staticflickr.com/8/7345/26840174984_01e6906f98_z.jpg&quot; width=&quot;640&quot;
                        alt=&quot;Protractor success&quot;&gt;&lt;/a&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;If you made it this far and have all 13 specs passing - congratulations!
                    You&apos;re well on your way to writing quality code with Angular 2 and verifying it works.&lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;sect1&quot;&gt;
   &lt;h3 id=&quot;_continuous_integration&quot;&gt;Continuous Integration&lt;/h3&gt;
    &lt;div class=&quot;paragraph&quot;&gt;
        &lt;p&gt;The angular2-seed project ships with a &lt;code&gt;.travis.yml&lt;/code&gt; that you can use to run continuous integration for this application
            through &lt;a href=&quot;https://travis-ci.org/&quot;&gt;Travis CI&lt;/a&gt;. To enable builds on Travis CI, login and enable builds for the
            GitHub repo you created the project in. Then trigger your first build with a &lt;code&gt;git push&lt;/code&gt;.&lt;/p&gt;
    &lt;/div&gt;
    &lt;div class=&quot;paragraph&quot;&gt;
        &lt;p&gt;When I first tried this, I received a &lt;a href=&quot;https://travis-ci.org/mraible/angular2-tutorial/jobs/135517549#L1189&quot;&gt;failure&lt;/a&gt; because
            Protractor on Travis CI is &lt;a href=&quot;https://github.com/mgechev/angular2-seed/issues/970&quot;&gt;unable to navigate directly&lt;/a&gt;
            to the search and edit components. I was able to workaround this by modifying &lt;code&gt;search.component.e2e-spec.ts&lt;/code&gt; to start
            at the top and navigate to the component.&lt;/p&gt;
    &lt;/div&gt;
    &lt;div class=&quot;listingblock&quot;&gt;
        &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;beforeEach(() =&amp;gt; {
  browser.get(&apos;/&apos;);
  element(by.linkText(&apos;SEARCH&apos;)).click();
});&lt;/pre&gt;
        &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class=&quot;paragraph&quot;&gt;
        &lt;p&gt;I did something similar with &lt;code&gt;edit.component.e2e-spec.ts&lt;/code&gt;:&lt;/p&gt;
    &lt;/div&gt;
    &lt;div class=&quot;listingblock&quot;&gt;
        &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;beforeEach(() =&amp;gt; {
  browser.get(&apos;/&apos;);
  element(by.linkText(&apos;SEARCH&apos;)).click();
  let search = element(by.css(&apos;sd-search form input&apos;));
  &apos;Man&apos;.split(&apos;&apos;).forEach((c) =&amp;gt; search.sendKeys(c));
  element(by.css(&apos;sd-search form button&apos;)).click();
  element(by.linkText(&apos;Peyton Manning&apos;)).click();
});&lt;/pre&gt;
        &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class=&quot;paragraph&quot;&gt;
        &lt;p&gt;After making these changes, &lt;a href=&quot;https://travis-ci.org/mraible/angular2-tutorial/builds/135598015&quot;&gt;all e2e tests passed in Travis CI&lt;/a&gt;.&lt;/p&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;source_code&quot;&gt;Source code&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&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/angular2-tutorial&quot;&gt;https://github.com/mraible/angular2-tutorial&lt;/a&gt;.
                If you have ideas for improvements, please leave a comment or send a pull request.
            &lt;/p&gt;
            &lt;p&gt;
                I wrote this tutorial in &lt;a href=&quot;http://asciidoctor.org/&quot;&gt;Asciidoctor&lt;/a&gt; so you can also &lt;a href=&quot;https://github.com/mraible/angular2-tutorial/blob/master/TESTING.adoc&quot;&gt;read this tutorial on GitHub&lt;/a&gt; or
                &lt;a href=&quot;http://gist.asciidoctor.org/?github-mraible%2Fangular2-tutorial%2F%2FTESTING.adoc&quot;&gt;using DocGist&lt;/a&gt;.
            &lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;summary&quot;&gt;Summary&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;I hope you&apos;ve enjoyed this quick-and-easy tutorial on testing Angular 2.0 RC1 applications. You can see
                the test coverage of your project by running
                &lt;code&gt;npm run serve.coverage&lt;/code&gt;. You&apos;ll notice that the new components and service could use some
                additional coverage. I&apos;ll leave that as a task
                for the reader.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div id=&quot;test-coverage&quot; class=&quot;imageblock&quot;&gt;
            &lt;div style=&quot;text-align: center&quot;&gt;
                &lt;a href=&quot;https://c3.staticflickr.com/8/7381/27350245922_7e184fc630_c.jpg&quot; title=&quot;Test Coverage&quot;
                   rel=&quot;lightbox[testing-angular2.0rc1]&quot;
                   data-href=&quot;https://www.flickr.com/photos/mraible/27350245922/in/datetaken-public/&quot;&gt;&lt;img
                    src=&quot;https://c3.staticflickr.com/8/7381/27350245922_7e184fc630_z.jpg&quot; width=&quot;640&quot;
                    alt=&quot;Test Coverage&quot;&gt;&lt;/a&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;I learned a lot about testing from &lt;a href=&quot;https://www.ng-book.com/2/&quot;&gt;ng-book 2&lt;/a&gt; and
                its Testing chapter. If you have any Angular 2 testing tips and tricks you&apos;d like to share, I&apos;d love to
                hear about them.&lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/getting_started_with_angular_2_rc1</id>
        <title type="html">Getting Started with Angular 2.0 RC1</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/getting_started_with_angular_2_rc1"/>
        <published>2016-06-03T07:16:18-06:00</published>
        <updated>2016-08-23T23:09:58-06:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="asciidoctor" scheme="http://roller.apache.org/ns/tags/" />
        <category term="npm" scheme="http://roller.apache.org/ns/tags/" />
        <category term="javascript" scheme="http://roller.apache.org/ns/tags/" />
        <category term="node" scheme="http://roller.apache.org/ns/tags/" />
        <category term="git" scheme="http://roller.apache.org/ns/tags/" />
        <category term="typescript" scheme="http://roller.apache.org/ns/tags/" />
        <category term="angular2" scheme="http://roller.apache.org/ns/tags/" />
        <summary type="html">&lt;p&gt;A few months ago, I wrote a tutorial on &lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_2&quot;&gt;Getting
    Started with Angular 2&lt;/a&gt;. That tutorial was based on Angular 2.0.0 Beta 9. Rather than simply updating that
    tutorial
    and blog post for 2.0.0 RC1, I decided to create a new version for posterity&apos;s sake. The 2.0 Beta 9 version will
    remain on my blog and I&apos;ve &lt;a href=&quot;https://github.com/mraible/angular2-tutorial/releases/tag/2.0.0-beta.9&quot;&gt;tagged
        the source on GitHub&lt;/a&gt;.
    This is an updated version of Getting Started with Angular 2, complete with the
    &lt;a href=&quot;https://angular.io/docs/ts/latest/guide/router.html&quot;&gt;largely undocumented component router&lt;/a&gt;,
    and lazy-loaded components.
&lt;/p&gt;
&lt;p&gt;If you&apos;d just like to see what&apos;s changed since the last release of this tutorial, you can
    &lt;a href=&quot;https://github.com/mraible/angular2-tutorial/pull/2&quot;&gt;view the pull request on GitHub&lt;/a&gt;. Note that I did
    &lt;a href=&quot;https://help.github.com/articles/syncing-a-fork/&quot;&gt;sync my angular2-tutorial project&lt;/a&gt; with &lt;a
        href=&quot;https://github.com/mgechev/angular2-seed&quot;&gt;angular2-seed&lt;/a&gt;. This made it fairly easy to upgrade,
    believe it or not. My upgrade notes are &lt;a href=&quot;&quot;&gt;in a gist&lt;/a&gt;. The best diff to read to see what changed is
    likely &lt;a href=&quot;https://github.com/mraible/angular2-tutorial/pull/2/files#diff-7bd4a925c695c2eb0eced3872b9b965f&quot;&gt;
    the diff of this tutorial&lt;/a&gt;.
&lt;/p&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;what_you_ll_build&quot;&gt;What you&apos;ll build&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;You&apos;ll build a simple web application with Angular 2 and TypeScript. You&apos;ll add search and edit features
                with mock data.&lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;what_you_ll_need&quot;&gt;What you&apos;ll need&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;ulist&quot;&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. I recommend using &lt;a
                    href=&quot;https://github.com/creationix/nvm&quot;&gt;nvm&lt;/a&gt;.
                &lt;/li&gt;
            &lt;/ul&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;create_your_project&quot;&gt;Create your project&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Clone the &lt;a href=&quot;https://github.com/mgechev/angular2-seed&quot;&gt;angular2-seed&lt;/a&gt; repository using git:&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre&gt;git clone https://github.com/mgechev/angular2-seed.git angular2-tutorial
cd angular2-tutorial&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;</summary>
        <content type="html">&lt;p&gt;A few months ago, I wrote a tutorial on &lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_2&quot;&gt;Getting
    Started with Angular 2&lt;/a&gt;. That tutorial was based on Angular 2.0.0 Beta 9. Rather than simply updating that
    tutorial
    and blog post for 2.0.0 RC1, I decided to create a new version for posterity&apos;s sake. The 2.0 Beta 9 version will
    remain on my blog and I&apos;ve &lt;a href=&quot;https://github.com/mraible/angular2-tutorial/releases/tag/2.0.0-beta.9&quot;&gt;tagged
        the source on GitHub&lt;/a&gt;.
    This is an updated version of Getting Started with Angular 2, complete with the
    &lt;a href=&quot;https://angular.io/docs/ts/latest/guide/router.html&quot;&gt;largely undocumented component router&lt;/a&gt;,
    and lazy-loaded components.
&lt;/p&gt;
&lt;p&gt;If you&apos;d just like to see what&apos;s changed since the last release of this tutorial, you can
    &lt;a href=&quot;https://github.com/mraible/angular2-tutorial/pull/2&quot;&gt;view the pull request on GitHub&lt;/a&gt;. Note that I did
    &lt;a href=&quot;https://help.github.com/articles/syncing-a-fork/&quot;&gt;sync my angular2-tutorial project&lt;/a&gt; with &lt;a
        href=&quot;https://github.com/mgechev/angular2-seed&quot;&gt;angular2-seed&lt;/a&gt;. This made it fairly easy to upgrade,
    believe it or not. My upgrade notes are &lt;a href=&quot;https://gist.github.com/mraible/d9754864249e1b4bfa344ce80074d73d&quot;&gt;in a gist&lt;/a&gt;. The best diff to read to see what changed is
    likely &lt;a href=&quot;https://github.com/mraible/angular2-tutorial/pull/2/files#diff-7bd4a925c695c2eb0eced3872b9b965f&quot;&gt;
    the diff of this tutorial&lt;/a&gt;.
&lt;/p&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;what_you_ll_build&quot;&gt;What you&apos;ll build&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;You&apos;ll build a simple web application with Angular 2 and TypeScript. You&apos;ll add search and edit features
                with mock data.&lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;what_you_ll_need&quot;&gt;What you&apos;ll need&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;ulist&quot;&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. I recommend using &lt;a
                    href=&quot;https://github.com/creationix/nvm&quot;&gt;nvm&lt;/a&gt;.
                &lt;/li&gt;
            &lt;/ul&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;create_your_project&quot;&gt;Create your project&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Clone the &lt;a href=&quot;https://github.com/mgechev/angular2-seed&quot;&gt;angular2-seed&lt;/a&gt; repository using git:&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre&gt;git clone https://github.com/mgechev/angular2-seed.git angular2-tutorial
cd angular2-tutorial&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;p&gt;
            NOTE: The angular2-seed project requires node v4.x.x or higher and npm 2.14.7. I used node v4.2.6 and npm
            3.9.3.
        &lt;/p&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Install the project&apos;s dependencies:&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
                &lt;pre&gt;npm install&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;run_the_application&quot;&gt;Run the application&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;The project is configured with a simple web server for development. To start it, run:&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
                &lt;pre&gt;npm start&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;You should see a screen like the one below at &lt;a href=&quot;http://localhost:5555&quot;&gt;http://localhost:5555&lt;/a&gt;.
            &lt;/p&gt;
        &lt;/div&gt;
        &lt;div id=&quot;default-homepage&quot; class=&quot;imageblock&quot;&gt;
            &lt;div style=&quot;text-align: center&quot;&gt;
                &lt;a href=&quot;https://farm2.staticflickr.com/1560/25886693062_4dd41acd3d_c.jpg&quot; title=&quot;Default homepage&quot;
                   rel=&quot;lightbox[getting-started-with-angular2]&quot;
                   data-href=&quot;https://www.flickr.com/photos/mraible/25886693062/in/datetaken-public/&quot;&gt;&lt;img
                    src=&quot;https://farm2.staticflickr.com/1560/25886693062_4dd41acd3d_z.jpg&quot; width=&quot;640&quot;
                    alt=&quot;Default homepage&quot;&gt;&lt;/a&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;You can see your new project&apos;s test coverage by running &lt;code&gt;npm test&lt;/code&gt;:&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre&gt;=============================== Coverage summary ===============================
Statements   : 96.36% ( 159/165 )
Branches     : 62.5% ( 120/192 )
Functions    : 100% ( 35/35 )
Lines        : 100% ( 129/129 )
================================================================================&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;add_a_search_feature&quot;&gt;Add a search feature&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&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 angular2-seed to.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;sect2&quot;&gt;
            &lt;h3 id=&quot;the_basics&quot;&gt;The Basics&lt;/h3&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Create a &lt;code&gt;src/client/app/+search&lt;/code&gt; folder and a &lt;code&gt;search.component.html&lt;/code&gt; file in it. The + prefix is an indicator
                    to Angular that you want to lazy-load the components in this directory. Populate the HTML file you created with the following:
                &lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;&amp;lt;h2&amp;gt;Search&amp;lt;/h2&amp;gt;
&amp;lt;form&amp;gt;
  &amp;lt;input type=&quot;search&quot; [(ngModel)]=&quot;query&quot; (keyup.enter)=&quot;search()&quot;&amp;gt;
  &amp;lt;button type=&quot;button&quot; (click)=&quot;search()&quot;&amp;gt;Search&amp;lt;/button&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;div *ngIf=&quot;loading&quot;&amp;gt;loading...&amp;lt;/div&amp;gt;
&amp;lt;pre&amp;gt;{{searchResults | json}}&amp;lt;/pre&amp;gt;
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Create &lt;code&gt;src/client/app/+search/search.component.ts&lt;/code&gt; to define the
                    &lt;code&gt;SearchComponent&lt;/code&gt; and point to this template.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;import { Component } from &apos;@angular/core&apos;;
import { CORE_DIRECTIVES, FORM_DIRECTIVES } from &apos;@angular/common&apos;;
import { ROUTER_DIRECTIVES } from &apos;@angular/router&apos;;

@Component({
  selector: &apos;sd-search&apos;,
  moduleId: module.id,
  templateUrl: &apos;search.component.html&apos;,
  directives: [FORM_DIRECTIVES, CORE_DIRECTIVES, ROUTER_DIRECTIVES]
})
export class SearchComponent {
  loading: boolean;
  query: string;
  searchResults: any;

  constructor() {
    console.log(&apos;initialized search component&apos;);
  }
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Create &lt;code&gt;src/client/app/+search/index.ts&lt;/code&gt; to export &lt;code&gt;SearchComponent&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;/**
* This barrel file provides the export for the lazy loaded SearchComponent.
*/
export * from &apos;./search.component&apos;;
&lt;/pre&gt;
&lt;p&gt;Update &lt;code&gt;src/client/app/app.component.ts&lt;/code&gt; to import this component and include its route.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;import { SearchComponent } from &apos;./+search/index&apos;;

@Routes([
  { path: &apos;/&apos;, component: HomeComponent },
  { path: &apos;/about&apos;, component: AboutComponent },
  { path: &apos;/search&apos;, component: SearchComponent }
])
&lt;/pre&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Your browser should refresh automatically, thanks to &lt;a href=&quot;http://browsersync.io&quot;&gt;Browsersync&lt;/a&gt;.
                    Navigate to
                    &lt;a href=&quot;http://localhost:5555/search&quot;&gt;http://localhost:5555/search&lt;/a&gt; and you should see the
                    search component.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div id=&quot;search-component&quot; class=&quot;imageblock&quot;&gt;
                &lt;div style=&quot;text-align: center&quot;&gt;
                    &lt;a href=&quot;https://farm2.staticflickr.com/1552/25374766404_b0830ff897_c.jpg&quot; title=&quot;Search component&quot;
                       rel=&quot;lightbox[getting-started-with-angular2]&quot;
                       data-href=&quot;https://www.flickr.com/photos/mraible/25374766404/in/datetaken-public/&quot;&gt;&lt;img
                        src=&quot;https://farm2.staticflickr.com/1552/25374766404_b0830ff897_z.jpg&quot; width=&quot;640&quot;
                        alt=&quot;Search component&quot;&gt;&lt;/a&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;You can see it needs a bit of styling. Angular 2 allows you to provide styles specific for your
                    component using a &lt;code&gt;styleUrls&lt;/code&gt;
                    property on your component.
                    Add this property to &lt;code&gt;search.component.ts&lt;/code&gt; like you see below.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;templateUrl: &apos;search.component.html&apos;,
styleUrls: [&apos;search.component.css&apos;],
directives: [FORM_DIRECTIVES, CORE_DIRECTIVES, ROUTER_DIRECTIVES]
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Create &lt;code&gt;src/client/app/+search/search.component.css&lt;/code&gt; and add some CSS.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: css&quot;&gt;:host {
  display: block;
  padding: 0 16px;
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;There, that looks better!&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;imageblock&quot;&gt;
                &lt;div style=&quot;text-align: center&quot;&gt;
                    &lt;a href=&quot;https://c2.staticflickr.com/8/7433/26828737033_fd196ee3f3_c.jpg&quot;
                       title=&quot;Search component with styling&quot; rel=&quot;lightbox[getting-started-with-angular2]&quot;
                       data-href=&quot;https://www.flickr.com/photos/mraible/25886691122/in/datetaken-public/&quot;&gt;&lt;img
                        src=&quot;https://c2.staticflickr.com/8/7433/26828737033_fd196ee3f3_z.jpg&quot; width=&quot;640&quot;
                        alt=&quot;Search component with styling&quot;&gt;&lt;/a&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Finally, update &lt;code&gt;src/client/app/shared/navbar/navbar.component.html&lt;/code&gt; to include a link to the search
                    route.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;&amp;lt;nav&amp;gt;
  &amp;lt;a &amp;#91;routerLink&amp;#93;=&quot;&amp;#91;&apos;/&apos;&amp;#93;&quot;&amp;gt;HOME&amp;lt;/a&amp;gt;
  &amp;lt;a &amp;#91;routerLink&amp;#93;=&quot;&amp;#91;&apos;/about&apos;&amp;#93;&quot;&amp;gt;ABOUT&amp;lt;/a&amp;gt;
  &amp;lt;a &amp;#91;routerLink&amp;#93;=&quot;&amp;#91;&apos;/search&apos;&amp;#93;&quot;&amp;gt;SEARCH&amp;lt;/a&amp;gt;
&amp;lt;/nav&amp;gt;
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;This section has shown you how to add a new component to a basic Angular 2 application.
                    The next section shows you how to create a use a JSON file and &lt;code&gt;localStorage&lt;/code&gt; to create a
                    fake API.&lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;sect2&quot;&gt;
            &lt;h3 id=&quot;the_backend&quot;&gt;The Backend&lt;/h3&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;To get search results, create a &lt;code&gt;SearchService&lt;/code&gt; that makes HTTP requests to a JSON file.
                    Start
                    by creating &lt;code&gt;src/client/app/shared/search/data/people.json&lt;/code&gt; to hold your data.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;[
  {
    &quot;id&quot;: 1,
    &quot;name&quot;: &quot;Peyton Manning&quot;,
    &quot;phone&quot;: &quot;(303) 567-8910&quot;,
    &quot;address&quot;: {
      &quot;street&quot;: &quot;1234 Main Street&quot;,
      &quot;city&quot;: &quot;Greenwood Village&quot;,
      &quot;state&quot;: &quot;CO&quot;,
      &quot;zip&quot;: &quot;80111&quot;
    }
  },
  {
    &quot;id&quot;: 2,
    &quot;name&quot;: &quot;Demaryius Thomas&quot;,
    &quot;phone&quot;: &quot;(720) 213-9876&quot;,
    &quot;address&quot;: {
      &quot;street&quot;: &quot;5555 Marion Street&quot;,
      &quot;city&quot;: &quot;Denver&quot;,
      &quot;state&quot;: &quot;CO&quot;,
      &quot;zip&quot;: &quot;80202&quot;
    }
  },
  {
    &quot;id&quot;: 3,
    &quot;name&quot;: &quot;Von Miller&quot;,
    &quot;phone&quot;: &quot;(917) 323-2333&quot;,
    &quot;address&quot;: {
      &quot;street&quot;: &quot;14 Mountain Way&quot;,
      &quot;city&quot;: &quot;Vail&quot;,
      &quot;state&quot;: &quot;CO&quot;,
      &quot;zip&quot;: &quot;81657&quot;
    }
  }
]
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Create &lt;code&gt;src/client/app/shared/search/search.service.ts&lt;/code&gt; and provide &lt;code&gt;Http&lt;/code&gt; as a
                    dependency in its constructor.
                    In this same file, define the &lt;code&gt;Address&lt;/code&gt; and &lt;code&gt;Person&lt;/code&gt; classes that JSON will be
                    marshalled to.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;import { Injectable } from &apos;@angular/core&apos;;
import { Http, Response } from &apos;@angular/http&apos;;

@Injectable()
export class SearchService {
  constructor(private http:Http) {}

  getAll() {
    return this.http.get(&apos;app/shared/search/data/people.json&apos;).map((res:Response) =&amp;gt; res.json());
  }
}

export class Address {
  street:string;
  city:string;
  state:string;
  zip:string;

  constructor(obj?:any) {
    this.street = obj &amp;amp;&amp;amp; obj.street || null;
    this.city = obj &amp;amp;&amp;amp; obj.city || null;
    this.state = obj &amp;amp;&amp;amp; obj.state || null;
    this.zip = obj &amp;amp;&amp;amp; obj.zip || null;
  }
}

export class Person {
  id:number;
  name:string;
  phone:string;
  address:Address;

  constructor(obj?:any) {
    this.id = obj &amp;amp;&amp;amp; Number(obj.id) || null;
    this.name = obj &amp;amp;&amp;amp; obj.name || null;
    this.phone = obj &amp;amp;&amp;amp; obj.phone || null;
    this.address = obj &amp;amp;&amp;amp; obj.address || null;
  }
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;In &lt;code&gt;search.component.ts&lt;/code&gt;, add imports for these classes.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;import { Person, SearchService } from &apos;../shared/index&apos;;
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;You can now add a type to the searchResults variable. While you&apos;re there, modify the constructor to
                    inject the
                    &lt;code&gt;SearchService&lt;/code&gt;.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;searchResults: Array&amp;lt;Person&amp;gt;;

constructor(public searchService: SearchService) {}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Then implement the &lt;code&gt;search()&lt;/code&gt; method to call the service&apos;s &lt;code&gt;getAll()&lt;/code&gt; method.
                &lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;search(): void {
  this.searchService.getAll().subscribe(
    data =&amp;gt; {this.searchResults = data;},
    error =&amp;gt; console.log(error)
  );
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;At this point, you&apos;ll likely see the following message in your browser&apos;s console.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
                    &lt;pre&gt;ORIGINAL EXCEPTION: No provider for SearchService!&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;This happens because the app hasn&apos;t provided this service to components. To fix this, create
                    &lt;code&gt;src/client/app/shared/search/index.ts&lt;/code&gt; and populate it with the following.
                &lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;/**
 * This barrel file provides the export for the shared SearchService.
 */
export * from &apos;./search.service&apos;;
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;
                    Then add a reference to this file in &lt;code&gt;src/client/app/shared/index.ts&lt;/code&gt; so its
                    included in the shared export.
                &lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;/**
 * This barrel file provides the exports for the shared resources (services, components).
 */
export * from &apos;./name-list/index&apos;;
export * from &apos;./navbar/index&apos;;
export * from &apos;./toolbar/index&apos;;
export * from &apos;./search/index&apos;;&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;To fix the &quot;No provider&quot; error from above, update &lt;code&gt;app.component.ts&lt;/code&gt; to import the
                    &lt;code&gt;SearchService&lt;/code&gt; and add the service to the list of provider.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;import { NameListService, NavbarComponent, ToolbarComponent, SearchService } from &apos;./shared/index&apos;;

@Component({
  selector: &apos;sd-app&apos;,
  viewProviders: [NameListService, SearchService, HTTP_PROVIDERS]
  moduleId: module.id,
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Now clicking the search button should work. To make the results look better,
                    remove the &lt;code&gt;&amp;lt;pre&amp;gt;&lt;/code&gt; tag in &lt;code&gt;search.component.html&lt;/code&gt;
                    and replace it with a &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt;.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;&amp;lt;table *ngIf=&quot;searchResults&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 *ngFor=&quot;let person of searchResults; let i=index&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;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Then add some additional CSS for this component in &lt;code&gt;search.component.css&lt;/code&gt;.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: css&quot;&gt;table {
  margin-top: 10px;
  border-collapse: collapse;
}

th {
  text-align: left;
  border-bottom: 2px solid #ddd;
  padding: 8px;
}

td {
  border-top: 1px solid #ddd;
  padding: 8px;
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Now the search results look better.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div id=&quot;search-results&quot; class=&quot;imageblock&quot;&gt;
                &lt;div style=&quot;text-align: center&quot;&gt;
                    &lt;a href=&quot;https://farm2.staticflickr.com/1677/25706917270_bf07c541e4_c.jpg&quot; title=&quot;Search results&quot;
                       rel=&quot;lightbox[getting-started-with-angular2]&quot;
                       data-href=&quot;https://www.flickr.com/photos/mraible/25706917270/in/datetaken-public/&quot;&gt;&lt;img
                        src=&quot;https://farm2.staticflickr.com/1677/25706917270_bf07c541e4_z.jpg&quot; width=&quot;640&quot;
                        alt=&quot;Search results&quot;&gt;&lt;/a&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;But wait, we still don&apos;t have search functionality! To add a search feature, add a
                    &lt;code&gt;search()&lt;/code&gt; method to
                    &lt;code&gt;search.service.ts&lt;/code&gt;.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;search(q:string) {
  if (!q || q === &apos;*&apos;) {
    q = &apos;&apos;;
  } else {
    q = q.toLowerCase();
  }
  return this.getAll().map(data =&amp;gt; {
    let results:any = [];
    data.map(item =&amp;gt; {
      if (JSON.stringify(item).toLowerCase().includes(q)) {
        results.push(item);
      }
    });
    return results;
  });
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Then refactor &lt;code&gt;SearchComponent&lt;/code&gt; to call this method with its &lt;code&gt;query&lt;/code&gt; variable.
                &lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;search(): void {
  this.searchService.search(this.query).subscribe(
    data =&amp;gt; {this.searchResults = data;},
    error =&amp;gt; console.log(error)
  );
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Now search results will be filtered by the query value you type in.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;This section showed 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;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;sect2&quot;&gt;
            &lt;h3 id=&quot;add_an_edit_feature&quot;&gt;Add an edit feature&lt;/h3&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Modify &lt;code&gt;search.component.html&lt;/code&gt; to add a link for editing a person.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;&amp;lt;td&amp;gt;&amp;lt;a &amp;#91;routerLink&amp;#93;=&quot;&amp;#91;&apos;/edit&apos;, person.id&amp;#93;&quot;&amp;gt;{{person.name}}&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Create &lt;code&gt;src/client/app/+edit/edit.component.html&lt;/code&gt; to display an editable form. You might
                    notice I&apos;ve added &lt;code&gt;id&lt;/code&gt;
                    attributes to most elements. This is to
                    make things easier when writing integration tests with Protractor.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;&amp;lt;div *ngIf=&quot;person&quot;&amp;gt;
  &amp;lt;h3&amp;gt;{{editName}}&amp;lt;/h3&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;label&amp;gt;Id:&amp;lt;/label&amp;gt;
    {{person.id}}
  &amp;lt;/div&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;label&amp;gt;Name:&amp;lt;/label&amp;gt;
    &amp;lt;input [(ngModel)]=&quot;editName&quot; id=&quot;name&quot; placeholder=&quot;name&quot;/&amp;gt;
  &amp;lt;/div&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;label&amp;gt;Phone:&amp;lt;/label&amp;gt;
    &amp;lt;input [(ngModel)]=&quot;editPhone&quot; id=&quot;phone&quot; placeholder=&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&amp;gt;
      &amp;lt;input [(ngModel)]=&quot;editAddress.street&quot; id=&quot;street&quot;&amp;gt;&amp;lt;br/&amp;gt;
      &amp;lt;input [(ngModel)]=&quot;editAddress.city&quot; id=&quot;city&quot;&amp;gt;,
      &amp;lt;input [(ngModel)]=&quot;editAddress.state&quot; id=&quot;state&quot; size=&quot;2&quot;&amp;gt;
      &amp;lt;input [(ngModel)]=&quot;editAddress.zip&quot; id=&quot;zip&quot; size=&quot;5&quot;&amp;gt;
    &amp;lt;/address&amp;gt;
  &amp;lt;/fieldset&amp;gt;
  &amp;lt;button (click)=&quot;save()&quot; id=&quot;save&quot;&amp;gt;Save&amp;lt;/button&amp;gt;
  &amp;lt;button (click)=&quot;cancel()&quot; id=&quot;cancel&quot;&amp;gt;Cancel&amp;lt;/button&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Create an &lt;code&gt;EditComponent&lt;/code&gt; in &lt;code&gt;src/search/components/edit.component.ts&lt;/code&gt; that
                    references this template and handles communication with the &lt;code&gt;SearchService&lt;/code&gt;.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;import { Component, OnInit } from &apos;@angular/core&apos;;
import { Person, Address, SearchService } from &apos;../shared/index&apos;;
import { RouteSegment, Router } from &apos;@angular/router&apos;;

@Component({
  selector: &apos;sd-edit&apos;,
  moduleId: module.id,
  templateUrl: &apos;edit.component.html&apos;,
  styleUrls: [&apos;edit.component.css&apos;]
})
export class EditComponent implements OnInit {

  person: Person;
  editName: string;
  editPhone: string;
  editAddress: Address;

  constructor(
    private _service: SearchService,
    private _router: Router,
    private _routeSegment: RouteSegment
  ) { }

  ngOnInit() {
    let id = +this._routeSegment.getParam(&apos;id&apos;);
    this._service.get(id).subscribe(person =&gt; {
      if (person) {
        this.editName = person.name;
        this.editPhone = person.phone;
        this.editAddress = person.address;
        this.person = person;
      } else {
        this.gotoList();
      }
    });
  }

  cancel() {
    this._router.navigate([&apos;/search&apos;]);
  }

  save() {
    this.person.name = this.editName;
    this.person.phone = this.editPhone;
    this.person.address = this.editAddress;
    this._service.save(this.person);
    this.gotoList();
  }

  gotoList() {
    if (this.person) {
      this._router.navigate([&apos;/search&apos;, {term: this.person.name} ]);
    } else {
      this._router.navigate([&apos;/search&apos;]);
    }
  }
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Create &lt;code&gt;src/client/app/+edit/index.ts&lt;/code&gt; to export &lt;code&gt;EditComponent&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;/**
 * This barrel file provides the export for the lazy loaded EditComponent.
 */
export * from &apos;./edit.component&apos;;
&lt;/pre&gt;
                &lt;p&gt;Modify &lt;code&gt;SearchService&lt;/code&gt; to contain functions for finding a person by their id, and saving
                    them. While you&apos;re in there, modify
                    the &lt;code&gt;search()&lt;/code&gt; method to
                    be aware of updated objects in &lt;code&gt;localStorage&lt;/code&gt;.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;search(q:string) {
  if (!q || q === &apos;*&apos;) {
    q = &apos;&apos;;
  } else {
    q = q.toLowerCase();
  }
  return this.getAll().map(data =&amp;gt; {
    let results = [];
    data.map(item =&amp;gt; {
      // check for item in localStorage
      if (localStorage[&apos;person&apos; + item.id]) {
        item = JSON.parse(localStorage[&apos;person&apos; + item.id]);
      }
      if (JSON.stringify(item).toLowerCase().includes(q)) {
        results.push(item);
      }
    });
    return results;
  });
}

get(id: number) {
  return this.getAll().map(all =&amp;gt; {
    if (localStorage[&apos;person&apos; + id]) {
      return JSON.parse(localStorage[&apos;person&apos; + id]);
    }
    return all.find(e =&amp;gt; e.id === id);
  });
}

save(person: Person) {
  localStorage[&apos;person&apos; + person.id] = JSON.stringify(person);
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;To make the app aware of this new component, add an import and route configuration in &lt;code&gt;app.component.ts&lt;/code&gt;.
                &lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;import { EditComponent } from &apos;../+edit/index&apos;;

@Routes([
  { path: &apos;/&apos;, component: HomeComponent },
  { path: &apos;/about&apos;, component: AboutComponent },
  { path: &apos;/search&apos;, component: SearchComponent },
  { path: &apos;/edit/:id&apos;, component: EditComponent }
])
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Then create &lt;code&gt;src/client/app/+edit/edit.component.css&lt;/code&gt; to make the form look a bit better.
                &lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: css&quot;&gt;:host {
  display: block;
  padding: 0 16px;
}

button {
  margin-top: 10px;
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;At this point, you should be able to search for a person and update their information.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div id=&quot;edit-form&quot; class=&quot;imageblock&quot;&gt;
                &lt;div style=&quot;text-align: center&quot;&gt;
                    &lt;a href=&quot;https://farm2.staticflickr.com/1603/25886692692_42abb78ef6_c.jpg&quot; title=&quot;Edit component&quot;
                       rel=&quot;lightbox[getting-started-with-angular2]&quot;
                       data-href=&quot;https://www.flickr.com/photos/mraible/25886692692/in/datetaken-public/&quot;&gt;&lt;img
                        src=&quot;https://farm2.staticflickr.com/1603/25886692692_42abb78ef6_z.jpg&quot; width=&quot;640&quot;
                        alt=&quot;Edit component&quot;&gt;&lt;/a&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;The &amp;lt;form&amp;gt; in &lt;code&gt;src/client/app/+edit/edit.component.html&lt;/code&gt; calls a
                    &lt;code&gt;save()&lt;/code&gt; function to update a person&apos;s
                    data. You already implemented this above.
                    The function calls a &lt;code&gt;gotoList()&lt;/code&gt; function that appends the person&apos;s name to the URL when
                    sending the user back to the
                    search screen.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;gotoList() {
  if (this.person) {
    this._router.navigate([&apos;/search&apos;, {term: this.person.name} ]);
  } else {
    this._router.navigate([&apos;/search&apos;]);
  }
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Since the &lt;code&gt;SearchComponent&lt;/code&gt; doesn&apos;t execute a search automatically when you execute this
                    URL, add the following logic to do
                    so in its constructor.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;constructor(public searchService: SearchService, routeSegment: RouteSegment) {
  if (routeSegment.getParam(&apos;term&apos;)) {
    this.query = decodeURIComponent(routeSegment.getParam(&apos;term&apos;));
    this.search();
  }
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;You&apos;ll need to import &lt;code&gt;RouteSegment&lt;/code&gt; in order for everything to compile.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
                    &lt;pre class=&quot;brush: js&quot;&gt;import { ROUTER_DIRECTIVES, RouteSegment } from &apos;@angular/router&apos;;&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;After making all these changes, you should be able to search/edit/update a person&apos;s information. If
                    it works - nice job!&lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;source_code&quot;&gt;Source code&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&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/angular2-tutorial&quot;&gt;https://github.com/mraible/angular2-tutorial&lt;/a&gt;.
                If you have ideas for improvements, please leave a comment or send a pull request.
            &lt;/p&gt;
            &lt;p&gt;
                I originally wrote this tutorial in &lt;a href=&quot;http://asciidoctor.org/&quot;&gt;Asciidoctor&lt;/a&gt; because it has a
                slick feature where you
                can include the source code from files rather than copying and pasting. Since GitHub
                &lt;a href=&quot;https://github.com/github/markup/issues/172&quot;&gt;doesn&apos;t support includes&lt;/a&gt;, I changed things so the
                code is now embedded in the document. You can now &lt;a href=&quot;https://github.com/mraible/angular2-tutorial/blob/master/README.adoc&quot;&gt;read this tutorial on GitHub&lt;/a&gt; or
                &lt;a href=&quot;http://gist.asciidoctor.org/?github-mraible%2Fangular2-tutorial%2F%2FREADME.adoc&quot;&gt;using DocGist&lt;/a&gt;.
            &lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;summary&quot;&gt;Summary&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;I hope you&apos;ve enjoyed this quick-and-easy tutorial on how to get started with Angular 2.0 RC1. In a future
                tutorial, I&apos;ll show you &lt;a href=&quot;//raibledesigns.com/rd/entry/testing_angular_2_0_rc1&quot;&gt;how to write unit tests and integration tests&lt;/a&gt; for this application.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;sect2&quot;&gt;
            &lt;h3 id=&quot;resources&quot;&gt;Resources&lt;/h3&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;I used a number of resources while creating this application. &lt;a href=&quot;https://www.ng-book.com/2/&quot;&gt;ng-book
                    2&lt;/a&gt; was an invaluable resource
                    and I highly recommend it if you&apos;re learning Angular 2. I found Chariot Solution&apos;s article on
                    &lt;a href=&quot;http://chariotsolutions.com/blog/post/angular2-observables-http-separating-services-components/&quot;&gt;Angular2
                        Observables, Http, and
                        separating services and components&lt;/a&gt;
                    to be quite helpful. Finally, the &lt;a href=&quot;https://github.com/angular/angular-cli&quot;&gt;angular-cli&lt;/a&gt;
                    project was a big help, especially its
                    &lt;code&gt;ng generate route &amp;lt;object&amp;gt;&lt;/code&gt; feature.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Kudos to all the pioneers in Angular 2 land that&apos;ve been using it and writing about it on blogs and
                    Stack Overflow.
                    Getting started with Angular 2 would&apos;ve been a real pain without your trailblazing.&lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/testing_angular_2_applications</id>
        <title type="html">Testing Angular 2 Applications</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/testing_angular_2_applications"/>
        <published>2016-03-29T08:08:58-06:00</published>
        <updated>2016-06-06T19:28:40-06:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="node" scheme="http://roller.apache.org/ns/tags/" />
        <category term="javascript" scheme="http://roller.apache.org/ns/tags/" />
        <category term="angular2" scheme="http://roller.apache.org/ns/tags/" />
        <category term="karma" scheme="http://roller.apache.org/ns/tags/" />
        <category term="npm" scheme="http://roller.apache.org/ns/tags/" />
        <category term="jasmine" scheme="http://roller.apache.org/ns/tags/" />
        <category term="protractor" scheme="http://roller.apache.org/ns/tags/" />
        <category term="asciidoctor" scheme="http://roller.apache.org/ns/tags/" />
        <category term="git" scheme="http://roller.apache.org/ns/tags/" />
        <summary type="html">&lt;p class=&quot;alert alert-info&quot;&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/testing_angular_2_0_rc1&quot;&gt;Click here&lt;/a&gt; to see an updated version of this tutorial that&apos;s been upgraded for Angular 2.0 RC1.&lt;/p&gt;
&lt;p&gt;This article is the second in a series about learning &lt;a href=&quot;https://angular.io/&quot;&gt;Angular 2&lt;/a&gt;. It describes
    how to test a simple Angular 2 application. In a previous article, &lt;a
        href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_2&quot;&gt;Getting Started with Angular 2&lt;/a&gt;, I
    showed how to develop a simple search and edit feature.
    In this tutorial, I did my best to keep the tests similar to last year&apos;s &lt;a
        href=&quot;//raibledesigns.com/rd/entry/testing_angularjs_applications&quot;&gt;
        Testing AngularJS Applications&lt;/a&gt; so you can compare &lt;a
        href=&quot;https://github.com/mraible/angular-tutorial/tree/testing&quot;&gt;
        the code between AngularJS&lt;/a&gt; and Angular 2.
&lt;/p&gt;


&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;what_you_ll_build&quot;&gt;What you&apos;ll build&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;You&apos;ll learn to use &lt;a href=&quot;http://jasmine.github.io/&quot;&gt;Jasmine&lt;/a&gt; for unit testing controllers and &lt;a
                href=&quot;https://angular.github.io/protractor/&quot;&gt;Protractor&lt;/a&gt; for
                integration testing. Angular&apos;s documentation has a good &lt;a
                    href=&quot;https://angular.io/docs/ts/latest/guide/testing.html&quot;&gt;guide to unit testing&lt;/a&gt;
                if you&apos;d
                like more information on testing and why it&apos;s important.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;The best reason for writing tests is to automate your testing. Without tests, you&apos;ll likely be testing
                manually.
                This manual testing will take longer and longer as your codebase grows.&lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;what_you_ll_need&quot;&gt;What you&apos;ll need&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;ulist&quot;&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. I recommend using &lt;a
                    href=&quot;https://github.com/creationix/nvm&quot;&gt;nvm&lt;/a&gt;.
                &lt;/li&gt;
            &lt;/ul&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;get_the_tutorial_project&quot;&gt;Get the tutorial project&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Clone the &lt;a href=&quot;https://github.com/mraible/angular2-tutorial&quot;&gt;angular2-tutorial repository&lt;/a&gt; using
                git and install its dependencies.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre&gt;git clone https://github.com/mraible/angular2-tutorial.git
cd angular2-tutorial
npm install&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;If you haven&apos;t completed the &lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_2&quot;&gt;Getting
                Started with Angular 2&lt;/a&gt; tutorial,
                you should peruse it so you understand how this application works.
                You can also simply start the app with &lt;code&gt;npm start&lt;/code&gt; and view it in your browser at &lt;a
                    href=&quot;http://localhost:5555/&quot; class=&quot;bare&quot;&gt;http://localhost:5555/&lt;/a&gt;.
            &lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;</summary>
        <content type="html">&lt;p class=&quot;alert alert-info&quot;&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/testing_angular_2_0_rc1&quot;&gt;Click here&lt;/a&gt; to see an updated version of this tutorial that&apos;s been upgraded for Angular 2.0 RC1.&lt;/p&gt;
&lt;p&gt;This article is the second in a series about learning &lt;a href=&quot;https://angular.io/&quot;&gt;Angular 2&lt;/a&gt;. It describes
    how to test a simple Angular 2 application. In a previous article, &lt;a
        href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_2&quot;&gt;Getting Started with Angular 2&lt;/a&gt;, I
    showed how to develop a simple search and edit feature.
    In this tutorial, I did my best to keep the tests similar to last year&apos;s &lt;a
        href=&quot;//raibledesigns.com/rd/entry/testing_angularjs_applications&quot;&gt;
        Testing AngularJS Applications&lt;/a&gt; so you can compare &lt;a
        href=&quot;https://github.com/mraible/angular-tutorial/tree/testing&quot;&gt;
        the code between AngularJS&lt;/a&gt; and Angular 2.
&lt;/p&gt;


&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;what_you_ll_build&quot;&gt;What you&apos;ll build&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;You&apos;ll learn to use &lt;a href=&quot;http://jasmine.github.io/&quot;&gt;Jasmine&lt;/a&gt; for unit testing controllers and &lt;a
                href=&quot;https://angular.github.io/protractor/&quot;&gt;Protractor&lt;/a&gt; for
                integration testing. Angular&apos;s documentation has a good &lt;a
                    href=&quot;https://angular.io/docs/ts/latest/guide/testing.html&quot;&gt;guide to unit testing&lt;/a&gt;
                if you&apos;d
                like more information on testing and why it&apos;s important.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;The best reason for writing tests is to automate your testing. Without tests, you&apos;ll likely be testing
                manually.
                This manual testing will take longer and longer as your codebase grows.&lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;what_you_ll_need&quot;&gt;What you&apos;ll need&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;ulist&quot;&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. I recommend using &lt;a
                    href=&quot;https://github.com/creationix/nvm&quot;&gt;nvm&lt;/a&gt;.
                &lt;/li&gt;
            &lt;/ul&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;get_the_tutorial_project&quot;&gt;Get the tutorial project&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Clone the &lt;a href=&quot;https://github.com/mraible/angular2-tutorial&quot;&gt;angular2-tutorial repository&lt;/a&gt; using
                git and install its dependencies.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre&gt;git clone https://github.com/mraible/angular2-tutorial.git
cd angular2-tutorial
npm install&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;If you haven&apos;t completed the &lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_2&quot;&gt;Getting
                Started with Angular 2&lt;/a&gt; tutorial,
                you should peruse it so you understand how this application works.
                You can also simply start the app with &lt;code&gt;npm start&lt;/code&gt; and view it in your browser at &lt;a
                    href=&quot;http://localhost:5555/&quot; class=&quot;bare&quot;&gt;http://localhost:5555/&lt;/a&gt;.
            &lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;unit_test_the_searchservice&quot;&gt;Unit test the SearchService&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Create &lt;code&gt;src/shared/services/search.service.spec.ts&lt;/code&gt; and setup the test&apos;s infrastructure using
                &lt;a href=&quot;https://angular.io/docs/js/latest/api/http/testing/MockBackend-class.html&quot;&gt;MockBackend&lt;/a&gt;
                and &lt;a href=&quot;https://angular.io/docs/js/latest/api/http/BaseRequestOptions-class.html&quot;&gt;BaseRequestOptions&lt;/a&gt;.
            &lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import {it, describe, expect, inject, fakeAsync, beforeEachProviders, tick} from &apos;angular2/testing&apos;;
import {MockBackend} from &apos;angular2/http/testing&apos;;
import {provide} from &apos;angular2/core&apos;;
import &apos;rxjs/add/operator/map&apos;;
import {Http, ConnectionBackend, BaseRequestOptions, Response, ResponseOptions} from &apos;angular2/http&apos;;

import {SearchService} from &apos;./search.service&apos;;

export function main() {
  describe(&apos;Search Service&apos;, () =&gt; {
    beforeEachProviders(() =&gt; {
      return [BaseRequestOptions, MockBackend, SearchService,
        provide(Http, {
          useFactory: (backend:ConnectionBackend, defaultOptions:BaseRequestOptions) =&gt; {
            return new Http(backend, defaultOptions);
          }, deps: [MockBackend, BaseRequestOptions]
        }),
      ];
    });
  });
}
&lt;/pre&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;If you run &lt;code&gt;npm test&lt;/code&gt;, you&apos;ll get a failed build from a number of unused imports. You can fix
                those by adding the first test of &lt;code&gt;getAll()&lt;/code&gt;.
                This test shows how &lt;code&gt;MockBackend&lt;/code&gt; can be used to mock results and set the response.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;When you are testing code that returns either a Promise or an RxJS Observable, you can use the &lt;code&gt;fakeAsync&lt;/code&gt;
                helper to test that code as
                if it were synchronous.
                Promises are be fulfilled and Observables are notified immediately after you call &lt;code&gt;tick()&lt;/code&gt;.
            &lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;The test below should be on the same level as &lt;code&gt;beforeEachProviders&lt;/code&gt;.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
it(&apos;should retrieve all search results&apos;,
  inject([SearchService, MockBackend], fakeAsync((searchService:SearchService, mockBackend:MockBackend) =&gt; {
    var res:Response;
    mockBackend.connections.subscribe(c =&gt; {
      expect(c.request.url).toBe(&apos;shared/data/people.json&apos;);
      let response = new ResponseOptions({body: &apos;[{&quot;name&quot;: &quot;John Elway&quot;}, {&quot;name&quot;: &quot;Gary Kubiak&quot;}]&apos;});
      c.mockRespond(new Response(response));
    });
    searchService.getAll().subscribe((response) =&gt; {
      res = response;
    });
    tick();
    expect(res[0].name).toBe(&apos;John Elway&apos;);
  }))
);
&lt;/pre&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Running &lt;code&gt;npm test&lt;/code&gt; should result in &quot;10 tests completed&quot;. Add a couple more tests for
                filtering by search term and fetching by id.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
it(&apos;should filter by search term&apos;,
  inject([SearchService, MockBackend], fakeAsync((searchService:SearchService, mockBackend:MockBackend) =&gt; {
    var res:Response;
    mockBackend.connections.subscribe(c =&gt; {
      expect(c.request.url).toBe(&apos;shared/data/people.json&apos;);
      let response = new ResponseOptions({body: &apos;[{&quot;name&quot;: &quot;John Elway&quot;}, {&quot;name&quot;: &quot;Gary Kubiak&quot;}]&apos;});
      c.mockRespond(new Response(response));
    });
    searchService.search(&apos;john&apos;).subscribe((response) =&gt; {
      res = response;
    });
    tick();
    expect(res[0].name).toBe(&apos;John Elway&apos;);
  }))
);

it(&apos;should fetch by id&apos;,
  inject([SearchService, MockBackend], fakeAsync((searchService:SearchService, mockBackend:MockBackend) =&gt; {
    var res:Response;
    mockBackend.connections.subscribe(c =&gt; {
      expect(c.request.url).toBe(&apos;shared/data/people.json&apos;);
      let response = new ResponseOptions({body: &apos;[{&quot;id&quot;: 1, &quot;name&quot;: &quot;John Elway&quot;}, {&quot;id&quot;: 2, &quot;name&quot;: &quot;Gary Kubiak&quot;}]&apos;});
      c.mockRespond(new Response(response));
    });
    searchService.search(&apos;2&apos;).subscribe((response) =&gt; {
      res = response;
    });
    tick();
    expect(res[0].name).toBe(&apos;Gary Kubiak&apos;);
  }))
);
&lt;/pre&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;If you want to have tests continually run as you add them, you can run the following commands in separate
                shell windows.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre&gt;npm run build.test.watch
npm run karma.start&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;p&gt;NOTE: See &lt;a
            href=&quot;https://www.jetbrains.com/help/idea/15.0/running-unit-tests-on-karma.html?origin=old_help&quot;&gt;Running
            Unit Tests on Karma&lt;/a&gt; to
            learn how to run your tests from IntelliJ IDEA.&lt;/p&gt;

    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;unit_test_the_searchcomponent&quot;&gt;Unit test the SearchComponent&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;To unit test the &lt;code&gt;SearchComponent&lt;/code&gt;, create a &lt;code&gt;MockSearchProvider&lt;/code&gt; that has &lt;a
                href=&quot;http://angular-tips.com/blog/2014/03/introduction-to-unit-test-spies/&quot;&gt;spies&lt;/a&gt;.
                These allow you to &lt;em&gt;spy&lt;/em&gt; on functions to check if they were called.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Create &lt;code&gt;src/shared/services/mocks/search.service.ts&lt;/code&gt; and populate it with spies for each
                method, as well as methods to set the response and subscribe to results.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import {provide} from &apos;angular2/core&apos;;
import {SpyObject} from &apos;angular2/testing_internal&apos;;

import {SearchService} from &apos;../search.service&apos;;
import Spy = jasmine.Spy;

export class MockSearchService extends SpyObject {
  getAllSpy:Spy;
  getByIdSpy:Spy;
  searchSpy:Spy;
  saveSpy:Spy;
  fakeResponse;

  constructor() {
    super(SearchService);

    this.fakeResponse = null;
    this.getAllSpy = this.spy(&apos;getAll&apos;).andReturn(this);
    this.getByIdSpy = this.spy(&apos;get&apos;).andReturn(this);
    this.searchSpy = this.spy(&apos;search&apos;).andReturn(this);
    this.saveSpy = this.spy(&apos;save&apos;).andReturn(this);
  }

  subscribe(callback) {
    callback(this.fakeResponse);
  }

  setResponse(json: any): void {
    this.fakeResponse = json;
  }

  getProviders(): Array&amp;lt;any&amp;gt; {
    return [provide(SearchService, {useValue: this})];
  }
}
&lt;/pre&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;In this same directory, create &lt;code&gt;routes.ts&lt;/code&gt; to mock Angular&apos;s &lt;code&gt;Router&lt;/code&gt;, &lt;code&gt;RouteParams&lt;/code&gt;
                and &lt;code&gt;RouterProvider&lt;/code&gt;.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import {provide} from &apos;angular2/core&apos;;
import {
  ComponentInstruction,
  Router,
  RouteParams
} from &apos;angular2/router&apos;;
import {ResolvedInstruction} from &apos;angular2/src/router/instruction&apos;;
import {SpyObject} from &apos;angular2/testing_internal&apos;;

export class MockRouteParams extends SpyObject {
  private ROUTE_PARAMS = {};

  constructor() { super(RouteParams); }

  set(key: string, value: string) {
    this.ROUTE_PARAMS[key] = value;
  }

  get(key: string) {
    return this.ROUTE_PARAMS[key];
  }
}

export class MockRouter extends SpyObject {
  constructor() { super(Router); }
  isRouteActive(s) { return true; }
  generate(s) {
    return new ResolvedInstruction(new ComponentInstruction(&apos;detail&apos;, [], null, null, true, &apos;0&apos;), null, {});
  }
}

export class MockRouterProvider {
  mockRouter: MockRouter = new MockRouter();
  mockRouteParams: MockRouteParams = new MockRouteParams();

  setRouteParam(key: string, value: any) {
    this.mockRouteParams.set(key, value);
  }

  getProviders(): Array&amp;lt;any&gt; {
    return [
      provide(Router, {useValue: this.mockRouter}),
      provide(RouteParams, {useValue: this.mockRouteParams}),
    ];
  }
}
&lt;/pre&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;With mocks in place, you can create a spec for &lt;code&gt;SearchComponent&lt;/code&gt; that uses these as providers.
                Create a file at &lt;code&gt;src/search/components/search.component.spec.ts&lt;/code&gt; and populate it with the
                following code.
            &lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import {
  it,
  describe,
  expect,
  injectAsync,
  beforeEachProviders,
  TestComponentBuilder,
} from &apos;angular2/testing&apos;;

import {MockRouterProvider} from &apos;../../shared/services/mocks/routes&apos;;
import {MockSearchService} from &apos;../../shared/services/mocks/search.service&apos;;

import {SearchComponent} from &apos;./search.component&apos;;

export function main() {
  describe(&apos;Search component&apos;, () =&gt; {
    var mockSearchService:MockSearchService;
    var mockRouterProvider:MockRouterProvider;

    beforeEachProviders(() =&gt; {
      mockSearchService = new MockSearchService();
      mockRouterProvider = new MockRouterProvider();

      return [
        mockSearchService.getProviders(), mockRouterProvider.getProviders()
      ];
    });
  });
}
&lt;/pre&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Add two tests, one to verify a search term is used when it&apos;s set on the component and a second to verify
                search is called when a term is passed in as a route
                parameter.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
it(&apos;should search when a term is set and search() is called&apos;, injectAsync([TestComponentBuilder], (tcb:TestComponentBuilder) =&gt; {
  return tcb.createAsync(SearchComponent).then((fixture) =&gt; {
    let searchComponent = fixture.debugElement.componentInstance;
    searchComponent.query = &apos;M&apos;;
    searchComponent.search();
    expect(mockSearchService.searchSpy).toHaveBeenCalledWith(&apos;M&apos;);
  });
}));

it(&apos;should search automatically when a term is on the URL&apos;, injectAsync([TestComponentBuilder], (tcb:TestComponentBuilder) =&gt; {
  mockRouterProvider.setRouteParam(&apos;term&apos;, &apos;peyton&apos;);
  return tcb.createAsync(SearchComponent).then((fixture) =&gt; {
    fixture.detectChanges();
    expect(mockSearchService.searchSpy).toHaveBeenCalledWith(&apos;peyton&apos;);
  });
}));
&lt;/pre&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Add a spec for the &lt;code&gt;EditComponent&lt;/code&gt; as well, verifying fetching a single record works. Notice
                how you can access the component directly with
                &lt;code&gt;fixture.debugElement.componentInstance&lt;/code&gt;, or its rendered version with &lt;code&gt;fixture.debugElement.nativeElement&lt;/code&gt;.
                Create a file at &lt;code&gt;src/search/components/edit.component.spec.ts&lt;/code&gt; and populate it with the code
                below.
            &lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
import {
  it,
  describe,
  expect,
  injectAsync,
  beforeEachProviders,
  TestComponentBuilder,
} from &apos;angular2/testing&apos;;

import {MockRouterProvider} from &apos;../../shared/services/mocks/routes&apos;;
import {MockSearchService} from &apos;../../shared/services/mocks/search.service&apos;;

import {EditComponent} from &apos;./edit.component&apos;;

export function main() {
  describe(&apos;Edit component&apos;, () =&gt; {
    var mockSearchService:MockSearchService;
    var mockRouterProvider:MockRouterProvider;

    beforeEachProviders(() =&gt; {
      mockSearchService = new MockSearchService();
      mockRouterProvider = new MockRouterProvider();

      return [
        mockSearchService.getProviders(), mockRouterProvider.getProviders()
      ];
    });

    it(&apos;should fetch a single record&apos;, injectAsync([TestComponentBuilder], (tcb:TestComponentBuilder) =&gt; {
      mockRouterProvider.setRouteParam(&apos;id&apos;, &apos;1&apos;);
      return tcb.createAsync(EditComponent).then((fixture) =&gt; {
        let person = {name: &apos;Emmanuel Sanders&apos;, address: {city: &apos;Denver&apos;}};
        mockSearchService.setResponse(person);

        fixture.detectChanges();
        // verify service was called
        expect(mockSearchService.getByIdSpy).toHaveBeenCalledWith(1);

        // verify data was set on component when initialized
        let editComponent = fixture.debugElement.componentInstance;
        expect(editComponent.editAddress.city).toBe(&apos;Denver&apos;);

        // verify HTML renders as expected
        var compiled = fixture.debugElement.nativeElement;
        expect(compiled.querySelector(&apos;h3&apos;)).toHaveText(&apos;Emmanuel Sanders&apos;);
      });
    }));
  });
}
&lt;/pre&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;You should see &quot;&lt;span style=&quot;color: green&quot;&gt;? 20 tests completed&lt;/span&gt;&quot; in the shell window that&apos;s
                running &lt;code&gt;npm run karma.start&lt;/code&gt;. If you don&apos;t, try cancelling the command and restarting.&lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;integration_test_the_search_ui&quot;&gt;Integration test the search UI&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;To test if the application works end-to-end, you can write tests with &lt;a
                href=&quot;http://angular.github.io/protractor&quot;&gt;Protractor&lt;/a&gt;. These are also known as integration tests,
                since they test the &lt;em&gt;integration&lt;/em&gt; between all layers of your application.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;To verify end-to-end tests work in the project before you begin, run the following commands in three
                different console windows.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre&gt;# npm run webdriver-update &amp;lt;- You will need to run this the first time
npm run webdriver-start
npm run serve.e2e
npm run e2e&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;You should receive an error stating that the &quot;nav text for About&quot; is incorrect.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div id=&quot;protractor-nav-error&quot; class=&quot;imageblock&quot;&gt;
            &lt;div style=&quot;text-align: center&quot;&gt;
                &lt;a href=&quot;https://farm2.staticflickr.com/1614/25706918470_49c4b4fd22_c.jpg&quot;
                   title=&quot;Protractor nav test error&quot; rel=&quot;lightbox[testing-angular2-applications]&quot;
                   data-href=&quot;https://www.flickr.com/photos/mraible/25706918470/in/album-72157666324227025/&quot;&gt;&lt;img
                    src=&quot;https://farm2.staticflickr.com/1614/25706918470_49c4b4fd22_z.jpg&quot; width=&quot;640&quot;
                    alt=&quot;Protractor nav test error&quot;&gt;&lt;/a&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;This happens because we added a Search link to the navbar and didn&apos;t update the test (in &lt;code&gt;app.component.e2e.ts&lt;/code&gt;)
                that looks for the last child. &lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
it(&apos;should have correct nav text for About&apos;, () =&gt; {
    expect(element(by.css(&apos;sd-app sd-navbar nav a:last-child&apos;)).getText()).toEqual(&apos;ABOUT&apos;);
});
&lt;/pre&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Replace this test with the one below, and add a new one to verify the Search link is last.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
it(&apos;should have correct nav text for About&apos;, () =&gt; {
  expect(element(by.css(&apos;sd-app sd-navbar nav a:nth-child(2)&apos;)).getText()).toEqual(&apos;ABOUT&apos;);
});

it(&apos;should have correct nav text for Search&apos;, () =&gt; {
  expect(element(by.css(&apos;sd-app sd-navbar nav a:last-child&apos;)).getText()).toEqual(&apos;SEARCH&apos;);
});
&lt;/pre&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Now when you run &lt;code&gt;npm run e2e&lt;/code&gt;, all specs should pass.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;sect2&quot;&gt;
            &lt;h3 id=&quot;_testing_the_search_feature&quot;&gt;Testing the search feature&lt;/h3&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Create a new &lt;code&gt;search.component.e2e.ts&lt;/code&gt; spec in the same directory as your &lt;code&gt;SearchComponent&lt;/code&gt;.
                    Add tests to verify elements are rendered correctly and
                    search works. At the time of this writing, Protractor&apos;s &lt;code&gt;by.model&lt;/code&gt; and
                    &lt;code&gt;by.repeater&lt;/code&gt; don&apos;t work with Angular 2. For this reason, I used &lt;code&gt;by.css&lt;/code&gt; to
                    verify the HTML renders as expected.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
describe(&apos;Search&apos;, () =&gt; {

  beforeEach(() =&gt; {
    browser.get(&apos;/search&apos;);
  });

  it(&apos;should have an input and search button&apos;, () =&gt; {
    expect(element(by.css(&apos;sd-app sd-search form input&apos;)).isPresent()).toEqual(true);
    expect(element(by.css(&apos;sd-app sd-search form button&apos;)).isPresent()).toEqual(true);
  });

  it(&apos;should allow searching&apos;, () =&gt; {
    let searchButton = element(by.css(&apos;button&apos;));
    let searchBox = element(by.css(&apos;input&apos;));
    searchBox.sendKeys(&apos;M&apos;);
    searchButton.click().then(() =&gt; {
      // doesn&apos;t work as expected - results in 0
      //expect(element.all(by.repeater(&apos;person of searchResults&apos;)).count()).toEqual(3);
      var list = element.all(by.css(&apos;sd-search table tbody tr&apos;));
      expect(list.count()).toBe(3);
    });
  });
});
&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;sect2&quot;&gt;
            &lt;h3 id=&quot;_testing_the_edit_feature&quot;&gt;Testing the edit feature&lt;/h3&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Create a &lt;code&gt;edit.component.e2e.ts&lt;/code&gt; spec to verify the &lt;code&gt;EditComponent&lt;/code&gt; renders a
                    person&apos;s information and that you can update their information.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;
describe(&apos;Edit&apos;, () =&gt; {
 
  beforeEach(() =&gt; {
    browser.get(&apos;/edit/1&apos;);
  });
 
  let name = element(by.id(&apos;name&apos;));
  let street = element(by.id(&apos;street&apos;));
  let city = element(by.id(&apos;city&apos;));
 
  it(&apos;should allow viewing a person&apos;, () =&gt; {
    expect(element(by.css(&apos;h3&apos;)).getText()).toEqual(&apos;Peyton Manning&apos;);
    expect(name.getAttribute(&apos;value&apos;)).toEqual(&apos;Peyton Manning&apos;);
    expect(street.getAttribute(&apos;value&apos;)).toEqual(&apos;1234 Main Street&apos;);
    expect(city.getAttribute(&apos;value&apos;)).toEqual(&apos;Greenwood Village&apos;);
  });
 
  it(&apos;should allow updating a name&apos;, function () {
    let save = element(by.id(&apos;save&apos;));
    // send individual characters since sendKeys passes partial values sometimes
    // https://github.com/angular/protractor/issues/698
    &apos; Won!&apos;.split(&apos;&apos;).forEach((c) =&gt; name.sendKeys(c));
    save.click();
    // verify one element matched this change
    var list = element.all(by.css(&apos;sd-search table tbody tr&apos;));
    expect(list.count()).toBe(1);
  });
});
&lt;/pre&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Run &lt;code&gt;npm run e2e&lt;/code&gt; to verify all your end-to-end tests pass. You should see a success
                    message similar to the one below in your terminal window.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div id=&quot;protractor-success&quot; class=&quot;imageblock&quot;&gt;
                &lt;div style=&quot;text-align: center&quot;&gt;
                    &lt;a href=&quot;https://farm2.staticflickr.com/1558/25706917370_e88e4a6d0f_c.jpg&quot;
                       title=&quot;Protractor success&quot; rel=&quot;lightbox[testing-angular2-applications]&quot;
                       data-href=&quot;https://www.flickr.com/photos/mraible/25706917370/in/album-72157666324227025/&quot;&gt;&lt;img
                        src=&quot;https://farm2.staticflickr.com/1558/25706917370_e88e4a6d0f_z.jpg&quot; width=&quot;640&quot;
                        alt=&quot;Protractor success&quot;&gt;&lt;/a&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;If you made it this far and have all 13 specs passing - congratulations!
                    You&apos;re well on your way to writing quality code with Angular 2 and verifying it works.&lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;source_code&quot;&gt;Source code&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&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/angular2-tutorial&quot;&gt;https://github.com/mraible/angular2-tutorial&lt;/a&gt;.
                If you have ideas for improvements, please leave a comment or send a pull request.
            &lt;/p&gt;
            &lt;p&gt;
                I originally wrote this tutorial in &lt;a href=&quot;http://asciidoctor.org/&quot;&gt;Asciidoctor&lt;/a&gt; because it has a
                slick feature where you
                can include the source code from files rather than copying and pasting. Unfortunately, GitHub
                &lt;a href=&quot;https://github.com/github/markup/issues/172&quot;&gt;doesn&apos;t support includes&lt;/a&gt;. You can
                &lt;a href=&quot;http://gist.asciidoctor.org/?github-mraible%2Fangular2-tutorial%2F%2FTESTING.adoc&quot;&gt;use DocGist
                    to view this tutorial&lt;/a&gt;,
                but &lt;a href=&quot;https://github.com/asciidoctor/docgist/issues/11&quot;&gt;includes don&apos;t work&lt;/a&gt; there either.
            &lt;/p&gt;
            &lt;p&gt;If you&apos;d like to see the Asciidoctor-generated version of this tutorial, you can install the gem,
                checkout the project from GitHub,
                and then run &lt;code&gt;asciidoctor TESTING.adoc&lt;/code&gt; to produce a &lt;code&gt;TESTING.html&lt;/code&gt; file.
            &lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;summary&quot;&gt;Summary&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;I hope you&apos;ve enjoyed this quick-and-easy tutorial on testing Angular 2 applications. You can see the
                test coverage of your project by running
                &lt;code&gt;npm run serve.coverage&lt;/code&gt;. You&apos;ll notice that the new components and service could use some
                additional coverage. I&apos;ll leave that as a task
                for the reader.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div id=&quot;test-coverage&quot; class=&quot;imageblock&quot;&gt;
            &lt;div style=&quot;text-align: center&quot;&gt;
                &lt;a href=&quot;https://farm2.staticflickr.com/1547/25706915830_f764f63717_c.jpg&quot; title=&quot;Test Coverage&quot;
                   rel=&quot;lightbox[testing-angular2-applications]&quot;
                   data-href=&quot;https://www.flickr.com/photos/mraible/25706915830/in/album-72157666324227025/&quot;&gt;&lt;img
                    src=&quot;https://farm2.staticflickr.com/1547/25706915830_f764f63717_z.jpg&quot; width=&quot;640&quot;
                    alt=&quot;Test Coverage&quot;&gt;&lt;/a&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;I learned a lot about testing from &lt;a href=&quot;https://www.ng-book.com/2/&quot;&gt;ng-book 2&lt;/a&gt; and
                its Testing chapter. If you have any Angular 2 testing tips and tricks you&apos;d like to share, I&apos;d love to
                hear about them.&lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/getting_started_with_angular_2</id>
        <title type="html">Getting Started with Angular 2</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/getting_started_with_angular_2"/>
        <published>2016-03-23T09:23:53-06:00</published>
        <updated>2016-06-03T13:19:37-06:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="javascript" scheme="http://roller.apache.org/ns/tags/" />
        <category term="angular2" scheme="http://roller.apache.org/ns/tags/" />
        <category term="typescript" scheme="http://roller.apache.org/ns/tags/" />
        <category term="node" scheme="http://roller.apache.org/ns/tags/" />
        <category term="git" scheme="http://roller.apache.org/ns/tags/" />
        <category term="asciidoctor" scheme="http://roller.apache.org/ns/tags/" />
        <category term="npm" scheme="http://roller.apache.org/ns/tags/" />
        <summary type="html">&lt;p class=&quot;alert alert-info&quot;&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_2_rc1&quot;&gt;Click here&lt;/a&gt; to see an updated version of this tutorial that&apos;s been upgraded for Angular 2.0 RC1.&lt;/p&gt;
&lt;p&gt;I was hired by one of my current clients in November to help them develop a project management
    application with AngularJS. I&apos;m proud to say we&apos;ve built the application, it looks great, and it&apos;s
    scheduled to be released next month. The team had lots of experience with ExtJS, but was new to AngularJS.
    While using AngularJS worked, they&apos;re keen on moving to Angular 2 shortly after it&apos;s released.
&lt;/p&gt;
&lt;p&gt;
    To help them learn Angular 2, I decided to write a couple tutorials similar to the AngularJS tutorials
    I wrote last year. In this tutorial, I did my best to keep the functionality and features similar
    to &lt;a href=&quot;http://raibledesigns.com/rd/entry/getting_started_with_angularjs&quot;&gt;Getting Started with AngularJS&lt;/a&gt;
    so you can compare &lt;a href=&quot;https://github.com/mraible/angular-tutorial&quot;&gt;the code&lt;/a&gt; between the two.
&lt;/p&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;what_you_ll_build&quot;&gt;What you&apos;ll build&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;You&apos;ll build a simple web application with Angular 2 and TypeScript. You&apos;ll add search and edit features with mock data.&lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;what_you_ll_need&quot;&gt;What you&apos;ll need&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;ulist&quot;&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. I recommend using &lt;a href=&quot;https://github.com/creationix/nvm&quot;&gt;nvm&lt;/a&gt;.
                &lt;/li&gt;
            &lt;/ul&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&quot;create_your_project&quot;&gt;Create your project&lt;/h3&gt;
&lt;p&gt;Clone the &lt;a href=&quot;https://github.com/mgechev/angular2-seed&quot;&gt;angular2-seed&lt;/a&gt; repository using git:&lt;/p&gt;
&lt;pre&gt;git clone https://github.com/mgechev/angular2-seed.git angular2-tutorial
cd angular2-tutorial&lt;/pre&gt;</summary>
        <content type="html">&lt;p class=&quot;alert alert-info&quot;&gt;&lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angular_2_rc1&quot;&gt;Click here&lt;/a&gt; to see an updated version of this tutorial that&apos;s been upgraded for Angular 2.0 RC1.&lt;/p&gt;
&lt;p&gt;I was hired by one of my current clients in November to help them develop a project management
    application with AngularJS. I&apos;m proud to say we&apos;ve built the application, it looks great, and it&apos;s
    scheduled to be released next month. The team had lots of experience with ExtJS, but was new to AngularJS.
    While using AngularJS worked, they&apos;re keen on moving to Angular 2 shortly after it&apos;s released.
&lt;/p&gt;
&lt;p&gt;
    To help them learn Angular 2, I decided to write a couple tutorials similar to the AngularJS tutorials
    I wrote last year. In this tutorial, I did my best to keep the functionality and features similar
    to &lt;a href=&quot;http://raibledesigns.com/rd/entry/getting_started_with_angularjs&quot;&gt;Getting Started with AngularJS&lt;/a&gt;
    so you can compare &lt;a href=&quot;https://github.com/mraible/angular-tutorial&quot;&gt;the code&lt;/a&gt; between the two.
&lt;/p&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;what_you_ll_build&quot;&gt;What you&apos;ll build&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;You&apos;ll build a simple web application with Angular 2 and TypeScript. You&apos;ll add search and edit features with mock data.&lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;what_you_ll_need&quot;&gt;What you&apos;ll need&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;ulist&quot;&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. I recommend using &lt;a href=&quot;https://github.com/creationix/nvm&quot;&gt;nvm&lt;/a&gt;.
                &lt;/li&gt;
            &lt;/ul&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;create_your_project&quot;&gt;Create your project&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Clone the &lt;a href=&quot;https://github.com/mgechev/angular2-seed&quot;&gt;angular2-seed&lt;/a&gt; repository using git:&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre&gt;git clone https://github.com/mgechev/angular2-seed.git angular2-tutorial
cd angular2-tutorial&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;p&gt;
            NOTE: The angular2-seed project requires node v4.x.x or higher and npm 2.14.7. I used node v4.2.6 and npm 3.6.0.
        &lt;/p&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Install &lt;code&gt;ts-node&lt;/code&gt; for TypeScript:&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
                &lt;pre&gt;npm install -g ts-node&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;Install the project&apos;s dependencies:&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
                &lt;pre&gt;npm install&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;run_the_application&quot;&gt;Run the application&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;The project is configured with a simple web server for development. To start it, run:&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
                &lt;pre&gt;npm start&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;You should see a screen like the one below at &lt;a href=&quot;http://localhost:5555&quot;&gt;http://localhost:5555&lt;/a&gt;.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div id=&quot;default-homepage&quot; class=&quot;imageblock&quot;&gt;
            &lt;div style=&quot;text-align: center&quot;&gt;
                &lt;a href=&quot;https://farm2.staticflickr.com/1560/25886693062_4dd41acd3d_c.jpg&quot; title=&quot;Default homepage&quot; rel=&quot;lightbox[getting-started-with-angular2]&quot; data-href=&quot;https://www.flickr.com/photos/mraible/25886693062/in/datetaken-public/&quot;&gt;&lt;img src=&quot;https://farm2.staticflickr.com/1560/25886693062_4dd41acd3d_z.jpg&quot; width=&quot;640&quot; alt=&quot;Default homepage&quot;&gt;&lt;/a&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;You can see your new project&apos;s test coverage by running &lt;code&gt;npm test&lt;/code&gt;:&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;listingblock&quot;&gt;
            &lt;div class=&quot;content&quot;&gt;
&lt;pre&gt;=============================== Coverage summary ===============================
Statements : 86.11% ( 93/108 )
Branches : 48.28% ( 70/145 )
Functions : 100% ( 25/25 )
Lines : 94.32% ( 83/88 )
================================================================================&lt;/pre&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;add_a_search_feature&quot;&gt;Add a search feature&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&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 angular2-seed to.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;sect2&quot;&gt;
            &lt;h3 id=&quot;the_basics&quot;&gt;The Basics&lt;/h3&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Create a file at &lt;code&gt;src/search/components/search.component.html&lt;/code&gt; with the following HTML:&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;&amp;lt;h2&amp;gt;Search&amp;lt;/h2&amp;gt;
&amp;lt;form&amp;gt;
  &amp;lt;input type=&quot;search&quot; [(ngModel)]=&quot;query&quot; (keyup.enter)=&quot;search()&quot;&amp;gt;
  &amp;lt;button type=&quot;button&quot; (click)=&quot;search()&quot;&amp;gt;Search&amp;lt;/button&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;div *ngIf=&quot;loading&quot;&amp;gt;loading...&amp;lt;/div&amp;gt;
&amp;lt;pre&amp;gt;{{searchResults | json}}&amp;lt;/pre&amp;gt;
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Create &lt;code&gt;src/search/components/search.component.ts&lt;/code&gt; to define the &lt;code&gt;SearchComponent&lt;/code&gt; and point to this template.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;import {Component} from &apos;angular2/core&apos;;
import {CORE_DIRECTIVES, FORM_DIRECTIVES} from &apos;angular2/common&apos;;
import {ROUTER_DIRECTIVES} from &apos;angular2/router&apos;;

@Component({
  selector: &apos;sd-search&apos;,
  moduleId: module.id,
  templateUrl: &apos;./search.component.html&apos;,
  directives: [FORM_DIRECTIVES, CORE_DIRECTIVES, ROUTER_DIRECTIVES]
})
export class SearchComponent {
  loading: boolean;
  query: string;
  searchResults: any;

  constructor() {
    console.log(&apos;initialized search component&apos;);
  }
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Update &lt;code&gt;src/app/components/app.component.ts&lt;/code&gt; to import this component and include its route.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;import {SearchComponent} from &apos;../../search/components/search.component&apos;;

@RouteConfig([
  { path: &apos;/&apos;,      name: &apos;Home&apos;,  component: HomeComponent  },
  { path: &apos;/about&apos;, name: &apos;About&apos;, component: AboutComponent },
  { path: &apos;/search&apos;, name: &apos;Search&apos;, component: SearchComponent }
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Your browser should refresh automatically, thanks to &lt;a href=&quot;http://browsersync.io&quot;&gt;Browsersync&lt;/a&gt;. Navigate to
                    &lt;a href=&quot;http://localhost:5555/search&quot;&gt;http://localhost:5555/search&lt;/a&gt; and you should see the search component.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div id=&quot;search-component&quot; class=&quot;imageblock&quot;&gt;
                &lt;div style=&quot;text-align: center&quot;&gt;
                    &lt;a href=&quot;https://farm2.staticflickr.com/1552/25374766404_b0830ff897_c.jpg&quot; title=&quot;Search component&quot; rel=&quot;lightbox[getting-started-with-angular2]&quot; data-href=&quot;https://www.flickr.com/photos/mraible/25374766404/in/datetaken-public/&quot;&gt;&lt;img src=&quot;https://farm2.staticflickr.com/1552/25374766404_b0830ff897_z.jpg&quot; width=&quot;640&quot; alt=&quot;Search component&quot;&gt;&lt;/a&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;You can see it needs a bit of styling. Angular 2 allows you to provide styles specific for your component using a &lt;code&gt;styleUrls&lt;/code&gt;
                    property on your component.
                    Add this property to &lt;code&gt;search.component.ts&lt;/code&gt; you see below.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;templateUrl: &apos;./search.component.html&apos;,
styleUrls: [&apos;./search.component.css&apos;],
directives: [FORM_DIRECTIVES, CORE_DIRECTIVES, ROUTER_DIRECTIVES]
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Create &lt;code&gt;src/search/components/search.component.css&lt;/code&gt; and add some CSS.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: css&quot;&gt;:host {
  display: block;
  padding: 0 16px;
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;There, that looks better!&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;imageblock&quot;&gt;
                &lt;div style=&quot;text-align: center&quot;&gt;
                    &lt;a href=&quot;https://farm2.staticflickr.com/1563/25886691122_c209b90e33_c.jpg&quot; title=&quot;Search component with styling&quot; rel=&quot;lightbox[getting-started-with-angular2]&quot; data-href=&quot;https://www.flickr.com/photos/mraible/25886691122/in/datetaken-public/&quot;&gt;&lt;img src=&quot;https://farm2.staticflickr.com/1563/25886691122_c209b90e33_z.jpg&quot; width=&quot;640&quot; alt=&quot;Search component with styling&quot;&gt;&lt;/a&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Finally, update &lt;code&gt;src/app/components/navbar.component.html&lt;/code&gt; to include a link to the search route.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;&amp;lt;nav&amp;gt;
  &amp;lt;a [routerLink]=&quot;[&apos;Home&apos;]&quot;&amp;gt;HOME&amp;lt;/a&amp;gt;
  &amp;lt;a [routerLink]=&quot;[&apos;About&apos;]&quot;&amp;gt;ABOUT&amp;lt;/a&amp;gt;
  &amp;lt;a [routerLink]=&quot;[&apos;Search&apos;]&quot;&amp;gt;SEARCH&amp;lt;/a&amp;gt;
&amp;lt;/nav&amp;gt;
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;This section has shown you how to add a new component to a basic Angular 2 application.
                    The next section shows you how to create a use a JSON file and &lt;code&gt;localStorage&lt;/code&gt; to create a fake API.&lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;sect2&quot;&gt;
            &lt;h3 id=&quot;the_backend&quot;&gt;The Backend&lt;/h3&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;To get search results, create a &lt;code&gt;SearchService&lt;/code&gt; that makes HTTP requests to a JSON file. Start
                    by creating &lt;code&gt;src/shared/data/people.json&lt;/code&gt; to hold your data.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;[
  {
    &quot;id&quot;: 1,
    &quot;name&quot;: &quot;Peyton Manning&quot;,
    &quot;phone&quot;: &quot;(303) 567-8910&quot;,
    &quot;address&quot;: {
      &quot;street&quot;: &quot;1234 Main Street&quot;,
      &quot;city&quot;: &quot;Greenwood Village&quot;,
      &quot;state&quot;: &quot;CO&quot;,
      &quot;zip&quot;: &quot;80111&quot;
    }
  },
  {
    &quot;id&quot;: 2,
    &quot;name&quot;: &quot;Demaryius Thomas&quot;,
    &quot;phone&quot;: &quot;(720) 213-9876&quot;,
    &quot;address&quot;: {
      &quot;street&quot;: &quot;5555 Marion Street&quot;,
      &quot;city&quot;: &quot;Denver&quot;,
      &quot;state&quot;: &quot;CO&quot;,
      &quot;zip&quot;: &quot;80202&quot;
    }
  },
  {
    &quot;id&quot;: 3,
    &quot;name&quot;: &quot;Von Miller&quot;,
    &quot;phone&quot;: &quot;(917) 323-2333&quot;,
    &quot;address&quot;: {
      &quot;street&quot;: &quot;14 Mountain Way&quot;,
      &quot;city&quot;: &quot;Vail&quot;,
      &quot;state&quot;: &quot;CO&quot;,
      &quot;zip&quot;: &quot;81657&quot;
    }
  }
]
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Create &lt;code&gt;src/shared/services/search.service.ts&lt;/code&gt; and provide &lt;code&gt;Http&lt;/code&gt; as a dependency in its constructor.
                    In this same file, define the &lt;code&gt;Address&lt;/code&gt; and &lt;code&gt;Person&lt;/code&gt; classes that JSON will be marshalled to.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;import {Injectable} from &apos;angular2/core&apos;;
import {Http, Response} from &apos;angular2/http&apos;;

@Injectable()
export class SearchService {
  constructor(private http:Http) {}

  getAll() {
    return this.http.get(&apos;shared/data/people.json&apos;).map((res:Response) =&amp;gt; res.json());
  }
}

export class Address {
  street:string;
  city:string;
  state:string;
  zip:string;

  constructor(obj?:any) {
    this.street = obj &amp;amp;&amp;amp; obj.street || null;
    this.city = obj &amp;amp;&amp;amp; obj.city || null;
    this.state = obj &amp;amp;&amp;amp; obj.state || null;
    this.zip = obj &amp;amp;&amp;amp; obj.zip || null;
  }
}

export class Person {
  id:number;
  name:string;
  phone:string;
  address:Address;

  constructor(obj?:any) {
    this.id = obj &amp;amp;&amp;amp; Number(obj.id) || null;
    this.name = obj &amp;amp;&amp;amp; obj.name || null;
    this.phone = obj &amp;amp;&amp;amp; obj.phone || null;
    this.address = obj &amp;amp;&amp;amp; obj.address || null;
  }
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;In &lt;code&gt;search.component.ts&lt;/code&gt;, add imports for these classes.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;import {Person, SearchService} from &apos;../../shared/services/search.service&apos;;
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;You can now add a type to the searchResults variable. While you&apos;re there, modify the constructor to inject the
                    &lt;code&gt;SearchService&lt;/code&gt;.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;searchResults: Array&amp;lt;Person&amp;gt;;

constructor(public searchService: SearchService) {}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Then implement the &lt;code&gt;search()&lt;/code&gt; method to call the service&apos;s &lt;code&gt;getAll()&lt;/code&gt; method.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;search(): void {
  this.searchService.getAll().subscribe(
    data =&amp;gt; {this.searchResults = data;},
    error =&amp;gt; console.log(error)
  );
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;At this point, you&apos;ll likely see the following message in your browser&apos;s console.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
                    &lt;pre&gt;EXCEPTION: No provider for SearchService! (SearchComponent -&amp;gt; SearchService)&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;This happens because the app hasn&apos;t provided this service to components. To fix this, modify
                    &lt;code&gt;app.component.ts&lt;/code&gt; to import this component and add the service to the list of providers.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;import {NameListService} from &apos;../../shared/services/name-list.service&apos;;
import {SearchService} from &apos;../../shared/services/search.service&apos;;

@Component({
  selector: &apos;sd-app&apos;,
  viewProviders: [NameListService, SearchService],
  moduleId: module.id,
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Next, you&apos;ll likely get an error about the &lt;code&gt;Http&lt;/code&gt; dependency in &lt;code&gt;SearchService&lt;/code&gt;.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
                    &lt;pre&gt;EXCEPTION: No provider for Http! (SearchComponent -&amp;gt; SearchService -&amp;gt; Http)&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;To solve this problem, modify &lt;code&gt;src/main.ts&lt;/code&gt; to import the &lt;code&gt;Http&lt;/code&gt; service and make it
                    available to the app.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;import {HTTP_PROVIDERS} from &apos;angular2/http&apos;;

bootstrap(AppComponent, [
  HTTP_PROVIDERS, ROUTER_PROVIDERS,
  provide(APP_BASE_HREF, { useValue: &apos;&amp;lt;%= APP_BASE %&amp;gt;&apos; })
]);
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Now the page will load without errors. However, when you click on the button, you&apos;ll see the following error.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
                    &lt;pre&gt;ORIGINAL EXCEPTION: TypeError: this.http.get(...).map is not a function&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;I was stuck here for quite some time when I first encountered this issue. I was able to solve it
                    with a simple import in &lt;code&gt;main.ts&lt;/code&gt;.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
                    &lt;pre class=&quot;brush: js&quot;&gt;import &apos;rxjs/add/operator/map&apos;;&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Now clicking the search button should work. To make the results look better,
                    remove the &lt;code&gt;&amp;lt;pre&amp;gt;&lt;/code&gt; tag in &lt;code&gt;search.component.html&lt;/code&gt;
                    and replace it with a &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt;.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;&amp;lt;table *ngIf=&quot;searchResults&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 *ngFor=&quot;#person of searchResults; #i=index&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;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Then add some additional CSS for this component in &lt;code&gt;search.component.css&lt;/code&gt;.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: css&quot;&gt;table {
  margin-top: 10px;
  border-collapse: collapse;
}

th {
  text-align: left;
  border-bottom: 2px solid #ddd;
  padding: 8px;
}

td {
  border-top: 1px solid #ddd;
  padding: 8px;
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Now the search results look better.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div id=&quot;search-results&quot; class=&quot;imageblock&quot;&gt;
                &lt;div style=&quot;text-align: center&quot;&gt;
                    &lt;a href=&quot;https://farm2.staticflickr.com/1677/25706917270_bf07c541e4_c.jpg&quot; title=&quot;Search results&quot; rel=&quot;lightbox[getting-started-with-angular2]&quot; data-href=&quot;https://www.flickr.com/photos/mraible/25706917270/in/datetaken-public/&quot;&gt;&lt;img src=&quot;https://farm2.staticflickr.com/1677/25706917270_bf07c541e4_z.jpg&quot; width=&quot;640&quot; alt=&quot;Search results&quot;&gt;&lt;/a&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;But wait, we still don&apos;t have search functionality! To add a search feature, add a &lt;code&gt;search()&lt;/code&gt; method to
                    &lt;code&gt;search.service.ts&lt;/code&gt;.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;search(q:string) {
  if (!q || q === &apos;*&apos;) {
    q = &apos;&apos;;
  } else {
    q = q.toLowerCase();
  }
  return this.getAll().map(data =&amp;gt; {
    let results = [];
    data.map(item =&amp;gt; {
      if (JSON.stringify(item).toLowerCase().includes(q)) {
        results.push(item);
      }
    });
    return results;
  });
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Then refactor &lt;code&gt;SearchComponent&lt;/code&gt; to call this method with its &lt;code&gt;query&lt;/code&gt; variable.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;search(): void {
  this.searchService.search(this.query).subscribe(
    data =&amp;gt; {this.searchResults = data;},
    error =&amp;gt; console.log(error)
  );
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Now search results will be filtered by the query value you type in.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;This section showed 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;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;sect2&quot;&gt;
            &lt;h3 id=&quot;add_an_edit_feature&quot;&gt;Add an edit feature&lt;/h3&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Modify &lt;code&gt;search.component.html&lt;/code&gt; to add a link for editing a person.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;&amp;lt;td&amp;gt;&amp;lt;a [routerLink]=&quot;[&apos;Edit&apos;, { id: person.id }]&quot;&amp;gt;{{person.name}}&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Create &lt;code&gt;src/search/components/edit.component.html&lt;/code&gt; to display an editable form. You might notice I&apos;ve added &lt;code&gt;id&lt;/code&gt;
                    attributes to most elements. This is to
                    make things easier when writing integration tests with Protractor.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;&amp;lt;div *ngIf=&quot;person&quot;&amp;gt;
  &amp;lt;h3&amp;gt;{{editName}}&amp;lt;/h3&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;label&amp;gt;Id:&amp;lt;/label&amp;gt;
    {{person.id}}
  &amp;lt;/div&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;label&amp;gt;Name:&amp;lt;/label&amp;gt;
    &amp;lt;input [(ngModel)]=&quot;editName&quot; id=&quot;name&quot; placeholder=&quot;name&quot;/&amp;gt;
  &amp;lt;/div&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;label&amp;gt;Phone:&amp;lt;/label&amp;gt;
    &amp;lt;input [(ngModel)]=&quot;editPhone&quot; id=&quot;phone&quot; placeholder=&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&amp;gt;
      &amp;lt;input [(ngModel)]=&quot;editAddress.street&quot; id=&quot;street&quot;&amp;gt;&amp;lt;br/&amp;gt;
      &amp;lt;input [(ngModel)]=&quot;editAddress.city&quot; id=&quot;city&quot;&amp;gt;,
      &amp;lt;input [(ngModel)]=&quot;editAddress.state&quot; id=&quot;state&quot; size=&quot;2&quot;&amp;gt;
      &amp;lt;input [(ngModel)]=&quot;editAddress.zip&quot; id=&quot;zip&quot; size=&quot;5&quot;&amp;gt;
    &amp;lt;/address&amp;gt;
  &amp;lt;/fieldset&amp;gt;
  &amp;lt;button (click)=&quot;save()&quot; id=&quot;save&quot;&amp;gt;Save&amp;lt;/button&amp;gt;
  &amp;lt;button (click)=&quot;cancel()&quot; id=&quot;cancel&quot;&amp;gt;Cancel&amp;lt;/button&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Create an &lt;code&gt;EditComponent&lt;/code&gt; in &lt;code&gt;src/search/components/edit.component.ts&lt;/code&gt; that references this template and handles communication with the &lt;code&gt;SearchService&lt;/code&gt;.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;import {Component, OnInit} from &apos;angular2/core&apos;;
import {Person, Address, SearchService} from &apos;../../shared/services/search.service&apos;;
import {RouteParams, Router} from &apos;angular2/router&apos;;
import {CanDeactivate, ComponentInstruction} from &apos;angular2/router&apos;;

@Component({
  selector: &apos;sd-edit&apos;,
  moduleId: module.id,
  templateUrl: &apos;./edit.component.html&apos;,
  styleUrls: [&apos;./edit.component.css&apos;]
})
export class EditComponent implements OnInit, CanDeactivate {

  person: Person;
  editName: string;
  editPhone: string;
  editAddress: Address;

  constructor(
    private _service: SearchService,
    private _router: Router,
    private _routeParams: RouteParams
  ) { }

  ngOnInit() {
    let id = +this._routeParams.get(&apos;id&apos;);
    this._service.get(id).subscribe(person =&amp;gt; {
      if (person) {
        this.editName = person.name;
        this.editPhone = person.phone;
        this.editAddress = person.address;
        this.person = person;
      } else {
        this.gotoList();
      }
    });
  }

  routerCanDeactivate(next: ComponentInstruction, prev: ComponentInstruction): any {
    if (!this.person || this.person.name === this.editName || this.person.phone === this.editPhone
      || this.person.address === this.editAddress) {
      return true;
    }

    return new Promise&amp;lt;boolean&amp;gt;((resolve, reject) =&amp;gt; resolve(window.confirm(&apos;Discard changes?&apos;)));
  }

  cancel() {
    this._router.navigate([&apos;Search&apos;]);
  }

  save() {
    this.person.name = this.editName;
    this.person.phone = this.editPhone;
    this.person.address = this.editAddress;
    this._service.save(this.person);
    this.gotoList();
  }

  gotoList() {
    if (this.person) {
      this._router.navigate([&apos;Search&apos;, { term: this.person.name }]);
    } else {
      this._router.navigate([&apos;Search&apos;]);
    }
  }
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Modify &lt;code&gt;SearchService&lt;/code&gt; to contain functions for finding a person by their id, and saving them. While you&apos;re in there, modify
                    the &lt;code&gt;search()&lt;/code&gt; method to
                    be aware of updated objects in &lt;code&gt;localStorage&lt;/code&gt;.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;search(q:string) {
  if (!q || q === &apos;*&apos;) {
    q = &apos;&apos;;
  } else {
    q = q.toLowerCase();
  }
  return this.getAll().map(data =&amp;gt; {
    let results = [];
    data.map(item =&amp;gt; {
      // check for item in localStorage
      if (localStorage[&apos;person&apos; + item.id]) {
        item = JSON.parse(localStorage[&apos;person&apos; + item.id]);
      }
      if (JSON.stringify(item).toLowerCase().includes(q)) {
        results.push(item);
      }
    });
    return results;
  });
}

get(id: number) {
  return this.getAll().map(all =&amp;gt; {
    if (localStorage[&apos;person&apos; + id]) {
      return JSON.parse(localStorage[&apos;person&apos; + id]);
    }
    return all.find(e =&amp;gt; e.id === id);
  });
}

save(person: Person) {
  localStorage[&apos;person&apos; + person.id] = JSON.stringify(person);
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;To make the app are of this new component, add an import and route configuration in &lt;code&gt;app.component.ts&lt;/code&gt;.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;import {EditComponent} from &apos;../../search/components/edit.component&apos;;

@RouteConfig([
  { path: &apos;/&apos;,      name: &apos;Home&apos;,  component: HomeComponent  },
  { path: &apos;/about&apos;, name: &apos;About&apos;, component: AboutComponent },
  { path: &apos;/search&apos;, name: &apos;Search&apos;, component: SearchComponent },
  { path: &apos;/edit/:id&apos;, name: &apos;Edit&apos;, component: EditComponent }
])
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Then create &lt;code&gt;src/search/components/edit.component.css&lt;/code&gt; to make the form look a bit better.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: css&quot;&gt;:host {
  display: block;
  padding: 0 16px;
}

button {
  margin-top: 10px;
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;At this point, you should be able to search for a person and update their information.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div id=&quot;edit-form&quot; class=&quot;imageblock&quot;&gt;
                &lt;div style=&quot;text-align: center&quot;&gt;
                    &lt;a href=&quot;https://farm2.staticflickr.com/1603/25886692692_42abb78ef6_c.jpg&quot; title=&quot;Edit component&quot; rel=&quot;lightbox[getting-started-with-angular2]&quot; data-href=&quot;https://www.flickr.com/photos/mraible/25886692692/in/datetaken-public/&quot;&gt;&lt;img src=&quot;https://farm2.staticflickr.com/1603/25886692692_42abb78ef6_z.jpg&quot; width=&quot;640&quot; alt=&quot;Edit component&quot;&gt;&lt;/a&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;The &amp;lt;form&amp;gt; in &lt;code&gt;src/search/components/edit.component.html&lt;/code&gt; calls a &lt;code&gt;save()&lt;/code&gt; function to update a person&apos;s
                    data. You already implemented this above.
                    The function calls a &lt;code&gt;gotoList()&lt;/code&gt; function that appends the person&apos;s name to the URL when sending the user back to the
                    search screen.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;gotoList() {
  if (this.person) {
    this._router.navigate([&apos;Search&apos;, { term: this.person.name }]);
  } else {
    this._router.navigate([&apos;Search&apos;]);
  }
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Since the &lt;code&gt;SearchComponent&lt;/code&gt; doesn&apos;t execute a search automatically when you execute this URL, add the following logic to do
                    so in its constructor.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
&lt;pre class=&quot;brush: js&quot;&gt;constructor(public searchService: SearchService, params: RouteParams) {
  if (params.get(&apos;term&apos;)) {
    this.query = decodeURIComponent(params.get(&apos;term&apos;));
    this.search();
  }
}
&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;You&apos;ll need to import &lt;code&gt;RouteParams&lt;/code&gt; in order for everything to compile.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;listingblock&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;
                    &lt;pre class=&quot;brush: js&quot;&gt;import {ROUTER_DIRECTIVES, RouteParams} from &apos;angular2/router&apos;;&lt;/pre&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;After making all these changes, you should be able to search/edit/update a person&apos;s information. If it works - nice job!&lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;source_code&quot;&gt;Source code&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&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/angular2-tutorial&quot;&gt;https://github.com/mraible/angular2-tutorial&lt;/a&gt;.
                  If you have ideas for improvements, please leave a comment or send a pull request. 
            &lt;/p&gt;
            &lt;p&gt;
                I originally wrote this tutorial in &lt;a href=&quot;http://asciidoctor.org/&quot;&gt;Asciidoctor&lt;/a&gt; because it has a slick feature where you
                can include the source code from files rather than copying and pasting. Unfortunately, GitHub
                &lt;a href=&quot;https://github.com/github/markup/issues/172&quot;&gt;doesn&apos;t support includes&lt;/a&gt;. You can
                &lt;a href=&quot;http://gist.asciidoctor.org/?github-mraible%2Fangular2-tutorial%2F%2FREADME.adoc&quot;&gt;use DocGist to view this tutorial&lt;/a&gt;,
                but &lt;a href=&quot;https://github.com/asciidoctor/docgist/issues/11&quot;&gt;includes don&apos;t work&lt;/a&gt; there either.
            &lt;/p&gt;
            &lt;p&gt;If you&apos;d like to see the Asciidoctor-generated version of this tutorial, you can install the gem, checkout the project from GitHub,
                and then run &lt;code&gt;asciidoctor README.adoc&lt;/code&gt; to produce a &lt;code&gt;README.html&lt;/code&gt; file.
            &lt;/p&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;sect1&quot;&gt;
    &lt;h3 id=&quot;summary&quot;&gt;Summary&lt;/h3&gt;
    &lt;div class=&quot;sectionbody&quot;&gt;
        &lt;div class=&quot;paragraph&quot;&gt;
            &lt;p&gt;I hope you&apos;ve enjoyed this quick-and-easy tutorial on how to get started with Angular 2. In a future tutorial,
                I&apos;ll show you &lt;a href=&quot;//raibledesigns.com/rd/entry/testing_angular_2_applications&quot;&gt;how to write unit tests and integration tests for this application&lt;/a&gt;. I&apos;ve also started looking into creating an ES6 version of this tutorial using So&#243;s G&#225;bor&apos;s &lt;a href=&quot;https://github.com/blacksonic/angular2-es6-starter&quot;&gt;angular2-es6-starter&lt;/a&gt;. If you know of a better starter for Angular 2 and ES6, please let me know.&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;sect2&quot;&gt;
            &lt;h3 id=&quot;resources&quot;&gt;Resources&lt;/h3&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;I used a number of resources while creating this application. &lt;a href=&quot;https://www.ng-book.com/2/&quot;&gt;ng-book 2&lt;/a&gt; was an invaluable resource
                    and I highly recommend it if you&apos;re learning Angular 2. I found Chariot Solution&apos;s article on
                    &lt;a href=&quot;http://chariotsolutions.com/blog/post/angular2-observables-http-separating-services-components/&quot;&gt;Angular2 Observables, Http, and
                        separating services and components&lt;/a&gt;
                    to be quite helpful. Finally, the &lt;a href=&quot;https://github.com/angular/angular-cli&quot;&gt;angular-cli&lt;/a&gt; project was a big help, especially its
                    &lt;code&gt;ng generate route &amp;lt;object&amp;gt;&lt;/code&gt; feature.&lt;/p&gt;
            &lt;/div&gt;
            &lt;div class=&quot;paragraph&quot;&gt;
                &lt;p&gt;Kudos to all the pioneers in Angular 2 land that&apos;ve been using it and writing about it on blogs and Stack Overflow.
                    Getting started with Angular 2 would&apos;ve been a real pain without your trailblazing.&lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/how_do_i_become_a</id>
        <title type="html">How do I become a programmer?</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/how_do_i_become_a"/>
        <published>2015-08-13T08:32:43-06:00</published>
        <updated>2015-08-13T14:37:48-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="learning" scheme="http://roller.apache.org/ns/tags/" />
        <category term="computerscience" scheme="http://roller.apache.org/ns/tags/" />
        <category term="job" scheme="http://roller.apache.org/ns/tags/" />
        <category term="programmer" scheme="http://roller.apache.org/ns/tags/" />
        <category term="computers" scheme="http://roller.apache.org/ns/tags/" />
        <category term="career" scheme="http://roller.apache.org/ns/tags/" />
        <category term="developer" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">&lt;p&gt;Yesterday, I received a message from a friend, asking about how to become a programmer. It&apos;s not the first time I&apos;ve been asked this. In fact, this summer I&apos;ve been asked by several friends how to get into the field. It seems that as people grow older, they see the lifestyle of working remotely and enjoying their job as an attractive thing to do. In yesterday&apos;s case, this friend is a mom that now has her days free because all her kids are in school. Here&apos;s what she wrote:
&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
Now that my girls are both in school full day, I&apos;ve been thinking about taking some programming classes. It&apos;s something I started to do while I was working at [ABC Company], but obviously didn&apos;t pursue once I quit to have kids. I&apos;m thinking of getting my MIS in web development or specializing in designing apps if that&apos;s even a thing? Anyway, what languages would you recommend I concentrate on? JavaScript, Python? Lastly, is there a particular school you would recommend? I can&apos;t afford DU on my stay-at-home-mom salary, or even Regis which is where I started when I was getting tuition reimbursement. I was hoping I could do most of my education online while the kids are in school? Any advice or words of wisdom would be greatly appreciated! &lt;/p&gt;
&lt;p&gt;Since this is a common question I see, I figured I&apos;d publish my answers here, and get some advice from y&apos;all too. Here&apos;s my response:&lt;/p&gt;
&lt;div class=&quot;quote&quot;&gt;
&lt;p style=&quot;margin-top: 0&quot;&gt;
Python would definitely be good, as would JavaScript. JavaScript can be done on the client and server these days, so you could do that and be able to do front-end and backend development. 
&lt;/p&gt;
&lt;p&gt;
For programming specifically, I&apos;ve heard these guys have a good JavaScript course: &lt;a href=&quot;https://www.codecademy.com/&quot;&gt;https://www.codecademy.com&lt;/a&gt;. Here&apos;s how to get started with Python in eight weeks: &lt;a href=&quot;http://lifehacker.com/how-i-taught-myself-to-code-in-eight-weeks-511615189&quot;&gt;http://lifehacker.com/how-i-taught-myself-to-code-in-eight-weeks-511615189&lt;/a&gt;. And one of my favorites: &lt;a href=&quot;http://programming-motherfucker.com/become.html&quot;&gt;http://programming-motherfucker.com/become.html&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
I&apos;ve taken a Scala course from Coursera, it was hard and intense, but I learned a lot. They have lots of courses and give you certifications you can put on your LinkedIn profile: &lt;a href=&quot;https://www.coursera.org/&quot;&gt;https://www.coursera.org&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
I&apos;ve also recommended &lt;a href=&quot;https://teamtreehouse.com&quot;&gt;https://teamtreehouse.com&lt;/a&gt; to folks and &lt;a href=&quot;https://www.khanacademy.org/&quot;&gt;https://www.khanacademy.org&lt;/a&gt; has always been good, even for kids.&lt;/p&gt;
&lt;p style=&quot;margin-bottom: 0&quot;&gt;
Ultimately, the best way to learn to code is by doing. It&apos;s definitely good to study, learn and practice, but it&apos;ll probably won&apos;t sink in and become real knowledge until you&apos;re getting paid to do it. With the plethora of high-priced programmers out there, you can likely find a junior position, show a willingness to learn and come up to speed quickly. If you can couple that with a remote position, I think you&apos;ll really enjoy yourself.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Her response was interesting, as she thought she might need a &lt;abbr title=&quot;Computer Science&quot;&gt;CS&lt;/abbr&gt; degree to even get a programming job.&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
Coincidentally I looked over many of these coding sites yesterday but wasn&apos;t sure if I needed an accredited diploma. It sounds like it&apos;s more important that I just get some experience.&lt;/p&gt;
&lt;p&gt;From my experience, a college degree matters, but not a CS degree. I told her people skills make programmers stand out and she&apos;s a witty person that certainly has those. What&apos;s your advice as a programmer? What would you tell people to do if they want to break into the field?&lt;/p&gt;
&lt;p&gt;More importantly, if you&apos;re on the hiring side, what would it take for you to hire a 40-something person with no programming background? If they&apos;ve been studying for six months and have really good people skills, would you hire them for a junior position?&lt;/p&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/uberconf_2015_my_presentations_on</id>
        <title type="html">UberConf 2015: My Presentations on Apache Camel and Java Webapp Security</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/uberconf_2015_my_presentations_on"/>
        <published>2015-07-27T08:08:48-06:00</published>
        <updated>2015-07-27T14:08:48-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="apacheshiro" 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="apachecamel" scheme="http://roller.apache.org/ns/tags/" />
        <category term="springsecurity" scheme="http://roller.apache.org/ns/tags/" />
        <category term="webapp" scheme="http://roller.apache.org/ns/tags/" />
        <category term="java" scheme="http://roller.apache.org/ns/tags/" />
        <category term="uberconf" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">Last week I had the pleasure of speaking at &lt;a href=&quot;http://uberconf.com/conference/denver/2015/07/home&quot;&gt;UberConf 2015&lt;/a&gt;. My first talk was on &lt;a href=&quot;http://uberconf.com/conference/denver/2015/07/session?id=33782&quot;&gt;Developing, Testing and Scaling with Apache Camel&lt;/a&gt;. This presentation contained an intro to &lt;a href=&quot;http://camel.apache.org&quot;&gt;Apache Camel&lt;/a&gt; and a recap of my experience &lt;a href=&quot;//raibledesigns.com/rd/entry/developing_services_with_apache_camel3&quot;&gt;using it at a client last year&lt;/a&gt;. You can click through the presentation below, download it from &lt;a href=&quot;//raibledesigns.com/rd/page/publications&quot;&gt;my presentations page&lt;/a&gt;, or &lt;a href=&quot;http://www.slideshare.net/mraible/developing-testing-and-scaling-with-apache-camel-uberconf-2015&quot;&gt;view it on SlideShare&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/key/k1ZZ5oFKjjH7bI&quot; width=&quot;512&quot; height=&quot;325&quot; frameborder=&quot;0&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; scrolling=&quot;no&quot; style=&quot;border-top: 1px solid black; border-bottom: 1px solid black&quot; allowfullscreen&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;My second presentation was about implementing &lt;a href=&quot;http://uberconf.com/conference/denver/2015/07/session?id=33746&quot;&gt;Java Web Application Security with Java EE, Spring Security and Apache Shiro&lt;/a&gt;. I updated this presentation to use Java EE 7 and Jersey, as well as Spring Boot. I used Spring Boot to manage dependencies in all three projects, then showed the slick &lt;a href=&quot;http://docs.spring.io/spring-boot/docs/1.2.5.RELEASE/reference/htmlsingle/#boot-features-security&quot;&gt;out-of-the-box security Spring Boot has&lt;/a&gt; (when you include the Spring Security on the classpath). For &lt;a href=&quot;http://shiro.apache.org&quot;&gt;Apache Shiro&lt;/a&gt;, I configured its filter and required dependencies using Spring&apos;s JavaConfig. You can click through my security presentation below, download it from &lt;a href=&quot;//raibledesigns.com/rd/page/publications&quot;&gt;my presentations page&lt;/a&gt;, or &lt;a href=&quot;http://www.slideshare.net/mraible/java-web-application-security-with-java-ee-spring-security-and-apache-shiro-uberconf-2015&quot;&gt;view it on SlideShare&lt;/a&gt;.
&lt;div style=&quot;text-align: center&quot;&gt;
&lt;iframe src=&quot;//www.slideshare.net/slideshow/embed_code/key/7pd6dSYxXtj8u5&quot; width=&quot;512&quot; height=&quot;325&quot; frameborder=&quot;0&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; scrolling=&quot;no&quot; style=&quot;border-top: 1px solid black; border-bottom: 1px solid black&quot; allowfullscreen&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;One thing that didn&apos;t make it into the presentation was the &lt;a href=&quot;https://github.com/mraible/java-webapp-security-examples/pull/3&quot;&gt;super-helpful pull request&lt;/a&gt; from &lt;a href=&quot;https://spring.io/team/rwinch&quot;&gt;Rob Winch&lt;/a&gt;, Spring Security Lead. He showed me how you can use basic and form-based authentication in the same app, as well how to write tests with &lt;a href=&quot;https://github.com/rwinch/java-webapp-security-examples/commit/5357d7ae94f24e18e7641f9f2b98a36132a016d4&quot;&gt;MockMvc and Spring Security&apos;s Testing support&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
The next time I do this presentation (at the &lt;a href=&quot;http://www.therichwebexperience.com/conference/fort_lauderdale/2015/12/speakers/matt_raible&quot;&gt;Rich Web Experience&lt;/a&gt;), I&apos;d like to see if it&apos;s possible to use all-Java to configure the Java EE 7 example. I used web.xml in this example and the &lt;a href=&quot;https://blogs.oracle.com/swchan/entry/follow_up_on_servlet_3&quot;&gt;Servlet 3.0 Security Annotations&lt;/a&gt; might offer enough to get rid of it.&lt;/p&gt;
&lt;p&gt;All the demos I did during the security presentation can be seen in my &lt;a href=&quot;https://github.com/mraible/java-webapp-security-examples&quot;&gt;java-webapp-security-examples project on GitHub&lt;/a&gt;. There&apos;s branches for where I started (javaee-start, springsecurity-start and apacheshiro-start) as well as &quot;complete&quot; branches for where I finished. The complete examples should also be in-sync with the master branch.&lt;/p&gt;
&lt;p&gt;If you have any questions about either presentation, please let me know.&lt;/p&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/how_to_setup_your_own1</id>
        <title type="html">How To Setup Your Own Software Development Company, 6 Years Later</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/how_to_setup_your_own1"/>
        <published>2015-03-02T09:26:01-07:00</published>
        <updated>2015-03-03T00:03:22-07:00</updated> 
        <category term="/Java" label="Java" />
        <category term="healthoverwealth" scheme="http://roller.apache.org/ns/tags/" />
        <category term="contract" scheme="http://roller.apache.org/ns/tags/" />
        <category term="scorp" scheme="http://roller.apache.org/ns/tags/" />
        <category term="fulltime" scheme="http://roller.apache.org/ns/tags/" />
        <category term="development" scheme="http://roller.apache.org/ns/tags/" />
        <category term="time" scheme="http://roller.apache.org/ns/tags/" />
        <category term="insurance" scheme="http://roller.apache.org/ns/tags/" />
        <category term="career" scheme="http://roller.apache.org/ns/tags/" />
        <category term="smallbusiness" scheme="http://roller.apache.org/ns/tags/" />
        <category term="software" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">&lt;p&gt;Just over six years ago, I wrote a popular post titled &lt;a href=&quot;http://raibledesigns.com/rd/entry/how_to_setup_your_own&quot;&gt;How To Setup Your Own Software Development Company&lt;/a&gt;. I&apos;d just left LinkedIn a few months earlier and was enjoying consulting life again, working with a group of friends at Evite. In the article, I wrote about how I liked consulting because it forces you to keep your skills up-to-date and it pays a lot better. I also talked about the type of legal entity you should form (I have an S Corp), what business insurance you should buy, what I had for health insurance and how I automated payroll and tax payments.&lt;/p&gt;
&lt;p&gt;I recently received an email from a reader, asking me if I had any updated thoughts.&lt;/p&gt;
&lt;blockquote class=&quot;quote&quot;&gt;
&lt;p style=&quot;margin-top: 0&quot;&gt;
It&apos;s been nearly six years since you wrote &lt;a href=&quot;http://raibledesigns.com/rd/entry/how_to_setup_your_own&quot;&gt;the article about starting your own business&lt;/a&gt; ... and thanks, by the way. 
&lt;/p&gt;
&lt;p&gt;
I am starting my venture into independent contract work as a software engineer (Java technology) in California and most likely will setup an S corp entity. 
&lt;/p&gt;
&lt;p&gt;
Seeing that you wrote this six years ago and things have considerably changed in the U.S. (economy, health care, etc.), I was wondering if you had some updated thoughts to share, perhaps some learned lessons even. 
&lt;/p&gt;
&lt;p style=&quot;margin-bottom: 0&quot;&gt;
And also, I have questions about business insurance: what type of insurance should I opt for? Is there really an umbrella insurance out there? Or does each (or many) clients out there dictate the insurance you need? &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Yes, a lot has changed in the last six years. The economy has improved and health care costs have risen. Through this time, I&apos;ve been able to continue to operate as an independent software developer and keep the contracts flowing. Personally, the biggest changes in my life have been outside of work. I &lt;a href=&quot;http://raibledesigns.com/rd/entry/an_epic_weekend_in_estes&quot;&gt;met an exceptional woman&lt;/a&gt;, traveled to &lt;a href=&quot;http://raibledesigns.com/rd/entry/an_awesome_trip_to_amsterdam&quot;&gt;conferences&lt;/a&gt; all &lt;a href=&quot;http://raibledesigns.com/rd/entry/a_fun_week_in_florida&quot;&gt;around&lt;/a&gt; &lt;a href=&quot;http://raibledesigns.com/rd/entry/two_opening_days_with_a&quot;&gt;the&lt;/a&gt; &lt;a href=&quot;http://raibledesigns.com/rd/entry/devoxx_france_a_great_conference&quot;&gt;world&lt;/a&gt; with her, got &lt;a href=&quot;http://raibledesigns.com/rd/entry/matrimony_in_montana&quot;&gt;married&lt;/a&gt;, &lt;a href=&quot;http://raibledesigns.com/rd/entry/around_the_world_honeymoon_1st&quot;&gt;traveled&lt;/a&gt; some more, then bought a &lt;a href=&quot;http://raibledesigns.com/rd/entry/the_syncro_rescue_road_trip&quot;&gt;VW Westfalia&lt;/a&gt; so we could have &lt;a href=&quot;http://raibledesigns.com/rd/entry/farewell_to_the_2013_2014&quot;&gt;lots of fun&lt;/a&gt; &lt;a href=&quot;http://raibledesigns.com/rd/entry/syncro_solstice_2014&quot;&gt;traveling&lt;/a&gt; in our own &lt;a href=&quot;http://raibledesigns.com/rd/entry/rafting_the_yampa_through_dinosaur&quot;&gt;backyard&lt;/a&gt;. All the while, I&apos;ve worked for some great clients. I &lt;a href=&quot;http://raibledesigns.com/rd/entry/how_s_the_ol_team&quot;&gt;built a team of hot shots&lt;/a&gt; at Time Warner Cable (many of them still work there), I skied the &lt;a href=&quot;http://raibledesigns.com/rd/entry/the_greatest_snow_on_earth&quot;&gt;awesome powder of Utah&lt;/a&gt; while working at Overstock and I enjoyed a long-term contract at Oracle. After Oracle, I got into the healthcare industry and I&apos;ve been working in it ever since. &lt;/p&gt;
&lt;p&gt;In fact, I just finished working for a healthcare company last week and I&apos;m on the hunt for my next gig in April. Check out &lt;a href=&quot;https://www.linkedin.com/in/mraible&quot;&gt;my LinkedIn profile&lt;/a&gt; if you&apos;d like to see my r&#233;sum&#233;.&lt;/p&gt;
&lt;p&gt;I&apos;ve learned quite a few lessons over the last several years. As an independent developer, the biggest thing I&apos;ve learned is &lt;em&gt;marketing is key&lt;/em&gt;. I&apos;ve always known this, but I&apos;ve been reminded of its importance a few times. When I worked at Taleo (after Overstock), I was on a 3-month contract that turned into a 9-month contract that got a 1-year extension when Oracle bought them. The work was challenging, but the application was outdated. Getting them to adopt new technologies like Bootstrap and AngularJS was difficult. When Oracle took over, they offered me a 1-year contract at a great rate. I accepted, never thinking it would be difficult to get paid from someone like Oracle. It took them over &lt;em&gt;three months&lt;/em&gt; to pay my first invoice and it took me another three months to get payments flowing regularly. I felt like I was trapped. I felt like I could quit, but that wouldn&apos;t speed up the process of getting my invoices paid. From this experience, I&apos;m hesitant to start with any contract that&apos;s longer than three months.&lt;/p&gt;
&lt;p&gt;During my time at Oracle, I didn&apos;t blog as much as I had previously (because the day-to-day work wasn&apos;t that exciting), but I did still speak at conferences. Last year, I took the year off from speaking at conferences altogether. Speaking is an excellent marketing tool. Because of my lack of speaking, I saw a downturn in contract opportunities in Q4 last year.&lt;/p&gt;
&lt;p&gt;As far as health insurance is concerned, I continued to have a disaster prevention plan, with a $5K per year deductible. I paid around $300/month for this, and rarely used it. By riding my bike to my office in downtown Denver, and skiing a bunch in the winter, I felt like I was pretty healthy. After I &lt;a href=&quot;http://raibledesigns.com/rd/entry/the_21_day_sugar_detox&quot;&gt;stopped eating sugar&lt;/a&gt; last fall, I became much healthier. So much healthier that I&apos;ve stopped taking high blood pressure medication. Today, I don&apos;t pay for health insurance. Trish went back to IT Security Sales in November and she was able to get me on her company&apos;s plan for $100 cheaper than what I was paying. I didn&apos;t have dental insurance for the last five years and I did have to shell out $5K for a tooth implant at one point.&lt;/p&gt;
&lt;p&gt;For business insurance, I have the &lt;a href=&quot;http://www.thehartford.com/business-owner-policy/&quot;&gt;Business Owner&apos;s Policy&lt;/a&gt; from The Hartford. I pay around $600/year and I&apos;ve gotten that back when I&apos;ve had laptops stolen or accidentally killed my iPhone. I&apos;ve got automated backups going all the time, so I haven&apos;t lost any data in several years. This insurance policy and its liability coverage has been &quot;good enough&quot; for all my clients, including the big ones.&lt;/p&gt;
&lt;p&gt;I think the biggest lesson I&apos;ve learned in the last several years is that the best way to be rich is to be rich in &lt;em&gt;time&lt;/em&gt;. I&apos;ve always dreamed of making $500/hour and working 20 hours per week. While $500/hour sounds crazy, you know there&apos;s consultants out there that are making that kinda cash. They&apos;re probably not in software, maybe they&apos;re political consultants, or former professional athletes, but those consulting rates do exist. In software, there&apos;s certainly companies that bill those kinda rates. My rates for the last several years haven&apos;t been &lt;em&gt;that&lt;/em&gt; good, but they&apos;ve been pretty awesome. 
&lt;/p&gt;
&lt;p&gt;
Earlier this year, I had the opportunity to work 20 hours per week instead of 40. It was one of the greatest work-life experiences I&apos;ve had to date. I was still able to pay all my bills, and I had time during each-and-every-day to do something fun. When working 40 hours per week, exercising and cooking dinner were somewhat of a chore. When I flipped to working less, work became the chore and exercise and cooking became the fun parts of my day. I read somewhere recently that if Americans valued &lt;em&gt;health over wealth&lt;/em&gt;, we&apos;d be a lot better off. I felt like I did this when working less and that I was &lt;em&gt;rich in time&lt;/em&gt;. 
&lt;/p&gt;
&lt;p&gt;Related to feeling better over making more, I&apos;ve started to target employment opportunities that offer a good team to work with. For the last year, most of my contracts have been with remote clients, where they haven&apos;t required me to travel onsite. While this sounds great in theory, I do miss the comradery that exists when working with a team. Working with someone over a Skype/HipChat call is nothing like sitting next to each other and cracking jokes while writing code. Don&apos;t get me wrong, I love remote work, but I do think it&apos;s important to be onsite and collaborating face-to-face at least once per month.&lt;/p&gt;
&lt;p&gt;To those individuals looking to start their own Solopreneurship, I hope this advice helps. It&apos;s been a great experience for me.&lt;/p&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/testing_angularjs_applications</id>
        <title type="html">Testing AngularJS Applications</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/testing_angularjs_applications"/>
        <published>2015-02-02T10:11:56-07:00</published>
        <updated>2015-02-02T20:31:21-07:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="angularjs" scheme="http://roller.apache.org/ns/tags/" />
        <category term="karma" scheme="http://roller.apache.org/ns/tags/" />
        <category term="javascript" scheme="http://roller.apache.org/ns/tags/" />
        <category term="node" scheme="http://roller.apache.org/ns/tags/" />
        <category term="jasmine" scheme="http://roller.apache.org/ns/tags/" />
        <category term="git" scheme="http://roller.apache.org/ns/tags/" />
        <category term="protractor" scheme="http://roller.apache.org/ns/tags/" />
        <category term="npm" scheme="http://roller.apache.org/ns/tags/" />
        <summary type="html">&lt;p&gt;This article is the second in a series about learning &lt;a href=&quot;https://angularjs.org/&quot;&gt;AngularJS&lt;/a&gt;. It describes
    how to test a simple AngularJS application. In a previous article, &lt;a
            href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angularjs&quot;&gt;Getting Started with AngularJS&lt;/a&gt;, I
    showed how to develop a simple search and edit feature.&lt;/p&gt;
&lt;h3&gt;What you&apos;ll learn&lt;/h3&gt;
&lt;p&gt;You&apos;ll learn to use &lt;a href=&quot;http://jasmine.github.io/&quot;&gt;Jasmine&lt;/a&gt; for unit testing controllers and &lt;a
        href=&quot;http://angular.github.io/protractor/#/&quot;&gt;Protractor&lt;/a&gt; for integration testing. Angular&apos;s documentation
    has a
    good &lt;a href=&quot;https://docs.angularjs.org/guide/unit-testing&quot;&gt;developer&apos;s guide to unit testing&lt;/a&gt; if you&apos;d like
    more information on testing and why it&apos;s important. &lt;/p&gt;
&lt;p&gt;The best reason for writing tests is to automate your testing. Without tests, you&apos;ll likely be testing manually. This
    manual testing will take longer and longer as your codebase grows. &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. We 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;This article is the second in a series about learning &lt;a href=&quot;https://angularjs.org/&quot;&gt;AngularJS&lt;/a&gt;. It describes
    how to test a simple AngularJS application. In a previous article, &lt;a
            href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angularjs&quot;&gt;Getting Started with AngularJS&lt;/a&gt;, I
    showed how to develop a simple search and edit feature.&lt;/p&gt;
&lt;h3&gt;What you&apos;ll learn&lt;/h3&gt;
&lt;p&gt;You&apos;ll learn to use &lt;a href=&quot;http://jasmine.github.io/&quot;&gt;Jasmine&lt;/a&gt; for unit testing controllers and &lt;a
        href=&quot;http://angular.github.io/protractor/#/&quot;&gt;Protractor&lt;/a&gt; for integration testing. Angular&apos;s documentation
    has a
    good &lt;a href=&quot;https://docs.angularjs.org/guide/unit-testing&quot;&gt;developer&apos;s guide to unit testing&lt;/a&gt; if you&apos;d like
    more information on testing and why it&apos;s important. &lt;/p&gt;
&lt;p&gt;The best reason for writing tests is to automate your testing. Without tests, you&apos;ll likely be testing manually. This
    manual testing will take longer and longer as your codebase grows. &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. We 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;Get the tutorial project&lt;/h3&gt;
&lt;p&gt;Clone the angular-tutorial repository using &lt;a href=&quot;http://git-scm.com/&quot;&gt;git&lt;/a&gt; and
    install the dependencies. &lt;/p&gt;

&lt;pre&gt;
git clone https://github.com/mraible/angular-tutorial.git
cd angular-tutorial
npm install&lt;/pre&gt;
    &lt;p&gt;If you haven&apos;t completed the &lt;a href=&quot;//raibledesigns.com/rd/entry/getting_started_with_angularjs&quot;&gt;Getting
        Started with AngularJS&lt;/a&gt; tutorial, you should peruse it so you
        understand how this application works. You can also simply start the app with &amp;quot;npm start&amp;quot; and view it
        in your browser at &lt;a href=&quot;http://localhost:8000/app/&quot;&gt;http://localhost:8000/app/&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;Test the SearchController&lt;/h3&gt;
&lt;ol&gt;
    &lt;li&gt;
        &lt;p&gt;Create &lt;code&gt;app/search/search_test.js&lt;/code&gt; and populate it with the basic test infrastructure. This code
            sets up a mock &lt;code&gt;SearchService&lt;/code&gt; that has the first function we want to test:
            &lt;code&gt;query(term)&lt;/code&gt;. It uses &lt;code&gt;$provide&lt;/code&gt; to override the default &lt;code&gt;SearchService&lt;/code&gt;.
            &lt;a href=&quot;http://stackoverflow.com/questions/20828179/angular-unit-test-controllers-mocking-service-inside-controller&quot;&gt;Angular
                unit-test controllers - mocking service inside controller&lt;/a&gt; was a useful question on Stack Overflow
            for figuring out how to mock services in controllers.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;&apos;use strict&apos;;

describe(&apos;myApp.search module&apos;, function() {
    var mockSearchService;

    beforeEach(module(&apos;myApp.search&apos;, function($provide) {
        mockSearchService = {query: function(term) {}};
        $provide.value(&quot;SearchService&quot;, mockSearchService);
    }));
});&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Modify &lt;code&gt;karma.conf.js&lt;/code&gt; (in the root directory) to add the search implementation and test.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;files : [
  ...
  &apos;app/components/**/*.js&apos;,
  &apos;app/search/search.js&apos;,
  &apos;app/search/search_test.js&apos;,
  &apos;app/view*/**/*.js&apos;
],&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Add the first test to &lt;code&gt;search_test.js&lt;/code&gt;. This test verifies that setting a search term and
            executing the &lt;code&gt;search()&lt;/code&gt; function will call the service and return results. You can see the results returned
            from the service are mocked with
            &lt;code&gt;deferred.resolve()&lt;/code&gt;. The &lt;code&gt;deferred.resolve()&lt;/code&gt; call is &lt;a
                    href=&quot;http://entwicklertagebuch.com/blog/2013/10/how-to-handle-angularjs-promises-in-jasmine-unit-tests/&quot;&gt;how
                to handle promises in unit tests&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;describe(&apos;search by term&apos;, function() {
    var scope, rootScope, controller, deferred;

	// setup the controller with dependencies.
    beforeEach(inject(function($rootScope, $controller, $q) {
        rootScope = $rootScope;
        scope = $rootScope.$new();
        controller = $controller(&apos;SearchController&apos;, {$scope: scope, SearchService: mockSearchService });
        deferred = $q.defer();
    }));

    it(&apos;should search when a term is set and search() is called&apos;, function() {
        spyOn(mockSearchService, &apos;query&apos;).andReturn(deferred.promise);
        scope.term = &apos;M&apos;;
        scope.search();
        deferred.resolve({data: {name: &quot;Peyton Manning&quot;}});
        rootScope.$apply();
        expect(scope.searchResults).toEqual({name: &quot;Peyton Manning&quot;});
    });
});&lt;/pre&gt;
        &lt;p&gt;Related: &lt;a href=&quot;http://angular-tips.com/blog/2014/03/introduction-to-unit-test-spies/&quot;&gt;Introduction to
            Unit Test: Spies&lt;/a&gt; is a good introduction to using spies in unit tests.&lt;br/&gt;&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Run the following command on the command line to start the Karma test runner. You can leave this process
            running and new tests will be run automatically. You can change the mocked data and expectation to see your
            test fail.&lt;/p&gt;
        &lt;pre&gt;npm test&lt;/pre&gt;
        &lt;div class=&quot;alert alert-success&quot;&gt;&lt;strong&gt;Running Tests from IntelliJ IDEA&lt;/strong&gt;&lt;br&gt;
            See &lt;a href=&quot;https://www.jetbrains.com/idea/help/running-unit-tests-on-karma.html&quot;&gt;Running Unit Tests on
                Karma&lt;/a&gt; to learn how to run your tests from IntelliJ IDEA.
        &lt;/div&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Add a test to verify a search occurs automatically when the term is in the URL. Notice how the code structure had
            to change a bit to handle &lt;code&gt;$routeParams&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;describe(&apos;search by term automatically&apos;, function() {
    var scope, rootScope, controller, location, deferred;

    beforeEach(inject(function($rootScope, $controller, $q) {
        rootScope = $rootScope;
        scope = $rootScope.$new();

        // in this case, expectations need to be setup before controller is initialized
        var routeParams = {&quot;term&quot;: &quot;peyton&quot;};
        deferred = $q.defer();
        spyOn(mockSearchService, &apos;query&apos;).andReturn(deferred.promise);
        deferred.resolve({data: {name: &quot;Peyton Manning&quot;}});

        controller = $controller(&apos;SearchController&apos;, {$scope: scope, $routeParams: routeParams, SearchService: mockSearchService });
    }));

    it(&apos;should search automatically when a term is on the URL&apos;, function() {
        rootScope.$apply();
        expect(scope.searchResults).toEqual({name: &quot;Peyton Manning&quot;});
    });
});&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Add a test to verify the &lt;code&gt;EditController&lt;/code&gt; works as expected.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;describe(&apos;edit person&apos;, function() {
    var scope, rootScope, controller, location, deferred;

    beforeEach(inject(function($rootScope, $controller, $q) {
        rootScope = $rootScope;
        scope = $rootScope.$new();

        // expectations need to be setup before controller is initialized
        var routeParams = {&quot;id&quot;: &quot;1&quot;};
        deferred = $q.defer();
        spyOn(mockSearchService, &apos;fetch&apos;).andReturn(deferred.promise);
        deferred.resolve({data: {name: &quot;Peyton Manning&quot;, address: {street: &quot;12345 Blake Street&quot;, city: &quot;Denver&quot;}}});

        controller = $controller(&apos;EditController&apos;, {$scope: scope, $routeParams: routeParams, SearchService: mockSearchService });
    }));

    it(&apos;should fetch a single record&apos;, function() {
        rootScope.$apply();
        expect(scope.person.name).toBe(&quot;Peyton Manning&quot;);
        expect(scope.person.address.street).toBe(&quot;12345 Blake Street&quot;);
    });
});&lt;/pre&gt;
&lt;p&gt;After adding this test, you&apos;ll likely see the following error in your terminal.&lt;/p&gt;
&lt;pre class=&quot;brush: shell&quot;&gt;
Chrome 40.0.2214 (Mac OS X 10.10.2) myApp.search module edit person should fetch a single record FAILED
	fetch() method does not exist
	TypeError: Cannot read property &apos;name&apos; of undefined
&lt;/pre&gt;
        &lt;p&gt;This happens because the &lt;code&gt;mockSearchService&lt;/code&gt; does not have a fetch method defined. Modify the &lt;code&gt;beforeEach()&lt;/code&gt; on line 7 to add this function.&lt;/p&gt;
    &lt;pre class=&quot;brush: js&quot;&gt;mockSearchService = {query: function(term) {}, fetch: function(id) {}};&lt;/pre&gt;
    &lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Extra Credit&lt;/h3&gt;
&lt;p&gt;Create a test for saving a person. &lt;a
        href=&quot;http://stackoverflow.com/questions/13664144/how-to-unit-test-angularjs-controller-with-location-service&quot;&gt;Here&apos;s
    a question on Stack Overflow&lt;/a&gt; that might help you verify the location after a save has been performed.&lt;/p&gt;
&lt;h3&gt;Test the Search Feature&lt;/h3&gt;
&lt;p&gt;To test if the application works end-to-end, you can write scenarios with &lt;a
        href=&quot;http://angular.github.io/protractor/&quot;&gt;Protractor&lt;/a&gt;. These are also known as integration tests, as they
    test
    the &lt;em&gt;integration&lt;/em&gt; between all layers of your application.&lt;/p&gt;
&lt;p&gt;To verify end-to-end tests work in the project before you begin, run the following command in one terminal
    window:&lt;/p&gt;
&lt;pre&gt;npm start&lt;/pre&gt;
&lt;p&gt;Then in another window, run the following to execute the tests:&lt;/p&gt;
&lt;pre&gt;npm run protractor&lt;/pre&gt;
&lt;ol&gt;
    &lt;li&gt;
        &lt;p&gt;Write your first integration test to verify you can navigate to /search and enter a search term to see
            results. Add the following to &lt;code&gt;e2e-tests/scenarios.js&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;describe(&apos;search&apos;, function() {
  var searchTerm = element(by.model(&apos;term&apos;));
  var searchButton = element(by.id(&apos;search&apos;));

  beforeEach(function() {
    browser.get(&apos;index.html#/search&apos;);
  });

  it(&apos;should allow searching at /search&apos;, function() {
    searchTerm.sendKeys(&quot;M&quot;);
    searchButton.click().then(function() {
      expect(element.all(by.repeater(&apos;person in searchResults&apos;)).count()).toEqual(3);
    });
  });
});&lt;/pre&gt;
        &lt;p&gt;The &amp;quot;searchTerm&amp;quot; variable represents the input field. The &lt;code&gt;by.model()&lt;/code&gt; syntax binds to
            the &amp;quot;ng-model&amp;quot; attribute you defined in the HTML. &lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Run &amp;quot;npm run protractor&amp;quot;. This should fail with the following error.&lt;/p&gt;
&lt;pre class=&quot;brush: bash&quot;&gt;[launcher] Running 1 instances of WebDriver
Selenium standalone server started at http://172.16.6.39:64230/wd/hub
...F
Failures:
  1) my app search should allow searching at /search
   Message:
     NoSuchElementError: No element found using locator: By.id(&quot;search&quot;)&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;To fix, you need to add an &amp;quot;id&amp;quot; attribute to the Search button in
            &lt;code&gt;app/search/index.html&lt;/code&gt;.&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 id=&quot;search&quot;&amp;gt;Search&amp;lt;/button&amp;gt;
&amp;lt;/form&amp;gt;
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;Run the tests again using &amp;quot;npm run protractor&amp;quot;. They should all pass this time.&lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Write another test to verify editing a user displays their information.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;describe(&apos;edit person&apos;, function() {
  var name = element(by.model(&apos;person.name&apos;));
  var street = element(by.model(&apos;person.address.street&apos;));
  var city = element(by.model(&apos;person.address.city&apos;));

  beforeEach(function() {
    browser.get(&apos;index.html#/edit/1&apos;);
  });

  it(&apos;should allow viewing a person&apos;, function() {
    // getText() doesn&apos;t work with input elements, see the following for more information:
    // https://github.com/angular/protractor/blob/master/docs/faq.md#the-result-of-gettext-from-an-input-element-is-always-empty
    expect(name.getAttribute(&apos;value&apos;)).toEqual(&quot;Peyton Manning&quot;);
    expect(street.getAttribute(&apos;value&apos;)).toEqual(&quot;1234 Main Street&quot;);
    expect(city.getAttribute(&apos;value&apos;)).toEqual(&quot;Greenwood Village&quot;);
  });
});&lt;/pre&gt;
        &lt;p&gt;Verify it works with &amp;quot;npm run protractor&amp;quot;.&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;Finally, write a test to verify you can save a person and their details are updated. Figuring out how to
            verify the URL after it changed was assisted by &lt;a href=&quot;https://github.com/angular/protractor/issues/610&quot;&gt;this
                issue&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;brush: js&quot;&gt;describe(&apos;save person&apos;, function() {
  var name = element(by.model(&apos;person.name&apos;));
  var save = element(by.id(&apos;save&apos;));

  beforeEach(function() {
    browser.get(&apos;index.html#/edit/1&apos;);
  });

  it(&apos;should allow updating a name&apos;, function() {
    name.sendKeys(&quot; Updated&quot;);
    save.click().then(function() {
      // verify url set back to search results
      browser.driver.wait(function() {
        return browser.driver.getCurrentUrl().then(function(url) {
          expect(url).toContain(&apos;/search/Peyton%20Manning%20Updated&apos;);
          return url;
        });
      });
      // verify one element matched this change
      expect(element.all(by.repeater(&apos;person in searchResults&apos;)).count()).toEqual(1);
    });
  });
});&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
        &lt;p&gt;When you run this test with &amp;quot;npm run protractor&amp;quot;, it should fail because there&apos;s no element with
            &lt;code&gt;id=&amp;quot;save&amp;quot;&lt;/code&gt; in &lt;code&gt;app/search/edit.html&lt;/code&gt;. Add it to the Save button in this
            file and try again. You should see something similar to the following:&lt;/p&gt;
&lt;pre class=&quot;brush: bash&quot;&gt;Finished in 4.478 seconds
6 tests, 9 assertions, 0 failures&lt;/pre&gt;
    &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; on the &lt;strong&gt;testing&lt;/strong&gt; branch.
&lt;/p&gt;
&lt;p&gt;There are two commits that make the changes for the two 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/f3551cc84bd2f0fb35b4dd9ac38cbf6f417c106f&quot;&gt;Unit Tests&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;https://github.com/mraible/angular-tutorial/commit/12e37eb7ad8cde82c6ec92c8562700a7fb6952cc&quot;&gt;Integration Tests&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Summary&lt;/h3&gt;
&lt;p&gt;I hope you&apos;ve enjoyed this quick-and-easy tutorial on testing AngularJS applications. The first couple AngularJS applications
I developed didn&apos;t have tests and required a lot of manual testing to verify their quality. After learning that testing AngularJS
apps is pretty easy, I now do it on all my projects. Hopefully this tutorial motivates you to do do the same.
&lt;/p&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/devoxx4kids_denver_introduction_to_hardware</id>
        <title type="html">Devoxx4Kids - Denver: Introduction to Hardware Concepts with littleBits</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/devoxx4kids_denver_introduction_to_hardware"/>
        <published>2014-10-30T08:17:21-06:00</published>
        <updated>2014-10-30T14:20:06-06:00</updated> 
        <category term="/General" label="General" />
        <category term="denver" scheme="http://roller.apache.org/ns/tags/" />
        <category term="assemblyws" scheme="http://roller.apache.org/ns/tags/" />
        <category term="devoxx4kids" scheme="http://roller.apache.org/ns/tags/" />
        <category term="tackmobile" scheme="http://roller.apache.org/ns/tags/" />
        <category term="devoxx4kids-denver" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">&lt;a href=&quot;http://www.devoxx4kids.org&quot; title=&quot;Devoxx4Kids&quot;&gt;&lt;img src=&quot;//raibledesigns.com/repository/images/Devoxx4Kids-logo.png&quot; width=&quot;240&quot; height=&quot;84&quot; alt=&quot;Devoxx4Kids&quot; class=&quot;picture&quot; style=&quot;margin-top: -15px&quot;&gt;&lt;/a&gt;
I&apos;m pleased to announce the second meeting of the &lt;a href=&quot;http://www.meetup.com/Devoxx4Kids-Denver/&quot;&gt;Denver Chapter&lt;/a&gt; of &lt;a href=&quot;http://www.devoxx4kids.org/&quot;&gt;Devoxx4Kids&lt;/a&gt; is now &lt;a href=&quot;http://www.meetup.com/Devoxx4Kids-Denver/events/216557092/&quot;&gt;open for registration&lt;/a&gt;. It&apos;s a two hour class titled &lt;strong&gt;Introduction to Hardware Concepts with littleBits&lt;/strong&gt; and will be taught by Denver&apos;s own &lt;a href=&quot;http://tackmobile.com/&quot;&gt;Tack Mobile&lt;/a&gt;. To learn more about littleBits, see &lt;a href=&quot;http://littlebits.cc/&quot;&gt;http://littlebits.cc&lt;/a&gt;. If you or your company would like help by donating a &lt;a href=&quot;http://littlebits.cc/collections/workshop-set&quot;&gt;Workshop Set&lt;/a&gt;, please &lt;a href=&quot;//raibledesigns.com/contact.jsp&quot;&gt;contact me&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
The class will be held on Saturday, November 22nd, from 10am - 12pm at &lt;a href=&quot;http://www.assembly.ws/&quot;&gt;Assembly Workspace&lt;/a&gt;. Cost is $10, but you&apos;ll get that back in the form of a t-shirt. Age requirement is 9-18 and kids should have basic computer skills (copy/paste, opening applications, etc.).
&lt;/p&gt;
&lt;p&gt;I&apos;d like to thank &lt;a href=&quot;https://www.linkedin.com/in/juanchez&quot;&gt;Juan Sanchez&lt;/a&gt; for reaching out to me about this class and inspiring his company (and workspace) to make it all happen. It&apos;s been great working with you and your team Juan!&lt;/p&gt;
&lt;p&gt;When I started Devoxx4Kids Denver, I was hoping to host a class or two per year. Our &lt;a href=&quot;http://raibledesigns.com/rd/entry/first_devoxx4kids_in_denver_a&quot;&gt;first meetup in May was a wild success&lt;/a&gt;. After taking the summer off to relax, I started looking for more speakers in early October. The response has been great and we&apos;ll have another class about &lt;a href=&quot;http://www.greenfoot.org/door&quot;&gt;GreenFoot&lt;/a&gt; on December 13th. We&apos;re even in the planning stages for another session on &lt;a href=&quot;http://www.aldebaran.com/en/humanoid-robot/nao-robot&quot;&gt;NAO Humanoid Robot&lt;/a&gt; programming in Q1 2015.&lt;/p&gt;
&lt;p&gt;If you&apos;d like to get involved with Denver&apos;s Devoxx4Kids, please &lt;a href=&quot;http://www.meetup.com/Devoxx4Kids-Denver/&quot;&gt;join our meetup group&lt;/a&gt;.</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/this_site_now_powered_by</id>
        <title type="html">This site now powered by Java 8, Tomcat 7 and Wufoo Forms</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/this_site_now_powered_by"/>
        <published>2014-04-09T07:37:59-06:00</published>
        <updated>2014-05-08T20:09:00-06:00</updated> 
        <category term="/Roller" label="Roller" />
        <category term="tomcat7" scheme="http://roller.apache.org/ns/tags/" />
        <category term="wufoo" scheme="http://roller.apache.org/ns/tags/" />
        <category term="java8" scheme="http://roller.apache.org/ns/tags/" />
        <category term="ssl" scheme="http://roller.apache.org/ns/tags/" />
        <category term="apacheroller" scheme="http://roller.apache.org/ns/tags/" />
        <summary type="html">&lt;p&gt;I recently upgraded this site to use the latest version of &lt;a href=&quot;http://roller.apache.org/&quot;&gt;Apache Roller&lt;/a&gt;. It was a minor release (5.0.3), but I figured I&apos;d document the steps in case &quot;early onset&quot; comes soon. First of all, to download raibledesigns.com and get it running locally, I perform the following steps:
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Backup everything using ~/bin/backup.sh on raibledesigns.com&lt;/li&gt;
&lt;li&gt;scp backup file to local hard drive and expand&lt;/li&gt;
&lt;li&gt;Copy ROOT, skins and repository directories to local webapps&lt;/li&gt;
&lt;li&gt;Make sure activation, mail, mysql and jta JARs are in $CATALINA_HOME/lib&lt;/li&gt;
&lt;li&gt;Copy roller-custom.properties from raibledesigns.com&apos;s $CATALINA_HOME/lib&lt;/li&gt;
&lt;li&gt;Copy context files from hosted $CATALINA_HOME/conf/Catalina to local directory&lt;/li&gt;
&lt;li&gt;Import database and change roller-custom.properties to match local credentials&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Next, to upgrade to the latest Roller release, I do the following:
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Download latest Roller release and expand&lt;/li&gt;
&lt;li&gt;Copy JARs (from WEB-INF/lib) to existing install (to upgrade dependencies)&lt;/li&gt;
&lt;li&gt;Delete any lower-versioned JARS from WEB-INF/lib directory&lt;/li&gt;
&lt;li&gt;Copy JSPs (from WEB-INF/jsps) to existing install&lt;/li&gt;
&lt;li&gt;Run database migration scripts from WEB-INF/classes/dbscripts&lt;/li&gt;
&lt;li&gt;Use a diff tool (like SmartSynchronize) to compare new vs. existing&lt;/li&gt;
&lt;li&gt;Test and troubleshoot (if there&apos;s startup errors)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This process has worked well for the last 10 years, and it&apos;s been in my head the whole time. It&apos;s bound to escape someday.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Contact Form Enhancements&lt;/strong&gt;&lt;br/&gt;
In addition to upgrading Roller, I also upgraded Tomcat 6 to Tomcat 7.0.52. In doing so, I found &lt;a href=&quot;http://tomcat.markmail.org/thread/3jbtasxiaekzgi62&quot;&gt;Jakarta&apos;s Mailer Taglib doesn&apos;t work with Tomcat 7&lt;/a&gt;. As you can tell from the aforementioned thread, I&apos;ve known this for several years. That&apos;s the only thing that&apos;s stopped me from upgrading Tomcat the past couple years. 
&lt;/p&gt;</summary>
        <content type="html">&lt;p&gt;I recently upgraded this site to use the latest version of &lt;a href=&quot;http://roller.apache.org/&quot;&gt;Apache Roller&lt;/a&gt;. It was a minor release (5.0.3), but I figured I&apos;d document the steps in case &quot;early onset&quot; comes soon. First of all, to download raibledesigns.com and get it running locally, I perform the following steps:
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Backup everything using ~/bin/backup.sh on raibledesigns.com&lt;/li&gt;
&lt;li&gt;scp backup file to local hard drive and expand&lt;/li&gt;
&lt;li&gt;Copy ROOT, skins and repository directories to local webapps&lt;/li&gt;
&lt;li&gt;Make sure activation, mail, mysql and jta JARs are in $CATALINA_HOME/lib&lt;/li&gt;
&lt;li&gt;Copy roller-custom.properties from raibledesigns.com&apos;s $CATALINA_HOME/lib&lt;/li&gt;
&lt;li&gt;Copy context files from hosted $CATALINA_HOME/conf/Catalina to local directory&lt;/li&gt;
&lt;li&gt;Import database and change roller-custom.properties to match local credentials&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Next, to upgrade to the latest Roller release, I do the following:
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Download latest Roller release and expand&lt;/li&gt;
&lt;li&gt;Copy JARs (from WEB-INF/lib) to existing install (to upgrade dependencies)&lt;/li&gt;
&lt;li&gt;Delete any lower-versioned JARS from WEB-INF/lib directory&lt;/li&gt;
&lt;li&gt;Copy JSPs (from WEB-INF/jsps) to existing install&lt;/li&gt;
&lt;li&gt;Run database migration scripts from WEB-INF/classes/dbscripts&lt;/li&gt;
&lt;li&gt;Use a diff tool (like SmartSynchronize) to compare new vs. existing&lt;/li&gt;
&lt;li&gt;Test and troubleshoot (if there&apos;s startup errors)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This process has worked well for the last 10 years, and it&apos;s been in my head the whole time. It&apos;s bound to escape someday.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Contact Form Enhancements&lt;/strong&gt;&lt;br/&gt;
In addition to upgrading Roller, I also upgraded Tomcat 6 to Tomcat 7.0.52. In doing so, I found &lt;a href=&quot;http://tomcat.markmail.org/thread/3jbtasxiaekzgi62&quot;&gt;Jakarta&apos;s Mailer Taglib doesn&apos;t work with Tomcat 7&lt;/a&gt;. As you can tell from the aforementioned thread, I&apos;ve known this for several years. That&apos;s the only thing that&apos;s stopped me from upgrading Tomcat the past couple years. 
&lt;/p&gt;
Rather than using my own code to send email, I figured I&apos;d &lt;a href=&quot;https://twitter.com/mraible/status/448227865889210368&quot;&gt;explore my options&lt;/a&gt;. I looked into &lt;a href=&quot;http://kontactr.com/&quot;&gt;Kontactr&lt;/a&gt;, &lt;a href=&quot;http://www.emailmeform.com/&quot;&gt;EmailMeForm&lt;/a&gt; and &lt;a href=&quot;http://www.wufoo.com/&quot;&gt;Wufoo&lt;/a&gt;. The first two allowed me to customize their colors, but Wufoo was the only one that had a transparent background feature. Because Wufoo form&apos;s are transparent, and I&apos;d &lt;a href=&quot;http://issues.appfuse.org/browse/APF-309&quot;&gt;copied their CSS for AppFuse&apos;s forms&lt;/a&gt; in the past, I decided to use it. To make it work with both of this site&apos;s themes (light and dark), I did need to create two forms on Wufoo and do some local JavaScript modifications. You can see the results on &lt;a href=&quot;http://raibledesigns.com/rd/page/contact&quot;&gt;my contact page&lt;/a&gt; (view-source to see the JavaScript). If you switch the theme (by clicking the little rectangles in the top-right corner), you can see how the form is redrawn with the proper color scheme.
&lt;/p&gt;
&lt;p&gt;If I&apos;d found it sooner, I likely would&apos;ve used &lt;a href=&quot;http://forms.brace.io/&quot;&gt;Brace Forms&lt;/a&gt; for my contact form.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Theme Improvements&lt;/strong&gt;&lt;br/&gt;
Ever since I &lt;a href=&quot;http://raibledesigns.com/rd/entry/new_look_and_feel_designed&quot;&gt;switched to a new look and feel&lt;/a&gt;, there&apos;s been improvements I wanted to make. &lt;/p&gt;
&lt;blockquote class=&quot;quote&quot;&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 FaceBook Like Button requires you to specify the &quot;data-colorscheme&quot; in markup so this further supports moving to the server.
&lt;/blockquote&gt;
&lt;p&gt;To fix this issue, I tried &lt;a href=&quot;http://markmail.org/message/jdhfplj4kd27ub7e&quot;&gt;creating a cookie-reader plugin&lt;/a&gt; for Roller. This worked, but not as well as I&apos;d hoped. The theme cookie was read the first time the page loaded, but then cached for subsequent requests. If a user changed their theme, the new value would not be read by the cached page. My solution now is putting the JavaScript &lt;em&gt;set-default-theme&lt;/em&gt; logic right after the stylesheet to set it before the body loads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Powered by Java 8&lt;/strong&gt;&lt;br/&gt;
To make this site run on Java 8, I had to move to a new server. &lt;a href=&quot;http://kgbinternet.com&quot;&gt;Keith&lt;/a&gt; did a bunch of work on my old server to get Java 8 working, but found Linux JDK 7 code has dependancies on the 2.6 kernel. If you&apos;re reading this post, the DNS changes have propagated. This means I have my first Java 8 app in production! &lt;img src=&quot;//raibledesigns.com/images/smileys/wink.gif&quot; class=&quot;smiley&quot; alt=&quot;;)&quot; title=&quot;;)&quot;&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Secured by SSL&lt;/strong&gt;&lt;br/&gt;
The last change I made was to start using my SSL certificate on this site. You can read about the challenges I encountered &lt;a href=&quot;http://markmail.org/message/uorad3gme6hj3syy&quot;&gt;on the Roller mailing list&lt;/a&gt;. The final solution involved changing my Absolute URL to use https and then changing my theme to use the following for its base.&lt;/p&gt;
&lt;pre&gt;&amp;lt;base href=&quot;$absBaseURL.replace(&apos;https:&apos;, &apos;&apos;)&quot; /&gt;&lt;/pre&gt;
&lt;p&gt;I also had to change a number of iframes to use src=&quot;//...&quot; instead of src=&quot;//...&quot;. To force HTTPS for the admin pages, Keith configured Apache to do the redirect. Using Roller to redirect resulted in infinite loop errors. 
&lt;/p&gt;
&lt;p&gt;This means you can now access this site using &lt;a href=&quot;https://raibledesigns.com&quot;&gt;https://raibledesigns.com&lt;/a&gt; or &lt;a href=&quot;http://raibledesigns.com&quot;&gt;http://raibledesigns.com&lt;/a&gt;, whichever you prefer. Speaking of SSL, you might wonder if this new server is affected by &lt;a href=&quot;http://heartbleed.com/&quot;&gt;The Heartbleed Bug&lt;/a&gt;. According to Keith, it is not.
&lt;blockquote class=&quot;quote&quot;&gt;
This server is still running openSSL 1.0.0 which was shipped with Centos 6.1, and the Heartbleed vulnerability appeared in OpenSSL 1.0.1 which shipped Centos 6.5.&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;br/&gt;
This site has undergone quite a few updates this month: Roller 5.0.3, Tomcat 7.0.52, &lt;a href=&quot;http://raible.kgbinternet.com/rd/page/contact&quot;&gt;a Wufoo contact form (+ recent tweets on the same page)&lt;/a&gt;, Java 8 and optional SSL. If you see any issues, please let me know.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;May 8, 2014 Update&lt;/strong&gt;: I decided to revert to &quot;http&quot; for the absolute URL instead of &quot;https&quot;. However, I&apos;ve updated all &quot;src&quot; attributes on &amp;lt;iframe&gt; and &amp;lt;img&gt; tags to use schema-less URLs (// instead of http://). This means that both secure and non-secure URLs should work.&lt;/p&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/devoxx4kids_denver_chapter_begins</id>
        <title type="html">Devoxx4Kids - Denver Chapter Begins!</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/devoxx4kids_denver_chapter_begins"/>
        <published>2014-04-08T10:59:29-06:00</published>
        <updated>2014-10-30T14:11:13-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="devoxx4kids" scheme="http://roller.apache.org/ns/tags/" />
        <category term="scottdavis" scheme="http://roller.apache.org/ns/tags/" />
        <category term="minecraft" scheme="http://roller.apache.org/ns/tags/" />
        <category term="thrive" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">&lt;p&gt;&lt;a href=&quot;http://www.devoxx4kids.org&quot; title=&quot;Devoxx4Kids&quot;&gt;&lt;img src=&quot;//raibledesigns.com/repository/images/Devoxx4Kids-logo.png&quot; width=&quot;240&quot; height=&quot;84&quot; alt=&quot;Devoxx4Kids&quot; class=&quot;picture&quot;&gt;&lt;/a&gt;
I&apos;m happy to announce the first meeting of the &lt;a href=&quot;http://www.meetup.com/Devoxx4Kids-Denver/&quot;&gt;Denver Chapter&lt;/a&gt; of &lt;a href=&quot;http://www.devoxx4kids.org/&quot;&gt;Devoxx4Kids&lt;/a&gt; is now &lt;a href=&quot;http://www.meetup.com/Devoxx4Kids-Denver/events/172037212/&quot;&gt;open for registration&lt;/a&gt;. It&apos;s a two hour class titled &lt;strong&gt;Introduction to Server-Side Minecraft Programming&lt;/strong&gt; and will be taught by Denver&apos;s own &lt;a href=&quot;http://www.linkedin.com/in/scottdavis99&quot;&gt;Scott Davis&lt;/a&gt;. The class will be held on May 3rd, from 10am - 12pm at &lt;a href=&quot;http://www.businessatthrive.com/&quot;&gt;Thrive&lt;/a&gt;&apos;s Cherry Creek location, where we can comfortably fit 20 students. Cost is $10, but you&apos;ll get that back in the form of a t-shirt. Age requirement is 9-18 and kids should have basic computer skills (copy/paste, opening applications, etc.). Minecraft experience will certainly help too.
&lt;/p&gt;
&lt;p&gt;I&apos;d like to thank &lt;a href=&quot;http://be.linkedin.com/in/danieldeluca&quot;&gt;Daniel De Luca&lt;/a&gt; for his initial assistance with getting Devoxx4Kids setup in Denver and &lt;a href=&quot;http://www.linkedin.com/in/arunpgupta&quot;&gt;Arun Gupta&lt;/a&gt; for starting &lt;a href=&quot;http://www.devoxx4kids.org/usa/&quot;&gt;Devoxx4Kids USA&lt;/a&gt;. Arun has been a great help in getting things going and answering my questions over the last few months. Of course, none of it would be happening without Scott Davis or Thrive. If you join us on May 3rd, you&apos;ll see that Scott is an awesome teacher and Thrive has some incredible facilities.&lt;/p&gt;
&lt;p&gt;My initial goal with Devoxx4Kids in Denver is to host a class or two this year. If there&apos;s enough demand, we can expand. For now, we&apos;re starting small and seeing where it takes us. If you&apos;re interested in teaching a future class, please let me know. We&apos;d love to teach the kids a number of skills, from Scratch to NAO Humanoid Robot programming to building things with Arduino and Raspberry Pi.&lt;/p&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/you_shouldn_t_have_to</id>
        <title type="html">You shouldn&apos;t have to worry about front end optimization</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/you_shouldn_t_have_to"/>
        <published>2014-01-16T13:49:03-07:00</published>
        <updated>2014-01-16T20:02:51-07:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="pagespeed" scheme="http://roller.apache.org/ns/tags/" />
        <category term="optimization" scheme="http://roller.apache.org/ns/tags/" />
        <category term="http" scheme="http://roller.apache.org/ns/tags/" />
        <category term="concatenation" scheme="http://roller.apache.org/ns/tags/" />
        <category term="http2" scheme="http://roller.apache.org/ns/tags/" />
        <category term="minification" scheme="http://roller.apache.org/ns/tags/" />
        <summary type="html">&lt;p&gt;After writing yesterday&apos;s article on &lt;a href=&quot;http://raibledesigns.com/rd/entry/using_grunt_with_angularjs_for&quot;&gt;optimizing AngularJS apps with Grunt&lt;/a&gt; I received an &lt;a href=&quot;https://twitter.com/markj9/statuses/423540086873927682&quot;&gt;interesting reply from @markj9&lt;/a&gt; on Twitter.
&lt;div style=&quot;margin-left: 30px&quot;&gt;
&lt;blockquote class=&quot;twitter-tweet&quot; lang=&quot;en&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://twitter.com/mraible&quot;&gt;@mraible&lt;/a&gt; that might be a bad thing! you should go listen to Igor..&amp;#10;&lt;a href=&quot;http://t.co/HL2mho7R86&quot;&gt;http://t.co/HL2mho7R86&lt;/a&gt;&lt;/p&gt;&amp;mdash; Mark (@markj9) &lt;a href=&quot;https://twitter.com/markj9/statuses/423540086873927682&quot;&gt;January 15, 2014&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;/div&gt;&lt;p&gt;
I clicked on the &lt;a href=&quot;http://rubyrogues.com/135-rr-http-2-0-with-ilya-grigorik/&quot;&gt;provided link&lt;/a&gt;, listened to the podcast (RR HTTP 2.0 with &lt;a href=&quot;https://twitter.com/igrigorik&quot;&gt;Ilya Grigorik&lt;/a&gt;) and discovered some juicy bits at around 27:00. The text below is from the podcast&apos;s transcript at the bottom of the page. &lt;/p&gt;</summary>
        <content type="html">&lt;p&gt;After writing yesterday&apos;s article on &lt;a href=&quot;http://raibledesigns.com/rd/entry/using_grunt_with_angularjs_for&quot;&gt;optimizing AngularJS apps with Grunt&lt;/a&gt; I received an &lt;a href=&quot;https://twitter.com/markj9/statuses/423540086873927682&quot;&gt;interesting reply from @markj9&lt;/a&gt; on Twitter.
&lt;div style=&quot;margin-left: 30px&quot;&gt;
&lt;blockquote class=&quot;twitter-tweet&quot; lang=&quot;en&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://twitter.com/mraible&quot;&gt;@mraible&lt;/a&gt; that might be a bad thing! you should go listen to Igor..&amp;#10;&lt;a href=&quot;http://t.co/HL2mho7R86&quot;&gt;http://t.co/HL2mho7R86&lt;/a&gt;&lt;/p&gt;&amp;mdash; Mark (@markj9) &lt;a href=&quot;https://twitter.com/markj9/statuses/423540086873927682&quot;&gt;January 15, 2014&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;/div&gt;&lt;p&gt;
I clicked on the &lt;a href=&quot;http://rubyrogues.com/135-rr-http-2-0-with-ilya-grigorik/&quot;&gt;provided link&lt;/a&gt;, listened to the podcast (RR HTTP 2.0 with &lt;a href=&quot;https://twitter.com/igrigorik&quot;&gt;Ilya Grigorik&lt;/a&gt;) and discovered some juicy bits around 27:00. The text below is from the podcast&apos;s transcript at the bottom of the page. &lt;/p&gt;
&lt;div class=&quot;quote&quot;&gt;
&lt;p style=&quot;margin-top: 0&quot;&gt;&lt;b&gt;AVDI:&amp;nbsp; &lt;/b&gt;Yeah. If I pushed back a little, it&#8217;s only because I just wonder, are the improvements coming solely from the perspective of a Google or Facebook, or are they also coming from the perspective of the hundreds of thousands of people developing smaller applications and websites?&lt;/p&gt;
&lt;p&gt;&lt;b&gt;ILYA:&amp;nbsp; &lt;/b&gt;Yeah. So, I think it&#8217;s the latter, which is to say the primary objective here is actually to make the browsers faster. So, if you open a webpage over HTTP 2.0, it should load faster. And that&#8217;s one kind of population that will benefit from it. And the second one is actually developers. We want to make developing web applications easier. You shouldn&#8217;t have to worry about things like spriting and concatenating files and doing all this stuff, domain sharding and all this other mess, which are just completely unnecessary and actually makes performance worse in many cases because each one of those has negative repercussions.&lt;/p&gt;
&lt;p&gt;Things like, let&#8217;s say concatenating your style sheets or JavaScript. Why do we do that? Well, we do that because we want to reduce the number of requests because we have this connection limit with HTTP 1.0. But the downside then is let&#8217;s say you&#8217;ve &#8212; actually Rails does this, you concatenate all of your CSS into one giant bundle. Great, we reduced the number of requests. We can download it faster. Awesome. Then you go in and your designer changes one line from whatever, the background color from blue to red. And now, you have to download the entire bundle. You have to invalidate that certain file and you need to download the whole thing.&lt;/p&gt;
&lt;p&gt;Chances are, if you&#8217;re doing sound software development today, you already have things split into modules. Like here is my base.css, here is my other page.css. Here are my JavaScript modules. And there&#8217;s no reason why we need to concatenate those into one giant bundle and invalidate on every request. This is something that we&#8217;ve automated to some degree, but it&#8217;s unnecessary. And it actually slows down the browser, too, in unexpected ways.&lt;/p&gt;
&lt;p style=&quot;margin-bottom: 0&quot;&gt;We recently realized that serving these large JavaScript files actually hurts your performance because we can&#8217;t execute the JavaScript until we get the entire file. So, if you actually split it into a bunch of smaller chunks, it actually allows us to execute them incrementally, one chunk at a time. And that makes the site faster. Same thing for CSS, splitting all that stuff. And this may sound trivial, but in practice, it&#8217;s actually a giant pain for a lot of applications.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The conversation goes on to talk about how this change in thinking is largely caused by the fact that bandwidth is no longer a problem, latency is.&lt;/p&gt;
&lt;div class=&quot;quote&quot;&gt;
&lt;p style=&quot;margin-top: 0&quot;&gt;&lt;b&gt;JAMES:&amp;nbsp; &lt;/b&gt;That seems really, really weird to me though. Everything has been moving in that direction and you&#8217;re saying our data on that&#8217;s just wrong. It&#8217;s not faster?&lt;/p&gt;
&lt;p style=&quot;margin-bottom: 0&quot;&gt;&lt;b&gt;ILYA:&amp;nbsp;&lt;/b&gt; Yeah. Part of it is the connectivity profiles are also changing. So when we first started advocating for those sorts of changes back in, whatever it was, 2005, 2007, when this stuff started showing up, the connection speeds were different. We were primarily maybe DSL was state of the art and bandwidth was really an issue there. So, you spend more time just downloading resources. Now that bandwidth is much less of an issue, latency is the problem. And because of that, these &#8220;best practices&#8221; are changing. And with HTTP 2.0, you actually don&#8217;t have to do that at all. And in fact, some of those things will actually hurt your performance.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;As you can imagine, this news is quite surprising to me. Optimizations like gzipping and expires headers will continue to be important, but concatenating and minifying might become a &quot;worst&quot; practice? That seems crazy, especially when the tools I test with (YSlow and Page Speed browser plugins) give me higher grades for minifying and reducing the number of requests. 
&lt;/p&gt;
&lt;p&gt;
The good news is there&apos;s lots of good things coming in HTTP 2.0, and you can use it today. &lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;&lt;b&gt;ILYA:&amp;nbsp; &lt;/b&gt;... any application that&#8217;s delivered over HTTP 1.0 will work over HTTP 2.0. There&#8217;s nothing changing there. The semantics are all the same. It could be the case that certain optimizations that you&#8217;ve done for HTTP 1.1 will actually hurt in HTTP 2.0. And when I say hurt, in practice at least from what I&#8217;ve seen today, it doesn&#8217;t mean that your site is actually going to be slower. It&#8217;s just that it won&#8217;t be any better than HTTP 1.0.&lt;/p&gt;
&lt;p&gt;Upgrading my servers to support HTTP 2.0 brings up an interesting dilemma. How do I measure that non-minified and non-concatenated assets are actually better? Do I just benchmark page load times or are there better tools for proving things are faster and better?</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/a_webapp_makeover_with_spring</id>
        <title type="html">A Webapp Makeover with Spring 4 and Spring Boot</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/a_webapp_makeover_with_spring"/>
        <published>2013-12-11T12:47:15-07:00</published>
        <updated>2013-12-13T14:54:52-07:00</updated> 
        <category term="/Java" label="Java" />
        <category term="spring4" scheme="http://roller.apache.org/ns/tags/" />
        <category term="maven" scheme="http://roller.apache.org/ns/tags/" />
        <category term="jersey" scheme="http://roller.apache.org/ns/tags/" />
        <category term="springboot" scheme="http://roller.apache.org/ns/tags/" />
        <category term="spring" scheme="http://roller.apache.org/ns/tags/" />
        <summary type="html">&lt;p&gt;A typical Maven and Spring web application has a fair amount of XML and 
verbosity to it. Add in Jersey and Spring Security and you can have hundreds of lines of 
XML before you even start to write your Java code. As part of a recent project,
I was tasked with upgrading a webapp like this to use Spring 4 and 
&lt;a href=&quot;http://projects.spring.io/spring-boot/&quot;&gt;Spring Boot&lt;/a&gt;. I also figured I&apos;d try to minimize the XML.&lt;/p&gt; 
&lt;p&gt;This is my story on how I upgraded to Spring 4, Jersey 2, Java 8 and Spring Boot 0.5.0 M6. 
&lt;/p&gt;
&lt;p&gt;When I started, the app was using Spring 3.2.5, Spring Security 3.1.4 and Jersey 1.18. The
pom.xml had four Jersey dependencies, three Spring dependencies and three Spring Security
dependencies, along with a number of exclusions for &quot;jersey-spring&quot;.&lt;/p&gt;
&lt;p id=&quot;spring4&quot;&gt;&lt;strong&gt;Upgrading to Spring 4&lt;/strong&gt;&lt;br/&gt;
Upgrading to Spring 4 was easy, I changed the version property to 4.0.0.RC2 and added the new 
Spring &lt;a href=&quot;http://spring.io/blog/2013/12/03/spring-framework-4-0-rc2-available&quot;&gt;bill of materials&lt;/a&gt;
to my pom.xml. I also add the Spring milestone repo since Spring 4 won&apos;t be released to Maven central
until tomorrow.&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;dependencyManagement&amp;gt;
    &amp;lt;dependencies&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-framework-bom&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;${spring.framework.version}&amp;lt;/version&amp;gt;
            &amp;lt;type&amp;gt;pom&amp;lt;/type&amp;gt;
            &amp;lt;scope&amp;gt;import&amp;lt;/scope&amp;gt;
        &amp;lt;/dependency&amp;gt;
    &amp;lt;/dependencies&amp;gt;
&amp;lt;/dependencyManagement&amp;gt;

&amp;lt;repositories&amp;gt;
    &amp;lt;repository&amp;gt;
        &amp;lt;id&amp;gt;spring-milestones&amp;lt;/id&amp;gt;
        &amp;lt;url&amp;gt;http://repo.spring.io/milestone&amp;lt;/url&amp;gt;
        &amp;lt;snapshots&amp;gt;
            &amp;lt;enabled&amp;gt;true&amp;lt;/enabled&amp;gt;
        &amp;lt;/snapshots&amp;gt;
    &amp;lt;/repository&amp;gt;
&amp;lt;/repositories&amp;gt;
&lt;/pre&gt;</summary>
        <content type="html">&lt;p&gt;A typical Maven and Spring web application has a fair amount of XML and 
verbosity to it. Add in Jersey and Spring Security and you can have hundreds of lines of 
XML before you even start to write your Java code. As part of a recent project,
I was tasked with upgrading a webapp like this to use Spring 4 and 
&lt;a href=&quot;http://projects.spring.io/spring-boot/&quot;&gt;Spring Boot&lt;/a&gt;. I also figured I&apos;d try to minimize the XML.&lt;/p&gt; 
&lt;p&gt;This is my story on how I upgraded to Spring 4, Jersey 2, Java 8 and Spring Boot 0.5.0 M6. 
&lt;/p&gt;
&lt;p&gt;When I started, the app was using Spring 3.2.5, Spring Security 3.1.4 and Jersey 1.18. The
pom.xml had four Jersey dependencies, three Spring dependencies and three Spring Security
dependencies, along with a number of exclusions for &quot;jersey-spring&quot;.&lt;/p&gt;
&lt;p id=&quot;spring4&quot;&gt;&lt;strong&gt;Upgrading to Spring 4&lt;/strong&gt;&lt;br/&gt;
Upgrading to Spring 4 was easy, I changed the version property to 4.0.0.RC2 and added the new 
Spring &lt;a href=&quot;http://spring.io/blog/2013/12/03/spring-framework-4-0-rc2-available&quot;&gt;bill of materials&lt;/a&gt;
to my pom.xml. I also add the Spring milestone repo since Spring 4 won&apos;t be released to Maven central
until tomorrow.&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;dependencyManagement&amp;gt;
    &amp;lt;dependencies&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-framework-bom&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;${spring.framework.version}&amp;lt;/version&amp;gt;
            &amp;lt;type&amp;gt;pom&amp;lt;/type&amp;gt;
            &amp;lt;scope&amp;gt;import&amp;lt;/scope&amp;gt;
        &amp;lt;/dependency&amp;gt;
    &amp;lt;/dependencies&amp;gt;
&amp;lt;/dependencyManagement&amp;gt;

&amp;lt;repositories&amp;gt;
    &amp;lt;repository&amp;gt;
        &amp;lt;id&amp;gt;spring-milestones&amp;lt;/id&amp;gt;
        &amp;lt;url&amp;gt;http://repo.spring.io/milestone&amp;lt;/url&amp;gt;
        &amp;lt;snapshots&amp;gt;
            &amp;lt;enabled&amp;gt;true&amp;lt;/enabled&amp;gt;
        &amp;lt;/snapshots&amp;gt;
    &amp;lt;/repository&amp;gt;
&amp;lt;/repositories&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Next, I removed all the references to ${spring.framework.version} in dependencies since it&apos;d 
be controlled by &lt;a href=&quot;http://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Dependency_Management&quot;&gt;
Maven&apos;s dependency management feature&lt;/a&gt;. 
&lt;/p&gt;
&lt;pre class=&quot;brush: diff&quot;&gt;
     &amp;lt;dependency&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;version&amp;gt;${spring.framework.version}&amp;lt;/version&amp;gt;
     &amp;lt;/dependency&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
I also changed to use Maven 3&apos;s &lt;a href=&quot;http://maven.apache.org/pom.html#Exclusions&quot;&gt;wildcard syntax&lt;/a&gt; to exclude multiple 
dependencies.&lt;/p&gt;
&lt;pre class=&quot;brush: diff&quot;&gt;
    &amp;lt;dependency&amp;gt;
        &amp;lt;groupId&amp;gt;com.sun.jersey.contribs&amp;lt;/groupId&amp;gt;
        &amp;lt;artifactId&amp;gt;jersey-spring&amp;lt;/artifactId&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&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;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-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;artifactId&amp;gt;*&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;I confirmed the upgrade worked by running &quot;mvn dependency:tree | grep spring&quot;, followed by &quot;mvn jetty:run&quot; and viewing the app in my browser. &lt;/p&gt;
&lt;p id=&quot;jersey2&quot;&gt;&lt;strong&gt;Upgrading to Jersey 2&lt;/strong&gt;&lt;br/&gt;
The next item I tackled was upgrading to Jersey 2.4.1. I changed the version number in my pom.xml, then added the Jersey BOM.&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.glassfish.jersey&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;jersey-bom&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;${jersey.version}&amp;lt;/version&amp;gt;
    &amp;lt;type&amp;gt;pom&amp;lt;/type&amp;gt;
    &amp;lt;scope&amp;gt;import&amp;lt;/scope&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/pre&gt;
&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
You might ask &quot;why Jersey?&quot; if we already have Spring MVC and its REST support? You might also ask why not Play or Grails instead of a Java + Spring stack? For this particular project, I recommended technology options, and these were certainly among them. However, the team chose differently and I support their decision. The project is 
creating an iOS app, as well as a responsive HTML5 mobile/desktop app. We figured we had enough risk with new technologies on the front-end that we should play it a bit safer on the backend. To make the backend work a bit sexier, we&apos;ve decided to allow Spring 4, Java 8 and possibly some reactive principles.&lt;/p&gt;
&lt;p&gt;Next, I changed from the old &lt;i&gt;com.sun.jersey&lt;/i&gt; dependencies to &lt;i&gt;org.glassfish.jersey&lt;/i&gt; and removed jersey-spring. &lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.glassfish.jersey.containers&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;jersey-container-servlet&amp;lt;/artifactId&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.glassfish.jersey.media&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;jersey-media-json-jackson&amp;lt;/artifactId&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
The last thing I needed to do was change the servlet-class and param-name in web.xml:&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;servlet&amp;gt;
    &amp;lt;servlet-name&amp;gt;jersey-servlet&amp;lt;/servlet-name&amp;gt;
    &amp;lt;servlet-class&amp;gt;org.glassfish.jersey.servlet.ServletContainer&amp;lt;/servlet-class&amp;gt;
    &amp;lt;init-param&amp;gt;
        &amp;lt;param-name&amp;gt;jersey.config.server.provider.packages&amp;lt;/param-name&amp;gt;
        &amp;lt;param-value&amp;gt;com.raibledesigns.boot.service&amp;lt;/param-value&amp;gt;
    &amp;lt;/init-param&amp;gt;
    &amp;lt;load-on-startup&amp;gt;1&amp;lt;/load-on-startup&amp;gt;
&amp;lt;/servlet&amp;gt;
&lt;/pre&gt;
&lt;p id=&quot;java8&quot;&gt;&lt;strong&gt;Requiring Java 8&lt;/strong&gt;&lt;br/&gt;
Requiring Java 8 to compile was easy enough. I added the maven-compiler-plugin to enforce a minimum version.&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;plugin&amp;gt;
    &amp;lt;artifactId&amp;gt;maven-compiler-plugin&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;3.1&amp;lt;/version&amp;gt;
    &amp;lt;configuration&amp;gt;
        &amp;lt;source&amp;gt;1.8&amp;lt;/source&amp;gt;
        &amp;lt;target&amp;gt;1.8&amp;lt;/target&amp;gt;
    &amp;lt;/configuration&amp;gt;
&amp;lt;/plugin&amp;gt;
&lt;/pre&gt;&lt;p&gt;I &lt;a href=&quot;https://jdk8.java.net/download.html&quot;&gt;downloaded the latest Java 8 SDK&lt;/a&gt; and installed it. Then I set my JAVA_HOME to use it.&lt;/p&gt;
&lt;pre class=&quot;brush: shell&quot;&gt;
export JAVA_HOME=`/usr/libexec/java_home -v 1.8`
&lt;/pre&gt;
&lt;p id=&quot;boot&quot;&gt;&lt;strong&gt;Integrating Spring Boot&lt;/strong&gt;&lt;br/&gt;
I learned about Spring Boot a few weeks ago &lt;a href=&quot;http://raibledesigns.com/rd/entry/devoxx_2013_a_nordic_countries&quot;&gt;at Devoxx&lt;/a&gt;. &lt;a href=&quot;http://www.joshlong.com/&quot;&gt;Josh Long&lt;/a&gt; gave me a 3-minute demo at the speaker&apos;s dinner and showed me enough to pique my interest. To integrate it into my project, I started with the &lt;a href=&quot;http://projects.spring.io/spring-boot/#quick-start&quot;&gt;Quick Start&lt;/a&gt;. I added the boot-parent, dependencies for web, security and actuator (logging, metrics, etc.) and the Maven plugin. I removed all the Spring and Spring Security dependencies.
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;parent&amp;gt;
    &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;spring-boot-starter-parent&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;0.5.0.M6&amp;lt;/version&amp;gt;
&amp;lt;/parent&amp;gt;
...
&amp;lt;pluginRepositories&amp;gt;
    &amp;lt;pluginRepository&amp;gt;
        &amp;lt;id&amp;gt;spring-milestones&amp;lt;/id&amp;gt;
        &amp;lt;url&amp;gt;http://repo.spring.io/milestone&amp;lt;/url&amp;gt;
    &amp;lt;/pluginRepository&amp;gt;
&amp;lt;/pluginRepositories&amp;gt;
...
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;spring-boot-starter-web&amp;lt;/artifactId&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;spring-boot-starter-security&amp;lt;/artifactId&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;spring-boot-starter-actuator&amp;lt;/artifactId&amp;gt;
&amp;lt;/dependency&amp;gt;
...
&amp;lt;plugin&amp;gt;
    &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;spring-boot-maven-plugin&amp;lt;/artifactId&amp;gt;
&amp;lt;/plugin&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Upon restarting my app, I got an error about spring-security.xml using a 3.1 XSD. I fixed it by changing to 3.2. Next, I wanted to eliminate web.xml. First of all, I created an &lt;code&gt;ApplicationInitializer&lt;/code&gt; so the WAR could be started from the command line.&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
package com.raibledesigns.boot.config;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableAutoConfiguration
@ComponentScan
public class ApplicationInitializer extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(ApplicationInitializer.class);
    }

    public static void main(String[] args) {
        SpringApplication.run(ApplicationInitializer.class, args);
    }
}
&lt;/pre&gt;
&lt;p&gt;However, after adding this, I received the following error on startup:&lt;/p&gt;
&lt;pre class=&quot;brush: shell&quot;&gt;
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 
&apos;org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor&apos;: 
Invocation of init method failed; nested exception is 
java.lang.AbstractMethodError: org.hibernate.validator.internal.engine.ConfigurationImpl
.getDefaultParameterNameProvider()Ljavax/validation/ParameterNameProvider;
&lt;/pre&gt;
&lt;p&gt;Adding hibernate-validator as a dependency solved this problem:&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.hibernate&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;hibernate-validator&amp;lt;/artifactId&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/pre&gt;
&lt;p&gt;To configure Spring Security without web.xml and spring-security.xml, I created &lt;code&gt;WebSecurityConfig.java&lt;/code&gt;:
&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
package com.raibledesigns.boot.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
@Order(Ordered.LOWEST_PRECEDENCE - 6)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers(&quot;/&quot;, &quot;/home&quot;).permitAll()
                .antMatchers(&quot;/v1.0/**&quot;).hasRole(&quot;USER&quot;)
                .anyRequest().authenticated();
        http.httpBasic().realmName(&quot;My API&quot;);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder authManagerBuilder) throws Exception {
        authManagerBuilder.inMemoryAuthentication()
                .withUser(&quot;test&quot;).password(&quot;test123&quot;).roles(&quot;USER&quot;);
    }
}
&lt;/pre&gt;
&lt;p&gt;To configure Jersey without web.xml, I created a &lt;code&gt;JerseyConfig&lt;/code&gt; class:&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
package com.raibledesigns.boot.config;

import org.glassfish.jersey.filter.LoggingFilter;
import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.ServerProperties;

import javax.ws.rs.ApplicationPath;

@ApplicationPath(&quot;/v1.0&quot;)
public class JerseyConfig extends ResourceConfig {

    public JerseyConfig() {
        packages(&quot;com.raibledesigns.boot.service&quot;);
        property(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true);
        property(ServerProperties.JSON_PROCESSING_FEATURE_DISABLE, false);
        property(ServerProperties.MOXY_JSON_FEATURE_DISABLE, true);
        property(ServerProperties.WADL_FEATURE_DISABLE, true);
        register(LoggingFilter.class);
        register(JacksonFeature.class);
    }
}
&lt;/pre&gt;
&lt;p&gt;Finally, I created &lt;code&gt;MvcConfig.java&lt;/code&gt; to set the welcome page.&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
package com.raibledesigns.boot.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController(&quot;/&quot;).setViewName(&quot;index&quot;);
    }
}
&lt;/pre&gt;
&lt;p&gt;To cleanup, I deleted &lt;code&gt;src/main/webapp/WEB-INF&lt;/code&gt; and created &lt;code&gt;src/main/resources/logback.xml&lt;/code&gt;:
&lt;/p&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;configuration&amp;gt;
    &amp;lt;include resource=&quot;org/springframework/boot/logging/logback/base.xml&quot;/&amp;gt;
    &amp;lt;logger name=&quot;org.springframework.boot&quot; level=&quot;INFO&quot;/&amp;gt;
    &amp;lt;logger name=&quot;org.springframework.security&quot; level=&quot;ERROR&quot;/&amp;gt;
&amp;lt;/configuration&amp;gt;
&lt;/pre&gt;&lt;p&gt;Since Boot doesn&apos;t support JSP out-of-the-box, I renamed my index.jsp file to index.html and changed the URL in it to point to &quot;/v1.0/hello&quot;. I was pleased to see that everything worked nicely. I learned shortly after that I could remove the Spring BOM since Spring Boot &lt;a href=&quot;https://twitter.com/rob_winch/status/410609696639184896&quot;&gt;uses a &amp;lt;spring.version&amp;gt; property to control its Spring version&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;The only issue I found is when started the app with &quot;mvn package &amp;&amp; java -jar target/app.war&quot;, it failed to initialize Jersey. I tried adding a @Bean for the servlet:&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
@Bean
public ServletRegistrationBean jerseyServlet() {
    ServletRegistrationBean registration = new ServletRegistrationBean(new ServletContainer(), &quot;/v1.0/*&quot;);
    registration.addInitParameter(ServletProperties.JAXRS_APPLICATION_CLASS, JerseyConfig.class.getName());
    return registration;
}
&lt;/pre&gt;
&lt;p&gt;Unfortunately, when running it using &quot;java -jar&quot;, I get the following error:
&lt;/p&gt;
&lt;pre class=&quot;brush: shell&quot;&gt;
org.glassfish.hk2.api.MultiException: A MultiException has 1 exceptions.  They are:
1. org.glassfish.jersey.server.internal.scanning.ResourceFinderException: 
java.io.FileNotFoundException: /.../target/app.war!/WEB-INF/classes (No such file or directory)
	at org.jvnet.hk2.internal.Utilities.justCreate(Utilities.java:869)
	at org.jvnet.hk2.internal.ServiceLocatorImpl.create(ServiceLocatorImpl.java:814)
	at org.jvnet.hk2.internal.ServiceLocatorImpl.createAndInitialize(ServiceLocatorImpl.java:906)
	at org.jvnet.hk2.internal.ServiceLocatorImpl.createAndInitialize(ServiceLocatorImpl.java:898)
	at org.glassfish.jersey.server.ApplicationHandler.createApplication(ApplicationHandler.java:300)
	at org.glassfish.jersey.server.ApplicationHandler.&amp;lt;init&amp;gt;(ApplicationHandler.java:279)
	at org.glassfish.jersey.servlet.WebComponent.&amp;lt;init&amp;gt;(WebComponent.java:302)
&lt;/pre&gt;
&lt;p&gt;
This seems strange since there is a WEB-INF/classes in my WAR. Regardless, this is not a Boot problem per se, but more of a Jersey issue. From one of the Boot developers:&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
The whole idea with Boot is that servlets are just a transport - they are a means to an end, and hopefully not the only one - the &quot;container&quot; is Spring, not the servlet container. We probably could add some form of support for SCI but only by hacking the containers since the spec really doesn&apos;t allow for much control of their lifecycle. It hasn&apos;t been a priority so far.
&lt;/p&gt;
&lt;p id=&quot;summary&quot;&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;br/&gt;
I hope this article is useful to see how you to upgrade your Java webapps to use Spring 4 and Spring Boot. I&apos;ve created a &lt;a href=&quot;https://github.com/mraible/boot-makeover&quot;&gt;boot-makeover project on GitHub&lt;/a&gt; with all the code mentioned. You can also &lt;a href=&quot;https://github.com/mraible/boot-makeover/commits/master&quot;&gt;view the commits&lt;/a&gt; for each step. &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/upgrading_grails_from_2_0</id>
        <title type="html">Upgrading Grails from 2.0 to 2.2</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/upgrading_grails_from_2_0"/>
        <published>2013-03-22T09:16:37-06:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="grails" scheme="http://roller.apache.org/ns/tags/" />
        <category term="devoxx" scheme="http://roller.apache.org/ns/tags/" />
        <category term="smackdown" scheme="http://roller.apache.org/ns/tags/" />
        <category term="upgrade" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">&lt;a href=&quot;http://grails.org&quot;&gt;&lt;img src=&quot;//static.raibledesigns.com/repository/images/grails-logo-20130322.png&quot; alt=&quot;Grails&quot; width=&quot;166&quot; height=&quot;39&quot; class=&quot;picture&quot; style=&quot;border: 0&quot;/&gt;&lt;/a&gt;
In preparation for my &lt;a href=&quot;http://www.devoxx.com/display/FR13/Play+Framework+vs.+Grails+Smackdown&quot;&gt;Grails vs. Play Smackdown&lt;/a&gt; at &lt;a href=&quot;http://www.devoxx.com/display/FR13/Home&quot;&gt;Devoxx France&lt;/a&gt; next week, I recently upgraded my &lt;a href=&quot;https://github.com/jamesward/happytrails/tree/grails2&quot;&gt;Grails version&lt;/a&gt; of Happy Trails from Grails 2.0.3 to Grails 2.2.1. I ran into a few issues along the way and figured I&apos;d document them here to help others out.
&lt;/p&gt;
&lt;p id=&quot;source&quot;&gt;&lt;strong&gt;Fixing the source&lt;/strong&gt;&lt;br/&gt;The first issue I ran into was Spock and Groovy 2 incompatibilities. &lt;/p&gt;
&lt;pre class=&quot;brush: shell&quot;&gt;
| Resolving plugin JAR dependencies
| Error WARNING: Dependencies cannot be resolved for plugin [mail] due to error: startup failed:
Could not instantiate global transform class org.spockframework.compiler.SpockTransform specified at jar:file:/Users/mraible/.grails/ivy-cache/org.spockframework/spock-core/jars/spock-core-0.7-groovy-1.8.jar!/META-INF/services/org.codehaus.groovy.transform.ASTTransformation  because of exception org.spockframework.util.IncompatibleGroovyVersionException: The Spock compiler plugin cannot execute because Spock 0.7.0-groovy-1.8 is not compatible with Groovy 2.0.7. For more information, see http://versioninfo.spockframework.org
&lt;/pre&gt;
&lt;p&gt;I &lt;a href=&quot;http://stackoverflow.com/questions/15410368/upgrading-from-grails-2-0-3-to-2-2-1-server-access-error-connection-refused&quot;&gt;posted the problem to StackOverflow&lt;/a&gt; and got a response almost immediately. While &lt;a href=&quot;https://github.com/geb/geb-example-grails/pull/7/files&quot;&gt;this pull request&lt;/a&gt; helped me quite a bit, it was ultimately caused by my vision: I had two &quot;geb-spock&quot; dependencies listed in &lt;em&gt;BuildConfig.groovy&lt;/em&gt; with different groupIds.&lt;/p&gt;
&lt;p&gt;At this point, I also moved all my plugin dependencies from &lt;em&gt;application.properties&lt;/em&gt; to &lt;em&gt;BuildConfig.groovy&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The next problem I ran into was &lt;a href=&quot;http://stackoverflow.com/questions/15413701/upgrading-from-grails-2-0-3-to-2-2-1-tests-failing&quot;&gt;a unit test and functional tests failing&lt;/a&gt;. The unit testing issue was caused by my Direction model not being in the tests @Mock annotation. After I added it, validation kicked and I recognized my test was invalid. I added @Ignore and continued.&lt;/p&gt;
&lt;p&gt;The functional test seemed to be seemed to be caused by Geb and it trying to use the Chrome Driver. One of my tests didn&apos;t work with the default HtmlUnitDriver, so I used the ChromeDriver for the one test.&lt;/p&gt;
&lt;pre&gt;
| Running 11 spock tests... 6 of 11
| Failure:  signup as a new user(happytrails.AuthenticatedUserSpec)
|  org.openqa.selenium.WebDriverException: Unable to either launch or connect to Chrome. Please check that ChromeDriver is up-to-date. Using Chrome binary at: /Applications/Google Chrome.app/Contents/MacOS/Google Chrome (WARNING: The server did not provide any stacktrace information)
Command duration or timeout: 45.66 seconds
Build info: version: &apos;2.27.0&apos;, revision: &apos;18259&apos;, time: &apos;2012-12-05 11:30:53&apos;
System info: os.name: &apos;Mac OS X&apos;, os.arch: &apos;x86_64&apos;, os.version: &apos;10.8.2&apos;, java.version: &apos;1.7.0_04&apos;
Driver info: org.openqa.selenium.chrome.ChromeDriver
    at org.openqa.selenium.remote.ErrorHandler.createThrowable(ErrorHandler.java:187)
    at org.openqa.selenium.remote.ErrorHandler.throwIfResponseFailed(ErrorHandler.java:145)
    at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:533)
    at org.openqa.selenium.remote.RemoteWebDriver.startSession(RemoteWebDriver.java:216)
    at org.openqa.selenium.remote.RemoteWebDriver.&lt;init&gt;(RemoteWebDriver.java:111)
    at org.openqa.selenium.remote.RemoteWebDriver.&lt;init&gt;(RemoteWebDriver.java:115)
    at org.openqa.selenium.chrome.ChromeDriver.&lt;init&gt;(ChromeDriver.java:161)
    at org.openqa.selenium.chrome.ChromeDriver.&lt;init&gt;(ChromeDriver.java:107)
    at happytrails.AuthenticatedUserSpec.signup as a new user(AuthenticatedUserSpec.groovy:25)
&lt;/pre&gt;
&lt;p&gt;Even when running &quot;grails -Dgeb.env=chrome test-app&quot;, this still happened. This was caused by the fact that I had &lt;em&gt;GebConfig.groovy&lt;/em&gt; in test/functional/happytrails. Move it to test/functional solved the problem. I also discovered that I know longer needed Chrome to get this test to pass. Apparently, the &lt;a href=&quot;http://fbflex.wordpress.com/2013/03/18/how-to-configure-webdriver-in-grails-for-your-geb-tests/&quot;&gt;HtmlUnitDriver has issues with Grails 2.2&lt;/a&gt;, but it seems to work for me.&lt;/p&gt;
&lt;p&gt;After getting the Geb configuration fixed, I ran into a functional test failure:&lt;/p&gt;
&lt;pre&gt;
| Running 11 spock tests... 5 of 11
| Failure:  click signup link(happytrails.AuthenticatedUserSpec)
|  org.openqa.selenium.ElementNotVisibleException: Element must be displayed to click (WARNING: The server did not provide any stacktrace information)
&lt;/pre&gt;
&lt;p&gt;Even though I could see the &quot;signup&quot; link when I ran &quot;grails run-app&quot;, I could see that it didn&apos;t show up when running tests in Chrome. This turned out to be caused by an extraneous &lt;code&gt;&amp;lt;div class=&quot;nav-collapse&quot;&gt;&lt;/code&gt; I had in my main.gsp. Removing it solved the problem. It&apos;s strange that this never showed up with Grails 2.0. My only guess is that Geb someone didn&apos;t look at the visibility of the element.&lt;/p&gt;
&lt;p&gt;The last testing-related issue I ran into was a &lt;code&gt;InvalidElementStateException&lt;/code&gt;:
&lt;/p&gt;
&lt;pre&gt;
| Running 11 spock tests... 7 of 11
| Failure:  add new route to region(happytrails.AuthenticatedUserSpec)
|  org.openqa.selenium.InvalidElementStateException: Element must be user-editable in order to clear it. (WARNING: The server did not provide any stacktrace information)
&lt;/pre&gt;
&lt;p&gt;I was able to fix this by changing &lt;em&gt;AddRoutePage.groovy&lt;/em&gt; from:&lt;/p&gt;
&lt;pre class=&quot;brush: groovy&quot;&gt;
static content = {
    createButton(to: ShowRoutePage) { create() }
    name { value(&quot;Name&quot;) }
    distance { value(&quot;Distance&quot;) }
    location { value(&quot;Location&quot;) }
}
&lt;/pre&gt;
&lt;p&gt;To:&lt;/p&gt;
&lt;pre class=&quot;brush: groovy&quot;&gt;
static content = {
    createButton(to: ShowRoutePage) { create() }
    form { $(&quot;form&quot;) }
}
&lt;/pre&gt;
&lt;p&gt;And then referencing name, distance and location accordingly (form.name, etc.) in &lt;em&gt;AuthenticatedUserSpec.groovy&lt;/em&gt;.&lt;/p&gt;
&lt;p id=&quot;cloudbees&quot;&gt;&lt;strong&gt;CloudBees&lt;/strong&gt;&lt;br/&gt;
After I had everything working locally, I logged into Jenkins on &lt;a href=&quot;http://www.cloudbees.com/&quot;&gt;CloudBees&lt;/a&gt;. Since I hadn&apos;t used it in a while, I had to wait a bit while my Jenkins server was re-commissioned. Once it was up, I tried to select Grails 2.2.1 to build with, but found it wasn&apos;t available. After a &lt;a href=&quot;https://twitter.com/mraible/status/312636120112431104&quot;&gt;tweeting this&lt;/em&gt;, I learned about &lt;a href=&quot;http://grails.org/doc/2.1.0/ref/Command%20Line/wrapper.html&quot;&gt;Grails Wrapper&lt;/a&gt;, found that the latest Grails Jenkins plugin supported it and got everything working. I later discovered that CloudBees does support Grails 2.2.1, I just needed to setup another Grails installation to automatically download and install 2.2.1.&lt;/p&gt;
&lt;p id=&quot;heroku&quot;&gt;&lt;strong&gt;Heroku&lt;/strong&gt;&lt;br/&gt;
The last two issues I ran into were with Heroku. Since I was upgrading everything, I wanted Grails to build/run under Java 7 and use Servlet 3. I changed the appropriate properties in &lt;em&gt;BuildConfig.groovy&lt;/em&gt;, &lt;a href=&quot;https://devcenter.heroku.com/articles/add-java-version-to-an-existing-maven-app&quot;&gt;configured Heroku&lt;/a&gt; and deployed. No dice.&lt;/p&gt;
&lt;pre&gt;
Error Compilation error: startup failed:
Invalid commandline usage for javac.
javac: invalid source release: 1.7
Usage: javac &lt;options&gt; &lt;source files&gt;
use -help for a list of possible options
&lt;/pre&gt;
&lt;p&gt;Sidenote: I tried building with Java 8 on CloudBees, but discovered the searchable plugin doesn&apos;t support it.&lt;/p&gt;
&lt;pre&gt;
Compile error during compilation with javac.
/scratch/jenkins/workspace/Happy Trails - Grails 2/work/plugins/searchable-0.6.4/src/java/grails/plugin/searchable/internal/compass/index/DefaultUnindexMethod.java:94: error: reference to delete is ambiguous
                    session.delete(query);
                           ^
  both method delete(CompassQuery) in CompassOperations and method delete(CompassQuery) in CompassIndexSession match
&lt;/pre&gt;
&lt;p&gt;As far as Servlet 3, it was pretty obvious that the Jetty version Heroku uses for Grails doesn&apos;t support it. Therefore, I reverted back to Servlet 2.5.&lt;/p&gt;
&lt;pre&gt;
java.lang.NoClassDefFoundError: javax/servlet/AsyncContext
	at org.codehaus.groovy.util.LazyReference.getLocked(LazyReference.java:46)
	at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:59)
	at java.lang.Class.privateGetDeclaredMethods(Class.java:2444)
&lt;/pre&gt;
&lt;p&gt;I sent the Java 7 issue to Heroku Support a few days ago but haven&apos;t heard back yet.&lt;/p&gt;
&lt;p id=&quot;summary&quot;&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;br/&gt;
While upgrading Grails from 2.0 to 2.2 wasn&apos;t as easy as expected, it is understandable. After all, Grails 2.2 ships with Groovy 2.0, which has a &lt;a href=&quot;http://www.infoq.com/articles/new-groovy-20&quot;&gt;bunch of new features&lt;/a&gt; itself. All the issues I ran into were fairly easy to solve, except for Java 7 on Heroku. But hey, what do you expect from a free hosting service?&lt;/p&gt;
&lt;p&gt;If you&apos;re at Devoxx France next week, I look forward to &lt;a href=&quot;http://www.devoxx.com/display/FR13/Play+Framework+vs.+Grails+Smackdown&quot;&gt;sharing our research&lt;/a&gt; on Grails 2.2.1 vs. Play 2.1.0. </content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/the_html5_roadshow_rocks</id>
        <title type="html">The HTML5 Roadshow Rocks!</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/the_html5_roadshow_rocks"/>
        <published>2013-03-14T15:16:58-06:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="html5" scheme="http://roller.apache.org/ns/tags/" />
        <category term="scottdavis" scheme="http://roller.apache.org/ns/tags/" />
        <category term="html5roadshow" scheme="http://roller.apache.org/ns/tags/" />
        <category term="kylesimpson" scheme="http://roller.apache.org/ns/tags/" />
        <category term="training" scheme="http://roller.apache.org/ns/tags/" />
        <category term="javascript" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://html5roadshow.com/&quot; title=&quot;Canyonland&apos;s Sunset Road by Trish of McGinity Photo&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8248/8557138083_8b32d4a06a_o.png&quot; width=&quot;500&quot; height=&quot;183&quot; alt=&quot;Canyonland&apos;s Sunset Road by Trish of McGinity Photo&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
In early February, as I was creating my &lt;a href=&quot;http://raibledesigns.com/rd/entry/the_modern_java_web_developer&quot;&gt;Modern Java Web Developer&lt;/a&gt; presentation, I came across a &lt;a href=&quot;http://www.eventbrite.com/event/1470493285/eorg&quot;&gt;training event&lt;/a&gt; that was similarly named. Since I happened to know the instructor, I shot him off an email asking more about it. He promptly replied a week later with an intriguing description. The following sentence really stood out for me:
&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
They are NOT beginning workshops -- newbie information is easily found online, in books, at UG meetings, etc. They are targeted at working professionals who want to take their game to the next level. 
&lt;/p&gt;
&lt;p&gt;The other thing that really struck me about this training was it was &#224; la carte; meaning you could pick and pay for just the days you wanted to attend. Scott gave me a two-for-one deal and I signed up for the following workshops:&lt;/p&gt;
&lt;ul&gt;

&lt;li&gt;&lt;a href=&quot;http://html5roadshow.com/workshops#advanced_javascript&quot;&gt;Advanced JavaScript: The &quot;What You Need To Know&quot; Parts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://html5roadshow.com/workshops#airplane_mode_html5&quot;&gt;Airplane-Mode HTML5: Mobile Web Development for Tablets and Smartphones&lt;/a&gt;&lt;/li&gt;

&lt;/ul&gt;
&lt;p&gt;Normally, attending a training course would have an opportunity cost for me. However, since I work remotely and it doesn&apos;t matter &lt;em&gt;where&lt;/em&gt; I work, I decided to work from the class. It turned out to be a great idea because it had the same feel of a productive day at the coffeeshop, but it was a coffeeshop full of knowledge. Instead of looking up and doing some people watching or getting distracted by social media, I would look up and learn something new. I actually ended up getting more done in those two training classes than I normally do at my &lt;a href=&quot;http://www.businessatthrive.com/v3/&quot;&gt;LoDo Office&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advanced JavaScript&lt;/strong&gt;&lt;br/&gt;The Advanced JavaScript workshop was taught by &lt;a href=&quot;http://getify.me/&quot;&gt;Kyle Simpson&lt;/a&gt;. This was my first time meeting Kyle and I was immediately impressed by his resume, especially his &lt;em&gt;worked on Firefox Developer Tools for 9 months&lt;/em&gt; credentials. He started out talking about good JavaScript documentation on sites such as &lt;a href=&quot;https://developer.mozilla.org/&quot;&gt;MDN&lt;/a&gt; and &lt;a href=&quot;https://github.com/rwldrn/idiomatic.js&quot;&gt;Principles of Writing Consistent, Idiomatic JavaScript&lt;/a&gt;. Tip: append &quot;mdn&quot; to any searches for JavaScript topics and you&apos;re likely to get better results. Not only that, but MDN is a wiki so you can improve it too. From there, we moved onto DOM Events, event propagation (e.g. bubbling vs. capturing), event management, scopes, &lt;code&gt;this&lt;/code&gt;, closures and design patterns. It was a very deep dive into JavaScript and I enjoyed every minute of it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Airplane-Mode HTML5&lt;/strong&gt;&lt;br/&gt;The Airplane-Mode HTML5 workshop was taught by &lt;a href=&quot;http://thirstyhead.com&quot;&gt;Scott Davis&lt;/a&gt; (his site is currently down, should be fixed next week). Scott talked about many things I was already familiar with, but I still managed to learn a bunch of new tips and tricks. He started by having us install &lt;a href=&quot;http://nodejs.org/&quot;&gt;Node&lt;/a&gt;, &lt;a href=&quot;http://volojs.org/&quot;&gt;volo&lt;/a&gt; and creating projects. He showed how &lt;code&gt;package.json&lt;/code&gt; is the JS project&apos;s equivalent of &lt;code&gt;pom.xml&lt;/code&gt;. 
&lt;/p&gt;
&lt;p&gt;Next, he plunged into viewports, orientation and how to use CSS3 media queries. I&apos;ve always used min-device-width and max-device-width in my media queries, so &lt;code&gt;orientation: portrait&lt;/code&gt; and &lt;code&gt;orientation: landscape&lt;/code&gt; was a useful tip. He taught us about CSS3&apos;s pseudo-classes (esp. for validation) and pointed us to the &lt;a href=&quot;http://alistapart.com/article/forward-thinking-form-validation&quot;&gt;Forward Thinking Form Validation&lt;/a&gt; article. We learned about using &lt;a href=&quot;http://fmbip.com&quot;&gt;findmebyIP.com&lt;/a&gt; and how it can be a useful tool for finding your browser&apos;s capabilities, particularly when you&apos;re in a black box environment (e.g. a browser on a TV). I knew about &lt;a href=&quot;http://modernizr.com/&quot;&gt;Modernizr&lt;/a&gt;, but wasn&apos;t aware of &lt;a href=&quot;https://github.com/phiggins42/has.js/&quot;&gt;has.js&lt;/a&gt; for JavaScript feature detection. 
&lt;/p&gt;
&lt;p&gt;Scott talked about many more topics, from HTML5 forms and mobile links to geolocation and maps. He ended the day talking about local storage and application cache. 
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Good&lt;/strong&gt;&lt;br/&gt;
First of all, the price was perfect at around $250 per day. The best part for me was that I learned more than I expected to. Not only that, but I didn&apos;t have to endure any opportunity cost to attend these workshops. There were times I wish I didn&apos;t have to work, but it was cool to have &quot;learning stuff&quot; as a distraction rather than social media.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Bad&lt;/strong&gt;&lt;br/&gt;
The Location. I ride my bike to work year-round and only sit in traffic a few times per year. So I know I&apos;m biased. The location was in Louisville and while it should be only a 30-minute commute, I spent 2 hours in the car each day. Once I got there, the facility was great, the internet was fast and the lunch choices were splendid.
&lt;/p&gt;
&lt;p&gt;To see if the &lt;a href=&quot;http://html5roadshow.com/&quot;&gt;HTML5 Roadshow&lt;/a&gt; is coming to your town, checkout &lt;a href=&quot;http://thirstyhead.eventbrite.com/&quot;&gt;ThirstyHead on Eventbrite&lt;/a&gt; or &lt;a href=&quot;http://twitter.com/html5roadshow&quot;&gt;follow them on Twitter&lt;/a&gt;.</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/integrating_gwt_into_appfuse</id>
        <title type="html">Integrating GWT into AppFuse</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/integrating_gwt_into_appfuse"/>
        <published>2013-03-07T18:49:28-07: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="gwt" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">&lt;a href=&quot;https://developers.google.com/web-toolkit/images/gwt-logo.png&quot;&gt;&lt;img src=&quot;//developers.google.com/web-toolkit/images/gwt-logo.png&quot; class=&quot;picture&quot;  height=&quot;100&quot; width=&quot;100&quot;&gt;&lt;/a&gt;
I&apos;ve been interested in integrating &lt;a href=&quot;https://developers.google.com/web-toolkit/&quot;&gt;GWT&lt;/a&gt; into &lt;a href=&quot;http://appfuse.org&quot;&gt;AppFuse&lt;/a&gt; ever since &lt;a href=&quot;http://raibledesigns.com/rd/entry/gwt_and_appfuse&quot;&gt;I blogged about it 4 years ago&lt;/a&gt;. A few months after that post, I wrote about &lt;a href=&quot;http://raibledesigns.com/rd/entry/enhancing_evite_com_with_gwt&quot;&gt;Enhancing Evite.com with GWT and Grails&lt;/a&gt;. After Evite, I had a gig near Boston where I developed with GXT for the remainder of the year. When all was said and done, I ended up spending a year with GWT and really enjoyed my experience. I haven&apos;t used it much since.&lt;/p&gt;
&lt;p&gt;GWT is &lt;a href=&quot;http://appfuse.org/display/APF/Roadmap&quot;&gt;scheduled to be integrated into AppFuse&lt;/a&gt; in version 4.0. That&apos;s quite a ways off. The good news is you might not have to wait that long, thanks to &lt;a href=&quot;https://github.com/ivangsa&quot;&gt;Iv&#225;n Garc&#237;a Sainz-Aja&lt;/a&gt;. Iv&#225;n let us know about his work a couple weeks ago in an &lt;a href=&quot;http://appfuse.547863.n4.nabble.com/Creating-a-new-archetype-td4656359.html&quot;&gt;email to the appfuse-dev&lt;/a&gt; mailing list.&lt;/p&gt;
&lt;div class=&quot;quote&quot;&gt;
&lt;p&gt;
It&apos;s still work in progress but it has already most of AppFuse functionality.. 
&lt;/p&gt;&lt;p&gt;
If you want to give it a try 
&lt;/p&gt;&lt;p&gt;
&lt;a href=&quot;https://github.com/ivangsa/appfuse.git&quot;&gt;https://github.com/ivangsa/appfuse.git&lt;/a&gt;
&lt;/p&gt;&lt;p&gt;
the quickest way to have a go would be&lt;/p&gt;
&lt;pre&gt;
web/gwt&gt; mvn -P gwtDebug -Dgwt.inplace=true gwt:compile jetty:run  
&lt;/pre&gt;
&lt;p&gt;
at the moment it still requires this fork of gwt-bootstrap to be compiled first 
&lt;/p&gt;&lt;p&gt;
&lt;a href=&quot;https://github.com/ivangsa/gwt-bootstrap.git&quot;&gt;https://github.com/ivangsa/gwt-bootstrap.git&lt;/a&gt;
&lt;/p&gt;&lt;p&gt;
It needs a lot of testing yet but it&apos;s getting quite there 
&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;As you can imagine, I was very excited to hear about Iv&#225;n&apos;s work. So I cloned his repo, built gwt-bootstrap locally and checked it out. Functionality wise, it was great! However, when I dug into the source code, I found a whole lotta code. 
&lt;/p&gt;
&lt;p&gt;
To see how the GWT flavor compared to the other implementations in AppFuse, I &lt;a href=&quot;https://gist.github.com/mraible/5033218&quot;&gt;created a cloc report&lt;/a&gt; on the various web frameworks in AppFuse. I&apos;m sure these reports could be adjusted to be more accurate, but I believe they give a good general overview. I posted some graphs that displays my findings in visual form.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;

&lt;a href=&quot;http://farm9.staticflickr.com/8090/8538497066_be60cb73da_o.png&quot; title=&quot;Lines of Java&quot; rel=&quot;lightbox[appfuse-loc]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8090/8538497066_4f2a6ff71e_m.jpg&quot; width=&quot;240&quot; height=&quot;189&quot; alt=&quot;Lines of Java&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

&lt;a href=&quot;http://farm9.staticflickr.com/8235/8537391497_deaa49c22d_o.png&quot; title=&quot;Number of Files by mraible, on Flickr&quot; rel=&quot;lightbox[appfuse-loc]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8235/8537391497_3bbafe2383_m.jpg&quot; width=&quot;240&quot; height=&quot;189&quot; alt=&quot;Number of Files&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot;&gt;&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;When I sent this to the mailing list, Ivan &lt;a href=&quot;http://appfuse.547863.n4.nabble.com/Creating-a-new-archetype-tt4656359.html#a4656374&quot;&gt;responded that it was a lot of code&lt;/a&gt; and estimated &lt;em&gt;12 new files&lt;/em&gt; would be needed to &lt;abbr title=&quot;Create, Retrieve, Update, Delete&quot;&gt;CRUD&lt;/a&gt; an entity. This sure seems like a lot to me, but he &lt;a href=&quot;http://appfuse.547863.n4.nabble.com/GWT-RESTFull-backend-tt4656418.html&quot;&gt;defended this yesterday&lt;/a&gt; and noted that his implementation follows many of GWT&apos;s latest best 
practices: MVP pattern, Activities and Places, EventBus, Gin and Guice. He also shared a &lt;a href=&quot;https://github.com/ivangsa/appfuse/wiki&quot;&gt;wiki page&lt;/a&gt; with explanations and diagrams of how things work.&lt;/p&gt;
&lt;p&gt;The reason I&apos;m writing this post is to get more feedback on this implementation. First of all, &lt;em&gt;does GWT really require this much code&lt;/em&gt;? Secondly, are there other GWT implementations that reduce a lot of the boilerplate? &lt;a href=&quot;https://code.google.com/p/smartgwt/&quot;&gt;SmartGWT&lt;/a&gt;, &lt;a href=&quot;https://vaadin.com/home&quot;&gt;Vaadin&lt;/a&gt;* and &lt;a href=&quot;http://www.jboss.org/errai&quot;&gt;Errai&lt;/a&gt; come to mind. 
&lt;/p&gt;
&lt;p&gt;If you were starting a new GWT project and using AppFuse, how would &lt;em&gt;you&lt;/em&gt; want it implemented?&lt;/p&gt;
&lt;p style=&quot;font-size: .9em; border-top: 1px dotted silver; padding-top: 5px; color: #666&quot;&gt;* Vaadin 7 claims it &lt;a href=&quot;http://www.infoq.com/news/2013/02/vaadin-7&quot;&gt;can be used as a drop-in replacement for GWT&lt;/a&gt;. I tried &lt;a href=&quot;https://gist.github.com/mraible/5113636&quot;&gt;replacing the gwt-servlet and gwt-user dependencies&lt;/a&gt; with Vaadin&apos;s, but it &lt;a href=&quot;https://gist.github.com/mraible/5113607&quot;&gt;didn&apos;t work&lt;/a&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/2012_a_year_in_review</id>
        <title type="html">2012 - A Year in Review</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/2012_a_year_in_review"/>
        <published>2013-01-08T13:15:24-07:00</published>
        <updated>2014-05-08T19:47:26-06:00</updated> 
        <category term="/Roller" label="Roller" />
        <category term="2012" scheme="http://roller.apache.org/ns/tags/" />
        <category term="yearinreview" scheme="http://roller.apache.org/ns/tags/" />
        <category term="roller" scheme="http://roller.apache.org/ns/tags/" />
        <category term="blogging" scheme="http://roller.apache.org/ns/tags/" />
        <summary type="html">&lt;p&gt;I wrote my first &lt;a href=&quot;http://raibledesigns.com/rd/tags/yearinreview&quot;&gt;year in review&lt;/a&gt; blog entry way back in &lt;a href=&quot;http://raibledesigns.com/rd/entry/2005_a_year_in_review&quot;&gt;2005&lt;/a&gt;. That means this year&apos;s is number 8. Since they keep getting longer every year, I figured I&apos;d try something different this year and use sections similar to &lt;a href=&quot;http://remysharp.com/2012/12/31/my-2012/&quot;&gt;Remy Sharp&lt;/a&gt;. &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/2012_a_year_in_review#professional&quot;&gt;Professional&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/2012_a_year_in_review#business&quot;&gt;Business&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/2012_a_year_in_review#speaking&quot;&gt;Speaking&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/2012_a_year_in_review#projects&quot;&gt;Projects&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/2012_a_year_in_review#personal&quot;&gt;Personal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/2012_a_year_in_review#2013&quot;&gt;2013&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;professional&quot;&gt;Professional&lt;/h2&gt;
&lt;p&gt;I spent the entirety of the year with one client: Taleo. &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 them in February&lt;/a&gt;. In June, the transition to Oracle happened. My tasks and projects haven&apos;t changed much since the transition, but it has been a real pain to get paid on time. My contract with them is through the end of May. I hope to take July off (to get married) and August off (to honeymoon) and start a new gig in September.
&lt;/p&gt;
&lt;p&gt;I did minimal Java work throughout the year and spent most of my time doing CSS and JavaScript. I love doing front-end work much more than back-end, so day-to-day, it was very satisfying. 
&lt;/p&gt;</summary>
        <content type="html">I wrote my first &lt;a href=&quot;http://raibledesigns.com/rd/tags/yearinreview&quot;&gt;year in review&lt;/a&gt; blog entry way back in &lt;a href=&quot;http://raibledesigns.com/rd/entry/2005_a_year_in_review&quot;&gt;2005&lt;/a&gt;. That means this year&apos;s is number 8. Since they keep getting longer every year, I figured I&apos;d try something different this year and use sections similar to &lt;a href=&quot;http://remysharp.com/2012/12/31/my-2012/&quot;&gt;Remy Sharp&lt;/a&gt;. &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/2012_a_year_in_review#professional&quot;&gt;Professional&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/2012_a_year_in_review#business&quot;&gt;Business&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/2012_a_year_in_review#speaking&quot;&gt;Speaking&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/2012_a_year_in_review#projects&quot;&gt;Projects&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/2012_a_year_in_review#personal&quot;&gt;Personal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/2012_a_year_in_review#2013&quot;&gt;2013&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;professional&quot;&gt;Professional&lt;/h2&gt;
&lt;p&gt;I spent the entirety of the year with one client: Taleo. &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 them in February&lt;/a&gt;. In June, the transition to Oracle happened. My tasks and projects haven&apos;t changed much since the transition, but it has been a real pain to get paid on time. My contract with them is through the end of May. I hope to take July off (to get married) and August off (to honeymoon) and start a new gig in September.
&lt;/p&gt;
&lt;p&gt;I did minimal Java work throughout the year and spent most of my time doing CSS and JavaScript. I love doing front-end work much more than back-end, so day-to-day, it was very satisfying. 
&lt;/p&gt;

&lt;h3 id=&quot;business&quot;&gt;Business&lt;/h3&gt;
&lt;p&gt;Raible Designs had a great year, our best ever financially. I hired Trish as an assistant in January, doubling the size of the company. We had our Annual Shareholders Meeting on a &lt;a href=&quot;http://raibledesigns.com/rd/entry/cruising_around_the_western_caribbean&quot;&gt;Disney Cruise&lt;/a&gt; in April. I have no plans to do increase the size of the company in 2013, mostly because I enjoy not having the responsibility of employees. I&apos;ve employed sub-contractors in the past and it always seems to create more of a headache than it&apos;s worth.&lt;/p&gt;
&lt;p&gt;
&lt;a href=&quot;http://www.dailymile.com/people/mraible/training/2012/summary&quot; title=&quot;My Daily Mile 2012 Year End Report&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8227/8360832357_858f34a10e_t.jpg&quot; width=&quot;100&quot; height=&quot;52&quot; alt=&quot;Daily Mile 2012 Year End Report&quot; class=&quot;picture&quot;&gt;&lt;/a&gt;
We moved our offices from downtown Littleton to &lt;a href=&quot;http://www.businessatthrive.com/v3/&quot;&gt;Thrive&lt;/a&gt; (in LoDo) last April and I plan on staying there. It&apos;s an easy 6-mile bicycle commute and the guys there have been great to work with. I highly recommend it if you&apos;re looking for co-working space.&lt;/p&gt;

&lt;h3 id=&quot;speaking&quot;&gt;Speaking&lt;/h3&gt;
&lt;p&gt;I spoke at 5 events in 2012:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Silicon Valley Spring User Group on &lt;a href=&quot;http://raibledesigns.com/rd/entry/my_what_s_new_in&quot;&gt;What&apos;s New in Spring 3.1&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;JFokus on &lt;a href=&quot;http://raibledesigns.com/rd/entry/comparing_web_frameworks_and_html5&quot;&gt;Comparing Web Frameworks and HTML5 with Play Scala&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Spring I/O on &lt;a href=&quot;http://www.youtube.com/watch?v=QlQMt3W9fpU&quot;&gt;Comparing JVM Web Frameworks&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;JavaPosse Roundup on &lt;a href=&quot;http://raibledesigns.com/rd/entry/how_to_build_a_shot&quot;&gt;How to build a Shot-Ski&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&#220;berConf on &lt;a href=&quot;http://raibledesigns.com/rd/entry/play_vs_grails_smackdown_at&quot;&gt;Play vs. Grails&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Traveling to &lt;a href=&quot;http://raibledesigns.com/rd/entry/a_spectacular_trip_to_stockholm&quot;&gt;Stockholm and Madrid for JFokus and Spring I/O was spectacular&lt;/a&gt;. &lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
  &lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/8350765961/&quot; href=&quot;http://farm9.staticflickr.com/8357/8350765961_9eed5c5ee8.jpg&quot; title=&quot;James Ward and Enno Runne by Trish McGinity&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8357/8350765961_9eed5c5ee8_t.jpg&quot; width=&quot;100&quot; height=&quot;67&quot; alt=&quot;James Ward and Enno Runne by Trish McGinity&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

&lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/8351826862/&quot; href=&quot;http://farm9.staticflickr.com/8233/8351826862_87c90d6990.jpg&quot; title=&quot;Juergen Hoeller&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8233/8351826862_87c90d6990_t.jpg&quot; width=&quot;100&quot; height=&quot;67&quot; alt=&quot;Juergen Hoeller&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot;&gt;&lt;/a&gt;

&lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/8351825042/&quot; href=&quot;http://farm9.staticflickr.com/8227/8351825042_0a15401fa8.jpg&quot; title=&quot;Speakers Dinner Singers by Trish McGinity&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8227/8351825042_0a15401fa8_t.jpg&quot; width=&quot;100&quot; height=&quot;67&quot; alt=&quot;Speakers Dinner Singers&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot;&gt;&lt;/a&gt;

&lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/8351823962/&quot; href=&quot;http://farm9.staticflickr.com/8091/8351823962_5b8c06859f.jpg&quot; title=&quot;Matt Raible James Ward Rickard Oberg Jfokus speakers dinner by Trish McGinity&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8091/8351823962_5b8c06859f_t.jpg&quot; width=&quot;100&quot; height=&quot;67&quot; alt=&quot;Matt Raible James Ward Rickard Oberg Jfokus speakers dinner&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot;&gt;&lt;/a&gt;

&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/8351907734/&quot; href=&quot;http://farm9.staticflickr.com/8191/8351907734_28c5dd5e83.jpg&quot; title=&quot;Stockholm View by Trish McGinity&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8191/8351907734_28c5dd5e83.jpg&quot; width=&quot;500&quot; height=&quot;333&quot; alt=&quot;Stockholm View&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
    &lt;/p&gt;
    
    &lt;p style=&quot;text-align: center&quot;&gt;

    &lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/8355701252/&quot; href=&quot;http://farm9.staticflickr.com/8509/8355701252_3f507c646a.jpg&quot; title=&quot;Templo de Debod by Trish McGinity&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8509/8355701252_3f507c646a.jpg&quot; width=&quot;500&quot; height=&quot;333&quot; alt=&quot;Templo de Debod&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;&lt;/p&gt;
    
    &lt;p style=&quot;text-align: center&quot;&gt;

    &lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/8351930566/&quot; href=&quot;http://farm9.staticflickr.com/8366/8351930566_bb5f27f19a.jpg&quot; title=&quot;Iglesia San Gines by Trish McGinity&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8366/8351930566_bb5f27f19a_m.jpg&quot; width=&quot;240&quot; height=&quot;160&quot; alt=&quot;Iglesia San Gines&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

    &lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/8355691858/&quot; href=&quot;http://farm9.staticflickr.com/8050/8355691858_8878582d57.jpg&quot; title=&quot;Evening Commute Puerta de Alcala Madrid by Trish McGinity&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8050/8355691858_8878582d57_m.jpg&quot; width=&quot;240&quot; height=&quot;160&quot; alt=&quot;Evening Commute Puerta de Alcala Madrid&quot; style=&quot;margin-left: 15px; border: 1px solid black&quot;&gt;&lt;/a&gt;
    &lt;/p&gt;
    
    
&lt;p&gt;I took the 2nd half of the year off from speaking, celebrating my temporary retirement at a &lt;a href=&quot;http://twitter.com/mraible/status/217459157689831424&quot;&gt;Def Leppard concert&lt;/a&gt; in June. When I decided to do this, I was planning on my VW Bus being finished. I was planning on switching from Java Conferences to VW Shows as a hobby. Unfortunately, now it&apos;s January and &lt;a href=&quot;http://raibledesigns.com/roller-ui/authoring/preview/rd/category/The+Bus&quot;&gt;The Bus&lt;/a&gt; still isn&apos;t done. I have high hopes for it being finished in 2013.
&lt;/p&gt;
&lt;p&gt;I paid for 2 conferences in 2012, the &lt;a href=&quot;http://raibledesigns.com/rd/entry/spring_break&quot;&gt;JavaPosse Roundup&lt;/a&gt; in March and &lt;a href=&quot;http://monktoberfest.com/&quot;&gt;Monktoberfest&lt;/a&gt; in October. While I enjoyed both, Monktoberfest will likely become a tradition. 
&lt;/p&gt;
&lt;h3 id=&quot;projects&quot;&gt;Projects&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;AppFuse:&lt;/strong&gt; I started the year by &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; and blogged about the &lt;a href=&quot;http://raibledesigns.com/rd/entry/twitter_s_open_source_summit&quot;&gt;Twitter&apos;s Summit on Bootstrap&lt;/a&gt; shortly after. At the same time, I was overhauling &lt;a href=&quot;http://raibledesigns.com/rd/entry/refreshing_taleo_s_ui_with&quot;&gt;Taleo&apos;s UI with HTML5, Bootstrap and CSS3&lt;/a&gt;, but waited until August to blog about it.&lt;/p&gt;
&lt;p&gt;In the fall, the AppFuse Team &lt;a href=&quot;http://raibledesigns.com/rd/entry/appfuse_news_github_hibernate_search&quot;&gt;migrated to GitHub and integrated Hibernate Search&lt;/a&gt;. &lt;a href=&quot;http://raibledesigns.com/rd/entry/appfuse_2_2_1_released&quot;&gt;AppFuse 2.2.1 was released&lt;/a&gt; in December.
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Happy Trails:&lt;/strong&gt; In May and June, I worked on &lt;a href=&quot;https://github.com/jamesward/happytrails&quot;&gt;Happy Trails&lt;/a&gt; with James Ward as part of our &lt;a href=&quot;http://raibledesigns.com/rd/entry/play_vs_grails_smackdown_at&quot;&gt;Play vs. Grails Smackdown&lt;/a&gt;. This was a very enjoyable experience and I learned a lot about GitHub, Cloudbees and Heroku. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Play More:&lt;/strong&gt; In June, I wrote about &lt;a href=&quot;http://raibledesigns.com/rd/entry/upgrading_to_play_2_anorm&quot;&gt;Upgrading to Play 2: Anorm and Testing&lt;/a&gt;. A couple weeks later, I wrote about integrating &lt;a href=&quot;http://raibledesigns.com/rd/entry/migrating_to_play_2_and&quot;&gt;Validation and Secure Social&lt;/a&gt; into &lt;a href=&quot;http://play-more.com&quot;&gt;&lt;em&gt;Play More&lt;/em&gt;&lt;/a&gt;. In the aforementioned post, I also posted my &lt;a href=&quot;http://www.slideshare.net/mraible/html5-with-play-scala-coffeescript-and-jade-uberconf-2012&quot;&gt;&#220;berConf presentation&lt;/a&gt;, the &lt;a href=&quot;https://github.com/mraible/play-more&quot;&gt;source code&lt;/a&gt; and my thoughts on Play 2.&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
I found Anorm and Scalate to be &lt;em&gt;huge&lt;/em&gt; time sinks and don&apos;t know if I&apos;d recommend using either one in a Play 2 project. I&apos;m sure Scalate will be easier to use as its Play 2 integration gets more refined, but I don&apos;t know if there&apos;s any hope for a JDBC abstraction that doesn&apos;t produce error messages when things go south.
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Roller:&lt;/strong&gt; Although I didn&apos;t contribute much to &lt;a href=&quot;http://roller.apache.org&quot;&gt;Apache Roller&lt;/a&gt; in 2012, I do continue to use it frequently. This site celebrated its &lt;a href=&quot;http://raibledesigns.com/rd/entry/10_years_ago&quot;&gt;10-year anniversary&lt;/a&gt; in August. It got a &lt;a href=&quot;http://raibledesigns.com/rd/entry/new_look_and_feel_designed&quot;&gt;new look and feel&lt;/a&gt; shortly after.&lt;/p&gt; 
&lt;h2 id=&quot;personal&quot;&gt;Personal&lt;/h2&gt;
&lt;p&gt;Last year was a fantastic year for family time. Our &lt;a href=&quot;http://raibledesigns.com/rd/entry/cruising_around_the_western_caribbean&quot;&gt;cruise around the Western Caribbean&lt;/a&gt; was money well spent.&lt;/p&gt;&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/8358223863/&quot; href=&quot;http://farm9.staticflickr.com/8092/8358223863_5b7c2d31eb.jpg&quot; title=&quot;Raible&apos;s and McGinity&apos;s welcome to our Disney Cruise! by Trish, on Flickr&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8092/8358223863_5b7c2d31eb.jpg&quot; width=&quot;500&quot; height=&quot;333&quot; alt=&quot;Raible&apos;s and McGinity&apos;s welcome to our Disney Cruise!&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 rev=&quot;http://www.flickr.com/photos/mcginityphoto/8358600474/&quot; href=&quot;http://farm9.staticflickr.com/8213/8358600474_ea26b2cc90.jpg&quot; title=&quot;Raible and McGinity dinner by Trish, on Flickr&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8213/8358600474_ea26b2cc90.jpg&quot; width=&quot;500&quot; height=&quot;333&quot; alt=&quot;Raible and McGinity dinner&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

&lt;p&gt;My goal was to spend a month with my parents this year and we satisfied that by July. In total, we enjoyed their company for 8 weeks in 2012. 
&lt;/p&gt;
&lt;p&gt;
The worst ski season on record &lt;a href=&quot;http://raibledesigns.com/rd/entry/farewell_to_the_2011_2012&quot;&gt;ended in April&lt;/a&gt; and Trish and I headed for &lt;a href=&quot;http://raibledesigns.com/rd/entry/wine_tasting_in_napa_valley&quot;&gt;wine country&lt;/a&gt; in May.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a href=&quot;http://www.mcginityphoto.com/photos/i-cDtj2v6/1/M/i-cDtj2v6-M.jpg&quot; rel=&quot;lightbox[2012yearinreview]&quot; title=&quot;Cade Vineyard Cave Table&quot;&gt;
    &lt;img alt=&quot;Cade Vineyard Cave Table&quot; src=&quot;//www.mcginityphoto.com/photos/i-cDtj2v6/1/Th/i-cDtj2v6-Th.jpg&quot; width=&quot;150&quot; height=&quot;100&quot;  alt=&quot;Cade Vineyard Cave Table&quot; style=&quot;border: 1px solid black&quot;&gt;
    &lt;/a&gt;
    
    &lt;a href=&quot;http://www.mcginityphoto.com/photos/i-mCJJZR3/0/M/i-mCJJZR3-M.jpg&quot; rel=&quot;lightbox[2012yearinreview]&quot; title=&quot;Cade Vineyard Cask Row&quot;&gt;
    &lt;img src=&quot;//www.mcginityphoto.com/photos/i-mCJJZR3/0/Th/i-mCJJZR3-Th.jpg&quot; width=&quot;150&quot; height=&quot;99&quot;  alt=&quot;Cade Vineyard Cask Row&quot; style=&quot;margin-left: 10px; border: 1px solid black&quot;&gt;&lt;/a&gt;
    
    &lt;a href=&quot;http://www.mcginityphoto.com/photos/i-r4fmDX2/0/M/i-r4fmDX2-M.jpg&quot; rel=&quot;lightbox[2012yearinreview]&quot; title=&quot;Cade Flowers next to infinity pool&quot;&gt;
    &lt;img alt=&quot;Cade Flowers next to infinity pool&quot; src=&quot;//www.mcginityphoto.com/photos/i-r4fmDX2/0/Th/i-r4fmDX2-Th.jpg&quot; width=&quot;150&quot; height=&quot;100&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://www.mcginityphoto.com/photos/i-R9tmTz4/0/M/i-R9tmTz4-M.jpg&quot; rel=&quot;lightbox[2012yearinreview]&quot; title=&quot;Cade Winery&quot;&gt;
    &lt;img alt=&quot;Cade Winery&quot; src=&quot;//www.mcginityphoto.com/photos/i-R9tmTz4/0/S/i-R9tmTz4-S.jpg&quot; width=&quot;400&quot; height=&quot;266&quot; title=&quot;Cade Winery&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
    
    &lt;/p&gt;
    
    &lt;/p&gt;
    &lt;p&gt;Abbie and Jack&apos;s school year &lt;a href=&quot;http://raibledesigns.com/rd/entry/school_s_out_for_summer&quot;&gt;ended&lt;/a&gt; on June 5th and we &lt;a href=&quot;http://raibledesigns.com/rd/entry/father_s_day_weekend_at&quot;&gt;celebrated Father&apos;s Day on the Oregon coast&lt;/a&gt;.&lt;/p&gt;

&lt;p style=&quot;text-align: center&quot;&gt;

&lt;a data-href=&quot;http://farm8.staticflickr.com/7284/9071981163_9ecc04ec8f_c.jpg&quot; title=&quot;View from Ecola State Park Oregon&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7284/9071981163_9ecc04ec8f.jpg&quot; width=&quot;500&quot; height=&quot;332&quot; alt=&quot;View from Ecola State Park Oregon&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;/a&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a data-href=&quot;http://www.flickr.com/photos/mcginityphoto/9071962289/&quot; href=&quot;http://farm4.staticflickr.com/3675/9071962289_a7fc891f15_c.jpg&quot; title=&quot;Raible Family on the Oregon coast&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm4.staticflickr.com/3675/9071962289_a7fc891f15_q.jpg&quot; width=&quot;150&quot; height=&quot;150&quot; alt=&quot;Raible Family on the Oregon coast&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

&lt;a data-href=&quot;http://www.flickr.com/photos/mcginityphoto/9074185440/&quot; href=&quot;http://farm8.staticflickr.com/7404/9074185440_eebccb9acf_c.jpg&quot; title=&quot;Abbie and Jack frolicking in the waves&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm8.staticflickr.com/7404/9074185440_eebccb9acf_q.jpg&quot; width=&quot;150&quot; height=&quot;150&quot; alt=&quot;Abbie and Jack frolicking in the waves&quot; style=&quot;border: 1px solid black; margin-left: 15px&quot;&gt;&lt;/a&gt;

&lt;a data-href=&quot;http://www.flickr.com/photos/mcginityphoto/9074188854/&quot; href=&quot;http://farm3.staticflickr.com/2814/9074188854_f0b09a43ca_c.jpg&quot; title=&quot;Kalin and Joe on the beach&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm3.staticflickr.com/2814/9074188854_f0b09a43ca_q.jpg&quot; width=&quot;150&quot; height=&quot;150&quot; alt=&quot;Kalin and Joe on the beach&quot; style=&quot;border: 1px solid black; margin-left: 15px&quot;&gt;&lt;/a&gt;

&lt;/p&gt;

    &lt;p&gt;Raible Road Trip #17 began in late June and we spent two weeks parading, rafting and golfing in Montana. From there, we flew to Maui for two weeks. I wrote about it all in &lt;a href=&quot;http://raibledesigns.com/rd/entry/summer_vacation_2012_in_montana&quot;&gt;Summer Vacation 2012 in Montana and Maui&lt;/a&gt;.
        &lt;/p&gt;
        &lt;p style=&quot;text-align: center&quot;&gt;

                        &lt;a title=&quot;Holland Lake Patio&quot; rel=&quot;lightbox[2012yearinreview]&quot; href=&quot;http://www.mcginityphoto.com/Vacation/Montana-Summer-2012/i-rsL7ncX/0/M/DSC_4745-M.jpg&quot;&gt;
        &lt;img src=&quot;//www.mcginityphoto.com/Vacation/Montana-Summer-2012/i-rsL7ncX/0/Ti/DSC_4745-Ti.jpg&quot;
             style=&quot;border: 1px solid black&quot; width=&quot;95&quot; height=&quot;95&quot; alt=&quot;Holland Lake Patio&quot;&gt;&lt;/a&gt;

      &lt;a title=&quot;The Girls at Double Arrow&quot; rel=&quot;lightbox[2012yearinreview]&quot; href=&quot;http://www.mcginityphoto.com/Vacation/Montana-Summer-2012/i-sdLBDmF/0/M/DSC_4755-M.jpg&quot;&gt;
        &lt;img src=&quot;//www.mcginityphoto.com/Vacation/Montana-Summer-2012/i-sdLBDmF/0/Ti/DSC_4755-Ti.jpg&quot;
             style=&quot;border: 1px solid black&quot; width=&quot;95&quot; height=&quot;95&quot; alt=&quot;The Girls at Double Arrow&quot;&gt;&lt;/a&gt;

      &lt;a title=&quot;Hard to believe I caused this guy to move to Montana in 1997&quot; rel=&quot;lightbox[2012yearinreview]&quot; href=&quot;http://www.mcginityphoto.com/Vacation/Montana-Summer-2012/i-b5FCTrd/0/M/DSC_4845-2-M.jpg&quot;&gt;
        &lt;img src=&quot;//www.mcginityphoto.com/Vacation/Montana-Summer-2012/i-b5FCTrd/0/Ti/DSC_4845-2-Ti.jpg&quot;
             style=&quot;border: 1px solid black&quot; width=&quot;95&quot; height=&quot;95&quot; alt=&quot;Hard to believe I caused this guy to move to Montana in 1997&quot;&gt;&lt;/a&gt;

      &lt;a title=&quot;Holland Lake Sunset&quot; rel=&quot;lightbox[2012yearinreview]&quot; href=&quot;http://www.mcginityphoto.com/Vacation/Montana-Summer-2012/i-cH8CpGZ/0/M/HLL%20View-M.jpg&quot;&gt;
        &lt;img src=&quot;//www.mcginityphoto.com/Vacation/Montana-Summer-2012/i-cH8CpGZ/0/Ti/HLL%20View-Ti.jpg&quot;
             style=&quot;border: 1px solid black; margin-left: 1px&quot; width=&quot;95&quot; height=&quot;95&quot; alt=&quot;Holland Lake Sunset&quot;&gt;&lt;/a&gt;

                &lt;/p&gt;
                &lt;p style=&quot;text-align: center&quot;&gt;
&lt;a title=&quot;Api, Makao and Keaka. My Hawaiin name is Pualani&quot; rel=&quot;lightbox[2012yearinreview]&quot; href=&quot;http://www.mcginityphoto.com/Vacation/2012-Maui-Vacation/i-KHjFqdL/0/M/DSC_5402-M.jpg&quot;&gt;
        &lt;img src=&quot;//www.mcginityphoto.com/Vacation/2012-Maui-Vacation/i-KHjFqdL/0/S/DSC_5402-S.jpg&quot;
             width=&quot;400&quot; height=&quot;266&quot; style=&quot;border: 1px solid black&quot; alt=&quot;Api, Makao and Keaka. My Hawaiin name is Pualani&quot;&gt;&lt;/a&gt;
                            &lt;/p&gt;
&lt;p&gt;
    After returning from Hawaii, we dropped the kids off at their Mom&apos;s and headed to my cousin&apos;s wedding in Beacon, NY for the weekend. From there, my parents, Trish and I rode the train to Boston, spent a few days in Cape Cod and returned home in time for the beginning of school.
    &lt;/p&gt;
    &lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/7795952628/&quot; href=&quot;http://farm9.staticflickr.com/8294/7795952628_c0be6bc4ca.jpg&quot; title=&quot;Abbie and Jack on the First Day of School 2012 by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8294/7795952628_c0be6bc4ca_n.jpg&quot; width=&quot;209&quot; height=&quot;320&quot; alt=&quot;Abbie and Jack on the First Day of School 2012&quot; class=&quot;picture&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
    
    The kids &lt;a href=&quot;http://raibledesigns.com/rd/entry/the_first_day_of_school2&quot;&gt;started the school year in mid-August&lt;/a&gt; and we celebrated &lt;a href=&quot;http://raibledesigns.com/rd/entry/happy_birthday_jack4&quot;&gt;Jack&apos;s 8th birthday&lt;/a&gt; a couple weeks later.
    &lt;/p&gt;
&lt;p&gt;My lack of speaking engagements and work-related travel gave us more time to frolic in the Colorado mountains, so we &lt;a href=&quot;http://raibledesigns.com/rd/entry/we_bought_a_boat&quot;&gt;bought a boat&lt;/a&gt; for Labor Day weekend. That Saturday, we took it on its Maiden Voyage on the Colorado River, floating from Radium to Rancho del Rio. I guided and rowed the boat most of the time while our &lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/7921929062/in/photostream/&quot; href=&quot;http://farm9.staticflickr.com/8460/7921929062_9bcf7d15f0.jpg&quot; rel=&quot;lightbox[2012yearinreview]&quot; title=&quot;The Maiden Voyage Crew&quot;&gt;7 passengers (and 2 dogs)&lt;/a&gt; enjoyed cold beverages, great scenery and relaxing in the sun. 
    &lt;/p&gt;
    &lt;p style=&quot;text-align: center&quot;&gt;
        &lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/7921917130/&quot; href=&quot;http://farm9.staticflickr.com/8041/7921917130_7389475dea.jpg&quot; title=&quot;Abbie with our new boat by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8041/7921917130_7389475dea_m.jpg&quot; width=&quot;240&quot; height=&quot;160&quot; alt=&quot;Abbie with our new boat&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;&lt;/p&gt;
    &lt;p&gt;The next day, Abbie played her first game of golf.&lt;/p&gt;
    &lt;p style=&quot;text-align: center&quot;&gt;
    &lt;a rev=&quot;http://www.flickr.com/photos/mraible/7937704316/&quot; href=&quot;http://farm9.staticflickr.com/8317/7937704316_ee30d6bc55.jpg&quot; title=&quot;Nice form kiddo!&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8317/7937704316_ee30d6bc55_m.jpg&quot; width=&quot;240&quot; height=&quot;180&quot; alt=&quot;Nice form kiddo!&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

    &lt;a rev=&quot;http://www.flickr.com/photos/mraible/7937705606/&quot; href=&quot;http://farm9.staticflickr.com/8318/7937705606_9d754545de.jpg&quot; title=&quot;Great day of golf at Pole Creek. Got a sweet framed set of Abbie pics at the end too!&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8318/7937705606_9d754545de_m.jpg&quot; width=&quot;240&quot; height=&quot;180&quot; alt=&quot;Great day of golf at Pole Creek. Got a sweet framed set of Abbie pics at the end too!&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot;&gt;&lt;/a&gt;

    &lt;/p&gt;
    &lt;p&gt;In October, I &lt;a href=&quot;http://raibledesigns.com/rd/entry/the_deck_project&quot;&gt;finished our deck project&lt;/a&gt; and work continued on our kitchen remodel (started in July by &lt;a href=&quot;http://peppergc.com/&quot;&gt;Ted Pepper&lt;/a&gt;). Our kitchen was far enough along for us to host our 1st Annual Halloween Party.&lt;/p&gt;
    &lt;p&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/happy_birthday_abbie6&quot;&gt;Abbie turned 10&lt;/a&gt; the first week in November. That Friday, we headed to Kauai to &lt;a href=&quot;http://raibledesigns.com/rd/entry/happy_40th_anniversary_mom_and&quot;&gt;celebrate my parents&apos; 40th Anniversary&lt;/a&gt;. We spent &lt;a href=&quot;http://raibledesigns.com/rd/entry/november_travels_to_kauai_and#thanksgiving&quot;&gt;Thanksgiving in Wenatchee&lt;/a&gt;, Christmas in Boston/Vermont and &lt;a href=&quot;http://www.flickr.com/photos/mcginityphoto/sets/72157632448197212/&quot;&gt;New Years with good friends in Fraser&lt;/a&gt;.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a data-url=&quot;http://www.flickr.com/photos/mcginityphoto/8224084347/&quot; href=&quot;http://farm9.staticflickr.com/8341/8224084347_d146419dfd.jpg&quot; title=&quot;Our Lanai by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8341/8224084347_d146419dfd_t.jpg&quot; width=&quot;100&quot; height=&quot;67&quot; alt=&quot;Our Lanai&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

&lt;a data-url=&quot;http://www.flickr.com/photos/mcginityphoto/8224089763/&quot; href=&quot;http://farm9.staticflickr.com/8065/8224089763_03d7c6a657.jpg&quot; title=&quot;Hanalei Pier by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8065/8224089763_03d7c6a657_t.jpg&quot; width=&quot;100&quot; height=&quot;67&quot; alt=&quot;Hanalei Pier&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

&lt;a data-url=&quot;http://www.flickr.com/photos/mcginityphoto/8225168600/&quot; href=&quot;http://farm9.staticflickr.com/8059/8225168600_df36e98e1e.jpg&quot; title=&quot;Happy Family by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8059/8225168600_df36e98e1e_t.jpg&quot; width=&quot;100&quot; height=&quot;67&quot; alt=&quot;Happy Family&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

&lt;a data-url=&quot;http://www.flickr.com/photos/mcginityphoto/8225152842/&quot; href=&quot;http://farm9.staticflickr.com/8339/8225152842_6c987fae93.jpg&quot; title=&quot;Surf House by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8339/8225152842_6c987fae93_t.jpg&quot; width=&quot;100&quot; height=&quot;67&quot; alt=&quot;Surf House&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

&lt;a data-url=&quot;http://www.flickr.com/photos/mcginityphoto/8225154986/&quot; href=&quot;http://farm9.staticflickr.com/8480/8225154986_2641ec95bd.jpg&quot; title=&quot;St Regis Pool by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[2012yearinreview]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8480/8225154986_2641ec95bd_t.jpg&quot; width=&quot;100&quot; height=&quot;67&quot; alt=&quot;St Regis Pool&quot; style=&quot;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;https://www.facebook.com/photo.php?fbid=10200109112597408&amp;amp;set=a.10200104543563185.2198356.1438065285&amp;amp;type=1&quot;&gt;
&lt;img alt=&quot;Mom and Dad in Kauai&quot; src=&quot;//sphotos-a.xx.fbcdn.net/hphotos-prn1/621352_10200109112597408_143056394_o.jpg&quot; style=&quot;width: 520px; height: 346px; border: 1px solid black&quot;&gt;
&lt;/a&gt;
&lt;/p&gt;
&lt;h2 id=&quot;2013&quot;&gt;2013&lt;/h2&gt;
&lt;p&gt;My goals for 2013 are to be happy and to &lt;em&gt;slow down&lt;/em&gt;. I don&apos;t plan on traveling any less, or spending less time with my family. I&apos;d just like to walk slower, smell the good ol&apos; Rocky Mountain air more and enjoy life. I believe our raft will help us facilitate this and we&apos;re already planning trips to the &lt;a href=&quot;http://www.tagalong.com/index.php?page=greeno&quot;&gt;Green River&lt;/a&gt; in Utah and &lt;a href=&quot;http://www.montanaliving.com/Outdoors/Schafer_Meadows_a_wilderness_trip_of_a_lifetime_97-097&quot;&gt;Schafer Meadows&lt;/a&gt; in the Bob.&lt;/p&gt;
&lt;p&gt;The big event for the year will be Trish and my wedding this summer. We&apos;ll be doing it in my hometown in Montana and we expect it to be quite the shindig. Afterward, we plan on &lt;a href=&quot;https://twitter.com/mraible/status/264452753596751874&quot;&gt;traveling the world&lt;/a&gt; for our honeymoon.&lt;/p&gt;
&lt;p&gt;Of course, if the bus gets finished, that&apos;s a huge bonus too.&lt;/p&gt;
&lt;p&gt;Professionally, I&apos;m starting an exciting project at Taleo that will involve heavy JavaScript, CSS3 and maybe even Canvas. I have speaking engagements lined up at &lt;a href=&quot;http://www.meetup.com/DenverJavaUsersGroup/events/93932082/&quot;&gt;Denver JUG in February&lt;/a&gt; and &lt;a href=&quot;http://www.meetup.com/HTML5-Denver-Users-Group/&quot;&gt;HTML5 Denver&lt;/a&gt; in April. Also, I just submitted a couple talks to &lt;a href=&quot;http://www.devoxx.com/display/FR13/Home&quot;&gt;Devoxx France&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;For AppFuse, I&apos;d love to get 3-4 releases out this year, but 2 is probably more realistic. The &lt;a href=&quot;http://appfuse.org/display/APF/Roadmap&quot;&gt;AppFuse Roadmap&lt;/a&gt; shows our future releases + features. I&apos;d also like to work a bit more on my Play 2 app; finishing the mobile client and possibly switching from Anorm to Slick or Spring Data.&lt;/p&gt;
&lt;p&gt;While Oracle has been good to me, I look forward to finding and working for a new client in September.&lt;/p&gt;
&lt;p&gt;Last year &lt;a href=&quot;http://raibledesigns.com/rd/entry/2011_a_year_in_review&quot;&gt;at this time&lt;/a&gt;, we watched the Broncos execute the &lt;em&gt;Mile High Miracle&lt;/em&gt; over the Steelers. With tickets to both this week&apos;s game (vs. The Ravens) and next week&apos;s game (vs. The Patriots), I wouldn&apos;t be surprised if January turns out to be a great month. However, with the Powder Days, The Super Bowl, the raft trips, the Bus Shows and The Wedding, there&apos;s a good chance that all the months in 2013 are epic. &lt;img src=&quot;https://raibledesigns.com/images/smileys/smile.gif&quot; class=&quot;smiley&quot; alt=&quot;:)&quot; title=&quot;:)&quot; /&gt;</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/we_bought_a_boat</id>
        <title type="html">We bought a boat!</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/we_bought_a_boat"/>
        <published>2012-09-05T11:12:02-06:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/General" label="General" />
        <category term="golfing" scheme="http://roller.apache.org/ns/tags/" />
        <category term="abbie" scheme="http://roller.apache.org/ns/tags/" />
        <category term="downriver" scheme="http://roller.apache.org/ns/tags/" />
        <category term="rafting" scheme="http://roller.apache.org/ns/tags/" />
        <category term="maidenvoyage" scheme="http://roller.apache.org/ns/tags/" />
        <category term="boat" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">I&apos;ve always enjoyed whitewater rafting. I think the first time I did it was in college and I immediately fell in love. Through the years, I&apos;ve been on many trips with family and friends. However, it wasn&apos;t until this summer that I realized it was something I should do more often. It was Trish&apos;s friends, Chris and Bryce, that started it all. They bought a raft last year and we floated down the Colorado River with them a couple times over Memorial Day Weekend. Then we went to Montana and enjoyed a couple days on the Middle Fork of the Flathead with Dr. Barton and a bunch of raft guides. That weekend in July, we realized we&apos;d done more rafting than any other outdoor activities (mountain biking, camping and even golfing). That&apos;s when we decided to buy our own.
&lt;/p&gt;
&lt;p&gt;We had a lot of help in the process of buying a raft. First of all, I sat down with my friend Dr. Barton and made a list of all the things we&apos;d need. The good doctor was a whitewater guide in Montana for 5 years, has rescued trips from the wilderness and has even rafted the Grand Canyon - so I considered him a good source of information. After composing the list of necessary gear, we headed to &lt;a href=&quot;http://downriverequip.com/&quot;&gt;Down River Equipment&lt;/a&gt; on August 26th, the last day of their end-of-season sale. It took us an hour to pick out the raft we wanted (a &lt;a href=&quot;http://downriverequip.com/index.php/boats/rafts/down-river-pro-series/pro-140.html&quot;&gt;Pro 140&lt;/a&gt;) and gather up all the gear (frame, cooler, oars, dry box/bags, lifejackets, koozies, etc.). We asked them to have it ready by Friday and headed home.&lt;/p&gt;
&lt;p&gt;Last Friday, we picked up a raft trailer from &lt;a href=&quot;http://trailersourceinc.com/&quot;&gt;Trailer Source&lt;/a&gt; an hour before they closed, then journeyed to Down River where Mike (the owner) and Matt (the guy who helped us the previous Sunday) helped us setup our oars and load up our new boat. There was much rejoicing.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a rev=&quot;http://www.flickr.com/photos/mraible/7937703060/&quot; href=&quot;http://farm9.staticflickr.com/8312/7937703060_65f420692e.jpg&quot; title=&quot;We bought a boat!&quot; rel=&quot;lightbox[weboughtaboat]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8312/7937703060_65f420692e_m.jpg&quot; width=&quot;240&quot; height=&quot;180&quot; alt=&quot;We bought a boat!&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

&lt;a rev=&quot;http://www.flickr.com/photos/mraible/7937703702/&quot; href=&quot;http://farm9.staticflickr.com/8449/7937703702_df6db5bb4a.jpg&quot; title=&quot;Thanks to Mike and Matt from Down River&quot; rel=&quot;lightbox[weboughtaboat]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8449/7937703702_df6db5bb4a_m.jpg&quot; width=&quot;240&quot; height=&quot;180&quot; alt=&quot;Thanks to Mike and Matt from Down River&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot;&gt;&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;Saturday, we took it on its Maiden Voyage on the Colorado River, floating from Radium to Rancho del Rio. According to &lt;a href=&quot;http://www.blm.gov/co/st/en/fo/kfo/recreation_opportunities/rafting.html&quot;&gt;this page&lt;/a&gt;, there were some Class III rapids, but they all felt like Class II. I guided and rowed the boat most of the time while our &lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/7921929062/in/photostream/&quot; href=&quot;http://farm9.staticflickr.com/8460/7921929062_9bcf7d15f0.jpg&quot; rel=&quot;lightbox[weboughtaboat]&quot; title=&quot;The Maiden Voyage Crew&quot;&gt;7 passengers (and 2 dogs)&lt;/a&gt; enjoyed cold beverages, great scenery and relaxing in the sun. It took us a bit longer (4 hours) than expected (2 hours), but we all thought it was well worth it.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/7921917130/&quot; href=&quot;http://farm9.staticflickr.com/8041/7921917130_7389475dea.jpg&quot; title=&quot;Abbie with our new boat by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[weboughtaboat]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8041/7921917130_7389475dea.jpg&quot; width=&quot;500&quot; height=&quot;333&quot; alt=&quot;Abbie with our new boat&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Abbie&apos;s First Golf Game&lt;/strong&gt;&lt;br/&gt;After a long day of floating on Saturday, we decided to chill on Sunday with a little golf. We split the kids up for the weekend (Jack went with his mom), so we figured the proper way to treat our &lt;em&gt;only child&lt;/em&gt; was to take Abbie to play her first &lt;em&gt;real&lt;/em&gt; game of golf at &lt;a href=&quot;http://www.polecreekgolf.com/&quot;&gt;Pole Creek&lt;/a&gt;. We played 9 holes and both Abbie and I had a great time trying out our new clubs. We received a nice kids golfing tip from someone at the driving range: have them tee off from the 150 marker so they have a chance to par the hole.
&lt;/p&gt;
&lt;p&gt;
The course had a 50% discount for kids and we never saw anyone behind us the entire game. We were especially impressed when the course photographer offered us a framed set of Abbie pics for $15.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a rev=&quot;http://www.flickr.com/photos/mraible/7937704316/&quot; href=&quot;http://farm9.staticflickr.com/8317/7937704316_ee30d6bc55.jpg&quot; title=&quot;Nice form kiddo!&quot; rel=&quot;lightbox[weboughtaboat]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8317/7937704316_ee30d6bc55_m.jpg&quot; width=&quot;240&quot; height=&quot;180&quot; alt=&quot;Nice form kiddo!&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;

&lt;a rev=&quot;http://www.flickr.com/photos/mraible/7937705606/&quot; href=&quot;http://farm9.staticflickr.com/8318/7937705606_9d754545de.jpg&quot; title=&quot;Great day of golf at Pole Creek. Got a sweet framed set of Abbie pics at the end too!&quot; rel=&quot;lightbox[weboughtaboat]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8318/7937705606_9d754545de_m.jpg&quot; width=&quot;240&quot; height=&quot;180&quot; alt=&quot;Great day of golf at Pole Creek. Got a sweet framed set of Abbie pics at the end too!&quot; style=&quot;border: 1px solid black; margin-left: 10px&quot;&gt;&lt;/a&gt;

&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a rev=&quot;http://www.flickr.com/photos/mcginityphoto/7921909282/&quot; href=&quot;http://farm9.staticflickr.com/8454/7921909282_4ed3363133.jpg&quot; title=&quot;Abbie frolicking on the golf course by McGinityPhoto, on Flickr&quot; rel=&quot;lightbox[weboughtaboat]&quot;&gt;&lt;img src=&quot;//farm9.staticflickr.com/8454/7921909282_4ed3363133.jpg&quot; width=&quot;500&quot; height=&quot;333&quot; alt=&quot;Abbie frolicking on the golf course&quot; style=&quot;border: 1px solid black&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;We don&apos;t know how many more days of rafting we&apos;ll get in this year, but next year should be epic. We&apos;re hoping to do multi-day trips on the &lt;a href=&quot;http://www.utah.com/raft/rivers/green.htm&quot;&gt;Green River&lt;/a&gt;, the &lt;a href=&quot;http://www.bigskyfishing.com/River-Fishing/Central-MT-Rivers/smith-river/smith_floating.php&quot;&gt;Smith River&lt;/a&gt; and fly into &lt;a href=&quot;http://www.montanaliving.com/Outdoors/Schafer_Meadows_a_wilderness_trip_of_a_lifetime_97-097&quot;&gt;Schafer Meadows&lt;/a&gt; for a journey through the &lt;a href=&quot;http://en.wikipedia.org/wiki/Bob_Marshall_Wilderness&quot;&gt;Bob Marshall Wilderness&lt;/a&gt;. I grew up only 10 miles from &quot;The Bob&quot; and I&apos;ve never been in it. I can&apos;t wait! &lt;img src=&quot;https://raibledesigns.com/images/smileys/grin.gif&quot; class=&quot;smiley&quot; alt=&quot;:-D&quot; title=&quot;:-D&quot; /&gt;&lt;/p&gt;</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/summer_vacation_2012_in_montana</id>
        <title type="html">Summer Vacation 2012 in Montana and Maui</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/summer_vacation_2012_in_montana"/>
        <published>2012-08-01T09:30:27-06:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/General" label="General" />
        <category term="roadtrip" scheme="http://roller.apache.org/ns/tags/" />
        <category term="maui" scheme="http://roller.apache.org/ns/tags/" />
        <category term="vacation" scheme="http://roller.apache.org/ns/tags/" />
        <category term="thecabin" scheme="http://roller.apache.org/ns/tags/" />
        <category term="hawaii" scheme="http://roller.apache.org/ns/tags/" />
        <category term="travel" scheme="http://roller.apache.org/ns/tags/" />
        <category term="montana" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">I started writing about my summer vacations in Montana as soon as this blog gave me an opportunity: &lt;a href=&quot;http://raibledesigns.com/rd/entry/back_from_the_cabin&quot;&gt;July 2003&lt;/a&gt;. In July 2004, I didn&apos;t travel to the cabin for the 4th, but I did in &lt;a href=&quot;http://raibledesigns.com/rd/entry/off_to_big_sky_country&quot;&gt;2005&lt;/a&gt;. Then, 5 years ago, I started writing longer blog posts about our trips. 2008 was &lt;a href=&quot;http://raibledesigns.com/rd/entry/raible_road_trip_12_vacation&quot;&gt;Raible Road Trip #12&lt;/a&gt;. In 2009, I &lt;a href=&quot;http://raibledesigns.com/rd/entry/my_summer_vacation_in_montana&quot;&gt;spent a whole month in Montana&lt;/a&gt;. In 2010, I visited after my &lt;a href=&quot;http://raibledesigns.com/rd/entry/my_summer_vacation_in_montana1&quot;&gt;parents moved there full-time&lt;/a&gt;. Last year, I took &lt;a href=&quot;http://raibledesigns.com/rd/entry/4th_of_july_adventures_in&quot;&gt;Trish for the first time&lt;/a&gt;. 
&lt;/p&gt;
&lt;p&gt;
I don&apos;t know what Raible Road Trip number this year&apos;s trip was, but I&apos;m going to go ahead and assume #17. This year wasn&apos;t exactly a vacation for me. It was what I like to call a &lt;em&gt;workation&lt;/em&gt;. I tried to take the whole month off, but my current client needed my services. As a compromise, they agreed to let me work 20 hours per week during July. While I didn&apos;t get a chance to fully unplug, I did enjoy the reduced workload and time to spend with friends and family. And the views from The Cabin and Sugar Beach weren&apos;t bad either. &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;
Below is our trip itinerary in condensed form, complete with photos from my &lt;a href=&quot;http://mcginityphoto.com&quot;&gt;incredibly talented fianc&#233;&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
We embarked on our drive to Montana on June 30th, stopped for fireworks in Wyoming and spent the night in Yellowstone park. Arrived at the cabin, got to sleep in the &lt;em&gt;new house&lt;/em&gt; for the first time, built a cabin float, drove it in the parade, went boating at Holland Lake, played golf. Journeyed to West Glacier to celebrate &lt;a href=&quot;http://jasonjbarton.com/&quot;&gt;Dr. Barton&apos;s&lt;/a&gt; PhD at his sister&apos;s house. Rafted the Middlefork of the Flathead all weekend. Played some more golf and went waterskiing. 
&lt;/p&gt;
&lt;table cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; style=&quot;margin: 0 auto 5px auto; width: 500px&quot;&gt;
  &lt;tr&gt;
    &lt;td&gt;
      &lt;a title=&quot;Abbie at The Cabin&quot; rel=&quot;lightbox[summervacation2012]&quot; href=&quot;http://www.mcginityphoto.com/Vacation/Montana-Summer-2012/i-qqW8qqp/0/M/Abbie%20Cabin-M.jpg&quot;&gt;
        &lt;img src=&quot;//www.mcginityphoto.com/Vacation/Montana-Summer-2012/i-qqW8qqp/0/S/Abbie%20Cabin-S.jpg&quot;
             width=&quot;400&quot; height=&quot;265&quot; style=&quot;border: 1px solid black&quot; alt=&quot;Abbie at The Cabin&quot;&gt;&lt;/a&gt;
    &lt;/td&gt;

    &lt;td style=&quot;text-align: center; vertical-align: top&quot;&gt;
      &lt;a title=&quot;My Dad&quot; rel=&quot;lightbox[summervacation2012]&quot; href=&quot;http://www.mcginityphoto.com/Vacation/Montana-Summer-2012/i-fds69CJ/0/M/Joe%20Raible-M.jpg&quot;&gt;
        &lt;img src=&quot;//www.mcginityphoto.com/Vacation/Montana-Summer-2012/i-fds69CJ/0/Ti/Joe%20Raible-Ti.jpg&quot;
             style=&quot;border: 1px solid black&quot; width=&quot;85&quot; height=&quot;85&quot; alt=&quot;My Dad&quot;&gt;&lt;/a&gt;

      &lt;a title=&quot;Happy 4th!&quot; rel=&quot;lightbox[summervacation2012]&quot; href=&quot;http://www.mcginityphoto.com/Vacation/Montana-Summer-2012/i-WrDtWvH/0/M/DSC_4710-M.jpg&quot;&gt;
        &lt;img src=&quot;//www.mcginityphoto.com/Vacation/Montana-Summer-2012/i-WrDtWvH/0/Ti/DSC_4710-Ti.jpg&quot;
             style=&quot;border: 1px solid black; margin-top: 3px&quot; width=&quot;85&quot; height=&quot;85&quot; alt=&quot;Happy 4th!&quot;&gt;&lt;/a&gt;

      &lt;a title=&quot;Raible Homestead in the Swan Valley Parade&quot; rel=&quot;lightbox[summervacation2012]&quot; href=&quot;http://www.mcginityphoto.com/Vacation/Montana-Summer-2012/i-5R7sxj7/0/M/IMG_1162-M.jpg&quot;&gt;
        &lt;img src=&quot;//www.mcginityphoto.com/Vacation/Montana-Summer-2012/i-5R7sxj7/0/Ti/IMG_1162-Ti.jpg&quot;
             style=&quot;border: 1px solid black; margin-top: 3px&quot; width=&quot;85&quot; height=&quot;85&quot; alt=&quot;Raible Homestead in the Swan Valley Parade&quot;&gt;&lt;/a&gt;

    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td style=&quot;padding-top: 5px; white-space: nowrap&quot;&gt;

      &lt;a title=&quot;Holland Lake Patio&quot; rel=&quot;lightbox[summervacation2012]&quot; href=&quot;http://www.mcginityphoto.com/Vacation/Montana-Summer-2012/i-rsL7ncX/0/M/DSC_4745-M.jpg&quot;&gt;
        &lt;img src=&quot;//www.mcginityphoto.com/Vacation/Montana-Summer-2012/i-rsL7ncX/0/Ti/DSC_4745-Ti.jpg&quot;
             style=&quot;border: 1px solid black&quot; width=&quot;95&quot; height=&quot;95&quot; alt=&quot;Holland Lake Patio&quot;&gt;&lt;/a&gt;

      &lt;a title=&quot;The Girls at Double Arrow&quot; rel=&quot;lightbox[summervacation2012]&quot; href=&quot;http://www.mcginityphoto.com/Vacation/Montana-Summer-2012/i-sdLBDmF/0/M/DSC_4755-M.jpg&quot;&gt;
        &lt;img src=&quot;//www.mcginityphoto.com/Vacation/Montana-Summer-2012/i-sdLBDmF/0/Ti/DSC_4755-Ti.jpg&quot;
             style=&quot;border: 1px solid black&quot; width=&quot;95&quot; height=&quot;95&quot; alt=&quot;The Girls at Double Arrow&quot;&gt;&lt;/a&gt;

      &lt;a title=&quot;Hard to believe I caused this guy to move to Montana in 1997&quot; rel=&quot;lightbox[summervacation2012]&quot; href=&quot;http://www.mcginityphoto.com/Vacation/Montana-Summer-2012/i-b5FCTrd/0/M/DSC_4845-2-M.jpg&quot;&gt;
        &lt;img src=&quot;//www.mcginityphoto.com/Vacation/Montana-Summer-2012/i-b5FCTrd/0/Ti/DSC_4845-2-Ti.jpg&quot;
             style=&quot;border: 1px solid black&quot; width=&quot;95&quot; height=&quot;95&quot; alt=&quot;Hard to believe I caused this guy to move to Montana in 1997&quot;&gt;&lt;/a&gt;

      &lt;a title=&quot;Holland Lake Sunset&quot; rel=&quot;lightbox[summervacation2012]&quot; href=&quot;http://www.mcginityphoto.com/Vacation/Montana-Summer-2012/i-cH8CpGZ/0/M/HLL%20View-M.jpg&quot;&gt;
        &lt;img src=&quot;//www.mcginityphoto.com/Vacation/Montana-Summer-2012/i-cH8CpGZ/0/Ti/HLL%20View-Ti.jpg&quot;
             style=&quot;border: 1px solid black; margin-left: 1px&quot; width=&quot;95&quot; height=&quot;95&quot; alt=&quot;Holland Lake Sunset&quot;&gt;&lt;/a&gt;

    &lt;/td&gt;
    &lt;td style=&quot;text-align: center; vertical-align: top&quot;&gt;
      &lt;a title=&quot;Soy Fields in Montana&quot; rel=&quot;lightbox[summervacation2012]&quot; href=&quot;http://www.mcginityphoto.com/Vacation/Montana-Summer-2012/i-v8x8h68/0/M/Soy%20Fields%20in%20Montana-M.jpg&quot;&gt;
        &lt;img
            src=&quot;//www.mcginityphoto.com/Vacation/Montana-Summer-2012/i-v8x8h68/0/Ti/Soy%20Fields%20in%20Montana-Ti.jpg&quot;
            style=&quot;border: 1px solid black; margin-top: 5px&quot; width=&quot;85&quot; height=&quot;95&quot; alt=&quot;Soy Fields in Montana&quot;&gt;&lt;/a&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td colspan=&quot;2&quot; style=&quot;text-align: right; padding-right: 5px&quot;&gt;
      &lt;a href=&quot;http://www.mcginityphoto.com/Vacation/Montana-Summer-2012/n-TKzDc&quot;&gt;&amp;rarr; Trish&apos;s
        Montana Summer 2012 Album&lt;/a&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;
On July 14th, we flew to Maui and continued to explore. Our good friend&apos;s parents have a timeshare on Sugar Beach and got us a screaming deal on lodging. We had beachfront accommodations with a pool, hot tub and sauna. We played golf on my birthday (July 16th). Visited the Old Lahaina Luau, Maui Aquarium and Coconut Festival at Grand Wailea. Drove to the summit of Haleakala. Sipped cold handcrafted ales while watched the kids boogie board. Embarked on a sunset cruise, hiked in Iao Valley, did some horseback riding along the beach, sea kayaked and snorkeled with sea turtles. Savored Shawn&apos;s excellent cooking. Flew back to Missoula (Montana), slept for 6 hours and drove 12.5 hours back to Denver on July 27th.
&lt;/p&gt;
&lt;table cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; style=&quot;margin: 0 auto 5px auto; width: 500px&quot;&gt;
  &lt;tr&gt;
    &lt;td&gt;
      &lt;a title=&quot;Api, Makao and Keaka. My Hawaiin name is Pualani&quot; rel=&quot;lightbox[summervacation2012]&quot; href=&quot;http://www.mcginityphoto.com/Vacation/2012-Maui-Vacation/i-KHjFqdL/0/M/DSC_5402-M.jpg&quot;&gt;
        &lt;img src=&quot;//www.mcginityphoto.com/Vacation/2012-Maui-Vacation/i-KHjFqdL/0/S/DSC_5402-S.jpg&quot;
             width=&quot;400&quot; height=&quot;266&quot; style=&quot;border: 1px solid black&quot; alt=&quot;Api, Makao and Keaka. My Hawaiin name is Pualani&quot;&gt;&lt;/a&gt;
    &lt;/td&gt;

    &lt;td style=&quot;text-align: center; vertical-align: top&quot;&gt;
      &lt;a title=&quot;I&apos;m going to call him Honu!&quot; rel=&quot;lightbox[summervacation2012]&quot; href=&quot;http://www.mcginityphoto.com/Vacation/2012-Maui-Vacation/i-XbRpLxp/0/M/DSC_5518-M.jpg&quot;&gt;
        &lt;img src=&quot;//www.mcginityphoto.com/Vacation/2012-Maui-Vacation/i-XbRpLxp/0/Ti/DSC_5518-Ti.jpg&quot;
             style=&quot;border: 1px solid black&quot; width=&quot;85&quot; height=&quot;85&quot; alt=&quot;I&apos;m going to call him Honu!&quot;&gt;&lt;/a&gt;

      &lt;a title=&quot;Abbie with the Stingray&quot; rel=&quot;lightbox[summervacation2012]&quot; href=&quot;http://www.mcginityphoto.com/Vacation/2012-Maui-Vacation/i-67hJw8H/0/M/DSC_5573-M.jpg&quot;&gt;
        &lt;img src=&quot;//www.mcginityphoto.com/Vacation/2012-Maui-Vacation/i-67hJw8H/0/Ti/DSC_5573-Ti.jpg&quot;
             style=&quot;border: 1px solid black; margin-top: 3px&quot; width=&quot;85&quot; height=&quot;85&quot; alt=&quot;Abbie with the Stingray&quot;&gt;&lt;/a&gt;

      &lt;a title=&quot;Wahoo! We did it!&quot; rel=&quot;lightbox[summervacation2012]&quot; href=&quot;http://www.mcginityphoto.com/Vacation/2012-Maui-Vacation/i-fBjCfcc/0/M/Wahoo%20we%20did%20it-M.jpg&quot;&gt;
        &lt;img src=&quot;//www.mcginityphoto.com/Vacation/2012-Maui-Vacation/i-fBjCfcc/0/Ti/Wahoo%20we%20did%20it-Ti.jpg&quot;
             style=&quot;border: 1px solid black; margin-top: 3px&quot; width=&quot;85&quot; height=&quot;85&quot; alt=&quot;Wahoo! We did it!&quot;&gt;&lt;/a&gt;

    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td style=&quot;padding-top: 5px&quot;&gt;
&lt;a href=&quot;http://www.mcginityphoto.com/Vacation/2012-Maui-Vacation/i-Xdvps68/0/M/Saiboat%20Sunset%20Maui-M.jpg&quot; rel=&quot;lightbox[summervacation2012]&quot; title=&quot;Sunset sail from a catamaran in Maui. Epic!&quot;&gt;
&lt;img src=&quot;//www.mcginityphoto.com/Vacation/2012-Maui-Vacation/i-Xdvps68/0/S/Saiboat%20Sunset%20Maui-S.jpg&quot; width=&quot;400&quot; height=&quot;256&quot; alt=&quot;Sunset sail from a catamaran in Maui. Epic!&quot; style=&quot;border: 1px solid black&quot;/&gt;&lt;/a&gt; 
&lt;/td&gt;
&lt;td style=&quot;text-align: center; vertical-align: top; padding-top: 5px&quot;&gt;
&lt;a title=&quot;Where we stayed for 2 weeks!&quot; rel=&quot;lightbox[summervacation2012]&quot; href=&quot;http://www.mcginityphoto.com/Vacation/2012-Maui-Vacation/i-cKm2kdN/0/M/Sugar%20Beach%20Maui-M.jpg&quot;&gt;
&lt;img src=&quot;//www.mcginityphoto.com/Vacation/2012-Maui-Vacation/i-cKm2kdN/0/Ti/Sugar%20Beach%20Maui-Ti.jpg&quot; style=&quot;border: 1px solid black&quot; width=&quot;85&quot; height=&quot;82&quot; alt=&quot;Where we stayed for 2 weeks!&quot;&gt;&lt;/a&gt;

&lt;a title=&quot;Epic Maui Ride&quot; rel=&quot;lightbox[summervacation2012]&quot; href=&quot;http://www.mcginityphoto.com/Vacation/2012-Maui-Vacation/i-GcPsq6B/0/M/Epic%20Ride%20in%20Maui%21-M.jpg&quot;&gt;
&lt;img src=&quot;//www.mcginityphoto.com/Vacation/2012-Maui-Vacation/i-GcPsq6B/0/Ti/Epic%20Ride%20in%20Maui%21-Ti.jpg&quot; style=&quot;border: 1px solid black; margin-top: 3px&quot; width=&quot;85&quot; height=&quot;82&quot; alt=&quot;Epic Maui Ride&quot;&gt;&lt;/a&gt;

&lt;a title=&quot;Kayaking with Turtles&quot; rel=&quot;lightbox[summervacation2012]&quot; href=&quot;http://www.mcginityphoto.com/Vacation/2012-Maui-Vacation/i-V55LgwT/0/M/Turtle%21-M.jpg&quot;&gt;
&lt;img src=&quot;//www.mcginityphoto.com/Vacation/2012-Maui-Vacation/i-V55LgwT/0/Ti/Turtle%21-Ti.jpg&quot; style=&quot;border: 1px solid black; margin-top: 3px&quot; width=&quot;85&quot; height=&quot;82&quot; alt=&quot;Kayaking with Turtles&quot;&gt;&lt;/a&gt;

&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
    &lt;td colspan=&quot;2&quot; style=&quot;text-align: right; padding-right: 5px; padding-top: 5px&quot;&gt;
      &lt;a href=&quot;http://www.mcginityphoto.com/Vacation/2012-Maui-Vacation/n-T39HF&quot;&gt;&amp;rarr; Trish&apos;s
        Maui Summer 2012 Album&lt;/a&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;
Mahalo Montana and Maui. Thank you to our awesome friends: Mom, Dad, Owen, Jason, Ryan and Shawn. It was an incredible adventure.</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/migrating_to_play_2_and</id>
        <title type="html">Migrating to Play 2 and My &#220;berConf Presentation</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/migrating_to_play_2_and"/>
        <published>2012-06-21T16:09:22-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="scala" scheme="http://roller.apache.org/ns/tags/" />
        <category term="playframework" scheme="http://roller.apache.org/ns/tags/" />
        <category term="anorm" scheme="http://roller.apache.org/ns/tags/" />
        <category term="heroku" scheme="http://roller.apache.org/ns/tags/" />
        <category term="play-more" scheme="http://roller.apache.org/ns/tags/" />
        <category term="uberconf" scheme="http://roller.apache.org/ns/tags/" />
        <category term="iphone" scheme="http://roller.apache.org/ns/tags/" />
        <category term="jqtouch" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">In my &lt;a href=&quot;http://raibledesigns.com/rd/entry/upgrading_to_play_2_anorm&quot;&gt;last post&lt;/a&gt; about migrating to Play 2, I said I&apos;d write another post on the rest of my experience. While I&apos;m not completely finished with migrating to Play 2, I feel like I&apos;ve done enough to talk about the issues I encountered.
&lt;/p&gt;
&lt;p id=&quot;validation&quot;&gt;&lt;strong&gt;Validation and Displaying Errors&lt;/strong&gt;&lt;br/&gt;
With Play 1, I can&apos;t help but think validation was a bit more intuitive. For example, here&apos;s how I populated an object from request parameters, converted a value and validated its data was fit to put in a database.
&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
var workout = params.get(&quot;workout&quot;, classOf[Workout])

// change duration to time
var duration = params.get(&quot;workout.duration&quot;)
workout.duration = convertWatchToTime(duration)

Validation.valid(&quot;workout&quot;, workout)

if (Validation.hasErrors) {
  renderArgs.put(&quot;template&quot;, &quot;Profile/edit&quot;)
  edit(id);
  ...
} else { // put into db
&lt;/pre&gt;
&lt;p&gt;With Play Scala 2, you have to define a &lt;a href=&quot;http://www.playframework.org/documentation/2.0/ScalaForms&quot;&gt;Form structure&lt;/a&gt; and bind it from the request. Based on what I was able to conjure up, I ended up writing the following code to accomplish the same thing:
&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
val workoutForm = Form(
  mapping(
    &quot;id&quot; -&gt; ignored(NotAssigned: anorm.Pk[Long]),
    &quot;title&quot; -&gt; text,
    &quot;description&quot; -&gt; text,
    &quot;duration&quot; -&gt; nonEmptyText,
    &quot;distance&quot; -&gt; nonEmptyText,
    &quot;postedAt&quot; -&gt; optional(date),
    &quot;athleteId&quot; -&gt; optional(longNumber)
  )((id, title, description, duration, distance, postedAt, athleteId) =&gt;
    Workout(id, title, description, convertWatchToTime(duration), distance.toDouble, null, 0))
    ((w: Workout) =&gt;
      Some((w.id, w.title, w.description, w.duration.toString, w.distance.toString, null, Some(0))))
)
...
workoutForm.bindFromRequest.fold(
  form =&gt; {
    Ok(Scalate(&quot;/Profile/edit.jade&quot;).render(request, &apos;errors -&gt; form.errors))
  },
  workout =&gt; { // put into db
&lt;/pre&gt;
&lt;p&gt;First of all, the Play 2 version is quite a bit more verbose, but most of that comes from the re-defining of my model object as a form. It seems strange that the Java API allows you to &lt;a href=&quot;http://www.playframework.org/documentation/2.0/JavaForms&quot;&gt;do it in one line&lt;/a&gt; whereas the Scala version does not. Also, I was unable to figure out how to get the data from my &quot;form&quot; back into the request so I could refill input fields. I&apos;ll admit, I didn&apos;t spend a lot of time trying to figure it out, but it did fail the 10 minute test. Note to self: &lt;a href=&quot;http://wufoo.com/html5/attributes/09-required.html&quot;&gt;use HTML5&apos;s &lt;em&gt;required&lt;/em&gt; attribute&lt;/a&gt; to reduce the need for server-side validation on modern browsers.
&lt;/p&gt;
&lt;p&gt;On a more positive note, I did like the way I was able to use routes in my Jade templates. It was as simple as importing the routes class and using it as you would in Play&apos;s Scala Templates:
&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
-import controllers._

form(method=&quot;post&quot; class=&quot;form-stacked&quot; id=&quot;workoutForm&quot;
  action={routes.Profile.postWorkout(workout.map(_.id.get))})
  input(type=&quot;hidden&quot; name=&quot;id&quot; value=&quot;#{workout.map(_.id)}&quot;)
&lt;/pre&gt;
&lt;p id=&quot;securesocial&quot;&gt;&lt;strong&gt;Secure Social&lt;/strong&gt;&lt;br/&gt;After getting most of my UI working, I started looking at the &lt;a href=&quot;https://github.com/jaliss/securesocial&quot;&gt;Secure Social Module for Play 2&lt;/a&gt;. Below are the steps I had to go through to install it:
&lt;/p&gt;
&lt;ol&gt;
    &lt;li&gt;Cloned &lt;a href=&quot;https://github.com/jaliss/securesocial&quot;&gt;GitHub project&lt;/a&gt; to my hard drive.&lt;/li&gt;
    &lt;li&gt;Copied &lt;code&gt;module-code/*&lt;/code&gt; into my projects&apos; &lt;code&gt;modules/securesocial&lt;/code&gt; directory.&lt;/li&gt;
    &lt;li&gt;Modified &lt;code&gt;project/Build.scala&lt;/code&gt; to add secureSocial and dependsOn to my project.
&lt;pre class=&quot;brush: scala&quot;&gt;
val secureSocial = PlayProject(
  appName + &quot;-securesocial&quot;, appVersion, mainLang = SCALA, path = file(&quot;modules/securesocial&quot;)
)

val main = PlayProject(appName, appVersion, appDependencies, mainLang = SCALA).settings(
  // Add your own project settings here
).dependsOn(secureSocial).aggregate(secureSocial)
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;Added a &lt;a href=&quot;https://github.com/jaliss/securesocial/blob/master/samples/scala/demo/conf/securesocial.conf&quot;&gt;conf/securesocial.conf&lt;/a&gt; and included it in my application.conf with the following line:
&lt;pre style=&quot;margin-top: 5px&quot;&gt;
include &quot;securesocial.conf&quot;
&lt;/pre&gt;&lt;/li&gt;
    &lt;li&gt;Added a conf/play.plugins with the following to get Twitter to load as a provider:
&lt;pre style=&quot;margin-top: 5px&quot;&gt;
10000:securesocial.core.providers.TwitterProvider
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;Created an &lt;a href=&quot;https://github.com/jaliss/securesocial/blob/master/samples/scala/demo/app/service/InMemoryUserService.scala&quot;&gt;InMemoryUserService.scala&lt;/a&gt; and referenced it in my play.plugins file:
&lt;pre style=&quot;margin-top: 5px&quot;&gt;
9999:services.InMemoryUserService
&lt;/pre&gt;&lt;/li&gt;
    &lt;li&gt;Added &lt;a href=&quot;https://github.com/jaliss/securesocial/blob/master/samples/scala/demo/conf/routes&quot;&gt;Secure Social&apos;s routes&lt;/a&gt; to my conf/routes file.
        &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Once I&apos;d finished all these steps, I fired up my app and was pleasantly surprised to find I could navigate to /login and successfully authenticate via Twitter. Installing Secure Social in a Play 2 app is quite a bit harder than adding it as a dependency in Play 1, but I was thankful that I got it to work in under 10 minutes.
&lt;/p&gt;
&lt;p id=&quot;heroku&quot;&gt;
&lt;strong&gt;Heroku&lt;/strong&gt;&lt;br/&gt;
The next thing I did was attempt to deploy my app to Heroku. I knew there might be some issues with Scalate after reading &lt;a href=&quot;http://janhelwich.wordpress.com/2012/04/22/getting-play-2-with-scalate-to-run-on-heroku/&quot;&gt;Jan Helwich&apos;s blog post&lt;/a&gt; about Scalate on Heroku. The first things I encountered were 1) a successful startup and 2) an error in my browser.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;img src=&quot;//farm6.staticflickr.com/5346/7416344424_b25593ccdc_n.jpg&quot; width=&quot;320&quot; height=&quot;253&quot; alt=&quot;Action not found&quot;&gt;
&lt;/p&gt;
&lt;p&gt;I was able to reproduce this issue locally by running &quot;play clean stage&quot; and starting the app with &quot;target/start&quot;. After 30 minutes of banging my head against the wall, I guessed it might be caused by Secure Social. Removing Secure Social solved the problem and I was back in business. However, this time when I deployed, I received the error that Jan had mentioned might happen.
&lt;/p&gt;
&lt;pre&gt;
2012-06-21T07:07:12+00:00 app[web.1]: [error] o.f.s.l.DefaultLayoutStrategy - Unhandled: org.fusesource.scalate.TemplateException: target/../tmp/src/app/target/../app/views/layouts/default.jade.scala (No such file or directory)
2012-06-21T07:07:12+00:00 app[web.1]: [error] application - 
2012-06-21T07:07:12+00:00 app[web.1]: 
2012-06-21T07:07:12+00:00 app[web.1]: ! @6amfgf02h - Internal server error, for request [GET /] -&gt;
2012-06-21T07:07:12+00:00 app[web.1]: 
2012-06-21T07:07:12+00:00 app[web.1]: play.core.ActionInvoker$$anonfun$receive$1$$anon$1: Execution exception [[TemplateException: target/../tmp/src/app/target/../app/views/layouts/default.jade.scala (No such file or directory)]]
2012-06-21T07:07:12+00:00 app[web.1]:   at play.core.ActionInvoker$$anonfun$receive$1.apply(Invoker.scala:134) [play_2.9.1-2.0.1.jar:2.0.1]
2012-06-21T07:07:12+00:00 app[web.1]:   at play.core.ActionInvoker$$anonfun$receive$1.apply(Invoker.scala:115) [play_2.9.1-2.0.1.jar:2.0.1]
2012-06-21T07:07:12+00:00 app[web.1]:   at akka.actor.Actor$class.apply(Actor.scala:311) [akka-actor-2.0.1.jar:2.0.1]
2012-06-21T07:07:12+00:00 app[web.1]:   at play.core.ActionInvoker.apply(Invoker.scala:113) [play_2.9.1-2.0.1.jar:2.0.1]
2012-06-21T07:07:12+00:00 app[web.1]:   at akka.actor.ActorCell.invoke(ActorCell.scala:619) [akka-actor-2.0.1.jar:2.0.1]
2012-06-21T07:07:12+00:00 app[web.1]:   at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:196) [akka-actor-2.0.1.jar:2.0.1]
2012-06-21T07:07:12+00:00 app[web.1]: Caused by: org.fusesource.scalate.TemplateException: target/../tmp/src/app/target/../app/views/layouts/default.jade.scala (No such file or directory)
2012-06-21T07:07:12+00:00 app[web.1]:   at org.fusesource.scalate.TemplateEngine.compileAndLoad(TemplateEngine.scala:834) ~[scalate-core-1.5.3.jar:1.5.3]
2012-06-21T07:07:12+00:00 app[web.1]:   at org.fusesource.scalate.TemplateEngine.compileAndLoadEntry(TemplateEngine.scala:691) ~[scalate-core-1.5.3.jar:1.5.3]
2012-06-21T07:07:12+00:00 app[web.1]:   at org.fusesource.scalate.TemplateEngine.liftedTree1$1(TemplateEngine.scala:411) ~[scalate-core-1.5.3.jar:1.5.3]
2012-06-21T07:07:12+00:00 app[web.1]:   at org.fusesource.scalate.TemplateEngine.load(TemplateEngine.scala:405) ~[scalate-core-1.5.3.jar:1.5.3]
2012-06-21T07:07:12+00:00 app[web.1]:   at org.fusesource.scalate.TemplateEngine.load(TemplateEngine.scala:475) ~[scalate-core-1.5.3.jar:1.5.3]
2012-06-21T07:07:12+00:00 app[web.1]:   at org.fusesource.scalate.layout.DefaultLayoutStrategy.org$fusesource$scalate$layout$DefaultLayoutStrategy$$tryLayout(DefaultLayoutStrategy.scala:77) ~[scalate-core-1.5.3.jar:1.5.3]
2012-06-21T07:07:12+00:00 app[web.1]: Caused by: java.io.FileNotFoundException: target/../tmp/src/app/target/../app/views/layouts/default.jade.scala (No such file or directory)
2012-06-21T07:07:12+00:00 app[web.1]:   at java.io.FileOutputStream.open(Native Method) ~[na:1.6.0_20]
2012-06-21T07:07:12+00:00 app[web.1]:   at java.io.FileOutputStream.&lt;init&gt;(FileOutputStream.java:209) ~[na:1.6.0_20]
2012-06-21T07:07:12+00:00 app[web.1]:   at java.io.FileOutputStream.&lt;init&gt;(FileOutputStream.java:160) ~[na:1.6.0_20]
2012-06-21T07:07:12+00:00 app[web.1]:   at org.fusesource.scalate.util.IOUtil$.writeBinaryFile(IOUtil.scala:111) ~[scalate-util-1.5.3.jar:1.5.3]
2012-06-21T07:07:12+00:00 app[web.1]:   at org.fusesource.scalate.TemplateEngine.compileAndLoad(TemplateEngine.scala:747) ~[scalate-core-1.5.3.jar:1.5.3]
2012-06-21T07:07:12+00:00 app[web.1]:   at org.fusesource.scalate.TemplateEngine.compileAndLoadEntry(TemplateEngine.scala:691) ~[scalate-core-1.5.3.jar:1.5.3]
&lt;/pre&gt;
&lt;p&gt;
I tried his suggestion (removing the first slash on my Scalate paths) but it didn&apos;t work. I tried adding in Scalate pre-compilation, but that didn&apos;t solve the problem either. The good news is I did solve it this afternoon by &lt;a href=&quot;https://github.com/mraible/play-more/commit/3e981ab4f31c6333ee0276cd2d84b44cba47b7c8&quot;&gt;changing my Scalate object to use a canonical path&lt;/a&gt; instead of an absolute one.
&lt;/p&gt;
&lt;p id=&quot;iphone-app&quot;&gt;&lt;strong&gt;iPhone App&lt;/strong&gt;&lt;br/&gt;
In addition to the changes mentioned here, I re-wrote the iPhone app for Play More. I upgraded it to &lt;a href=&quot;http://phonegap.com/2012/06/13/phonegap-1-8-1-released/&quot;&gt;PhoneGap 1.8.1&lt;/a&gt;, used &lt;a href=&quot;http://www.jqtouch.com/&quot;&gt;jQTouch&lt;/a&gt;, developed with &lt;a href=&quot;http://www.jetbrains.com/objc/&quot;&gt;AppCode&lt;/a&gt; (instead of Xcode) and had a pretty good experience. The only issue I ran into was with the &lt;a href=&quot;https://github.com/DataZombies/jQTouch&quot;&gt;jqt.bars extension from
DataZombies&lt;/a&gt;. I briefly tried to integrate it and then decided not to. However, I left all its JS and CSS in my page and this caused scrolling to not work and made the app sluggish. Removing the files solved the problem. The other big improvement I made was moving all the static assets (JS, CSS, images) into the mobile app instead of referencing them from http://play-more.com. This reduced the startup time from 30-40 seconds to 3-4 seconds!&lt;/p&gt;
&lt;p&gt;
&lt;p id=&quot;presentation&quot;&gt;&lt;strong&gt;Presentation and Source Code&lt;/strong&gt;&lt;br/&gt;
I presented all of these findings and told my story at &lt;a href=&quot;http://uberconf.com&quot;&gt;&#220;berConf&lt;/a&gt; this morning. In addition, I announced that the code is now &lt;a href=&quot;http://github.com/mraible/play-more&quot;&gt;open source and available on GitHub&lt;/a&gt;. You can view my presentation below or &lt;a href=&quot;http://www.slideshare.net/mraible/html5-with-play-scala-coffeescript-and-jade-uberconf-2012&quot;&gt;on Slideshare&lt;/a&gt;.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;iframe src=&quot;//www.slideshare.net/slideshow/embed_code/13410673?rel=0&quot; width=&quot;510&quot; height=&quot;426&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&quot; allowfullscreen&gt;&lt;/iframe&gt;
&lt;/p&gt;
&lt;p id=&quot;conclusion&quot;&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;br/&gt;
Would I do it again? Learning Scala was my primary motivator for digging into Play. When Play 2 was announced, I thought migrating my app to the new version would be easy. Unfortunately, the Play Developers decided to break backwards-compatibility and wrote a whole new framework that still seems to be in its infancy. I think you can see from my last couple of posts that migrating from Play 1.x to 2.x was not an easy task. It&apos;s been nice to learn more about Play and Scala in the process, but living on the bleeding edge was also quite frustrating at times. Play Scala 1.x seemed to be quite a bit more productive than Play 2, especially because of the Magic[T] in Anorm, but also because it required less code in Controllers. 
&lt;/p&gt;
&lt;p&gt;I found Anorm and Scalate to be &lt;em&gt;huge&lt;/em&gt; time sinks and don&apos;t know if I&apos;d recommend using either one in a Play 2 project. I&apos;m sure Scalate will be easier to use as its Play 2 integration gets more refined, but I don&apos;t know if there&apos;s any hope for a JDBC abstraction that doesn&apos;t produce error messages when things go south.
&lt;/p&gt;
&lt;p&gt;On the upside, my experience with HTML5 and CoffeeScript was wonderful. They did what I asked them to do and didn&apos;t cause much pain. When a browser-based webapp couldn&apos;t handle geo running in the background, PhoneGap came to the rescue. 
&lt;/p&gt;
&lt;p&gt;
I plan on continuing to develop &lt;a href=&quot;http://play-more.com&quot;&gt;&lt;em&gt;Play More!&lt;/em&gt;&lt;/a&gt; If you&apos;d like to help, checkout the &lt;a href=&quot;https://github.com/mraible/play-more/issues?state=open&quot;&gt;open issues&lt;/a&gt; and viva la open source!  </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/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/more_scalate_goodness_for_play</id>
        <title type="html">More Scalate Goodness for Play</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/more_scalate_goodness_for_play"/>
        <published>2011-11-07T14:07:40-07:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="playframework" scheme="http://roller.apache.org/ns/tags/" />
        <category term="scala" scheme="http://roller.apache.org/ns/tags/" />
        <category term="play-more" scheme="http://roller.apache.org/ns/tags/" />
        <category term="devoxx2011" scheme="http://roller.apache.org/ns/tags/" />
        <category term="jade" scheme="http://roller.apache.org/ns/tags/" />
        <category term="scalate" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">&lt;a href=&quot;http://scalate.fusesource.org&quot;&gt;&lt;img src=&quot;//scalate.fusesource.org/images/project-icon-160x160.png&quot; width=&quot;80&quot; height=&quot;80&quot; alt=&quot;Scalate&quot; class=&quot;picture&quot; style=&quot;border: 0&quot;/&gt;&lt;/a&gt;
This article is the 6th in a series on about my adventures developing a web 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;li&gt;&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;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/play_scala_s_anorm_heroku&quot;&gt;Play Scala&apos;s Anorm, Heroku and PostgreSQL Issues&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Last week, I wrote about my adventures with &lt;a href=&quot;http://scala.playframework.org/documentation/scala-0.9.1/anorm&quot;&gt;Anorm&lt;/a&gt; and mentioned I&apos;d made some improvements to Scalate Play interoperability. First of all, I&apos;ve been using a Scalate trait and ScalateTemplate class to render Jade templates in my application. I described this setup in my &lt;a href=&quot;http://raibledesigns.com/rd/entry/integrating_scalate_and_jade_with&quot;&gt;first article on Scalate and Play&lt;/a&gt;. 
&lt;/p&gt;
&lt;p id=&quot;default-variables&quot;&gt;&lt;strong&gt;Adding SiteMesh Features and Default Variables&lt;/strong&gt;&lt;br/&gt;  
When I started making my app look good with CSS, I started longing for a feature I&apos;ve used in SiteMesh. That is, to have a body id or class that can identify the page and allow per-page CSS rules. To do this with SiteMesh, you&apos;d have something like the following in your page:
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;  
&amp;lt;body id=&quot;signup&quot;/&gt;
&lt;/pre&gt;
&lt;p&gt;  
And then read it in your decorator:
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;body&lt;decorator:getProperty property=&quot;body.id&quot; writeEntireProperty=&quot;true&quot;/&gt;&amp;lt;decorator:getProperty property=&quot;body.class&quot; writeEntireProperty=&quot;true&quot;/&gt;&gt;
&lt;/pre&gt;
&lt;p&gt;As I started looking into how to do this, I came across Play Scala&apos;s &lt;a href=&quot;https://github.com/playframework/play-scala/blob/master/src/play/mvc/ScalaController.scala&quot;&gt;ScalaController&lt;/a&gt; and how it was populating Play&apos;s default variables (request, response, flash, params, etc.). Based on this newfound knowledge, I added a &lt;em&gt;populateRenderArgs()&lt;/em&gt; method to set all the default variables and my desired &lt;em&gt;bodyClass&lt;/em&gt; variable.
&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
def populateRenderArgs(args: (Symbol, Any)*): Map[String, Any] = {
  val renderArgs = Scope.RenderArgs.current();

  args.foreach {
    o =&gt;
      renderArgs.put(o._1.name, o._2)
  }

  renderArgs.put(&quot;session&quot;, Scope.Session.current())
  renderArgs.put(&quot;request&quot;, Http.Request.current())
  renderArgs.put(&quot;flash&quot;, Scope.Flash.current())
  renderArgs.put(&quot;params&quot;, Scope.Params.current())
  renderArgs.put(&quot;errors&quot;, validationErrors)
  renderArgs.put(&quot;config&quot;, Play.configuration)

  // CSS class to add to body
  renderArgs.put(&quot;bodyClass&quot;, Http.Request.current().action.replace(&quot;.&quot;, &quot; &quot;).toLowerCase)
  renderArgs.data.toMap
}

implicit def validationErrors:Map[String,play.data.validation.Error] = {
  import scala.collection.JavaConverters._
  Map.empty[String,play.data.validation.Error] ++ 
    Validation.errors.asScala.map( e =&gt; (e.getKey, e) )
}
&lt;/pre&gt;&lt;p&gt;After adding this method, I was able to access these values in my templates by defining them at the top:
&lt;/pre&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
-@ val bodyClass: String 
-@ val params: play.mvc.Scope.Params
-@ val flash: play.mvc.Scope.Flash
&lt;/pre&gt;
&lt;p&gt;And then reading their values in my template:
&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
body(class=bodyClass)
...
- if (flash.get(&quot;success&quot;) != null) {
  div(class=&quot;alert-message success&quot; data-alert=&quot;alert&quot;)
    a(class=&quot;close&quot; href=&quot;#&quot;) &amp;&amp;times;
    | #{flash.get(&quot;success&quot;)}
- }
...
  fieldset
    legend Leave a comment &amp;rarr;
    div.clearfix
      label(for=&quot;author&quot;) Your name:
      input(type=&quot;text&quot; name=&quot;author&quot; class=&quot;xlarge&quot; value={params.get(&quot;author&quot;)})
    div.clearfix
      label(for=&quot;content&quot;) Your message:
      textarea(name=&quot;content&quot; class=&quot;xlarge&quot;) #{params.get(&quot;content&quot;)}
    div.actions
      button(type=&quot;submit&quot; class=&quot;btn primary&quot;) Submit your comment
      button(type=&quot;reset&quot; class=&quot;btn&quot;) Cancel
&lt;/pre&gt;  
&lt;p&gt;For a request like Home/index, the body tag is now rendered as:
&lt;pre class=&quot;brush: xml; toolbar: false&quot;&gt;
&amp;lt;body class=&quot;home index&quot;&gt;
&lt;/pre&gt;
This allows you to group CSS styles by Controller names as well as by method names.
&lt;/p&gt;
&lt;p&gt;
Next, I started developing forms and validation logic. I quickly discovered I needed an &lt;em&gt;action()&lt;/em&gt; method like the one defined in &lt;a href=&quot;https://github.com/playframework/play-scala/blob/master/src/play/templates/ScalaTemplate.scala&quot;&gt;ScalaTemplate&apos;s&lt;/a&gt; TemplateMagic class. 
&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
def action(action: =&gt; Any) = {
  new play.mvc.results.ScalaAction(action).actionDefinition.url
}
&lt;/pre&gt;
&lt;p&gt;
Since TemplateMagic is an inner class, I determined that copying the method into my ScalateTemplate class was the easiest workaround. After doing this, I was able to import the method and use it in my templates.
&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
-import controllers.ScalateTemplate._
...
form(method=&quot;post&quot; class=&quot;form-stacked&quot; id=&quot;commentForm&quot;
     action={action(controllers.Profile.postComment(workout._1.id()))})
&lt;/pre&gt;
&lt;p&gt;After getting the proper URL written into my form&apos;s action attribute, I encountered a new problem. The &lt;a href=&quot;http://scala.playframework.org/documentation/scala-0.9.1/guide4#aAddingvalidationa&quot;&gt;Play Scala Tutorial explains validation flow&lt;/a&gt; as follows:
&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
if (Validation.hasErrors) {
  show(postId)
} else {
  Comment.create(Comment(postId, author, content))
  Action(show(postId))
}
&lt;/pre&gt;
&lt;p&gt;However, when I had validation errors, I end up with the following error:
&lt;/p&gt;
&lt;pre&gt;
Could not load resource: [Timeline/postComment.jade]
&lt;/pre&gt;&lt;p&gt;To fix this, I added logic to my Scalate trait that looks for a &quot;template&quot; variable before using &lt;em&gt;Http.Request.current().action.replace(&quot;.&quot;, &quot;/&quot;)&lt;/em&gt; for the name. After making this change, I was able to use the following code to display validation errors.&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
if (Validation.hasErrors) {
  renderArgs.put(&quot;template&quot;, &quot;Timeline/show&quot;)
  show(postId)
} else {
  Comment.create(Comment(postId, author, content))
  Action(show(postId))
}
&lt;/pre&gt;
&lt;p&gt;Next, I wanted to give child pages the ability to set content in parent pages. With SiteMesh, I could use the &amp;lt;content&amp;gt; tag as follows:
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;content tag=&quot;underground&quot;&gt;
  HTML goes here
&amp;lt;/content&gt;
&lt;/pre&gt;
&lt;p&gt;This HTML could then be retrieved in the decorator using the &amp;lt;decorator:getProperty&amp;gt; tag:
&lt;/p&gt;
&lt;pre class=&quot;brush: xml; toolbar: false&quot;&gt;
&amp;lt;decorator:getProperty property=&quot;page.underground&quot;/&gt;
&lt;/pre&gt;
&lt;p&gt;With Scalate, I found it equally easy using the &lt;em&gt;captureAttribute()&lt;/em&gt; method. For example, here&apos;s how I captured a list of an athlete&apos;s workouts for display in a sidebar.
&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
- captureAttribute(&quot;sidebar&quot;)
  - Option(older).filterNot(_.isEmpty).map { workouts =&gt;
    .older-workouts
      h3
        | Older workouts
        span.from from this app
      - workouts.map { workout =&gt;
        - render(&quot;workout.jade&quot;, Map(&apos;workout -&gt; workout, &apos;mode -&gt; &quot;teaser&quot;))
      - }
  - }
- }
&lt;/pre&gt;
&lt;p&gt;Then in my layout, I was able to retrieve this and display it. Below is a snippet from the layout I&apos;m using (copied from &lt;a href=&quot;http://twitter.github.com/bootstrap/examples/container-app.html&quot;&gt;Twitter&apos;s Bootstrap example&lt;/a&gt;). You can see how the sidebar is included in the .span4 at the end.
&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
-@ val sidebar: String = &quot;&quot;
...
.container
  .content
    .page-header
      h1
        = pageHeader
        small
          = pageTagline
    .row
      .span10
        !~~ body
      .span4
        = unescape(sidebar)
  footer
&lt;/pre&gt;
&lt;p id=&quot;view-vs-render&quot;&gt;&lt;strong&gt;View vs. Render in Scalate&lt;/strong&gt;&lt;br/&gt;
In the sidebar code above, you might notice the &lt;em&gt;render()&lt;/em&gt; call. This is the &lt;a href=&quot;http://scalate.fusesource.org/documentation/user-guide.html#Render_templates&quot;&gt;Scalate version of server-side includes&lt;/a&gt;. It works well, but there&apos;s also a &lt;a href=&quot;http://scalate.fusesource.org/documentation/user-guide.html#Views&quot;&gt;&lt;em&gt;view()&lt;/em&gt; shortcut&lt;/a&gt; you can use if you want to have templates for rendering your model objects. I quickly discovered it might be difficult to use this feature in my app because my object was &lt;em&gt;Option[(models.Workout, models.Athlete, Seq[models.Comment])]&lt;/em&gt; instead of a simple object. You can read the &lt;a href=&quot;http://groups.google.com/group/scalate/browse_thread/thread/bcc6059fc08d4da0&quot;&gt;view vs. render thread&lt;/a&gt; on the Scalate Google Group if you&apos;re interested in learning more.
&lt;/p&gt;
&lt;p id=&quot;scalate-plugin&quot;&gt;&lt;strong&gt;Scalate as a Module&lt;/strong&gt;&lt;br/&gt;
The last enhancement I attempted to make was to put Scalate support into a &lt;a href=&quot;http://www.playframework.org/documentation/1.2.3/modules&quot;&gt;Play module&lt;/a&gt;. At first, I tried overriding Play&apos;s &lt;em&gt;Template&lt;/em&gt; class but &lt;a href=&quot;http://groups.google.com/group/play-framework/browse_thread/thread/8d1b00ec4304f4ea&quot;&gt;ran into compilation issues&lt;/a&gt;. Then Guillaume Bort (Play&apos;s lead developer) recommended I stick with the trait approach and I was able to get everything working. I looked at the &lt;a href=&quot;https://github.com/pk11/play-scalate&quot;&gt;outdated play-scalate module&lt;/a&gt; to figure out how to add Scala support to build.xml and copied its &lt;a href=&quot;https://github.com/pk11/play-scalate/blob/master/resources/500.scaml&quot;&gt;500.scaml&lt;/a&gt; page for error reporting.&lt;/p&gt;
&lt;p&gt;In order to get line-precise error reporting working, I had to wrap a try/catch around calling Scalate&apos;s &lt;em&gt;TemplateEngine.layout()&lt;/em&gt; method. Again, most of this code was copied from the outdated play-scalate module.
&lt;/p&gt;
&lt;pre class=&quot;brush: scala&quot;&gt;
case class Template(name: String) {
  
  def render(args: (Symbol, Any)*) = {
    val argsMap = populateRenderArgs(args: _*)
    
    val buffer = new StringWriter()
    var context = new DefaultRenderContext(name, scalateEngine, new PrintWriter(buffer))
    
    try {
      val templatePath = new File(Play.applicationPath+&quot;/app/views&quot;,&quot;/&quot;+name).toString
        .replace(new File(Play.applicationPath+&quot;/app/views&quot;).toString,&quot;&quot;)
      scalateEngine.layout(templatePath + scalateType, argsMap)
    } catch {
      case ex:TemplateNotFoundException =&gt; {
        if(ex.isSourceAvailable) {
          throw ex
        }
        val element = PlayException.getInterestingStrackTraceElement(ex)
        if (element != null) {
           throw new TemplateNotFoundException(name, 
             Play.classes.getApplicationClass(element.getClassName()), element.getLineNumber());
        } else {
           throw ex
        }
      }  
      case ex:InvalidSyntaxException =&gt; handleSpecialError(context,ex)
      case ex:CompilerException =&gt; handleSpecialError(context,ex)
      case ex:Exception =&gt; handleSpecialError(context,ex)
    } finally {
      if (buffer.toString.length &gt; 0)
        throw new ScalateResult(buffer.toString,name)
    }
  }
}
...
private def handleSpecialError(context:DefaultRenderContext,ex:Exception) {
  context.attributes(&quot;javax.servlet.error.exception&quot;) = ex
  context.attributes(&quot;javax.servlet.error.message&quot;) = ex.getMessage
  try {
    scalateEngine.layout(scalateEngine.load(errorTemplate), context)
  } catch {
    case ex:Exception =&gt;
      // TODO use logging API from Play here...
      println(&quot;Caught: &quot; + ex)
      ex.printStackTrace
  }
}

private def errorTemplate:String = {
  val fullPath = new File(Play.applicationPath,&quot;/app/views/errors/500.scaml&quot;).toString 
  fullPath.replace(new File(Play.applicationPath+&quot;/app/views&quot;).toString,&quot;&quot;)
}
&lt;/pre&gt;
&lt;p&gt;Once I had this in place, error messages from Scalate are much better. Not only do I see the error in my browser, but I can click on the offending line to open it directly in TextMate.
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://farm7.static.flickr.com/6054/6323523392_91888694bc_o.png&quot; title=&quot;Play Scalate Error Reporting&quot; rel=&quot;lightbox[scalate-goodness]&quot;&gt;&lt;img src=&quot;//farm7.static.flickr.com/6054/6323523392_affe4cf053.jpg&quot; width=&quot;500&quot; height=&quot;290&quot; alt=&quot;Play Scalate Error Reporting&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;I&apos;ve published my &lt;a href=&quot;https://github.com/mraible/play-scalate&quot;&gt;play-scalate module on GitHub&lt;/a&gt; so others can try it out. To give it a whirl, add the following to your dependencies.yml:
&lt;/p&gt;
&lt;pre&gt;
    - upgrades -&gt; play-scalate 0.1

repositories:
    - upgrades:
        type: http
        artifact: &quot;http://static.raibledesigns.com/[module]-[revision].zip&quot;
        contains:
            - upgrades -&gt; *
&lt;/pre&gt;&lt;p&gt;Then add &lt;em&gt;with play.modules.scalate.Scalate&lt;/em&gt; to your controllers and call the &lt;em&gt;render()&lt;/em&gt; method.
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;br/&gt;
After using Scalate and Play for almost 3 months, I&apos;m really enjoying the combination. When I first integrated Scalate with a simple trait, the error messages were always in the console. Now that I&apos;ve borrowed some smarts from Play&apos;s ScalaController and play-scalate&apos;s error reporting, I feel like it&apos;s practically a built-in solution. I was easily able to integrate my desired SiteMesh features and it even allows &lt;a href=&quot;http://groups.google.com/group/scalate/browse_thread/thread/f6df5b165024407e&quot;&gt;reusable template blocks&lt;/a&gt;. In the end, it&apos;s just Scala and Scalate does a good job of allowing you to leverage that.&lt;/p&gt;
&lt;p&gt;Other thoughts:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you&apos;re writing a lot of Jade and familiar with HTML, Don Park&apos;s &lt;a href=&quot;https://github.com/donpark/html2jade&quot;&gt;html2jade&lt;/a&gt; is a great tool that comes with Scalate support.&lt;/li&gt;
&lt;li&gt;I&apos;m really enjoying writing CSS with &lt;a href=&quot;http://lesscss.org/&quot;&gt;LESS&lt;/a&gt;, particularly the ability to nest rules and have programming features. The only issue I&apos;ve seen is IntelliJ&apos;s LESS plugin only does code-completion for variables rather than CSS values.&lt;/li&gt;
&lt;li&gt;The &lt;a href=&quot;http://www.packtpub.com/play-framework-cookbook/book&quot;&gt;Play Framework Cookbook&lt;/a&gt; is a great reference for learning how to write modules. Not only does it explain how to create modules, it has some great real-world examples for doing bytecode enhancement, implementing message queues, using Solr and how to do production monitoring.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If this series of articles has intrigued you and you&apos;ll be at &lt;a href=&quot;http://www.devoxx.com/display/DV11/Home&quot;&gt;Devoxx&lt;/a&gt; next week, you should stop by &lt;a href=&quot;http://www.devoxx.com/display/DV11/HTML5+with+Play+Scala%2C+CoffeeScript+and+Jade&quot;&gt;my talk on Thursday afternoon&lt;/a&gt;. In addition, there&apos;s several other Play talks at Devoxx and a possible meetup on Wednesday. Check out the &lt;a href=&quot;http://groups.google.com/group/play-framework/browse_thread/thread/eaef08eb36f9eb0a&quot;&gt;Devoxx, anyone?&lt;/a&gt; thread for more information.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: There&apos;s one thing I forgot to mention about the Play Scalate Module. When I had Scalate integrated in my app with a trait, I only included the scalate-core and scalate-util JARs in dependencies.yml:&lt;/p&gt;
&lt;pre&gt;
- org.fusesource.scalate -&gt; scalate-core 1.5.2-scala_2.8.1:
    transitive: false
- org.fusesource.scalate -&gt; scalate-util 1.5.2-scala_2.8.1:
    transitive: false
&lt;/pre&gt;&lt;p&gt;However, when I created the play-scalate module, I allowed more dependencies.&lt;/p&gt;
&lt;pre&gt;
- org.fusesource.scalate -&gt; scalate-core 1.5.2-scala_2.8.1:
    exclude:
        - javax.servlet -&gt; *
        - com.sun.jersey -&gt; *
        - org.osgi -&gt; *
- org.fusesource.scalate -&gt; scalate-util 1.5.2-scala_2.8.1
&lt;/pre&gt;Because Scalate depends on &lt;a href=&quot;http://logback.qos.ch/&quot;&gt;Logback&lt;/a&gt;, debug messages started showing up in my console. To fix this, I created &lt;em&gt;conf/logback.xml&lt;/em&gt; in my project and filled it with the following XML.
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;configuration&amp;gt;
  &amp;lt;appender name=&quot;STDOUT&quot; class=&quot;ch.qos.logback.core.ConsoleAppender&quot;&amp;gt;
      &amp;lt;encoder&amp;gt;
          &amp;lt;pattern&amp;gt;%msg%n&amp;lt;/pattern&amp;gt;
      &amp;lt;/encoder&amp;gt;
  &amp;lt;/appender&amp;gt;

  &amp;lt;root level=&quot;info&quot;&amp;gt;
    &amp;lt;appender-ref ref=&quot;STDOUT&quot; /&amp;gt;
  &amp;lt;/root&amp;gt;
&amp;lt;/configuration&amp;gt;
&lt;/pre&gt;
&lt;p&gt;This reduces the logging and allows me to increase Scalate&apos;s logging if I ever have the need.</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_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_part4</id>
        <title type="html">Java Web Application Security - Part V: Penetrating with Zed Attack Proxy</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/java_web_application_security_part4"/>
        <published>2011-06-21T07:45:41-06: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="owaspzap" scheme="http://roller.apache.org/ns/tags/" />
        <category term="zedattackproxy" scheme="http://roller.apache.org/ns/tags/" />
        <category term="owasp" scheme="http://roller.apache.org/ns/tags/" />
        <category term="security" scheme="http://roller.apache.org/ns/tags/" />
        <category term="appsec" scheme="http://roller.apache.org/ns/tags/" />
        <category term="tutorial" scheme="http://roller.apache.org/ns/tags/" />
        <category term="springsecurity" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">Web Application Security is an important part of developing applications. As developers, I think we often forget this, or simply ignore it. In my career, I&apos;ve learned a lot about web application security. However, I only recently learned and became familiar with the rapidly growing &quot;appsec&quot; industry. 
&lt;/p&gt;
&lt;p&gt;
I found a disconnect between what appsec consultants were selling and what I was developing. It seemed like appsec consultants were selling me fear, mostly because I thought my apps were secure. So I set out on a mission to learn more about web application security and penetration testing to see if my apps really were secure. This article is part of that mission, as are the previous articles I&apos;ve written in this series.
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/java_web_application_security_part&quot;&gt;Java Web Application Security - Part I: Java EE 6 Login Demo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/java_web_application_security_part1&quot;&gt;Java Web Application Security - Part II: Spring Security Login Demo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/java_web_application_security_part2&quot;&gt;Java Web Application Security - Part III: Apache Shiro Login Demo&lt;/a&gt;&lt;/li&gt;          
&lt;li&gt;&lt;a href=&quot;http://raibledesigns.com/rd/entry/java_web_application_security_part3&quot;&gt;Java Web Application Security - Part IV: Programmatic Login APIs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
When I first decided I wanted to do a talk on Webapp Security, I knew it would be more interesting if I showed the audience how to hack and fix an application. That&apos;s why I wrote it into my &lt;a href=&quot;http://raibledesigns.com/rd/entry/upcoming_conferences_tssjs_in_las&quot;&gt;original proposal&lt;/a&gt;:
&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;
&lt;p&gt;At the time, I hadn&apos;t done much &lt;a href=&quot;http://en.wikipedia.org/wiki/Penetration_test#Web_application_penetration_testing&quot;&gt;webapp pentesting&lt;/a&gt;. You can tell this from the fact that I mentioned &lt;em&gt;WebGoat&lt;/em&gt; as the pentesting tool. From &lt;a href=&quot;https://www.owasp.org/index.php/Category:OWASP_WebGoat_Project&quot;&gt;WebGoat&apos;s Project page&lt;/a&gt;:
&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;&lt;b&gt;WebGoat&lt;/b&gt; is a deliberately insecure J2EE web application maintained by &lt;a href=&quot;http://www.owasp.org&quot;&gt;OWASP&lt;/a&gt; designed to teach web application security lessons. In each lesson, users must demonstrate their understanding of a security issue by exploiting a real vulnerability in the WebGoat application. For example, in one of the lessons the user must use &lt;a href=&quot;https://www.owasp.org//index.php/SQL_injection&quot; title=&quot;SQL injection&quot;&gt;SQL injection&lt;/a&gt; to steal fake credit card numbers. The application is a realistic teaching environment, providing users with hints and code to further explain the lesson.&lt;/p&gt;
&lt;p&gt;What I really meant to say and use was &lt;a href=&quot;https://www.owasp.org/index.php/OWASP_Zed_Attack_Proxy_Project&quot;&gt;Zed Attack Proxy&lt;/a&gt;, also known as OWASP ZAP. ZAP is a Java Desktop application that you setup as a proxy for your browser, then use to find vulnerabilities in your application. This article explains how you can use ZAP to pentest a web applications and fix its vulnerabilities.
&lt;/p&gt;
&lt;p&gt;The application I&apos;ll be using in this article is the Ajax Login application I&apos;ve been using throughout this series. I think it&apos;s great that projects like &lt;a href=&quot;http://www.dvwa.co.uk/&quot;&gt;Damn Vulnerable Web App&lt;/a&gt; and WebGoat exist, but I wanted to test one that I &lt;em&gt;think&lt;/em&gt; is secure, rather than one I know is not secure. In this particular example, I&apos;ll be testing the Spring Security implementation, since that&apos;s the framework I most often use in my open source projects. 
&lt;/p&gt;

&lt;p id=&quot;zap-tutorial&quot;&gt;&lt;strong&gt;Zed Attack Proxy 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;#install-zap&quot;&gt;Install and Configure ZAP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#scan&quot;&gt;Perform a Scan&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#fix-vulnerabilities&quot;&gt;Fix Vulnerabilities&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-zap-tutorial-1.0.zip&quot;&gt;download the application&lt;/a&gt; and expand it on your hard drive. This app is the completed version of the Ajax Login application referenced in &lt;a href=&quot;http://raibledesigns.com/rd/entry/java_web_application_security_part1&quot;&gt;Java Web Application Security - Part II: Spring Security Login Demo&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 you need to login to do anything.&lt;/p&gt;

&lt;p id=&quot;install-zap&quot;&gt;&lt;strong&gt;Install and Configure ZAP&lt;/strong&gt;&lt;br/&gt;
The &lt;a href=&quot;https://www.owasp.org/index.php/OWASP_Zed_Attack_Proxy_Project&quot;&gt;Zed Attack Proxy&lt;/a&gt; (ZAP) is an easy to use integrated penetration testing tool for finding vulnerabilities in web applications. &lt;a href=&quot;http://code.google.com/p/zaproxy/downloads/list&quot;&gt;Download&lt;/a&gt; the latest version (I used 1.3.0) and install it on your system. After installing, launch the app and change the proxy port to 9000 (Tools &gt; Options &gt; Local Proxy). Next, configure your browser to proxy requests through port 9000 and allow localhost requests to be proxied. I used Firefox 4 (Preferences &gt; Advanced &gt; Network &gt; Connection Settings). When finished, your proxy settings should look like the following screenshot:
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://farm6.static.flickr.com/5301/5841907039_d8b4ec906d_o.jpg&quot; title=&quot;Firefox Proxy Settings&quot; rel=&quot;lightbox[security-zap]&quot;&gt;&lt;img src=&quot;//farm6.static.flickr.com/5301/5841907039_e8b204a6d8_m.jpg&quot; width=&quot;240&quot; height=&quot;239&quot; alt=&quot;Firefox Proxy Settings&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;Another option (instead of removing localhost) is to add an entry to your hosts file with your production domain name. This is what I&apos;ve done for this demo.&lt;/p&gt;
&lt;pre&gt;127.0.0.1       demo.raibledesigns.com&lt;/pre&gt;
&lt;p&gt;I&apos;ve also configured Apache to proxy requests to Jetty with the following mod_proxy settings in my httpd.conf:
  &lt;/p&gt;
&lt;pre class=&quot;brush: xml; auto-links: false&quot;&gt;
&amp;lt;IfModule mod_proxy.c&amp;gt;
    ProxyRequests Off 
    ProxyPreserveHost Off 

    &amp;lt;VirtualHost *:80&amp;gt;
       ProxyPass  /  http://localhost:8080/
    &amp;lt;/VirtualHost&amp;gt;

    &amp;lt;VirtualHost *:443&amp;gt;
        SSLEngine on
        SSLProxyEngine on
        SSLCertificateFile &quot;/etc/apache2/ssl.key/server.crt&quot;
        SSLCertificateKeyFile &quot;/etc/apache2/ssl.key/server.key&quot;

        ProxyPass  /  https://localhost:8443/
    &amp;lt;/VirtualHost&amp;gt;
&amp;lt;/IfModule&amp;gt;
&lt;/pre&gt;
&lt;p id=&quot;scan&quot;&gt;&lt;strong&gt;Perform a Scan&lt;/strong&gt;&lt;br/&gt;
Now you need to give ZAP some data to work with. Using Firefox, I navigated to http://demo.raibledesigns.com and browsed around a bit, listing users, added a new one and deleted an existing one. After doing this, I noticed a number of flags in the ZAP UI under Sites. I then right-clicked on each site (one for http and one for https) and selected Attack &gt; Active Scan site. You should be able to do this from the &quot;Active Scan&quot; tab at the bottom of ZAP, but &lt;a href=&quot;http://code.google.com/p/zaproxy/issues/detail?id=123&quot;&gt;there&apos;s a bug when the URLs are the same&lt;/a&gt;. After doing this, I received a number of alerts, ranging from high (cross-site scripting) to low (password autocomplete). The screenshot below shows the various issues.&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://www.flickr.com/photos/mraible/5842085881/&quot; title=&quot;ZAP Alerts&quot;&gt;&lt;img src=&quot;//farm4.static.flickr.com/3177/5842085881_c35dd95cd4_o.jpg&quot; width=&quot;394&quot; height=&quot;157&quot; alt=&quot;ZAP Alerts&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;Now let&apos;s take a look at how to fix them.
&lt;p id=&quot;fix-vulnerabilities&quot;&gt;&lt;strong&gt;Fix Vulnerabilities&lt;/strong&gt;&lt;br/&gt;
One of the things not mentioned by the scan, but #1 in &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;, is Custom Error Pages Not Configured. Custom error pages are configured in this app, but error.jsp contains the following code:
&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
&amp;lt;% if (exception != null) { %&amp;gt;
    &amp;lt;% exception.printStackTrace(new java.io.PrintWriter(out)); %&amp;gt;
&amp;lt;% } else { %&amp;gt;
    Please check your log files for further information.
&amp;lt;% } %&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Stack traces can be really useful to an attacker, so it&apos;s important to start by removing the above code from &lt;em&gt;src/main/webapp/error.jsp&lt;/em&gt;.
&lt;/p&gt;
&lt;p&gt;The rest of the issues have to do with XSS, autocomplete, and cookies. Let&apos;s start with the easy ones. Fixing autocomplete is easy enough; simply changed the HTML in login.jsp and userform.jsp to have &lt;code&gt;autocomplete=&quot;off&quot;&lt;/code&gt; as part of the &amp;lt;form&amp;gt; tag.&lt;/p&gt;
&lt;p&gt;Then modify web.xml so http-only and secure cookies are used. While you&apos;re at it, add session-timeout and tracking-mode as recommended by the aforementioned web.xml misconfigurations article.
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;session-config&amp;gt;
    &amp;lt;session-timeout&amp;gt;15&amp;lt;/session-timeout&amp;gt;
    &amp;lt;cookie-config&amp;gt;
        &amp;lt;http-only&amp;gt;true&amp;lt;/http-only&amp;gt;
        &amp;lt;secure&amp;gt;true&amp;lt;/secure&amp;gt;
    &amp;lt;/cookie-config&amp;gt;
    &amp;lt;tracking-mode&amp;gt;COOKIE&amp;lt;/tracking-mode&amp;gt;
&amp;lt;/session-config&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Next, modify Spring Security&apos;s Remember Me configuration so it uses secure cookies. To do this, add &lt;code&gt;use-secure-cookies=&quot;true&quot;&lt;/code&gt; to the &amp;lt;remember-me&amp;gt; element in &lt;em&gt;security.xml&lt;/em&gt;.
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;remember-me user-service-ref=&quot;userService&quot; key=&quot;e37f4b31-0c45-11dd-bd0b-0800200c9a66&quot;
             use-secure-cookie=&quot;true&quot;/&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Unfortunately, Spring Security &lt;a href=&quot;https://jira.springsource.org/browse/SEC-1761&quot;&gt;doesn&apos;t support HttpOnly cookies&lt;/a&gt;, but will in a future release.
&lt;/p&gt;
&lt;p&gt;The next issue to solve is disabling directory browsing. You can do this by copying Jetty&apos;s webdefault.xml (from the org.eclipse.jetty:jetty-webapp JAR) into &lt;em&gt;src/test/resources&lt;/em&gt; and changing its &quot;dirAllowed&quot; &amp;lt;init-param&amp;gt; to false:
&lt;/p&gt;              
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;servlet&amp;gt;
  &amp;lt;servlet-name&amp;gt;default&amp;lt;/servlet-name&amp;gt;
  &amp;lt;servlet-class&amp;gt;org.mortbay.jetty.servlet.DefaultServlet&amp;lt;/servlet-class&amp;gt;
  &amp;lt;init-param&amp;gt;
    &amp;lt;param-name&amp;gt;acceptRanges&amp;lt;/param-name&amp;gt;
    &amp;lt;param-value&amp;gt;true&amp;lt;/param-value&amp;gt;
  &amp;lt;/init-param&amp;gt;
  &amp;lt;init-param&amp;gt;
    &amp;lt;param-name&amp;gt;dirAllowed&amp;lt;/param-name&amp;gt;
    &amp;lt;param-value&amp;gt;false&amp;lt;/param-value&amp;gt;
  &amp;lt;/init-param&amp;gt;
  &amp;lt;init-param&amp;gt;
&lt;/pre&gt;
&lt;p&gt;You&apos;ll also need to modify the plugin&apos;s configuration to point to this file by adding it to the &amp;lt;webAppConfig&amp;gt; section in pom.xml.
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;configuration&amp;gt;
    &amp;lt;webAppConfig&amp;gt;
        &amp;lt;contextPath&amp;gt;/&amp;lt;/contextPath&amp;gt;
        &amp;lt;defaultsDescriptor&amp;gt;src/test/resources/webdefault.xml&amp;lt;/defaultsDescriptor&amp;gt;
    &amp;lt;/webAppConfig&amp;gt;
&lt;/pre&gt;&lt;p&gt;Of course, if you&apos;re running in production you&apos;ll want to configure this in your server&apos;s settings rather than in your pom.xml file.
&lt;/p&gt;
&lt;p&gt;Next, I set out to fix &lt;em&gt;secure page browser cache issues&lt;/em&gt;. I had the following settings in my SiteMesh decorator:&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;meta http-equiv=&quot;Cache-Control&quot; content=&quot;no-store&quot;/&gt;
&amp;lt;meta http-equiv=&quot;Pragma&quot; content=&quot;no-cache&quot;/&gt;
&lt;/pre&gt;  
&lt;p&gt;However, according to ZAP, the first meta tag should have &quot;no-cache&quot; instead of &quot;no-store&quot;, so I changed it to &quot;no-cache&quot;.&lt;/p&gt;
&lt;p&gt;After making all these changes, I created a new ZAP session and ran an active scan on both sites again. Below are the results:
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://www.flickr.com/photos/mraible/5854063700/&quot; title=&quot;Active Scan after Fixes&quot;&gt;&lt;img src=&quot;//farm6.static.flickr.com/5068/5854063700_8b57a2d49c_o.png&quot; width=&quot;345&quot; height=&quot;116&quot; alt=&quot;Active Scan after Fixes&quot;&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;I believe the first issue (parameter tampering) is because I show the error page when a duplicate user exists. To fix this, I changed UserFormController so it catches a UserExistsException and sends the user back to the form.
&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
try {
    userManager.saveUser(user);
} catch (UserExistsException uex) {
    result.addError(new ObjectError(&quot;user&quot;, uex.getMessage()));
    return &quot;userform&quot;;
}
&lt;/pre&gt;
&lt;p&gt;However, this still doesn&apos;t seem to cause the alert to go away. This is likely because I&apos;m not filtering/escaping HTML when it&apos;s first submitted. I believe the best solution for this would be to use something like OWASP&apos;s &lt;a href=&quot;https://www.owasp.org/index.php/Category:OWASP_Enterprise_Security_API&quot;&gt;ESAPI&lt;/a&gt; to filter parameter values. However, I was unable to find integration with Spring MVC&apos;s data binding, so I decided not to try and fix this vulnerability.&lt;/p&gt;
&lt;p&gt;Finally, I tried to disable jsessionid in URLs using &lt;a href=&quot;http://stackoverflow.com/questions/962729/is-it-possible-to-disable-jsessionid-in-tomcat-servlet&quot;&gt;suggestions from Stack Overflow&lt;/a&gt;. The previous setting in web.xml (&lt;code&gt;&amp;lt;tracking-mode&amp;gt;COOKIE&amp;lt;/tracking-mode&amp;gt;&lt;/code&gt;) should do this, but it doesn&apos;t seem to work with Jetty 8. The other issues (secure page browser cache, HttpOnly cookies and secure cookies), I was unable to solve.  The last two are issues caused by Spring Security as far as I can tell. 
&lt;p id=&quot;summary&quot;&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;br/&gt;
In this article, I&apos;ve shown you how to pentest a web application using Firefox and OWASP&apos;s Zed Attack Proxy (ZAP). I found ZAP to be a nice tool for figuring out vulnerabilities, but it&apos;d be nice if it had a &quot;retest&quot; feature to see if you fixed an issue for a particular URL. It does have a &quot;resend&quot; feature, but running it didn&apos;t seem to clear alerts after I&apos;d fixed them.
&lt;/p&gt;
&lt;p&gt;The issues I wasn&apos;t able to solve seemed to be mostly related to frameworks (e.g. Spring Security and HttpOnly cookies) or servers (Jetty not using cookies for tracking). My suspicion is the Jetty issues are because it doesn&apos;t support Servlet 3 as well as it advertises. I believe this is fair; I am using a milestone release after all. I tried scanning &lt;a href=&quot;http://demo.raibledesigns.com/ajax-login&quot;&gt;http://demo.raibledesigns.com/ajax-login&lt;/a&gt; (which runs on Tomcat 7 at &lt;a href=&quot;http://www.contegix.com&quot;&gt;Contegix&lt;/a&gt;) and confirmed that no jsessionid exists.
&lt;/p&gt;
&lt;p&gt;Hopefully this article has helped you understand how to figure out security vulnerabilities in your web applications. I believe ZAP will continue to get more popular as developers become aware of it. If you feel ambitious and want to try and solve &lt;em&gt;all&lt;/em&gt; of the issues in my Ajax Login application, feel free to fork it on &lt;a href=&quot;https://github.com/mraible/ajax-login&quot;&gt;GitHub&lt;/a&gt;. 
&lt;/p&gt;
&lt;p&gt;If you&apos;re interested in talking more about Webapp Security, please leave a comment, meet me at &lt;a href=&quot;http://jazoon.com/Conference/Thursday-23-June/Matt-Raible&quot;&gt;Jazoon&lt;/a&gt; later this week or let&apos;s talk in July at &lt;a href=&quot;http://uberconf.com/topics/java_web_application_security__develop__penetrate__protect__relax_&quot;&gt;&#220;ber Conf&lt;/a&gt;.</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/adding_search_to_appfuse</id>
        <title type="html">Adding Search to AppFuse with Compass</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/adding_search_to_appfuse"/>
        <published>2011-03-15T17:11:12-06:00</published>
        <updated>2012-11-08T14:19:27-07:00</updated> 
        <category term="/Java" label="Java" />
        <category term="appfuse" scheme="http://roller.apache.org/ns/tags/" />
        <category term="search" scheme="http://roller.apache.org/ns/tags/" />
        <category term="compass" scheme="http://roller.apache.org/ns/tags/" />
        <category term="elasticsearch" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">Over 5 years ago, I recognized that &lt;a href=&quot;http://appfuse.org&quot;&gt;AppFuse&lt;/a&gt; needed to have a search feature and &lt;a href=&quot;http://issues.appfuse.org/browse/APF-267&quot;&gt;entered an issue in JIRA&lt;/a&gt;. Almost 4 years later, a &lt;a href=&quot;http://code.google.com/p/pagingappfuse/wiki/CompassSearching&quot;&gt;Compass Tutorial&lt;/a&gt; was created and shortly after &lt;a href=&quot;http://www.kimchy.org/&quot;&gt;Shay Banon&lt;/a&gt; (Compass Founder), sent in a &lt;a href=&quot;http://issues.appfuse.org/browse/APF-267?focusedCommentId=12620&amp;page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-12620&quot;&gt;patch&lt;/a&gt;. From the message he sent me:&lt;/p&gt;
&lt;div class=&quot;quote&quot; style=&quot;margin-left: 0; margin-bottom: 10px&quot;&gt;
&lt;p&gt;A quick breakdown of enabling search:
&lt;/p&gt;
&lt;ol style=&quot;margin-bottom: 0&quot;&gt;
&lt;li&gt;Added Searchable annotations to the User and Address.&lt;/li&gt;
&lt;li&gt;Defined Compass bean, automatically scanning the model package for mapped searchable classes. It also automatically integrates with Spring transaction manager, and stores the index on the file system ([work dir]/target/test-index).&lt;/li&gt;
&lt;li&gt;Defined CompassTemplate (similar in concept to HibernateTemplate).&lt;/li&gt;
&lt;li&gt;Defined CompassSearchHelper. Really helps to perform search since it does pagination and so on.&lt;/li&gt;
&lt;li&gt;Defined CompassGps, basically it allows for index operation allowing to completely reindex the data from the database. JPA and Hiberante also automatically mirror changes done through their API to the index. iBatis uses AOP. &lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;p&gt;Fast forward 2 years and I finally found the time/desire to put a UI on the backend Compass implementation that Shay provided. Yes, I realize that &lt;a href=&quot;http://www.kimchy.org/the_future_of_compass/&quot;&gt;Compass is being replaced by ElasticSearch&lt;/a&gt;. I may change to use ElasticSearch in the future; now that the search feature exists, I hope to see it evolve and improve.
&lt;/p&gt;
&lt;p&gt;Since Shay&apos;s patch integrated the necessary Spring beans for indexing and searching, the only thing I had to do was to implement the UI. Rather than having an &quot;all objects&quot; results page, I elected to implement it so you could search on an entity&apos;s list screen. I started with Spring MVC and added a search() method to the UserController:
&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
@RequestMapping(method = RequestMethod.GET)
public ModelAndView handleRequest(@RequestParam(required = false, value = &quot;q&quot;) String query) throws Exception {
    if (query != null &amp;&amp; !&quot;&quot;.equals(query.trim())) {
        return new ModelAndView(&quot;admin/userList&quot;, Constants.USER_LIST, search(query));
    } else {
        return new ModelAndView(&quot;admin/userList&quot;, Constants.USER_LIST, mgr.getUsers());
    }
}

public List&amp;lt;User&amp;gt; search(String query) {
    List&amp;lt;User&amp;gt; results = new ArrayList&amp;lt;User&amp;gt;();
    CompassDetachedHits hits = compassTemplate.findWithDetach(query);
    log.debug(&quot;No. of results for &apos;&quot; + query + &quot;&apos;: &quot; + hits.length());
    for (int i = 0; i &amp;lt; hits.length(); i++) {
        results.add((User) hits.data(i));
    }
    return results;
}
&lt;/pre&gt;
&lt;p&gt;At first, I used &lt;em&gt;compassTemplate.find()&lt;/em&gt;, but got an error because I wasn&apos;t using an OpenSessionInViewFilter. I decided to go with findWithDetach() and added the following search form to the top of the userList.jsp page:&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
&amp;lt;div id=&quot;search&quot;&amp;gt;
&amp;lt;form method=&quot;get&quot; action=&quot;${ctx}/admin/users&quot; id=&quot;searchForm&quot;&amp;gt;
    &amp;lt;input type=&quot;text&quot; size=&quot;20&quot; name=&quot;q&quot; id=&quot;query&quot; value=&quot;${param.q}&quot;
           placeholder=&quot;Enter search terms&quot;/&amp;gt;
    &amp;lt;input type=&quot;submit&quot; value=&quot;&amp;lt;fmt:message key=&quot;button.search&quot;/&amp;gt;&quot;/&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/pre&gt;
&lt;p style=&quot;color: #666; margin-left: 20px&quot;&gt;&lt;em&gt;NOTE: I tried using HTML5&apos;s &amp;lt;input type=&quot;search&quot;&amp;gt;, but found &lt;a href=&quot;http://lists.canoo.com/pipermail/webtest/2011q1/013697.html&quot; style=&quot;color: #666&quot;&gt;Canoo WebTest doesn&apos;t support it.&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Next, I wrote a unit test to verify everything worked as expected. I found I had to call compassGps.index() as part of my test to make sure my index was created and up-to-date.&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
public class UserControllerTest extends BaseControllerTestCase {
    @Autowired
    private CompassGps compassGps;
    @Autowired
    private UserController controller;

    public void testSearch() throws Exception {
        compassGps.index();
        ModelAndView mav = controller.handleRequest(&quot;admin&quot;);
        Map m = mav.getModel();
        List results = (List) m.get(Constants.USER_LIST);
        assertNotNull(results);
        assertTrue(results.size() &amp;gt;= 1);
        assertEquals(&quot;admin/userList&quot;, mav.getViewName());
    }
}
&lt;/pre&gt;
&lt;p&gt;After getting this working, I started integrating similar code into AppFuse&apos;s other web framework modules (Struts, JSF and Tapestry). When I was finished, they all looked pretty similar from a UI perspective.
&lt;/p&gt;
&lt;p&gt;&lt;strong style=&quot;color: #666&quot;&gt;Struts:&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;div id=&quot;search&quot;&amp;gt;
&amp;lt;form method=&quot;get&quot; action=&quot;${ctx}/admin/users&quot; id=&quot;searchForm&quot;&amp;gt;
    &amp;lt;input type=&quot;text&quot; size=&quot;20&quot; name=&quot;q&quot; id=&quot;query&quot; value=&quot;${param.q}&quot;
           placeholder=&quot;Enter search terms...&quot;/&amp;gt;
    &amp;lt;input type=&quot;submit&quot; value=&quot;&amp;lt;fmt:message key=&quot;button.search&quot;/&amp;gt;&quot;/&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/pre&gt;
&lt;p&gt;&lt;strong style=&quot;color: #666&quot;&gt;JSF:&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;div id=&quot;search&quot;&amp;gt;
&amp;lt;h:form id=&quot;searchForm&quot;&amp;gt;
    &amp;lt;h:inputText id=&quot;q&quot; name=&quot;q&quot; size=&quot;20&quot; value=&quot;#{userList.query}&quot;/&amp;gt;
    &amp;lt;h:commandButton value=&quot;#{text&amp;#91;&apos;button.search&apos;&amp;#93;}&quot; action=&quot;#{userList.search}&quot;/&amp;gt;
&amp;lt;/h:form&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/pre&gt;
&lt;p&gt;&lt;strong style=&quot;color: #666&quot;&gt;Tapestry:&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;div id=&quot;search&quot;&amp;gt;
&amp;lt;t:form method=&quot;get&quot; t:id=&quot;searchForm&quot;&amp;gt;
    &amp;lt;t:textfield size=&quot;20&quot; name=&quot;q&quot; t:id=&quot;q&quot;/&amp;gt;
    &amp;lt;input t:type=&quot;submit&quot; value=&quot;${message:button.search}&quot;/&amp;gt;
&amp;lt;/t:form&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/pre&gt;
&lt;p&gt;One frustrating thing I found was that &lt;a href=&quot;http://tapestry.1045711.n5.nabble.com/Is-there-any-way-to-render-the-quot-get-quot-method-of-form-td2643651.html&quot;&gt;Tapestry doesn&apos;t support method=&quot;get&quot;&lt;/a&gt; and AFAICT, neither does JSF 2. With JSF, I had to make my UserList bean session-scoped or the query parameter would be null when it listed the results. Tapestry took me the longest to implement, mainly because I had issues figuring out how it&apos;s easy-to-understand-once-you-know onSubmit() handlers worked and I had the proper @Property and @Persist annotations on my &quot;q&quot; property. &lt;a href=&quot;http://www.crazymcphee.net/x/2009/08/26/tapestry-5-web-framework/&quot;&gt;This tutorial&lt;/a&gt; was the greatest help for me. Of course, now that it&apos;s all finished, the code looks pretty intuitive.&lt;/p&gt;
&lt;p&gt;Feeling proud of myself for getting this working, I started integrating this feature into AppFuse&apos;s code generation and found I had to add quite a bit of code to the generated list pages/controllers. 
&lt;/p&gt;
&lt;p&gt;
So I went on a bike ride...&lt;/p&gt;
&lt;p&gt;While riding, I thought of a much better solution and added the following search method to AppFuse&apos;s GenericManagerImpl.java. In the code I added to pages/controllers previously, I&apos;d already refactored to use CompassSearchHelper and I continued to do so in the service layer implementation.
&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
@Autowired
private CompassSearchHelper compass;

public List&amp;lt;T&amp;gt; search(String q, Class clazz) {
    if (q == null || &quot;&quot;.equals(q.trim())) {
        return getAll();
    }

    List&amp;lt;T&amp;gt; results = new ArrayList&amp;lt;T&amp;gt;();

    CompassSearchCommand command = new CompassSearchCommand(q);
    CompassSearchResults compassResults = compass.search(command);
    CompassHit&amp;#91;&amp;#93; hits = compassResults.getHits();

    if (log.isDebugEnabled() &amp;amp;&amp;amp; clazz != null) {
        log.debug(&quot;Filtering by type: &quot; + clazz.getName());
    }

    for (CompassHit hit : hits) {
        if (clazz != null) {
            if (hit.data().getClass().equals(clazz)) {
                results.add((T) hit.data());
            }
        } else {
            results.add((T) hit.data());
        }
    }

    if (log.isDebugEnabled()) {
        log.debug(&quot;Number of results for &apos;&quot; + q + &quot;&apos;: &quot; + results.size());
    }

    return results;
}
&lt;/pre&gt;
&lt;p&gt;This greatly simplified my page/controller logic because now all I had to do was call manager.search(query, User.class) instead of doing the Compass login in the controller. Of course, it&apos;d be great if I didn&apos;t have to pass in the Class to filter by object, but that&apos;s the &lt;a href=&quot;http://javanotepad.blogspot.com/2007/09/instanceof-doesnt-work-with-generics.html&quot;&gt;nature of generics and type erasure&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Other things I learned along the way:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;To index on startup, I added compassGps.index() to the StartupListener.&lt;/a&gt;.
&lt;li&gt;In unit tests that leveraged transactions around methods, I had to call compassGps.index() before any transactions started.&lt;/li&gt;
&lt;li&gt;To scan multiple packages for searchable classes, I had to add a &lt;a href=&quot;http://forum.compass-project.org/thread.jspa?threadID=216821&amp;tstart=0&quot;&gt;LocalCompassBeanPostProcessor&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But more than anything, I was reminded it always helps to take a bike ride when you don&apos;t like the design of your code. &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;
This feature and many more will be in AppFuse 2.1, which I hope to &lt;a href=&quot;http://issues.appfuse.org/secure/IssueNavigator.jspa?mode=hide&amp;requestId=10160&quot; title=&quot;Open Issues for 2.1&quot;&gt;finish by the end of the month&lt;/a&gt;. In the meantime, please feel free to try out the &lt;a href=&quot;http://appfuse.org/display/APF/AppFuse+QuickStart&quot;&gt;latest snapshot&lt;/a&gt;.</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/upgrading_to_jsf_2</id>
        <title type="html">Upgrading to JSF 2</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/upgrading_to_jsf_2"/>
        <published>2011-03-07T13:24:53-07:00</published>
        <updated>2011-03-07T19:30:33-07:00</updated> 
        <category term="/Java" label="Java" />
        <category term="urlrewritefilter" scheme="http://roller.apache.org/ns/tags/" />
        <category term="myfaces" scheme="http://roller.apache.org/ns/tags/" />
        <category term="jsf" scheme="http://roller.apache.org/ns/tags/" />
        <category term="richfaces" scheme="http://roller.apache.org/ns/tags/" />
        <category term="springsecurity" scheme="http://roller.apache.org/ns/tags/" />
        <category term="tomahawk" 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="facelets" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">Last week, I spent a few hours upgrading &lt;a href=&quot;http://appfuse.org&quot;&gt;AppFuse&lt;/a&gt; from JSF 1.2 to JSF 2.0. In reality, I upgraded from &lt;a href=&quot;http://myfaces.apache.org&quot;&gt;MyFaces&lt;/a&gt; 1.2.7 to 2.0.4, but all JSF implementations should be the same, right? All in all, it was a pretty easy upgrade with a few minor AppFuse-specific things. My goal in upgrading was to do the bare minimum to get things working and to leave integration of JSF 2 features for a later date.&lt;/p&gt;

&lt;p&gt;In addition to upgrading MyFaces, I had to upgrade Tomahawk by changing the dependency&apos;s artifactId to &lt;strong&gt;tomahawk20&lt;/strong&gt;. I was also able to remove the following listener from my web.xml:
&lt;/p&gt;
&lt;pre class=&quot;brush: xml; toolbar: false&quot;&gt;
&amp;lt;listener&gt;
    &amp;lt;listener-class&gt;org.apache.myfaces.webapp.StartupServletContextListener&amp;lt;/listener-class&gt;
&amp;lt;listener&gt;
&lt;/pre&gt;
&lt;p&gt;After that, I discovered that MyFaces uses a new URI (/javax.faces.resource/) for serving up some of its resource files. I kindly asked Spring Security to ignore these requests by adding the following to my security.xml file.
&lt;/p&gt;
&lt;pre class=&quot;brush: xml; toolbar: false&quot;&gt;
&amp;lt;intercept-url pattern=&quot;/javax.faces.resource/**&quot; filters=&quot;none&quot;/&gt;
&lt;/pre&gt;
&lt;p&gt;Since JSF 2 includes Facelets by default, I tried removing Facelets as a dependency. After doing this, I received the following error:
&lt;/p&gt;
&lt;pre&gt;
ERROR [308855416@qtp-120902214-7] ViewHandlerWrapper.fillChain(158) | Error instantiation parent Faces ViewHandler
java.lang.ClassNotFoundException: com.sun.facelets.FaceletViewHandler
        at org.codehaus.plexus.classworlds.strategy.SelfFirstStrategy.loadClass(SelfFirstStrategy.java:50)
        at org.codehaus.plexus.classworlds.realm.ClassRealm.loadClass(ClassRealm.java:244)
        at org.codehaus.plexus.classworlds.realm.ClassRealm.loadClass(ClassRealm.java:230)
        at org.mortbay.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:401)
        at org.mortbay.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:363)
        at org.ajax4jsf.framework.ViewHandlerWrapper.fillChain(ViewHandlerWrapper.java:144)
        at org.ajax4jsf.framework.ViewHandlerWrapper.calculateRenderKitId(ViewHandlerWrapper.java:68)
        at org.apache.myfaces.lifecycle.DefaultRestoreViewSupport.isPostback(DefaultRestoreViewSupport.java:179)
        at org.apache.myfaces.lifecycle.RestoreViewExecutor.execute(RestoreViewExecutor.java:113)
        at org.apache.myfaces.lifecycle.LifecycleImpl.executePhase(LifecycleImpl.java:171)
        at org.apache.myfaces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
        at javax.faces.webapp.FacesServlet.service(FacesServlet.java:189)
&lt;/pre&gt;
&lt;p&gt;Figuring this was caused by the following element in my web.xml ...
&lt;/p&gt;
&lt;pre class=&quot;brush: xml; toolbar: false&quot;&gt;
&amp;lt;context-param&amp;gt;
    &amp;lt;param-name&amp;gt;org.ajax4jsf.VIEW_HANDLERS&amp;lt;/param-name&amp;gt;
    &amp;lt;param-value&amp;gt;com.sun.facelets.FaceletViewHandler&amp;lt;/param-value&amp;gt;
&amp;lt;/context-param&amp;gt;
&lt;/pre&gt;
&lt;p&gt;... I removed it and tried again. This time I received a NoClassDefFoundError:&lt;/p&gt;
&lt;pre&gt;
java.lang.NoClassDefFoundError: com/sun/facelets/tag/TagHandler
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClassCond(ClassLoader.java:632)
        at java.lang.ClassLoader.defineClass(ClassLoader.java:616)
        at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141)
        at java.net.URLClassLoader.defineClass(URLClassLoader.java:283)
        at java.net.URLClassLoader.access$000(URLClassLoader.java:58)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:197)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
        at org.mortbay.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:392)
        at org.mortbay.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:363)
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:247)
        at org.apache.myfaces.shared_impl.util.ClassUtils.classForName(ClassUtils.java:184)
        at org.apache.myfaces.view.facelets.util.ReflectionUtil.forName(ReflectionUtil.java:67)
&lt;/pre&gt;
&lt;p&gt;Since everything seemed to work with Facelets in the classpath, I decided to save this headache for a later date. I entered two issues in AppFuse&apos;s JIRA, one for &lt;a href=&quot;http://issues.appfuse.org/browse/APF-1234&quot;&gt;removing Facelets&lt;/a&gt; and one for &lt;a href=&quot;http://issues.appfuse.org/browse/APF-1233&quot;&gt;replacing Ajax4JSF with RichFaces&lt;/a&gt;. 
&lt;/p&gt;
&lt;p&gt;The next issue I encountered was redirecting from AppFuse&apos;s password hint page. The navigation-rule for this page is as follows:&lt;/p&gt;
&lt;pre class=&quot;brush: xml; toolbar: false&quot;&gt;
&amp;lt;navigation-rule&amp;gt;
    &amp;lt;from-view-id&amp;gt;/passwordHint.xhtml&amp;lt;/from-view-id&amp;gt;
    &amp;lt;navigation-case&amp;gt;
        &amp;lt;from-outcome&amp;gt;success&amp;lt;/from-outcome&amp;gt;
        &amp;lt;to-view-id&amp;gt;/login&amp;lt;/to-view-id&amp;gt;
        &amp;lt;redirect/&amp;gt;
    &amp;lt;/navigation-case&amp;gt;
&amp;lt;/navigation-rule&amp;gt;
&lt;/pre&gt;
&lt;p&gt;With JSF 2.0, the rule changes the URL to /login.xhtml when redirecting (where it was left as /login with 1.2) and it was caught by the security setting in my web.xml that prevents users from viewing raw templates.&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;Protect XHTML Templates&amp;lt;/web-resource-name&amp;gt;
        &amp;lt;url-pattern&amp;gt;*.xhtml&amp;lt;/url-pattern&amp;gt;
    &amp;lt;/web-resource-collection&amp;gt;
    &amp;lt;auth-constraint/&amp;gt;
&amp;lt;/security-constraint&amp;gt;
&lt;/pre&gt;
&lt;p&gt;To solve this issue, I had to make a couple of changes:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Comment out the security-constraint in web.xml and move it to Spring Security&apos;s security.xml file.
&lt;pre class=&quot;brush: xml; toolbar: false&quot; style=&quot;margin: 5px 0 0 0&quot;&gt;
&amp;lt;intercept-url pattern=&quot;/**/*.xhtml&quot; access=&quot;ROLE_NOBODY&quot;/&gt;
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;Add a rule to urlrewrite.xml that redirects back to login (since login.xhtml doesn&apos;t exist and I&apos;m using extensionless URLs).
&lt;pre class=&quot;brush: xml&quot; style=&quot;margin: 5px 0 0 0&quot;&gt;
&amp;lt;rule match-type=&quot;regex&quot;&amp;gt;
    &amp;lt;from&amp;gt;^/login.xhtml$&amp;lt;/from&amp;gt;
    &amp;lt;to type=&quot;redirect&quot;&amp;gt;%{context-path}/login&amp;lt;/to&amp;gt;
&amp;lt;/rule&amp;gt;
&lt;/pre&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After getting the Password Hint feature passing in the browser, I tried running the integration tests (powered by &lt;a href=&quot;http://webtest.canoo.com&quot;&gt;Canoo WebTest&lt;/a&gt;). The Password Hint test kept failing with the following error:
&lt;/p&gt;
&lt;pre&gt;
[ERROR] /Users/mraible/dev/appfuse/web/jsf/src/test/resources/web-tests.xml:51: JavaScript error loading
page http://localhost:9876/appfuse-jsf-2.1.0-SNAPSHOT/passwordHint?username=admin: syntax error (http://
localhost:9876/appfuse-jsf-2.1.0-SNAPSHOT/javax.faces.resource/oamSubmit.js.jsf?ln=org.apache.myfaces#122)
&lt;/pre&gt;
&lt;p&gt;Figuring this was caused by my hack to &lt;a href=&quot;http://source.appfuse.org/browse/appfuse/trunk/web/jsf/src/main/webapp/passwordHint.xhtml?r=2866&quot;&gt;submit the form when the page was loaded&lt;/a&gt;, I turned to &lt;a href=&quot;http://ocpsoft.com/prettyfaces/&quot;&gt;Pretty Faces&lt;/a&gt;, which allows you to call a method directly from a URL. After adding the Pretty Faces dependencies to my pom.xml, I created a src/main/webapp/WEB-INF/pretty-config.xml file with the following XML:&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;url-mapping&amp;gt;
    &amp;lt;pattern value=&quot;/editProfile&quot;/&amp;gt;
    &amp;lt;view-id value=&quot;/userForm.jsf&quot;/&amp;gt;
    &amp;lt;action&amp;gt;#{userForm.edit}&amp;lt;/action&amp;gt;
&amp;lt;/url-mapping&amp;gt;

&amp;lt;url-mapping&amp;gt;
    &amp;lt;pattern value=&quot;/passwordHint/#{username}&quot;/&amp;gt;
    &amp;lt;view-id value=&quot;/passwordHint.jsf&quot;/&amp;gt;
    &amp;lt;action&amp;gt;#{passwordHint.execute}&amp;lt;/action&amp;gt;
&amp;lt;/url-mapping&amp;gt;
&lt;/pre&gt;
&lt;p&gt;This allowed me to remove both editProfile.xhtml and passwordHint.xhtml, both of which simply auto-submitted forms.&lt;/p&gt;
&lt;p&gt;At this point, I figured I&apos;d be good to go and ran my integration tests again. The first thing I discovered was that &quot;.jsf&quot; was being tacked onto my pretty URL, most likely by the UrlRewriteFilter. Adding the following to my PasswordHint.java class solved this.
&lt;/p&gt;
&lt;pre class=&quot;brush: java; toolbar: false&quot;&gt;
if (username.endsWith(&quot;.jsf&quot;)) {
    username = username.substring(0, username.indexOf(&quot;.jsf&quot;));
}
&lt;/pre&gt;
&lt;p&gt;The next thing was a cryptic error that took me a while to figure out.&lt;/p&gt;
&lt;pre&gt;
DEBUG [1152467051@qtp-144702232-0] PasswordHint.execute(38) | Processing Password Hint...
2011-03-05 05:48:52.471:WARN::/passwordHint/admin
com.ocpsoft.pretty.PrettyException: Exception occurred while processing &amp;lt;:#{passwordHint.execute}&gt; null
        at com.ocpsoft.pretty.faces.beans.ActionExecutor.executeActions(ActionExecutor.java:71)
        at com.ocpsoft.pretty.faces.event.PrettyPhaseListener.processEvent(PrettyPhaseListener.java:214)
        at com.ocpsoft.pretty.faces.event.PrettyPhaseListener.afterPhase(PrettyPhaseListener.java:108)
        at org.apache.myfaces.lifecycle.PhaseListenerManager.informPhaseListenersAfter(PhaseListenerManager.java:111)
        at org.apache.myfaces.lifecycle.LifecycleImpl.executePhase(LifecycleImpl.java:185)
        at org.apache.myfaces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
        at javax.faces.webapp.FacesServlet.service(FacesServlet.java:189)
&lt;/pre&gt;
&lt;p&gt;Digging into the bowels of MyFaces, I discovered a class was looking for a viewId with an extension and no view-id was being set. Adding the following to the top of my execute() method solved this.
&lt;/p&gt;
&lt;pre class=&quot;brush: java; toolbar: false&quot;&gt;
getFacesContext().getViewRoot().setViewId(&quot;/passwordHint.xhtml&quot;);
&lt;/pre&gt;
&lt;p&gt;After making this change, all AppFuse&apos;s integration tests are passing and the upgrade seems complete. The only other issues I encountered were logging-related. The first is an error about Tomahawk that doesn&apos;t seem to affect anything.
&lt;/p&gt;
&lt;pre&gt;
Mar 5, 2011 6:44:01 AM com.sun.facelets.compiler.TagLibraryConfig loadImplicit
SEVERE: Error Loading Library: jar:file:/Users/mraible/.m2/repository/org/apache/myfaces/tomahawk/tomahawk20/1.1.10/tomahawk20-1.1.10.jar!/META-INF/tomahawk.taglib.xml
java.io.IOException: Error parsing [jar:file:/Users/mraible/.m2/repository/org/apache/myfaces/tomahawk/tomahawk20/1.1.10/tomahawk20-1.1.10.jar!/META-INF/tomahawk.taglib.xml]: 
        at com.sun.facelets.compiler.TagLibraryConfig.create(TagLibraryConfig.java:410)
        at com.sun.facelets.compiler.TagLibraryConfig.loadImplicit(TagLibraryConfig.java:431)
        at com.sun.facelets.compiler.Compiler.initialize(Compiler.java:87)
        at com.sun.facelets.compiler.Compiler.compile(Compiler.java:104)
&lt;/pre&gt;
&lt;p&gt;The second is excessive logging from MyFaces. As far as I can tell, this is because MyFaces switched to java.util.logging instead of commons logging. With all the frameworks that AppFuse leverages, I think it has all the logging frameworks in its classpath now. I was hoping to fix this by &lt;a href=&quot;http://old.nabble.com/Turn-down-logging-in-2.0.4--td31068698.html&quot;&gt;posting a message to the mailing list&lt;/a&gt;, but haven&apos;t received a reply yet.
&lt;/p&gt;
&lt;pre&gt;
[WARNING] [talledLocalContainer] Mar 5, 2011 6:50:25 AM org.apache.myfaces.config.annotation.TomcatAnnotationLifecycleProvider newInstance
[WARNING] [talledLocalContainer] INFO: Creating instance of org.appfuse.webapp.action.BasePage
[WARNING] [talledLocalContainer] Mar 5, 2011 6:50:25 AM org.apache.myfaces.config.annotation.TomcatAnnotationLifecycleProvider destroyInstance
[WARNING] [talledLocalContainer] INFO: Destroy instance of org.appfuse.webapp.action.BasePage
&lt;/pre&gt;
&lt;p&gt;After successfully upgrading AppFuse, I turned to AppFuse Light, where things were &lt;a href=&quot;http://source.appfuse.org/changelog/appfuse-light/?cs=243&quot;&gt;much easier&lt;/a&gt;. 
&lt;/p&gt;
&lt;p&gt;
Now that AppFuse uses JSF 2, I hope to start leveraging some of its &lt;a href=&quot;http://www.ibm.com/developerworks/java/library/j-jsf2fu1/index.html&quot;&gt;new features&lt;/a&gt;. If you&apos;re yearning to get started with them today, I invite you to &lt;a href=&quot;http://appfuse.org/display/APF/Source+Repository&quot;&gt;grab the source&lt;/a&gt; and start integrating them yourself.</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/implementing_extensionless_urls_with_tapestry</id>
        <title type="html">Implementing Extensionless URLs with Tapestry, Spring MVC, Struts 2 and JSF</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/implementing_extensionless_urls_with_tapestry"/>
        <published>2011-02-10T16:53:27-07:00</published>
        <updated>2011-02-11T00:04:52-07:00</updated> 
        <category term="/Java" label="Java" />
        <category term="tapestry5" scheme="http://roller.apache.org/ns/tags/" />
        <category term="extensionlessurls" scheme="http://roller.apache.org/ns/tags/" />
        <category term="jsf" scheme="http://roller.apache.org/ns/tags/" />
        <category term="springmvc" scheme="http://roller.apache.org/ns/tags/" />
        <category term="struts2" scheme="http://roller.apache.org/ns/tags/" />
        <category term="appfuse" scheme="http://roller.apache.org/ns/tags/" />
        <category term="webframeworks" scheme="http://roller.apache.org/ns/tags/" />
        <category term="java" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">For the past couple of weeks, I&apos;ve spent several evening hours implementing extensionless URLs in &lt;a href=&quot;http://appfuse.org&quot;&gt;AppFuse&lt;/a&gt;. I&apos;ve been wanting to do this ever since I &lt;a href=&quot;http://raibledesigns.com/rd/entry/extensionless_urls_in_java_web&quot;&gt;wrote about how to do it&lt;/a&gt; a few years ago. This article details my experience and will hopefully help others implement this feature in their webapps.
&lt;/p&gt;
&lt;p&gt;First of all, I used the &lt;a href=&quot;http://www.tuckey.org/urlrewrite/&quot;&gt;UrlRewriteFilter&lt;/a&gt;, one of my favorite Java open source projects. Then I followed a pattern I found in Spring&apos;s &quot;mvc-basic&quot; sample app from &lt;a href=&quot;http://blog.springsource.com/2009/12/21/mvc-simplifications-in-spring-3-0/&quot;&gt;MVC Simplifications in Spring 3.0&lt;/a&gt;. The app has since changed (because SpringSource integrated UrlRewriteFilter-type functionality in Spring MVC), but the pattern was basically path-matching instead of extension-mapping. That is, the &quot;dispatcher&quot; for the web framework was mapped to /app/* instead of *.html. 
&lt;/p&gt;
&lt;p&gt;
Prior to the move to extensionless URLs, AppFuse used *.html for its mapping and this seemed to cause users problems when they wanted to serve up static HTML files. To begin with, I removed all extensions from URLs in tests (&lt;a href=&quot;http://webtest.canoo.com&quot;&gt;Canoo WebTest&lt;/a&gt; is used for testing the UI). I also did this for any links in the view pages and redirects in the Java code. This provided a decent foundation to verify my changes worked. Below are details about each framework I did this for, starting with the one that was easiest and moving to hardest.
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tapestry 5&lt;/strong&gt;&lt;br/&gt;
Tapestry was by far the easiest to integrate extensionless URLs into. This is because it&apos;s a native feature of the framework and was already integrated as part of &lt;a href=&quot;http://issues.appfuse.org/browse/APF-1116&quot;&gt;Serge Eby&apos;s Tapestry 5 implementation&lt;/a&gt;. In the end, the only things I had to do where 1) add a couple entries for CXF (mapped to /services/*) and DWR (/dwr/*) to my urlrewrite.xml and 2) change the UrlRewriteFilter so it was only mapped to REQUEST instead of both REQUEST and FORWARD. Below are the mappings I added for CXF and DWR.
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;urlrewrite default-match-type=&quot;wildcard&quot;&amp;gt;
    ...
    &amp;lt;rule&amp;gt;
        &amp;lt;from&amp;gt;/dwr/**&amp;lt;/from&amp;gt;
        &amp;lt;to&amp;gt;/dwr/$1&amp;lt;/to&amp;gt;
    &amp;lt;/rule&amp;gt;
    &amp;lt;rule&amp;gt;
        &amp;lt;from&amp;gt;/services/**&amp;lt;/from&amp;gt;
        &amp;lt;to&amp;gt;/services/$1&amp;lt;/to&amp;gt;
    &amp;lt;/rule&amp;gt;
&amp;lt;/urlrewrite&amp;gt;
&lt;/pre&gt;
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Spring MVC&lt;/strong&gt;&lt;br/&gt;
I had a fair amount of experience with Spring MVC and extensionless URLs. Both the Spring MVC applications we developed last year at Time Warner Cable used them. To change from a *.html mapping to /app/* was pretty easy and involved removing more code than I added. Previously, I had a &lt;a href=&quot;http://source.appfuse.org/browse/appfuse/trunk/web/common/src/main/java/org/appfuse/webapp/filter/StaticFilter.java?r=3250&quot;&gt;StaticFilter&lt;/a&gt; that looked for HTML files and if it didn&apos;t find them, it dispatched to Spring&apos;s DispatcherServlet. I was able to remove this class and make the web.xml file quite a bit cleaner. 
&lt;/p&gt;
&lt;p&gt;To make UrlRewriteFilter and Spring Security play well together, I had to move the securityFilter so it came &lt;em&gt;after&lt;/em&gt; the rewriteFilter and add an INCLUDE dispatcher so included JSPs would have a security context available to them. 
&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;The only other things I had to change were &lt;a href=&quot;http://source.appfuse.org/browse/appfuse/trunk/web/spring/src/main/webapp/WEB-INF/security.xml?r2=3458&amp;r1=3379&quot;&gt;security.xml&lt;/a&gt; and &lt;a href=&quot;http://source.appfuse.org/browse/appfuse/trunk/web/spring/src/main/webapp/WEB-INF/dispatcher-servlet.xml?r2=3458&amp;r1=3334&quot;&gt;dispatcher-servlet.xml&lt;/a&gt; to remove the .html extensions. The urlrewrite.xml file was fairly straightforward. I used the following at the bottom as a catch-all for dispatching to Spring MVC.
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;rule&amp;gt;
    &amp;lt;from&amp;gt;/**&amp;lt;/from&amp;gt;
    &amp;lt;to&amp;gt;/app/$1&amp;lt;/to&amp;gt;
&amp;lt;/rule&amp;gt;
&amp;lt;outbound-rule&amp;gt;
    &amp;lt;from&amp;gt;/app/**&amp;lt;/from&amp;gt;
    &amp;lt;to&amp;gt;/$1&amp;lt;/to&amp;gt;
&amp;lt;/outbound-rule&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
Then I added a number of other rules for j_security_check, DWR, CXF and static assets (/images, /scripts, /styles, /favicon.ico). You can view the &lt;a href=&quot;http://source.appfuse.org/browse/appfuse/trunk/web/spring/src/main/webapp/WEB-INF/urlrewrite.xml?r=HEAD&quot;&gt;current urlrewrite.xml in FishEye&lt;/a&gt;. The only major issue I ran into was that Spring Security recorded protected URLs as /app/URL so I had to add a rule to redirect when this happened after logging in.
&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;&lt;strong&gt;Struts 2&lt;/strong&gt;&lt;br/&gt;
Using extensionless URLs with Struts 2 is likely pretty easy thanks to the &lt;a href=&quot;http://struts.apache.org/2.1.8/docs/convention-plugin.html&quot;&gt;Convention Plugin&lt;/a&gt;. Even though this plugin is included in AppFuse, it&apos;s not configured with the proper &lt;a href=&quot;http://struts.apache.org/2.1.8/docs/converting-application-from-codebehind-to-convention-plugin.html&quot;&gt;constants&lt;/a&gt; and I have struts.convention.action.disableScanning=true in struts.xml. It looks like I had to do this when I &lt;a href=&quot;http://appfuse.markmail.org/thread/ktbqtx2mslvrkjkq&quot;&gt;upgraded from Struts 2.0.x to Struts 2.1.6&lt;/a&gt;. It&apos;s true AppFuse&apos;s Struts 2 support could use a bit of love to be aligned with Struts 2&apos;s recommended practices, but I didn&apos;t want to spend the time doing it as part of this exercise. 
&lt;/p&gt;
&lt;p&gt;With Struts 2, I tried the path-mapping like I did with Spring MVC, but ran into issues. Instead, I opted to use an &quot;.action&quot; extension by changing &lt;code&gt;struts.action.extension&lt;/code&gt; from &quot;html&quot; to &quot;action,&quot; in struts.xml. Then I had to do a bunch of filter re-ordering and dispatcher changes. Before, with a .html extension, I had all filters mapped to /* and in the following order.&lt;/p&gt;
&lt;table class=&quot;comparison&quot; style=&quot;width: 250px&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Filter Name&lt;/th&gt;&lt;th&gt;Dispatchers&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;securityFilter&lt;/td&gt;
&lt;td&gt;request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rewriteFilter&lt;/td&gt;
&lt;td&gt;request, forward&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;struts-prepare&lt;/td&gt;
&lt;td&gt;request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sitemesh&lt;/td&gt;
&lt;td&gt;request, forward, include&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;staticFilter&lt;/td&gt;
&lt;td&gt;request, forward&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;struts&lt;/td&gt;
&lt;td&gt;request&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Similar to Spring MVC, I had to remove the rewriteFilter in front of the securityFilter and I was able to remove the staticFilter. I also had to map the struts filter to *.action instead of /* to stop Struts from trying to catch static asset and DWR/CXF requests. Below is the order of filters and their dispatchers that seems to work best.
&lt;table class=&quot;comparison&quot; style=&quot;width: 250px&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Filter Name&lt;/th&gt;&lt;th&gt;Dispatchers&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;rewriteFilter&lt;/td&gt;
&lt;td&gt;request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;securityFilter&lt;/td&gt;
&lt;td&gt;request, forward, include&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;struts-prepare&lt;/td&gt;
&lt;td&gt;request, forward&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sitemesh&lt;/td&gt;
&lt;td&gt;request, forward, include&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;struts&lt;/td&gt;
&lt;td&gt;forward&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;From there, it was a matter of modifying &lt;a href=&quot;http://source.appfuse.org/browse/appfuse/trunk/web/struts/src/main/webapp/WEB-INF/urlrewrite.xml?r=HEAD&quot;&gt;urlrewrite.xml&lt;/a&gt; to have the following catch-all and rules for static assets, j_security_check and DWR/CXF.&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;rule match-type=&quot;regex&quot;&amp;gt;
    &amp;lt;from&amp;gt;^(&amp;#91;^?&amp;#93;*)/(&amp;#91;^?/\.&amp;#93;+)(\?.*)?$&amp;lt;/from&amp;gt;
    &amp;lt;to last=&quot;true&quot;&amp;gt;$1/$2.action$3&amp;lt;/to&amp;gt;
&amp;lt;/rule&amp;gt;
&amp;lt;outbound-rule match-type=&quot;regex&quot;&amp;gt;
    &amp;lt;from&amp;gt;^(.*)\.action(\?.*)?$&amp;lt;/from&amp;gt;
    &amp;lt;to last=&quot;false&quot;&amp;gt;$1$2&amp;lt;/to&amp;gt;
&amp;lt;/outbound-rule&amp;gt;
&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;JSF&lt;/strong&gt;&lt;br/&gt;
JSF was by far the most difficult to get extensionless URLs working with. I&apos;m not convinced it&apos;s impossible, but I spent a several hours over a few days and was unsuccessful in completely removing them. I was able to make things work so I could request pages without an extension, but found when clicking buttons and links, the extension would often show up in the URL. I&apos;m also still using JSF 1.2, so it&apos;s possible that upgrading to 2.0 would solve many of the issues I encountered. &lt;/p&gt;
&lt;p&gt;For the time being, I&apos;ve changed my FacesServlet mapping from *.html to *.jsf. As with Struts, I had issues when I tried to map it to /app/*. Other changes include &lt;a href=&quot;http://source.appfuse.org/browse/appfuse/trunk/web/jsf/src/main/webapp/WEB-INF/web.xml?r1=3384&amp;r2=3458#l188&quot;&gt;changing the order of dispatchers and filters&lt;/a&gt;, the good ol&apos; catch-all in &lt;a href=&quot;http://source.appfuse.org/browse/appfuse/trunk/web/jsf/src/main/webapp/WEB-INF/urlrewrite.xml?r=HEAD&quot;&gt;urlrewrite.xml&lt;/a&gt; and &lt;a href=&quot;http://source.appfuse.org/browse/appfuse/trunk/web/jsf/src/main/webapp/WEB-INF/security.xml?r1=3384&amp;r2=3458#l188&quot;&gt;modifying security.xml&lt;/a&gt;. For some reason, I wasn&apos;t able to get file upload working without adding an exception to the outbound-rule.&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;rule match-type=&quot;regex&quot;&amp;gt;
    &amp;lt;from&amp;gt;^(&amp;#91;^?&amp;#93;*)/(&amp;#91;^?/\.&amp;#93;+)(\?.*)?$&amp;lt;/from&amp;gt;
    &amp;lt;to last=&quot;true&quot;&amp;gt;$1/$2.jsf&amp;lt;/to&amp;gt;
&amp;lt;/rule&amp;gt;
&amp;lt;outbound-rule match-type=&quot;regex&quot;&amp;gt;
  &amp;lt;!-- TODO: Figure out how to make file upload work w/o using *.jsf --&amp;gt;
    &amp;lt;condition type=&quot;path-info&quot;&amp;gt;selectFile&amp;lt;/condition&amp;gt;
    &amp;lt;from&amp;gt;^(.*)\.jsf(\?.*)?$&amp;lt;/from&amp;gt;
    &amp;lt;to last=&quot;false&quot;&amp;gt;$1$2&amp;lt;/to&amp;gt;
&amp;lt;/outbound-rule&amp;gt;
&lt;/pre&gt;
&lt;p&gt;I also spent a couple hours trying to get &lt;a href=&quot;http://ocpsoft.com/prettyfaces/&quot;&gt;Pretty Faces&lt;/a&gt; to work. I wrote about my issues &lt;a href=&quot;http://ocpsoft.com/support/topic/rewrite-every-jsf&quot;&gt;on the forums&lt;/a&gt;. I tried writing a custom Processor to strip the extension, but found that I&apos;d get into an infinite loop where the processor kept getting called. To workaround this, I tried using Spring&apos;s RequestContextHolder to ensure the processor only got invoked once, but that proved fruitless. Finally, I tried inbound &lt;em&gt;and&lt;/em&gt; outbound custom processors, but failed to get those working. The final thing I tried was url-mappings for each page in pretty-config.xml.
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;url-mapping&amp;gt;
  &amp;lt;pattern value=&quot;/admin/users&quot;/&amp;gt;
  &amp;lt;view-id value=&quot;/admin/users.jsf&quot;/&amp;gt;
&amp;lt;/url-mapping&amp;gt;
&amp;lt;url-mapping&amp;gt;
  &amp;lt;pattern value=&quot;/mainMenu&quot;/&amp;gt;
  &amp;lt;view-id value=&quot;/mainMenu.jsf&quot;/&amp;gt;
&amp;lt;/url-mapping&amp;gt;
&lt;/pre&gt;
&lt;p&gt;The issue with doing this was that some of the navigation rules in my faces-config.xml stopped working. I didn&apos;t spend much time trying to diagnose the problem because I didn&apos;t like having to add an entry for each page in the application. The one nice thing about Pretty Faces is it did allow me to do things like the following, which I formerly did with a form that auto-submitted when the page loaded.
&lt;/p&gt;
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;url-mapping&amp;gt;
  &amp;lt;pattern value=&quot;/passwordHint/#{username}&quot;/&amp;gt;
  &amp;lt;view-id value=&quot;/passwordHint.jsf&quot;/&amp;gt;
  &amp;lt;action&amp;gt;#{passwordHint.execute}&amp;lt;/action&amp;gt;
&amp;lt;/url-mapping&amp;gt;
&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;br/&gt;
My journey implementing extensionless URLs was an interesting one, and I solidified my knowledge about ordering of filters, dispatchers and the UrlRewriteFilter. I still think I have more to learn about properly implementing extensionless URLs in Struts 2 and JSF and I hope to do that in the near future. I believe Struts&apos; Convention Plugin will help me and JSF 2 + Pretty Faces will hopefully work nicely too. Of course, it&apos;d be great if all Java Web Frameworks had an easy mechanism for producing and consuming extensionless URLs. In the meantime, thank goodness for the UrlRewriteFilter.
&lt;/p&gt;
&lt;p&gt;If you&apos;d like to try AppFuse and its shiny new URLs, see the &lt;a href=&quot;http://appfuse.org/display/APF/AppFuse+QuickStart&quot;&gt;QuickStart Guide&lt;/a&gt; and choose the 2.1.0-SNAPSHOT version.

</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/re_moving_from_spring_to</id>
        <title type="html">RE: Moving from Spring to Java EE 6: The Age of Frameworks is Over</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/re_moving_from_spring_to"/>
        <published>2010-10-16T15:19:07-06:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="javaee" 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="frameworks" scheme="http://roller.apache.org/ns/tags/" />
        <category term="webframeworks" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">Last Tuesday, Cameron McKenzie wrote an interesting article on TheServerSide titled &lt;a href=&quot;http://www.theserverside.com/discussions/thread.tss?thread_id=61023&quot;&gt;Moving from Spring to Java EE 6: The Age of Frameworks is Over&lt;/a&gt;. In this article, Cameron says the following:&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
J2EE represents the past, and Java EE 6 represents the future. Java EE 6 promises us the ability to go beyond frameworks. Frameworks like Spring are really just a bridge between the mistakes of the J2EE past and the success of the Java EE 6 future. Frameworks are out, and extensions to the Java EE 6 platform are in. Now is the time to start looking past Spring, and looking forward to Seam and Weld and CDI technologies.
&lt;/p&gt;
&lt;p&gt;He then links to an article titled &lt;a href=&quot;http://ocpsoft.com/java/spring-to-java-ee-a-migration-guide-cdi-jsf-jpa-jta-ejb/&quot;&gt;Spring to Java EE - A Migration Experience&lt;/a&gt;, an article written by JBoss&apos;s Lincoln Baxter. In this article, Lincoln talks about many of the technologies in Java EE 6, namely JPA, EJB, JSF, CDI and JAX-RS. He highlights all the various XML files you&apos;ll need to know about and the wide variety of Java EE 6 application servers: JBoss AS 6 and GlassFish v3.&lt;/p&gt;
&lt;p&gt;I don&apos;t have a problem with Lincoln&apos;s article, in fact I think it&apos;s very informative and some of the best documentation I&apos;ve seen for Java EE 6. 
&lt;/p&gt;
&lt;p&gt;
I do have some issues with Cameron&apos;s statements that frameworks are mistakes of the J2EE past and that Java EE 6 represents the future. Open source frameworks made J2EE successful. Struts and Hibernate came out in the early days of J2EE and still exist today. Spring came out shortly after and has turned into the do-everything J2EE implementation it was trying to fix. Java EE 6 &lt;em&gt;might be&lt;/em&gt; a better foundation to build upon, but it&apos;s certainly not going to replace frameworks.
&lt;/p&gt;
&lt;p&gt;
To prove my point, let&apos;s start by looking at the persistence layer. We used to have Hibernate based on JDBC, now we have JPA implementations built on top of the JPA API. Is JPA a replacement for all persistence frameworks? I&apos;ve worked with it and think it&apos;s a good API, but the 2.0 version &lt;a href=&quot;https://repository.sonatype.org/index.html#nexus-search;quick~javax.persistence&quot;&gt;isn&apos;t available in a Maven repo&lt;/a&gt; and &lt;a href=&quot;http://wiki.alfresco.com/wiki/Alfresco_Community_3.4.a#Hibernate_Removal&quot;&gt;Alfresco recently moved away from Hibernate&lt;/a&gt; (which == JPA IMO) to iBATIS for greater data access layer control and scalability. Looks like the age of frameworks isn&apos;t over for persistence frameworks.&lt;/p&gt;
&lt;p&gt;The other areas that Java EE 6 covers that I believe frameworks will continue to excel in: EJB, CDI, JSF and JAX-RS. Personally, I don&apos;t have a problem with EJB 3 and think it&apos;s a vast improvement on EJB 2.x. I don&apos;t have an issue with CDI either, and as long as it resembles Guice for dependency injection, it works for me. However, when you get into the space I&apos;ve been living in for the last couple years (high-traffic public internet sites), EJB and things like the &quot;conversation-scope&quot; feature of CDI don&apos;t buy you much. The way to make web application scale is to eliminate state and cache as much as possible, both of which Java EE doesn&apos;t provide much help for. In fact, to disable sessions in a servlet-container, you have to write a Filter like the following:&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
public class DisabledSessionFilter extends OncePerRequestFilter {

    /**
     * Filters requests to disable URL-based session identifiers.
     */
    @Override
    protected void doFilterInternal(final HttpServletRequest request,
                                    final HttpServletResponse response,
                                    final FilterChain chain)
            throws IOException, ServletException {

        HttpServletRequestWrapper wrappedRequest = new HttpServletRequestWrapper(request) {

            @Override
            public HttpSession getSession(final boolean create) {
                if (create) {
                    throw new UnsupportedOperationException(&quot;Session support disabled&quot;);
                }
                return null;
            }

            @Override
            public HttpSession getSession() {
                throw new UnsupportedOperationException(&quot;Session support disabled&quot;);
            }
        };

        // process next request in chain
        chain.doFilter(wrappedRequest, response);
    }
}
&lt;/pre&gt;
&lt;p&gt;What about JAX-RS? Does it replace the need for frameworks? I like the idea of having a REST API in Java. However, its reference implementation is &lt;a href=&quot;https://jersey.dev.java.net/&quot;&gt;Jersey&lt;/a&gt;, which seems more like a framework than just Java EE. If you choose to use JAX-RS in your application, you still have to choose between CXF, Jersey, RESTEasy and Restlet. &lt;a href=&quot;http://raibledesigns.com/rd/entry/my_experience_with_java_rest&quot;&gt;I compared these frameworks last year&lt;/a&gt; and found the Java EE implementation lacking in the features I needed. 
&lt;/p&gt;
&lt;p&gt;Finally, let&apos;s talk about my-least-framework-web-framework: JSF. The main reason I don&apos;t like JSF is because of its 1.x version. JSF 1.0 was released a year before the Ajax term was coined (see timeline below). Not only did it take forever to develop as a spec, but it tried to be a client-component framework that was very stateful by default. 
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://farm5.static.flickr.com/4067/4378559350_aef7d39d06_o.png&quot; title=&quot;History of Web Frameworks&quot; rel=&quot;lightbox&quot;&gt;
&lt;img src=&quot;//farm5.static.flickr.com/4067/4378559350_13f0755403.jpg&quot; width=&quot;500&quot; height=&quot;234&quot; alt=&quot;History of Web Frameworks&quot;/&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;Now that JSF 2.0 is out, it has Ajax integrated and allows you to use GET instead of POST-for-everything. However, the only people that like Ajax integrated into their web frameworks are programmers scared of JavaScript (who probably shouldn&apos;t be developing your UI). Also, the best component development platform for the web &lt;em&gt;is&lt;/em&gt; JavaScript. I recommend using an Ajax framework for your components if you really want a rich UI. 
&lt;/p&gt;
&lt;p&gt;
Sure you can use the likes of Tapestry and Wicket if you like POJO-based web development, but if you&apos;re looking to develop a webapp that&apos;s easy to maintain and understand, chances are that you&apos;ll do much better with traditional MVC frameworks like Spring MVC and Struts 2. The simplicity and popularity of Rails and Grails further emphasize that developers prefer these types of web frameworks.&lt;/p&gt;
&lt;p&gt;Another reason I don&apos;t like JSF: there&apos;s very few developers in the wild happy with it. The major promoters of JSF are book authors, trainers, Java EE Vendors and MyFaces developers. Whenever I speak at conferences, I ask folks to raise their hands for the various web frameworks they&apos;re using. I always ask the JSF users to keep their hands up if they like it. Rarely do they stay up.
&lt;/p&gt;
&lt;p&gt;
So it looks like we still need web frameworks. 
&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://jandiandme.blogspot.com/2010/10/spring-vs-java-ee-and-why-i-dont-care.html&quot;&gt;Eberhard Wolff has an interesting post&lt;/a&gt; where he defends Spring and talks about the productivity comparisons between Spring and Java EE. He recommends using Grails or Spring Roo if you want the level of productivity that Ruby on Rails provides. That&apos;s a valid recommendation if you&apos;re building CRUD-based webapps, but I haven&apos;t developed those in quite some time. Nowadays, the apps I develop are true SOFEA apps, where the backend serves up XML or JSON and the frontend client is HTML/JavaScript/CSS, Android, iPad or Sony Blu-Ray players. On my current project, our services don&apos;t even talk to a database, they talk to a CMS via RESTful APIs. We use Spring&apos;s RestTemplate for this and HttpClient when it doesn&apos;t have the features we need. Not much in Java EE 6 for this type of communication. Sure, &lt;a href=&quot;http://blogs.sun.com/enterprisetechtips/entry/consuming_restful_web_services_with&quot;&gt;Jersey has a client&lt;/a&gt;, but it&apos;s certainly not part of the Java EE spec.
&lt;/p&gt;
&lt;p&gt;As far as getting Ruby on Rails&apos; zero-turnaround productivity, I don&apos;t need Grails or Spring Roo, I simply use &lt;a href=&quot;http://www.jetbrains.com/idea/&quot;&gt;IDEA&lt;/a&gt; and &lt;a href=&quot;http://www.zeroturnaround.com/jrebel/&quot;&gt;JRebel&lt;/a&gt;. 
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;br/&gt;
I don&apos;t see how new features in Java EE 6 can mean the age of frameworks is over. Java SE and J2EE have always been foundations for frameworks. The Java EE 6 features are often frameworks in themselves that can be used outside of a Java EE container. Furthermore, Java EE 6 doesn&apos;t provide all the features you need to build a high-scale web app today. There&apos;s no caching, no stateless web framework that can serve up JSON and HTML and no hot-reload productivity enhancements like JRebel. Furthermore, there&apos;s real excitement in Javaland for languages like Scala, Groovy and JRuby. All of these languages have web frameworks that&apos;ve made many developers happy. 
&lt;/p&gt;
&lt;p&gt;
Here&apos;s to the Age of Frameworks - may it live as long as the JVM!
&lt;/p&gt;
&lt;p style=&quot;padding-top: 5px; border-top: 1px dotted silver; color: #666&quot;&gt;
P.S. If you&apos;d like to hear me talk about web frameworks on the JVM, I&apos;ll be speaking at &lt;a href=&quot;http://www.meetup.com/csopensource/calendar/15088624/&quot; style=&quot;color: #666&quot;&gt;The Colorado Springs Open Source Meetup&lt;/a&gt; and &lt;a href=&quot;http://www.devoxx.com/display/Devoxx2K10/Comparing+JVM+Web+Frameworks&quot; style=&quot;color: #666&quot;&gt;Devoxx 2010&lt;/a&gt; in the near future.&lt;/p&gt;
&lt;p&gt;
&lt;!--p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; I am an independent consultant and don&apos;t have any affiliations with Java EE vendors. I do have an affection for open source frameworks, particularly Spring and web frameworks. This is because I&apos;ve seen them successfully implemented at high scale web sites such as LinkedIn, Evite and Time Warner Cable.&lt;/p--&gt; </content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/highly_interactive_software_with_java</id>
        <title type="html">Highly Interactive Software with Java and Flex</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/highly_interactive_software_with_java"/>
        <published>2010-03-18T12:29:26-06:00</published>
        <updated>2010-03-18T18:32:00-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="mxml" scheme="http://roller.apache.org/ns/tags/" />
        <category term="spring" scheme="http://roller.apache.org/ns/tags/" />
        <category term="ria" scheme="http://roller.apache.org/ns/tags/" />
        <category term="jamesward" scheme="http://roller.apache.org/ns/tags/" />
        <category term="adobe" scheme="http://roller.apache.org/ns/tags/" />
        <category term="java" scheme="http://roller.apache.org/ns/tags/" />
        <category term="soap" scheme="http://roller.apache.org/ns/tags/" />
        <category term="flex" scheme="http://roller.apache.org/ns/tags/" />
        <category term="blazeds" scheme="http://roller.apache.org/ns/tags/" />
        <category term="actionscript" scheme="http://roller.apache.org/ns/tags/" />
        <category term="amf" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">This morning at TSSJS, I attended &lt;a href=&quot;http://jamesward.com&quot;&gt;James Ward&apos;s&lt;/a&gt; talk about &lt;a href=&quot;http://javasymposium.techtarget.com/html/theclientside.html%23JWardFlex&quot;&gt;Highly Interactive Software with Java and Flex&lt;/a&gt;. Below are my notes from his talk.
&lt;/p&gt;
&lt;p style=&quot;border-top: 1px dotted silver; padding-top: 10px&quot;&gt;
Application have moved from mainframes (hard to deploy, limited clients) to client/server (hard to deploy, full client capabilities) to web applications (easy to deploy, limited clients) to rich internet applications (easy to deploy, full client capabilities). 
&lt;/p&gt;
&lt;p&gt;
Shortly after showing a diagram of how applications have changed, James showed a demo of a sample Flex app for an automobile insurance company. It was very visually appealing, kinda like using an iPhone app. It was a multi-form application that slides right-to-left as you progress through the wizard. It also allowed you to interact with a picture of your car (to indicate where the damage happened) and a map (to indicate how the accident happened). Both of these interactive dialogs still performed data entry, they just did it in more of a visual way.
&lt;/p&gt;
&lt;p&gt;
Adobe&apos;s developer technology for building RIAs is Flex. There&apos;s two different languages in Flex: ActionScript and MXML. ActionScript was originally based on JavaScript, but now (in ActionScript 3) uses features from Java and C#. On top of ActionScript is MXML. It&apos;s a declarative language, but unlike JSP taglibs. All you can do with MXML is instantiate objects and set properties. It&apos;s merely a convenience language, but also allows tooling. The open source SDK compiler takes Flex files and compiles it into a *.swf file. This file can then be executed using the Flash Player (in browser) or Air (desktop).
&lt;/p&gt;
&lt;p&gt;
The reason Adobe developed two different runtimes was because they didn&apos;t want to bloat the Flash Player. Once the applications are running client-side, the application talks to the web server. Protocols that can be used for communication: SOAP, HTTP/S, AMF/S and RTMP/S. The web server can be composed of REST or SOAP Web Services, as well as BlazeDS or LC Data Services to talk directly to Java classes.
&lt;/p&gt;
&lt;p&gt;
To see all the possible Flex components, see &lt;a href=&quot;http://flex.org/tour&quot;&gt;Tour de Flex&lt;/a&gt;. It contains a number of components: core components, data access controls, AIR capabilities, cloud APIs, data visualization. The &lt;a href=&quot;http://www.adobe.com/devnet/flex/tourdeflex/web/%23sampleId=14050;illustIndex=0;docIndex=-1&quot;&gt;IBM ILOG Elixir real-time dashboard&lt;/a&gt; is particularly interesting, as is Doug McCune&apos;s &lt;a href=&quot;http://www.adobe.com/devnet/flex/tourdeflex/web/#sampleId=16300;illustIndex=0;docIndex=0&quot;&gt;Physics Form&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;Next James showed us some code. He used Flex Builder to create a new Flex project with BlazeDS. The backend for this application was a JSP page that talks to a database and displays the results in XML. In the main .mxml file, he used &amp;lt;s:HTTPService&gt; with a URL pointing to the URI of the JSP. Then he added an &amp;lt;mx:DataGrid&gt; and the data binding feature of Flex. To do this, he added dataProvider=&quot;{srv.lastResult.items.item}&quot; to the DataGrid tag, where &quot;srv&quot; is the id of the HTTPService. Then he added a Button with click=&quot;srv.send()&quot; and set the layout to VerticalLayout. This was a simple demo to show how to hook in a backend with XML. 
&lt;/p&gt;
&lt;p&gt;To show that Flex can interact with more than XML over HTTP, James wrote a SOAP service and changed &amp;lt;s:HTTPService&gt; to &amp;lt;s:WebService&gt; and changed the &quot;url&quot; attribute to &quot;wsdl&quot; (and adjusted the value as appropriate). Then rather than using {srv.lastResult.*}, he had to bind to a particular method and change it to {srv.getElements.lastResults}. The Button&apos;s click value also had to change to &quot;srv.getElements(0, 2000)&quot; (since the method takes 2 parameters). 
&lt;/p&gt;
&lt;p&gt;After doing coding in Flex Builder, James switched to his &lt;a href=&quot;http://www.jamesward.com/census&quot;&gt;Census&lt;/a&gt; to compare server-execution times. In the first example (Flash XML AS), most of the time was spent gzipping the 1MB XML file, but the transfer time is reduced because of this. The server execution time is around 800ms. Compare this to the Flex AMF3 example where the server execution time is 49ms. This is because the AMF (binary) protocol streamlines the data and doesn&apos;t include repeated metadata. 
&lt;/p&gt;
&lt;p&gt;
To integrate BlazeDS in your project, you add the dependencies and then map the MessageBrokerServlet in your web.xml. Then you use a services-config.xml to define the protocol and remoting-config.xml to define what Java classes to export as services. To use this in the Flex aplication, James changed &amp;lt;s:WebService&gt; to &amp;lt;s:RemoteObject&gt;. He changed the &quot;wsdl&quot; attribute to &quot;endpoint&quot; and added a &quot;destination&quot; attribute to specify the name of the aliased Java class to talk to. Next, James ran the demo and showed that he could change the number of rows from 2,000 to 20,000 and the load time was still much, much faster than the XML and SOAP versions.
&lt;/p&gt;
&lt;p&gt;
There&apos;s also a &lt;a href=&quot;http://www.springsource.org/spring-flex&quot;&gt;Spring BlazeDS Integration project&lt;/a&gt; that allows you to simply annotate beans to expose them as AMF services. 
&lt;/p&gt;
&lt;p&gt;
BlazeDS also includes a messaging service that you can use to create publishers and subscribers. The default channels in BlazeDS uses HTTP Streaming and HTTP Long Polling (comet), but it can be configurable (e.g. to use JMS). There&apos;s also an Adobe commercial product that keeps a connection open using NIO on the server and has a binary protocol. This is useful for folks that need more real-time data in their applications (e.g. trading floors). 
&lt;/p&gt;
&lt;p style=&quot;border-top: 1px dotted silver; padding-top: 10px&quot;&gt;
I thought this was a really good talk by James. It had some really cool visual demos and the demo was interesting in showing how easy it was to switch between different web services and protocols. This afternoon, I&apos;ll be duking it out with James at the &lt;a href=&quot;http://javasymposium.techtarget.com/html/theclientside.html#MRaibleSmack&quot;&gt;Flex vs. GWT Smackdown&lt;/a&gt;. If you have deficiencies of Flex you&apos;d like me to share during that talk, please let me know.
</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/17_macbook_pro_stolen_from</id>
        <title type="html">17&quot; MacBook Pro Stolen from Living Room</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/17_macbook_pro_stolen_from"/>
        <published>2010-02-03T09:45:52-07:00</published>
        <updated>2010-02-03T16:46:24-07:00</updated> 
        <category term="/Mac OS X" label="Mac OS X" />
        <category term="imac" scheme="http://roller.apache.org/ns/tags/" />
        <category term="theft" scheme="http://roller.apache.org/ns/tags/" />
        <category term="burlary" scheme="http://roller.apache.org/ns/tags/" />
        <category term="macbookpro" scheme="http://roller.apache.org/ns/tags/" />
        <category term="denver" scheme="http://roller.apache.org/ns/tags/" />
        <category term="du" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">Almost 3 years ago, I bought a &lt;a href=&quot;http://raibledesigns.com/rd/entry/a_new_17_powerhouse&quot;&gt;17&quot; MacBook Pro&lt;/a&gt;. This laptop served me well for several years, mostly as a home computer. A few months after I bought it, I started working at LinkedIn and got a brand new laptop as part of my &lt;a href=&quot;http://raibledesigns.com/rd/entry/first_day_at_linkedin&quot;&gt;first day on the job&lt;/a&gt;. After working with LinkedIn&apos;s 15&quot; for almost 2 years, I grew to love the form factor and &lt;a href=&quot;http://raibledesigns.com/rd/entry/new_15_macbook_pro_with&quot;&gt;purchased another one&lt;/a&gt; almost a year ago. I found the 17&quot; was too big for planes and the 15&quot; fits me perfectly.&lt;/p&gt;
&lt;p&gt;Fast forward to last night.&lt;/p&gt;
&lt;p&gt;I attended the first half of the &lt;a href=&quot;http://www.denveropensource.org/node/2&quot;&gt;Ignite talks at DOSUG&lt;/a&gt; and left around 7. When I arrived home, I suspected something might be out of whack when my front door was unlocked. Julie had come over to pick up Abbie&apos;s dance shoes around 6, so I figured she must&apos;ve forgot to lock the door on her way out. When I got inside and saw my coat closet open, I justified it by thinking she grabbed one of the kids coats out of the closet and forgot to close the door. When I walked into my living room and saw my space heater&apos;s remote in the middle of the floor, busted open with batteries out, it clicked that a stranger was in my house. The first thing that jumped into my head was &quot;Where&apos;s my laptop?&quot; As I looked at the bare mini-desk in my living room, I realized it had been stolen.
&lt;/p&gt;
&lt;p&gt;I called Julie and asked her if she left the front door open. She said no, but when she arrived at my house (and came through the back), the garage&apos;s light was on and my back door was wide open. I asked her if she saw the busted remote on the floor or if the closet door was open. She said no. Putting all the pieces together, it &lt;em&gt;appears&lt;/em&gt; that the burglar was actually hiding in my closet when Julie came into my house. Naturally, she&apos;s a little a freaked out by this, but she also saved the day by scaring off perpetrator before they took anything else.
&lt;/p&gt;
&lt;p&gt;This isn&apos;t a new trend for me and this incident is mostly my fault. I left my backdoor unlocked. Two years ago, &lt;a href=&quot;http://raibledesigns.com/rd/entry/snow_white_gets_molested&quot;&gt;my truck&apos;s stereo was stolen&lt;/a&gt; and there&apos;s a good chance I left the doors unlocked (and didn&apos;t turn on the alarm). Last year, &lt;a href=&quot;http://raibledesigns.com/rd/entry/r_i_p_giant_fcr3&quot;&gt;my bike was stolen&lt;/a&gt; and the lock was still there, indicating I missed the frame when locking it up. So getting robbed in the first part of every year seems somewhat par for the course.
&lt;/p&gt;
&lt;p&gt;With my truck&apos;s stereo, it worked out well because the rig needed a new stereo. My bike last year? There wasn&apos;t any silver lining to that incident, so I &lt;a href=&quot;http://raibledesigns.com/rd/entry/running_to_work&quot;&gt;made myself earn a new one&lt;/a&gt;. With this laptop incident, there &lt;em&gt;is&lt;/em&gt; a silver lining in that I&apos;ve been thinking about getting a 27&quot; iMac for a home computer. Other options include a Mac Pro for my office (and use my laptop for traveling/home use) or a Mac Mini for home and hook it up to my TV with a wireless keyboard and mouse. 
&lt;/p&gt;
&lt;p&gt;The home iMac seems like the best option, but I&apos;d also be interested to hear what others recommend. Of course, I&apos;ll be keeping my doors locked from now on. &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;
</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/grails_oauth_and_linkedin_apis</id>
        <title type="html">Grails OAuth and LinkedIn APIs</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/grails_oauth_and_linkedin_apis"/>
        <published>2009-12-22T15:37:57-07:00</published>
        <updated>2009-12-23T07:17:35-07:00</updated> 
        <category term="/Java" label="Java" />
        <category term="grails" scheme="http://roller.apache.org/ns/tags/" />
        <category term="github" scheme="http://roller.apache.org/ns/tags/" />
        <category term="linkedin" scheme="http://roller.apache.org/ns/tags/" />
        <category term="profile" scheme="http://roller.apache.org/ns/tags/" />
        <category term="oauth" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">Back in November, I wrote about &lt;a href=&quot;http://raibledesigns.com/rd/entry/gwt_oauth_and_linkedin_apis&quot;&gt;how to talk to LinkedIn APIs with GWT&lt;/a&gt;. A week later, I &lt;a href=&quot;http://twitter.com/mraible/status/6195066631&quot;&gt;figured out how to do it with Grails&lt;/a&gt; and contributed a &lt;a href=&quot;http://code.google.com/p/grails-oauth/issues/detail?id=1&quot;&gt;patch&lt;/a&gt; to the grails-oauth plugin. 
&lt;/p&gt;
&lt;p&gt;Since then, a few folks have asked how I did it. Since code speaks louder than words, I took some time and 1) verified the oauth plugin works as expected and 2) created an example application demonstrating functionality. You can find the results in &lt;a href=&quot;http://github.com/mraible/grails-oauth&quot;&gt;my fork of grails-oauth on GitHub&lt;/a&gt;. You can also &lt;a href=&quot;http://demo.raibledesigns.com/grails-oauth&quot;&gt;view the example online&lt;/a&gt;.
&lt;/p&gt;
&lt;p style=&quot;border-bottom: 1px dotted silver; padding-bottom: 5px&quot;&gt;Below is a quick tutorial explaining how to integrate LinkedIn into your Grails application.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;http://www.grails.org/Download&quot;&gt;Download&lt;/a&gt; and install Grails 1.1.2.&lt;/li&gt;
&lt;li&gt;Run &lt;em&gt;grails create-app&lt;/em&gt; to create your application.&lt;/li&gt;
&lt;li&gt;Add the following to the bottom of &lt;em&gt;grails-app/conf/Config.groovy&lt;/em&gt;:
&lt;pre class=&quot;brush: java&quot;&gt;
oauth {
    linkedin {
        requestTokenUrl=&quot;https://api.linkedin.com/uas/oauth/requestToken&quot;
        accessTokenUrl=&quot;https://api.linkedin.com/uas/oauth/accessToken&quot;
        authUrl=&quot;https://api.linkedin.com/uas/oauth/authorize&quot;
        consumer.key=&quot;XXX&quot;
        consumer.secret=&quot;XXX&quot;
    }
}
&lt;/pre&gt;
You can get your consumer.key and consumer.secret at &lt;a href=&quot;https://www.linkedin.com/secure/developer&quot;&gt;https://www.linkedin.com/secure/developer&lt;/a&gt;. Make sure to set the &lt;em&gt;OAuth Redirect URL&lt;/em&gt; to http://localhost:8080/{your.app.name}/oauth/callback for testing.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://github.com/mraible/grails-oauth/archives/master&quot;&gt;Download&lt;/a&gt; the oauth-plugin, extract it and build it using &lt;em&gt;grails package-plugin&lt;/em&gt;. Install it in your project using &lt;em&gt;grails install-plugin path/to/zip&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Add a link to the GSP you want to invoke LinkedIn Authentication from:
&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;g:oauthLink consumer=&apos;linkedin&apos; returnTo=&quot;&amp;#91;controller:&apos;profile&apos;&amp;#93;&quot;&amp;gt;
    Login with LinkedIn
&amp;lt;/g:oauthLink&amp;gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Create &lt;em&gt;grails-app/controllers/ProfileController.groovy&lt;/em&gt; to access your LinkedIn Profile.
&lt;pre class=&quot;brush: java&quot;&gt;
class ProfileController {
    def apiUrl = &quot;http://api.linkedin.com/v1/people/~&quot;
    def oauthService
    
    def index = {
 
        if (session.oauthToken == null) {
            redirect(uri:&quot;/&quot;)
        }
 
        if (params?.apiUrl) apiUrl = params.apiUrl
        
        def response = oauthService.accessResource(
                apiUrl, &apos;linkedin&apos;, &amp;#91;key:session.oauthToken.key, secret:session.oauthToken.secret&amp;#93;, &apos;GET&apos;)
 
        render(view: &apos;index&apos;, model: &amp;#91;profileXML: response, apiUrl: apiUrl&amp;#93;)
    }
 
    def change = {
        if (params?.apiUrl) {
            println(&quot;Setting api url to &quot; + params.apiUrl)
            apiUrl = params.apiUrl
        }
        
        redirect(action:index,params:params)
    }
}
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Create &lt;em&gt;grails-app/views/profile/index.gsp&lt;/em&gt; to display the retrieved profile and allow subsequent API calls.
&lt;pre class=&quot;brush: html&quot;&gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;Your Profile&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;a class=&quot;home&quot; href=&quot;${createLinkTo(dir:&apos;&apos;)}&quot;&amp;gt;Home&amp;lt;/a&amp;gt;
&amp;lt;g:hasOauthError&amp;gt;
    &amp;lt;div class=&quot;errors&quot;&amp;gt;
        &amp;lt;g:renderOauthError/&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/g:hasOauthError&amp;gt;

&amp;lt;g:form url=&quot;&amp;#91;action:&apos;change&apos;,controller:&apos;profile&apos;&amp;#93;&quot; method=&quot;get&quot;&amp;gt;
    Your LinkedIn Profile:
    &amp;lt;textarea id=&quot;payload&quot; style=&quot;width: 100%; height: 50%; color: red&quot;&amp;gt;${profileXML}&amp;lt;/textarea&amp;gt;
    &amp;lt;p&amp;gt;
        &amp;lt;g:textField name=&quot;apiUrl&quot; value=&quot;${apiUrl}&quot; size=&quot;100%&quot;/&amp;gt;
        &amp;lt;br/&amp;gt;
        &amp;lt;g:submitButton name=&quot;send&quot; value=&quot;Send Request&quot;/&amp;gt;
    &amp;lt;/p&amp;gt;
&amp;lt;/g:form&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Start your app using &lt;em&gt;grails run-app&lt;/em&gt; and enjoy.&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;border-top: 1px dotted silver; padding-top: 5px&quot;&gt;As mentioned earlier, you can &lt;a href=&quot;http://github.com/mraible/grails-oauth/archives/master&quot;&gt;download the grails-oauth-example&lt;/a&gt; or &lt;a href=&quot;http://demo.raibledesigns.com/grails-oauth&quot;&gt;view it online&lt;/a&gt;. 
&lt;/p&gt;
&lt;p&gt;One improvement I&apos;d like to see is to simplify the parsing of XML into a Profile object, much like the &lt;a href=&quot;http://pivotallabs.com/users/will/blog/articles/1096-linkedin-gem-for-a-web-app&quot;&gt;linkedin gem&lt;/a&gt; does for Rails. 
&lt;/p&gt;
&lt;p&gt;
If you&apos;re interested in learning more about LinkedIn and OAuth, I encourage you to checkout &lt;a href=&quot;http://www.linkedin.com/in/taylorsingletary&quot;&gt;Taylor Singletary&apos;s&lt;/a&gt; presentation &lt;a href=&quot;http://www.slideshare.net/episod/linkedin-oauth-zero-to-hero&quot;&gt;LinkedIn OAuth: Zero to Hero&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; I &lt;a href=&quot;http://github.com/mraible/grails-oauth/commit/6db20f3b8341383b869f49d6ca126ebd99ccb364&quot;&gt;updated&lt;/a&gt; the oauth-plugin so it&apos;s backwards-compatible with OAuth 1.0 and added Twitter to the example application to prove it. If you&apos;re seeing &quot;Cannot invoke method remove() on null object&quot;, it&apos;s likely caused by your redirect URL pointing to an application on a different domain.</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/gwt_oauth_and_linkedin_apis</id>
        <title type="html">GWT OAuth and LinkedIn APIs</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/gwt_oauth_and_linkedin_apis"/>
        <published>2009-11-24T15:46:05-07:00</published>
        <updated>2014-05-08T19:47:19-06:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="api" scheme="http://roller.apache.org/ns/tags/" />
        <category term="gwt" scheme="http://roller.apache.org/ns/tags/" />
        <category term="oauth" scheme="http://roller.apache.org/ns/tags/" />
        <category term="linkedin" scheme="http://roller.apache.org/ns/tags/" />
        <category term="profile" scheme="http://roller.apache.org/ns/tags/" />
        <category term="google" scheme="http://roller.apache.org/ns/tags/" />
        <category term="twitter" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">&lt;a href=&quot;http://www.linkedin.com&quot;&gt;&lt;img src=&quot;//static.raibledesigns.com/repository/images/linkedin-logo.gif&quot; width=&quot;129&quot; height=&quot;36&quot; alt=&quot;LinkedIn Logo&quot; class=&quot;picture&quot; style=&quot;border: 0&quot; /&gt;&lt;/a&gt;
When I worked at LinkedIn last year, I received a lot of inquiries from friends and developers about LinkedIn&apos;s APIs. After a while, I started sending the following canned response:&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
For API access to build LinkedIn features into your application, fill
out the following form:
&lt;br/&gt;&lt;br/&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a href=&quot;http://www.linkedin.com/static?key=developers_apis&quot;&gt;http://www.linkedin.com/static?key=developers_apis&lt;/a&gt;
&lt;br/&gt;&lt;br/&gt;
For requests to build an application, go to:
&lt;br/&gt;&lt;br/&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a href=&quot;http://www.linkedin.com/static?key=developers_opensocial&quot;&gt;http://www.linkedin.com/static?key=developers_opensocial&lt;/a&gt;
&lt;br/&gt;&lt;br/&gt;
I talked with the API team and they did say they look at every request that&apos;s sent via these forms. They don&apos;t respond to all of them b/c they know that many people would be angry if they told them &quot;no&quot;, so they&apos;d rather not have that headache.
&lt;/p&gt;
&lt;p&gt;Yesterday, I was pumped to see that they&apos;ve finally decided to &lt;a href=&quot;http://blog.linkedin.com/2009/11/23/linkedin-platform-launch/&quot;&gt;open up their API to Developers&lt;/a&gt;.
&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
Starting today, developers worldwide can integrate LinkedIn into their business applications and Web sites.  &lt;a href=&quot;http://developer.linkedin.com/&quot;&gt;Developer.linkedin.com&lt;/a&gt; is now &lt;strong&gt;live &lt;/strong&gt;and open for business.
&lt;/p&gt;
&lt;p&gt;First of all, congratulations to the API team on finally making this happen! I know it&apos;s no small feat. Secondly, it&apos;s great to see them using &lt;a href=&quot;http://www.jivesoftware.com/products&quot;&gt;Jive SBS&lt;/a&gt; for their API documentation and developer community. My current client uses this to facilitate development and I love how it integrates a wiki, JIRA, FishEye, Crucible and Bamboo into one central jumping off point.&lt;/p&gt;
&lt;p&gt;I&apos;ve always been a fan of LinkedIn, ever since I joined way back in &lt;a href=&quot;http://raibledesigns.com/rd/entry/happy_cinco_de_linko&quot;&gt;May 2003&lt;/a&gt;. However, I&apos;ve longed for a way to access my data. &lt;a href=&quot;http://developer.linkedin.com/community/widgets&quot;&gt;LinkedIn Widgets&lt;/a&gt; are nice, but there&apos;s something to be said for the full power of an API. Last night, I sat down for a couple hours and enhanced my &lt;a href=&quot;http://raibledesigns.com/rd/entry/implementing_oauth_with_gwt&quot;&gt;Implementing OAuth with GWT&lt;/a&gt; example to support LinkedIn&apos;s API.&lt;/p&gt;
&lt;p&gt;I&apos;m happy to report my experiment was a success and you can &lt;a href=&quot;http://static.raibledesigns.com/downloads/gwt-oauth-1.2.zip&quot;&gt;download GWT OAuth 1.2&lt;/a&gt; or &lt;a href=&quot;http://demo.raibledesigns.com/gwt-oauth&quot;&gt;view it online&lt;/a&gt;. For now, I&apos;m simply &lt;a href=&quot;http://developer.linkedin.com/docs/DOC-1008&quot;&gt;authenticating with OAuth&lt;/a&gt; and accessing the &lt;a href=&quot;http://developer.linkedin.com/docs/DOC-1002&quot;&gt;Profile API&lt;/a&gt;. 
&lt;/p&gt;
&lt;p style=&quot;text-align: center&quot;&gt;
&lt;a href=&quot;http://demo.raibledesigns.com/gwt-oauth&quot; title=&quot;OAuth with GWT Demo&quot;&gt;&lt;img src=&quot;//farm3.static.flickr.com/2655/4132814004_b423779e59.jpg&quot; width=&quot;453&quot; height=&quot;326&quot; alt=&quot;OAuth with GWT&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;In the process, I learned a couple things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;LinkedIn&apos;s OAuth implementation returns an &lt;a href=&quot;http://wiki.oauth.net/Signed-Callback-URLs&quot;&gt;oauth_verifier&lt;/a&gt; parameter after authenticating, whereas Google and Twitter do not. This parameter needs to be included when calling the &lt;em&gt;Access token path&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;The Profile API example I implemented gets the current user&apos;s profile with &lt;a href=&quot;http://api.linkedin.com/v1/people/~&quot;&gt;http://api.linkedin.com/v1/people/~&lt;/a&gt;. This returns a &quot;light&quot; version of your profile. To get a more detailed version, you need to use &lt;a href=&quot;http://developer.linkedin.com/docs/DOC-1014&quot;&gt;Field Selectors&lt;/a&gt;. For example:
&lt;span style=&quot;display: block; margin: 5px 5px&quot;&gt;
&lt;a href=&quot;http://api.linkedin.com/v1/people/~:(id,first-name,last-name,picture-url,headline,summary,positions,educations)&quot;&gt;http://api.linkedin.com/v1/people/~:(id,first-name,last-name,picture-url,headline,summary,positions,educations)&lt;/a&gt;
&lt;/span&gt;
&lt;/li&gt;
&lt;li&gt;LinkedIn&apos;s API only supports passing OAuth parameters in a header, rather than query parameters. To make this work, I modified my &lt;a href=&quot;http://raibledesigns.com/rd/entry/how_to_do_cross_domain#proxyServlet&quot;&gt;ProxyServlet&lt;/a&gt; to convert query parameters to an &quot;Authorization&quot; header at the end of the &lt;em&gt;setProxyRequestHeaders()&lt;/em&gt; method.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
// For LinkedIn&apos;s OAuth API, convert request parameters to an AuthorizationHeader
if (httpServletRequest.getRequestURL().toString().contains(&quot;linkedin-api&quot;)) {
    String&amp;#91;&amp;#93; parameters = httpServletRequest.getQueryString().split(&quot;&amp;amp;&quot;);
    StringBuilder sb = new StringBuilder(&quot;OAuth realm=\&quot;http://api.linkedin.com/\&quot;,&quot;);
    for (int i = 0; i &amp;lt; parameters.length; i++) {
        sb.append(parameters&amp;#91;i&amp;#93;);
        if (i &amp;lt; parameters.length - 1) {
            sb.append(&quot;,&quot;);
        }
    }

    Header authorization = new Header(&quot;Authorization&quot;, sb.toString());
    httpMethodProxyRequest.setRequestHeader(authorization);
}
&lt;/pre&gt;
&lt;p&gt;You might recall that my previous example had issues authenticating with Google, but worked well with Twitter. LinkedIn&apos;s authentication seems to work flawlessly. This leads me to believe that Twitter and LinkedIn have a much more mature OAuth implementation than Google.&lt;/p&gt;
&lt;p style=&quot;padding-top: 5px; border-top: 1px dotted silver; color: #666&quot;&gt;&lt;em&gt;&lt;strong&gt;Related OAuth News&lt;/strong&gt;: Apache Roller 5 will be shipping with OAuth support. See &lt;a href=&quot;http://rollerweblogger.org/roller&quot;&gt;Dave Johnson&apos;s&lt;/a&gt; &lt;a href=&quot;http://www.slideshare.net/snoopdave/whats-new-in-roller5&quot;&gt;What&apos;s New in Roller 5 presentation&lt;/a&gt; for more information.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update December 6, 2009:&lt;/strong&gt; I modified the gwt-oauth project to use GWT 1.7.1 and changed to the &lt;a href=&quot;http://mojo.codehaus.org/gwt-maven-plugin/&quot;&gt;Maven GWT Plugin&lt;/a&gt; from Codehaus. &lt;a href=&quot;http://static.raibledesigns.com/downloads/gwt-oauth-1.3.zip&quot;&gt;Download GWT OAuth 1.3&lt;/a&gt; or &lt;a href=&quot;http://demo.raibledesigns.com/gwt-oauth&quot;&gt;view it online&lt;/a&gt;.</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/user_interface_schema_definitions</id>
        <title type="html">User Interface Schema Definitions</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/user_interface_schema_definitions"/>
        <published>2009-11-06T15:18:25-07:00</published>
        <updated>2009-11-09T20:39:58-07:00</updated> 
        <category term="/The Web" label="The Web" />
        <category term="usixml" scheme="http://roller.apache.org/ns/tags/" />
        <category term="uiml" scheme="http://roller.apache.org/ns/tags/" />
        <category term="xml" scheme="http://roller.apache.org/ns/tags/" />
        <category term="ui" scheme="http://roller.apache.org/ns/tags/" />
        <category term="schemas" scheme="http://roller.apache.org/ns/tags/" />
        <category term="xforms" scheme="http://roller.apache.org/ns/tags/" />
        <category term="xsd" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">On my current project, we&apos;re developing a &quot;designer&quot; tool that allows users to build forms in their browser. These forms are displayed in various &lt;em&gt;channels&lt;/em&gt; (e.g. web, mobile, sms) to capture data and make decisions based on user input. 
&lt;/p&gt;
&lt;p&gt;
When I joined the project, it was in a proof-of-concept phase and the form definitions created where serialized as XML (using JAXB) with element names that seemed logical. Now that we&apos;re moving the PoC to production mode, we&apos;re thinking it might be better to change our form definitions to leverage something that&apos;s more &quot;industry standard&quot;. If nothing else, it&apos;ll help with marketing. ;-)
&lt;/p&gt;
&lt;p&gt;This week, I was tasked with doing research on &quot;existing user interface schema definitions&quot;. I&apos;m writing this post to see if there&apos;s any major specifications I&apos;m missing. I plan on providing my recommendation to my team on Monday. Here&apos;s what I&apos;ve found so far:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.uiml.org/&quot;&gt;User Interface Markup Language (UIML)&lt;/a&gt;: At 120 pages, the 4.0 spec seems very detailed. I&apos;m not sure we&apos;d use all of it, but it&apos;s interesting how the spec allows you to describe the initial tree structure (&amp;lt;structure&amp;gt;) and to dynamically modify its structure (&amp;lt;restructure&amp;gt;). It also has the notion of &lt;em&gt;templates&lt;/em&gt;, which mirrors a similar concept we have in our application. Furthermore, it has VoiceXML support, which could be useful if we use call centers as a channel.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.usixml.org/&quot;&gt;USer Interface eXtensible Markup Language (UsiXML)&lt;/a&gt;: I admit that I haven&apos;t read much of this specification -- mostly because the UIML spec seemed to cover most of what we needed (especially since we&apos;re most interested in describing forms). As far as a I can tell, the major difference between UsiXML is its being submitted to the W3C for standardization (&lt;a href=&quot;http://en.wikipedia.org/wiki/UsiXML&quot;&gt;according to Wikipedia&lt;/a&gt;), while UIML is being standardized by &lt;a href=&quot;http://en.wikipedia.org/wiki/OASIS_%28organization%29&quot;&gt;OASIS&lt;/a&gt;. Beyond that, I find it strange that UIML&apos;s spec is 120 pages and UsiXML is 121. Neither project seems to have any activity this year.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://xml.coverpages.org/userInterfaceXML.html&quot;&gt;Numerous others&lt;/a&gt;: including &lt;abbr title=&quot;Alternate Abstract Interface Markup Language&quot;&gt;AAIML&lt;/abbr&gt;, &lt;abbr title=&quot;Abstract User Interface Markup Language&quot;&gt;AUIML&lt;/abbr&gt;, &lt;abbr title=&quot;Extensible Interface Markup Language&quot;&gt;XIML&lt;/abbr&gt;, &lt;abbr title=&quot;Extensible User Interface Language&quot;&gt;XUL&lt;/abbr&gt;, &lt;abbr title=&quot;Microsoft Extensible Application Markup Language&quot;&gt;XAML&lt;/abbr&gt; and &lt;a href=&quot;http://xml.coverpages.org/userInterfaceXML.html#w3c-XForms&quot;&gt;XForms&lt;/a&gt;. XForms seems like it may be the most logical if we&apos;re only interested in form layout and describing elements within them.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If all we&apos;re interested in is an XSD to define our forms, the most appealing specs have them: &lt;a href=&quot;http://www.uiml.org/specs/uiml3/DTD.htm&quot;&gt;UIML&lt;/a&gt;, &lt;a href=&quot;http://www.usixml.org/documentation/usixml1.8.0/UsiXML.xsd.html&quot;&gt;UsiXML&lt;/a&gt; and &lt;a href=&quot;http://www.w3.org/MarkUp/Forms/2002/XForms-Schema.xsd&quot;&gt;XForms&lt;/a&gt;. If activity is any sort of motivator for adoption, it&apos;s interesting to note that &lt;a href=&quot;http://www.w3.org/TR/xforms/&quot;&gt;XForms 1.1&lt;/a&gt; was submitted as a W3C recommendation a couple weeks ago (October 20, 2009).&lt;/p&gt;
&lt;p&gt;If you&apos;ve developed some sort of &quot;form designer&quot; tool that renders to multiple channels, I&apos;d love to hear about your experience. Did you use some sort of industry standard to define your form elements, layout, etc. or did you come up with your own?</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/a_letter_to_the_appfuse</id>
        <title type="html">A Letter to the AppFuse Community</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/a_letter_to_the_appfuse"/>
        <published>2009-11-04T00:17:17-07:00</published>
        <updated>2009-11-04T07:32:42-07:00</updated> 
        <category term="/Java" label="Java" />
        <category term="spring" scheme="http://roller.apache.org/ns/tags/" />
        <category term="seam" scheme="http://roller.apache.org/ns/tags/" />
        <category term="rubyonrails" scheme="http://roller.apache.org/ns/tags/" />
        <category term="letter" scheme="http://roller.apache.org/ns/tags/" />
        <category term="springroo" scheme="http://roller.apache.org/ns/tags/" />
        <category term="appfuse" scheme="http://roller.apache.org/ns/tags/" />
        <category term="grails" scheme="http://roller.apache.org/ns/tags/" />
        <category term="play" scheme="http://roller.apache.org/ns/tags/" />
        <category term="community" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">The last &lt;a href=&quot;http://appfuse.org&quot;&gt;AppFuse&lt;/a&gt; release was way back in &lt;a href=&quot;http://raibledesigns.com/rd/entry/appfuse_2_0_2_released&quot;&gt;May 2008&lt;/a&gt;. Many folks have asked when the next release would be ever since. Often, I&apos;ve said &quot;sometimes this quarter&quot;, but obviously, that&apos;s never happened. For that, I apologize.
&lt;/p&gt;
&lt;p&gt;There are many reasons I haven&apos;t worked on AppFuse for the past 18 months, but it mostly comes down to the fact that I didn&apos;t make time for it. The good news is I&apos;m working on it again and &lt;em&gt;will&lt;/em&gt; have a release out sometime this month. Unfortunately, it probably won&apos;t be a 2.1 final release, but there&apos;s so many things that&apos;ve changed, I feel like a milestone release is a good idea. Here&apos;s a brief summary of changes so far:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Changed archetypes to include all source and tests for the &quot;webapp&quot; portion of the application. No more warpath plugin, merging wars and IDE issues. Using &quot;mvn jetty:run&quot; should work as expected.&lt;/li&gt;
&lt;li&gt;Moved from &lt;a href=&quot;http://raibledesigns.com/rd/entry/moving_from_spring_s_xml&quot;&gt;Spring XML to Annotations&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;AppFuse Light &lt;a href=&quot;http://raibledesigns.com/rd/entry/appfuse_light_converted_to_maven&quot;&gt;converted to Maven modules&lt;/a&gt; and now depends on AppFuse&apos;s backend.&lt;/li&gt;
&lt;li&gt;Published easier to use archetype selection form in the &lt;a href=&quot;http://appfuse.org/display/APF/AppFuse+QuickStart&quot;&gt;QuickStart Guide&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Published &lt;a href=&quot;http://static.appfuse.org/light/archetypes.html&quot;&gt;archetype selection form for AppFuse Light&lt;/a&gt;. I do plan on combining these forms as soon as I figure out the best UI and instructions for users to choose AppFuse or AppFuse Light.&lt;/li&gt;
&lt;li&gt;Upgraded all libraries to latest released versions (Spring 3 hasn&apos;t had a final release yet).&lt;/li&gt;
&lt;li&gt;Upgraded to Tapestry 5 thanks to &lt;a href=&quot;http://code.google.com/p/tapestry5-appfuse/&quot;&gt;Serge Eby&lt;/a&gt;. I still need to complete tests and code generation for tests.&lt;/li&gt;
&lt;li&gt;Added &lt;a href=&quot;http://issues.appfuse.org/browse/APF-267&quot;&gt;Compass support&lt;/a&gt; thanks to a patch from &lt;a href=&quot;http://www.kimchy.org/&quot;&gt;Shay Banon&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Upgraded from &lt;a href=&quot;http://issues.appfuse.org/browse/APF-1125&quot;&gt;XFire to CXF&lt;/a&gt; for Web Services.&lt;/li&gt;
&lt;li&gt;Moved Maven repository to &lt;a href=&quot;https://docs.sonatype.com/display/NX/OSS+Repository+Hosting&quot;&gt;Sonatype&apos;s OSS Repository Hosting&lt;/a&gt; for snapshots and releasing to Maven Central. There are no longer any AppFuse-specific artifacts, all are available in central.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I realize there&apos;s many full-stack frameworks that do the same thing as AppFuse with less code. Examples include &lt;a href=&quot;http://rubyonrails.org&quot;&gt;Ruby on Rails&lt;/a&gt;, &lt;a href=&quot;http://grails.org&quot;&gt;Grails&lt;/a&gt;, &lt;a href=&quot;http://seamframework.org&quot;&gt;Seam&lt;/a&gt;, &lt;a href=&quot;http://www.springsource.org/roo&quot;&gt;Spring Roo&lt;/a&gt; and the &lt;a href=&quot;http://www.playframework.org/&quot;&gt;Play framework&lt;/a&gt;. However, there seems to be quite a few folks that continue to use AppFuse and it stills serves the community as a nice example of how to integrate frameworks. Furthermore, it helps me keep up with the latest framework releases, their quirks and issues that happen when you try to integrate them. In short, working on it helps me stay up to speed with Java open source frameworks.
&lt;/p&gt;
&lt;p&gt;
For those folks that like the 1.x, Ant-based version of AppFuse, there will &lt;em&gt;not&lt;/em&gt; be a 1.9.5 release. I know I promised it for years, but it&apos;s simply something I will not use, so I&apos;d rather not invest my time in it. I&apos;m sorry for lying to those that expected it.&lt;/p&gt;
&lt;p&gt;So what&apos;s the future of AppFuse? Will it continue to integrate web frameworks with Spring and popular persistence frameworks? Possibly, but it seems more logical to align it with the types of Ajax + REST applications I&apos;m creating these days. I&apos;m currently thinking AppFuse 3.0 would be nice as a RESTful backend with GWT and Flex UIs. I might create the backend with &lt;a href=&quot;http://cxf.apache.org/&quot;&gt;CXF&lt;/a&gt;, but it&apos;s possible I&apos;d use one of the frameworks mentioned above and simply leverage it to create the default features AppFuse users have come to expect.&lt;/p&gt;
&lt;p&gt;More than anything, I&apos;m writing this letter to let you know that the AppFuse project is not dead and you can expect a release in the near future.&lt;/p&gt;
&lt;p&gt;Thanks for your support,&lt;/p&gt;
&lt;p&gt;Matt</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/lean_teams_doing_more_with</id>
        <title type="html">Lean Teams: Doing more with less</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/lean_teams_doing_more_with"/>
        <published>2009-09-23T21:20:28-06:00</published>
        <updated>2009-09-29T12:17:06-06:00</updated> 
        <category term="/Open Source" label="Open Source" />
        <category term="software" scheme="http://roller.apache.org/ns/tags/" />
        <category term="softwaredevelopment" scheme="http://roller.apache.org/ns/tags/" />
        <category term="kanban" scheme="http://roller.apache.org/ns/tags/" />
        <category term="lean" scheme="http://roller.apache.org/ns/tags/" />
        <category term="rails" scheme="http://roller.apache.org/ns/tags/" />
        <category term="denver" scheme="http://roller.apache.org/ns/tags/" />
        <category term="derailed" scheme="http://roller.apache.org/ns/tags/" />
        <category term="agile" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">This evening I attended the Denver Rails User Group (a.k.a. &lt;a href=&quot;http://groups.google.com/group/derailed&quot;&gt;DeRailed&lt;/a&gt;) to hear a presentation by &lt;a href=&quot;http://martyhaught.com&quot;&gt;Marty Haught&lt;/a&gt;. It was titled &quot;Lean Teams: Doing more with less&quot; and the following are my notes from the event.
&lt;/p&gt;
&lt;p&gt;Today&apos;s talk is about &quot;Rocking with Ramen&quot; - a.k.a. working with less funds to make great things. Lean comes from the manufacturing world in that you should &lt;strong&gt;Add Nothing but Value&lt;/strong&gt;. The most important thing you should do is &lt;em&gt;add business value&lt;/em&gt;.
&lt;/p&gt;
&lt;p&gt;The Seven Wasteful Sins for manufacturing are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Overproduction&lt;/li&gt;
&lt;li&gt;Inventory&lt;/li&gt;
&lt;li&gt;Extra Processing Steps&lt;/li&gt;
&lt;li&gt;Motion&lt;/li&gt;
&lt;li&gt;Defects&lt;/li&gt;
&lt;li&gt;Waiting&lt;/li&gt;
&lt;li&gt;Transportation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The key to fighting &lt;strong&gt;overproduction&lt;/strong&gt; in software is to trim features to those that achieve the greatest value. You should do &quot;the simplest thing that could possibly work&quot; and delay commitment as long as you can because &lt;a href=&quot;http://en.wikipedia.org/wiki/You_ain&apos;t_gonna_need_it&quot;&gt;YAGNI&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;
A &lt;strong&gt;minimum viable product&lt;/strong&gt; is a starting place for validated learning with the least amount of effort. It should be embarrassing. Early adopters see the potential. &lt;a href=&quot;http://blog.railsrumble.com/&quot;&gt;Rails Rumble&lt;/a&gt; and &lt;a href=&quot;http://startupweekend.org/&quot;&gt;Startup Weekend&lt;/a&gt; are good examples of promoting this type of development. 
&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;Unused and useless features&lt;/strong&gt; are best solved by &lt;em&gt;feedback-driven development&lt;/em&gt;. This is a process for validating value and creating software that people use. The end result is that you create software that people use and you&apos;re able to pivot your plan as you learn. The benefit of this is you stay humble and you don&apos;t drink the Kool-Aid (e.g. VC&apos;s tell you you&apos;re going to be the next Twitter).
&lt;/p&gt;
&lt;p&gt;The first part of feedback is &quot;&lt;a href=&quot;http://www.slideshare.net/dmc500hats/startup-metrics-for-pirates-long-version&quot;&gt;Pirate Metrics&lt;/a&gt;&quot; by Dave McClure. The main things to track are acquisition, activation, retention, referral and revenue (AARRRR!). The main things you should gather from metrics is they&apos;re actionable and should help you make decisions. Vanity metrics like hits-per-month and such should be ignored.&lt;/p&gt;
&lt;p&gt;Other feedback options include net promoter score (popup question to ask if users would recommend to a friend), feedback form (make it easy for users to tell you what you think about your product), A/B testing, and usability testing. 
&lt;/p&gt;
&lt;p&gt;The final point is that it&apos;s OK to remove features.&lt;/p&gt;
&lt;p&gt;To reduce extra processing and waiting, you should implement &quot;&lt;a href=&quot;http://en.wikipedia.org/wiki/Kanban&quot;&gt;Kanban&lt;/a&gt;&quot;. It&apos;s a pull-based system for a continuous flow of work and can be used in software projects to manage/schedule work for cross functional teams. It&apos;s an expression of just-in-time and has an emphasis on flow. It&apos;s all about getting across the board as fast as possible. In agile development, this is often expressed as a card-based system on a wall in the same room as your development team. Things can only move from the left-to-right as there is space for them. Marty is showing a screenshot of a &quot;&lt;a href=&quot;http://agilezen.com/&quot;&gt;Zen&lt;/a&gt;&quot; tool he uses on his projects. It has 3 columns (Definition, Work and Verification) from left-to-right that allows you to easily move stuff.&lt;/p&gt;
&lt;p&gt;The most important thing about &lt;em&gt;Kanban&lt;/em&gt; is it helps to eliminate constraints. The Zen tool only allows a certain amount of items in the &quot;Work&quot; column and it visually communicates blocked items by moving them to the top and highlighting them with a red border. The Zen tool that Marty is showing looks similar to Rally, but is much more visually appealing.
&lt;/p&gt;
&lt;p&gt;The benefits of Kanban include:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;simple, less process&lt;/li&gt;
&lt;li&gt;less inventory of requirements/stories&lt;/li&gt;
&lt;li&gt;limit work in progress, maximize throughput&lt;/li&gt;
&lt;li&gt;less time in meetings&lt;/li&gt;
&lt;li&gt;more naturally represents story lifecycle&lt;/li&gt;
&lt;li&gt;more easily spot bottlenecks&lt;/li&gt;
&lt;li&gt;estimate only if it adds value&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Kanban promotes tracking how long it takes for a story get across the board and into production vs. tracking velocity of a team.&lt;/p&gt;
&lt;p class=&quot;quote&quot; style=&quot;font-style: italic; color: #666&quot;&gt;
On my current project, we use Rally, a small team and have two week iterations. Because the things that Marty is talking about seem to be things we&apos;re already doing, I asked him how Kanban differs from Scrum with small teams. He explained that this biggest difference is Kanban is most useful when you&apos;re pushing things to production with each iteration.
&lt;/p&gt;
&lt;p&gt;The most controversial practice that Marty promotes is &lt;em&gt;Continuous Deployment&lt;/em&gt;. This is the automated deployment of code to production. It includes automated testing and continuous integration, simple deployment/rollback scripts, a successful CI build triggers deployment, and there&apos;s real-time alerts in production. When shit goes wrong, you should use the &quot;five whys&quot; to perform root cause analysis. Marty admits that this is only a good idea when there&apos;s a high-level of trust in your development team and lots of tests to prove nothing is broken.&lt;!--(in other words, you don&apos;t work with any douchebags)--&gt;
&lt;/p&gt;
&lt;p&gt;The benefits of continuous deployment is there&apos;s a lower story cycle time, you eliminate waste in deploying code, you deliver features/bugs fixes faster and you find integration issues quicker and in isolation. It&apos;s also a great way to promote &lt;em&gt;not checking in shitty code&lt;/em&gt;.
&lt;p&gt;The skeptics think this is a bad idea because 1) it&apos;s scary, 2) they believe it causes lower quality and 3) it causes more issues in production. The good news is you can still control production deployments with your source control system (e.g. branches and such). More than anything, it forces you to have a high quality continuous integration system that acts as the gatekeeper for what goes to production.
&lt;/p&gt;
&lt;p&gt;You can learn more about topics Marty covered in this talk at the following sites:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Eric Ries - &lt;a href=&quot;http://www.startuplessonslearned.com/&quot;&gt;http://www.startuplessonslearned.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Steve Blank - &lt;a href=&quot;http://www.amazon.com/Four-Steps-Epiphany-Steven-Blank/dp/0976470705&quot;&gt;The Four Steps to the Epiphany&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.poppendieck.com/&quot;&gt;Mary Poppendieck&lt;/a&gt; - Lean Software Development&lt;/li&gt;
&lt;li&gt;Dave McClure - &lt;a href=&quot;http://davemcclure.com/&quot;&gt;http://davemcclure.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.infoq.com/articles/hiranabe-lean-agile-kanban&quot;&gt;Kanban Applied to Software Development: from Agile to Lean&lt;/a&gt;&lt;/li&gt;

&lt;/ul&gt;
&lt;p&gt;If you&apos;re lucky enough to be attending &lt;a href=&quot;http://www.alohaonrails.com/&quot;&gt;Aloha on Rails&lt;/a&gt;, Marty will be &lt;a href=&quot;http://www.alohaonrails.com/sessions/#lean-teams-how-to-do-more-with-less&quot;&gt;presenting&lt;/a&gt; there. I recommend you attend his talk if you&apos;re trying to get stuff done quickly and get it into production even quicker. His techniques seem to be invaluable for developers that are trying to maximize their efficiency and reduce the time it takes to get their code into production.</content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/my_experience_with_java_rest</id>
        <title type="html">My Experience with Java REST Frameworks (specifically Jersey and CXF)</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/my_experience_with_java_rest"/>
        <published>2009-08-27T14:20:51-06:00</published>
        <updated>2012-11-11T02:00:40-07:00</updated> 
        <category term="/Java" label="Java" />
        <category term="jersey" scheme="http://roller.apache.org/ns/tags/" />
        <category term="cxf" scheme="http://roller.apache.org/ns/tags/" />
        <category term="json" scheme="http://roller.apache.org/ns/tags/" />
        <category term="gwt" scheme="http://roller.apache.org/ns/tags/" />
        <category term="soap" scheme="http://roller.apache.org/ns/tags/" />
        <category term="rpc" scheme="http://roller.apache.org/ns/tags/" />
        <category term="rest" scheme="http://roller.apache.org/ns/tags/" />
        <category term="webservices" scheme="http://roller.apache.org/ns/tags/" />
        <category term="enunciate" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">Recently I was tasked with developing a server-side REST strategy for a client. When I started working with them, they were using GWT RPC for exposing services. They wanted to move to RESTful services to allow for a more open platform, that multiple types of clients could talk to. There were interested in supporting SOAP and GWT RPC as well, but it wasn&apos;t required. They are using Spring, a &lt;a href=&quot;http://blog.springsource.com/2006/08/28/creating-a-spring-20-namespace-use-springs-abstractbeandefintionparser-hierarchy/&quot;&gt;custom namespace&lt;/a&gt; (for easily creating remote services) and an HTML documentation generator to expose their API in human readable form.
&lt;/p&gt;
&lt;p&gt;When I first starting developing, I chose to try &lt;a href=&quot;http://enunciate.codehaus.org/&quot;&gt;Enunciate&lt;/a&gt;. From Enunciate&apos;s homepage:&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
Enunciate is an engine for creating, maintaining, and deploying your rich Web service API on the Java platform.
If that sounds complicated, it&apos;s not. All you have to do is define your service interfaces in Java source code. ... 
Then invoke Enunciate.
&lt;/p&gt;
&lt;p&gt;Sounds pretty sweet, eh? At first glance, the things I liked about Enunciate were:
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The ability to generate multiple endpoints (and clients).&lt;/li&gt;
&lt;li&gt;Generates nice-looking documentation.&lt;/li&gt;
&lt;li&gt;Allows selecting different frameworks (for example, &lt;a href=&quot;http://docs.codehaus.org/display/ENUNCIATE/Using+CXF+or+XFire&quot;&gt;CXF instead of JAX-WS RI&lt;/a&gt;).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Initially, the hardest part of using Enunciate was integrating it into my project. This was somewhat related to having a &lt;a href=&quot;http://docs.codehaus.org/display/ENUNCIATE/Multi-Module+Projects&quot;&gt;multi-module project&lt;/a&gt;, but moreso related to getting the settings right in my &lt;em&gt;enunciate.xml&lt;/em&gt; file. After getting everything working, I encountered &lt;a href=&quot;http://markmail.org/thread/g3wj2gyvtqq5v3f4&quot;&gt;a few Spring wiring issues&lt;/a&gt; with the GWT Endpoints and with Jersey&apos;s Spring support. 
&lt;/p&gt;
&lt;p&gt;
The good news is I believe most of these issues were related to my project and how it proxies Spring beans that Jersey couldn&apos;t find. Jersey&apos;s &lt;a href=&quot;http://blogs.sun.com/enterprisetechtips/entry/jersey_and_spring&quot;&gt;Spring support&lt;/a&gt; only supports annotations at this time, so I was unable to tell it the proxied bean names via XML (or even its own annotations). I&apos;m sure this problem is solvable, but after struggling with it for a day or two, I decided to give up on Enunciate. I had to get something working, and fast.
&lt;/p&gt;
&lt;p&gt;At this point, I was back to the drawing board. I knew there were plenty of good Java REST frameworks available, but Spring and CXF (for SOAP) were already available dependencies in my project, so I chose that route. I was able to get something up and running fairly quickly with &lt;a href=&quot;http://raulraja.com/2009/06/25/spring-exposing-a-bean-as-rest-xml-json-and-soap-webservice/&quot;&gt;this tutorial&lt;/a&gt; and CXF&apos;s &lt;a href=&quot;http://cwiki.apache.org/CXF20DOC/jax-rs.html&quot;&gt;JAX-RS documentation&lt;/a&gt;. I ran into an &lt;a href=&quot;http://twitter.com/mraible/statuses/3407586205&quot;&gt;Invalid JSON Namespace issue&lt;/a&gt;, but was able to solve it by &lt;a href=&quot;https://issues.apache.org/jira/browse/CXF-1671&quot;&gt;adding a custom namespace&lt;/a&gt;. 
&lt;/p&gt;
&lt;p&gt;It&apos;s not all rosy though, there are still a couple CXF issues I haven&apos;t solved:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I&apos;d like to remove the namespace prefixes on returned JSON. I&apos;ve never seen JSON with &quot;prefix.&quot; notation before and it doesn&apos;t seem right.&lt;/li&gt;
&lt;li&gt;I&apos;m &lt;a href=&quot;http://www.nabble.com/No-message-body-writer-found-for-response-class-%3A-String---tt25070046.html#a25070046&quot;&gt;unable to return String[] from services&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While CXF does take a bit of XML for each service, it&apos;s kinda slick in that it only requires you to annotate your service interfaces. It also generates documentation for your services (WSDL and WADL), but it&apos;s not as pretty as Enunciate&apos;s. To solve this, I &lt;a href=&quot;http://docs.codehaus.org/display/ENUNCIATE/Documentation+Only&quot;&gt;added Enunciate for documentation only&lt;/a&gt;. To make Enunciate work, I did have to add some annotations to my service implementation classes and &lt;a href=&quot;http://markmail.org/thread/r6i4rv45lkaknzv7&quot;&gt;parse the generated HTML&lt;/a&gt; to fix some links to CXF&apos;s services, but ultimately it works pretty well. 
&lt;/p&gt;
&lt;p&gt;In the end, I recommended my client not use Enunciate for generating endpoints. This was primarily related to their unique Spring configuration, but also because I was able to easily use the same classes for REST, SOAP and GWT Endpoints. However, I will continue to keep my eye on Enunciate. It could be very useful for the upcoming &lt;a href=&quot;http://jira.codehaus.org/browse/ENUNCIATE-290&quot;&gt;appfuse-ws&lt;/a&gt; archetype. I&apos;m also eager to see &lt;a href=&quot;http://jira.codehaus.org/browse/ENUNCIATE-319&quot;&gt;better GWT support&lt;/a&gt; and the ability to &lt;a href=&quot;http://jira.codehaus.org/browse/ENUNCIATE-290&quot;&gt;generate Overlay types&lt;/a&gt; in future releases. </content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/browser_based_username_password_autocomplete</id>
        <title type="html">My attempt at browser-based username/password autocomplete with GWT</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/browser_based_username_password_autocomplete"/>
        <published>2009-08-07T08:09:11-06:00</published>
        <updated>2009-08-07T14:09:12-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="autocomplete" scheme="http://roller.apache.org/ns/tags/" />
        <category term="gwt" scheme="http://roller.apache.org/ns/tags/" />
        <category term="safari" scheme="http://roller.apache.org/ns/tags/" />
        <category term="ie" scheme="http://roller.apache.org/ns/tags/" />
        <category term="firefox" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">Last night, I did a quick spike to try and implement username/password autocomplete in my GXT application. By &quot;autocomplete&quot;, I don&apos;t mean Ajax-style autocomplete, but rather browser-based autocomplete. The best information I found on doing this is in the following post:
&lt;/p&gt;
&lt;p&gt;
&lt;a href=&quot;http://osdir.com/ml/GoogleWebToolkit/2009-04/msg01838.html&quot;&gt;http://osdir.com/ml/GoogleWebToolkit/2009-04/msg01838.html&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
I didn&apos;t use this technique (wrapping an existing form with a FormPanel) because I&apos;m using GXT and didn&apos;t want to lose the look-and-feel of my login form.&lt;/p&gt;
&lt;p&gt;
I was successful in getting everything to work in Firefox (it populates both the username and password). In IE, it only populates the username, not the password. In Safari/Chrome, it doesn&apos;t work at all. Here&apos;s how I did it:
&lt;/p&gt;
&lt;p&gt;
1. Created a hidden HTML form on my HTML page that embeds GWT.
&lt;pre class=&quot;brush: xml&quot;&gt;
&amp;lt;form method=&quot;post&quot; action=&quot;javascript:void(0)&quot; style=&quot;display: none&quot;&amp;gt;
    &amp;lt;input type=&quot;text&quot; id=&quot;username&quot; name=&quot;username&quot; value=&quot;&quot;/&amp;gt;
    &amp;lt;input type=&quot;password&quot; id=&quot;password&quot; name=&quot;password&quot; value=&quot;&quot;/&amp;gt;
    &amp;lt;input type=&quot;submit&quot; value=&quot;Login&quot; id=&quot;login&quot;/&amp;gt;
&amp;lt;/form&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
2. When a user clicks on the &quot;Login&quot; button in my GWT application, populate the fields in this hidden form and &quot;click&quot; on the Login button (which will do nothing since the &lt;em&gt;action=&quot;javascript:void(0)&quot;&lt;/em&gt;).
&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
// Set the hidden fields to trigger the browser to remember
DOM.getElementById(&quot;username&quot;).setAttribute(&quot;value&quot;, username.getValue());
DOM.getElementById(&quot;password&quot;).setAttribute(&quot;value&quot;, password.getValue());
clickFormLogin();

...

public static native void clickFormLogin() /*-{
$doc.getElementById(&quot;login&quot;).click();
}-*/;
&lt;/pre&gt;
&lt;p&gt;This works in Firefox 3.5 and prompts me to save the user/pass at the top of the screen. Why doesn&apos;t this work in Safari/Chrome? My guess is because the form&apos;s action doesn&apos;t go anywhere and the form is not submitted. If I change the action to be an actual URL and show the form, clicking on the form&apos;s Login button will save it in those browsers.&lt;/p&gt;
&lt;p&gt;Grabbing the actual populated values when the screen loads did turn out to be a bit tricky. With IE, I was able to grab the username, but not the password. It seems that keyboard or mouse interaction (tabbing off or focusing/blurring) with the username field is necessary to get the password field populated. In Firefox, I was unable to grab the values if I did it straight away. The solution turned out to be wrapping everything in a Timer and waiting a 1/2 second.
&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
// grab user/pass from browser and hidden login form (if user has stored them)
// runs 1/2 second after page loads so cursor goes at end of username and works in Firefox
Timer t = new Timer() {
    public void run() {
        username.setValue(getElementValue(&quot;username&quot;));
        password.setValue(getElementValue(&quot;password&quot;));
        if (username.getValue() != null) {
            password.focus();
        }
        validate();
    }
};

t.schedule(500);

...

public static native String getElementValue(String domId) /*-{
    return $doc.getElementById(domId).value;
}-*/;
&lt;/pre&gt;
&lt;p&gt;While I&apos;m glad I got it working in Firefox, I&apos;m disappointed with IE&apos;s lack of password autocompletion. More than anything, I can&apos;t help but think there&apos;s a way to make this work in WebKit-based browsers. If you&apos;ve successfully implemented cross-browser username/password autocomplete in GWT, please share your experience. If you share your experience about successfully doing it in Safari/Chrome, I might even buy you a beer or two. &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/integrating_gwt_with_spring_security</id>
        <title type="html">Integrating GWT with Spring Security</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/integrating_gwt_with_spring_security"/>
        <published>2009-08-06T08:50:15-06:00</published>
        <updated>2012-10-17T17:35:41-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="authentication" scheme="http://roller.apache.org/ns/tags/" />
        <category term="security" scheme="http://roller.apache.org/ns/tags/" />
        <category term="spring" scheme="http://roller.apache.org/ns/tags/" />
        <category term="gwt" scheme="http://roller.apache.org/ns/tags/" />
        <category term="springsecurity" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">Yesterday, I wrote about &lt;a href=&quot;http://raibledesigns.com/rd/entry/how_to_do_cross_domain&quot;&gt;How to do cross-domain GWT RPC with a ProxyServlet&lt;/a&gt;. Today I&apos;ll be discussing
    how to modify the ProxyServlet to authenticate with Spring Security. For the application I&apos;m working on, the ProxyServlet
    is only used in development (when running GWT&apos;s hosted mode) and isn&apos;t necessary when deploying the client and
    server on the same server. Using the ProxyServlet allows cross-domain requests so you can run GWT in hosted mode and
    talk to your backend running on another server. This setup can be especially handy in that you
    can easily point your hosted client at different backends (for example, if you have testing and staging environments).
&lt;/p&gt;
&lt;p&gt;
    In this example, the backend application is a JSF/Spring application that has Spring Security wired in to protect
    services with both Basic and Form-based authentication. Basic authentication will kick in if a &quot;Authorization&quot; header
    is sent, otherwise Form-based authentication is used. Here&apos;s the Spring Security context file that makes this happen:
&lt;/p&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;beans:beans xmlns=&quot;http://www.springframework.org/schema/security&quot;
             xmlns:beans=&quot;http://www.springframework.org/schema/beans&quot;
             xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
             xsi:schemaLocation=&quot;...&quot;&amp;gt;

    &amp;lt;http auto-config=&quot;true&quot; realm=&quot;My Web Application&quot;&amp;gt;
        &amp;lt;intercept-url pattern=&quot;/faces/welcome.jspx&quot; access=&quot;ROLE_USER&quot;/&amp;gt;
        &amp;lt;intercept-url pattern=&quot;/*.rpc&quot; access=&quot;ROLE_USER&quot;/&amp;gt;
        &amp;lt;http-basic/&amp;gt;
        &amp;lt;form-login login-page=&quot;/faces/login.jspx&quot; authentication-failure-url=&quot;/faces/accessDenied.jspx&quot;
                    login-processing-url=&quot;/j_spring_security_check&quot; default-target-url=&quot;/redirect.jsp&quot;
                    always-use-default-target=&quot;true&quot;/&amp;gt;
    &amp;lt;/http&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&quot;/&amp;gt;
        &amp;lt;/user-service&amp;gt;
    &amp;lt;/authentication-provider&amp;gt;
&amp;lt;/beans:beans&amp;gt;
&lt;/pre&gt;
&lt;p&gt;
The easiest way to configure your GWT application to talk to a Spring Security protected resource is to
&lt;a href=&quot;http://www.dotnetguru2.org/bmarchesson/index.php?p=678&amp;amp;more=1&amp;amp;c=1&amp;amp;tb=1&amp;amp;pb=1&quot;&gt;protect your HTML page that GWT is embedded in&lt;/a&gt;. This is the documented way to integrate GWT with Spring Security (ref: 
&lt;a href=&quot;http://code.google.com/p/google-web-toolkit-incubator/wiki/LoginSecurityFAQ&quot;&gt;GWT&apos;s LoginSecurityFAQ&lt;/a&gt;, search for &quot;Acegi&quot;).
This works well for production, but not for hosted-mode development.&lt;/p&gt;
&lt;p&gt;
&lt;strong&gt;Basic Authentication&lt;/strong&gt;&lt;br/&gt;
To authenticate with Basic Authentication, you can use GWT&apos;s RequestBuilder and set an &quot;Authentication&quot; header that
contains the user&apos;s (base64-encoded) credentials.
&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
private class LoginRequest {
    public LoginRequest(RequestCallback callback) {
        String url = &quot;/services/faces/welcome.jspx&quot;;

        RequestBuilder rb = new RequestBuilder(RequestBuilder.POST, url);
        rb.setHeader(&quot;Authorization&quot;, createBasicAuthToken());
        rb.setCallback(callback);
        try {
            rb.send();
        } catch (RequestException e) {
            Window.alert(e.getMessage());
        }
    }
}

protected String createBasicAuthToken() {
    byte[] bytes = stringToBytes(username.getValue() + &quot;:&quot; + password.getValue());
    String token = Base64.encode(bytes);
    return &quot;Basic &quot; + token;
}

protected byte[] stringToBytes(String msg) {
    int len = msg.length();
    byte[] bytes = new byte[len];
    for (int i = 0; i &amp;lt; len; i++)
        bytes[i] = (byte) (msg.charAt(i) &amp;amp; 0xff);
    return bytes;
}
&lt;/pre&gt;
&lt;p&gt;
To use this LoginRequest class, create it with a callback and look for a 401 response code to determine if
authentication failed.
&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
new LoginRequest(new RequestCallback() {
    public void onResponseReceived(Request request, Response response) {
        if (response.getStatusCode() != Response.SC_UNAUTHORIZED &amp;amp;&amp;amp;
                response.getStatusCode() != Response.SC_OK) {
            onError(request, new RequestException(response.getStatusText() + &quot;:\n&quot; + response.getText()));
            return;
        }

        if (response.getStatusCode() == Response.SC_UNAUTHORIZED) {
            Window.alert(&quot;You have entered an incorrect username or password. Please try again.&quot;);
        } else {
            // authentication worked, show a fancy dashboard screen
        }
    }

    public void onError(Request request, Throwable throwable) {
        Window.alert(throwable.getMessage());
    }
});
&lt;/pre&gt;
&lt;p&gt;If your GWT application is included in the &quot;services&quot; war, everything should work at this point. However, if you try to login
with invalid credentials, your browser&apos;s login dialog will appear. To suppress this in the aforementioned
ProxyServlet, you&apos;ll need to make a change in its &lt;em&gt;executeProxyRequest()&lt;/em&gt; method so the &quot;WWW-Authenticate&quot; header
is not copied.&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
// Pass the response code back to the client
httpServletResponse.setStatus(intProxyResponseCode);

// Pass response headers back to the client
Header[] headerArrayResponse = httpMethodProxyRequest.getResponseHeaders();
for (Header header : headerArrayResponse) {
    if (header.getName().equals(&quot;Transfer-Encoding&quot;) &amp;amp;&amp;amp; header.getValue().equals(&quot;chunked&quot;) ||
            header.getName().equals(&quot;Content-Encoding&quot;) &amp;amp;&amp;amp; header.getValue().equals(&quot;gzip&quot;) ||
            header.getName().equals(&quot;WWW-Authenticate&quot;)) { // don&apos;t copy WWW-Authenticate header
    } else {
        httpServletResponse.setHeader(header.getName(), header.getValue());
    }
}
&lt;/pre&gt;
&lt;p&gt;I&apos;m not sure how to suppress the browser prompt when not using the ProxyServlet. If you have a solution, please
&lt;a href=&quot;http://raibledesigns.com/rd/entry/integrating_gwt_with_spring_security#comments&quot;&gt;let me know&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;Basic Authentication works well for GWT applications because you don&apos;t need additional logic to retain the
authenticated state after the initial login. While Basic Authentication over SSL might offer a decent solution,
the downside is you can&apos;t logout. Form-based Authentication allows you to logout.&lt;/p&gt;
&lt;strong&gt;Form-based Authentication&lt;/strong&gt;&lt;br/&gt;
&lt;p&gt;Before I show you how to implement form-based authentication, you should be aware that Google does not recommend this.
Below is a warning from their LoginSecurityFAQ.&lt;/p&gt;
&lt;p class=&quot;quote&quot;&gt;
Do &lt;i&gt;NOT&lt;/i&gt; attempt to use the &lt;code&gt;Cookie&lt;/code&gt; header to transfer the sessionID from GWT to the server; it is
fraught with security issues that will become clear in the rest of this article. You &lt;strong&gt;MUST&lt;/strong&gt; transfer
the sessionID in the payload of the request. For an example of why this can fail, see CrossSiteRequestForgery.
&lt;/p&gt;
&lt;p&gt;In my experiment, I didn&apos;t want to change the server-side Spring Security configuration, so I ignored this
warning. If you know how to configure Spring Security so it looks for the sessionID in the payload of the request
(rather than in a cookie), I&apos;d love to hear about it. The upside of the example below is it should work with 
container-managed authentication as well.&lt;/p&gt;
&lt;p&gt;
The LoginRequest class for form-based authentication is similar to the previous one, except it has a different URL and
sends the user&apos;s credentials in the request body.
&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
private class LoginRequest {
    public LoginRequest(RequestCallback callback) {
        String url = &quot;/services/j_spring_security_check&quot;;

        RequestBuilder rb = new RequestBuilder(RequestBuilder.POST, url);
        rb.setHeader(&quot;Content-Type&quot;, &quot;application/x-www-form-urlencoded&quot;);
        rb.setRequestData(&quot;j_username=&quot; + URL.encode(username.getValue()) +
                    &quot;&amp;amp;j_password=&quot; + URL.encode(password.getValue()));

        rb.setCallback(callback);
        try {
            rb.send();
        } catch (RequestException e) {
            Window.alert(e.getMessage());
        }
    }
}
&lt;/pre&gt;
&lt;p&gt;If you deploy your GWT application in the same WAR your services are hosted in, this is all you&apos;ll need to do. If
you&apos;re using the ProxyServlet, there&apos;s a couple of changes you&apos;ll need to make in order to set/send cookies when
running in hosted mode.
&lt;/p&gt;
&lt;p&gt;
First of all, you&apos;ll need to make sure you&apos;ve configured the servlet to follow redirects (by subclassing or simply modifying its default).
After that, add the following logic on line 358 (or just look for &quot;&lt;code&gt;if (followRedirects)&lt;/code&gt;&quot;) to expose the sessionID to the client. The most important part is setting the cookie&apos;s path to &quot;/&quot; so the client (running at localhost:8888) can see it.&lt;/p&gt;
&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
if (followRedirects) {
    // happens on first login attempt
    if (stringLocation.contains(&quot;jsessionid&quot;)) { 
        Cookie cookie = new Cookie(&quot;JSESSIONID&quot;,
                stringLocation.substring(stringLocation.indexOf(&quot;jsessionid=&quot;) + 11));
        cookie.setPath(&quot;/&quot;);
        httpServletResponse.addCookie(cookie);
    // the following happens if you refresh your GWT app after already logging in once
    } else if (httpMethodProxyRequest.getResponseHeader(&quot;Set-Cookie&quot;) != null) {
        Header header = httpMethodProxyRequest.getResponseHeader(&quot;Set-Cookie&quot;);
        String[] cookieDetails = header.getValue().split(&quot;;&quot;);
        String[] nameValue = cookieDetails[0].split(&quot;=&quot;);

        Cookie cookie = new Cookie(nameValue[0], nameValue[1]);
        cookie.setPath(&quot;/&quot;);
        httpServletResponse.addCookie(cookie);
    }
    httpServletResponse.sendRedirect(stringLocation.replace(getProxyHostAndPort() +
            this.getProxyPath(), stringMyHostName));
    return;
}
&lt;/pre&gt;
&lt;p style=&quot;font-style: italic&quot;&gt;Click &lt;a href=&quot;http://www.flickr.com/photos/mraible/3794558459/sizes/l/&quot;&gt;here&lt;/a&gt; to see a screenshot of the diff of the ProxyServlet after this code has been added.&lt;/p&gt;
&lt;p&gt;Figuring out that headers needed to be parsed &lt;strong&gt;after&lt;/strong&gt; authenticating successfully and &lt;strong&gt;before&lt;/strong&gt; redirecting was the hardest part for me. If you grab the JSESSIONID from
the &quot;Set-Cookie&quot; header anywhere else, the JSESSIONID is one that hasn&apos;t been authenticated. While the login will work,
subsequent calls to services will fail.&lt;/p&gt;
&lt;p&gt;To make subsequent calls with the cookie in the header, you&apos;ll need to make an additional modification to ProxyServlet to 
send cookies as headers. First of all, add a &lt;em&gt;setProxyRequestCookies()&lt;/em&gt; method:&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
/**
 * Retrieves all of the cookies from the servlet request and sets them on
 * the proxy request
 *
 * @param httpServletRequest The request object representing the client&apos;s
 *                            request to the servlet engine
 * @param httpMethodProxyRequest The request that we are about to send to
 *                                the proxy host
 */
@SuppressWarnings(&quot;unchecked&quot;)
private void setProxyRequestCookies(HttpServletRequest httpServletRequest, 
                                    HttpMethod httpMethodProxyRequest) {
    // Get an array of all of all the cookies sent by the client
    Cookie[] cookies = httpServletRequest.getCookies();
    if (cookies == null) {
        return;
    }
    
    for (Cookie cookie : cookies) {
        cookie.setDomain(stringProxyHost);
        cookie.setPath(httpServletRequest.getServletPath());
        httpMethodProxyRequest.setRequestHeader(&quot;Cookie&quot;, cookie.getName() +  
                &quot;=&quot; + cookie.getValue() + &quot;; Path=&quot; + cookie.getPath());
    }
}
&lt;/pre&gt;
&lt;p&gt;Next, in the &lt;em&gt;doGet()&lt;/em&gt; and &lt;em&gt;doPost()&lt;/em&gt; methods, add the following line just after the call to &lt;em&gt;setProxyRequestHeaders()&lt;/em&gt;.&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
setProxyRequestCookies(httpServletRequest, getMethodProxyRequest);
&amp;nbsp;
&lt;/pre&gt;
&lt;p&gt;After making these modifications to ProxyServlet, you can create LoginRequest and attempt to authenticate. To detect a failed attempt, I&apos;m looking for text in Spring Security&apos;s &quot;authentication-failure-url&quot; page.
&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
new LoginRequest(new RequestCallback() {

    public void onResponseReceived(Request request, Response response) {
        if (response.getStatusCode() != Response.SC_OK) {
            onError(request, new RequestException(response.getStatusText() + &quot;:\n&quot; + response.getText()));
            return;
        }
        
        if (response.getText().contains(&quot;Access Denied&quot;)) {
            Window.alert(&quot;You have entered an incorrect username or password. Please try again.&quot;);
        } else {
            // authentication worked, show a fancy dashboard screen
        }
    }

    public void onError(Request request, Throwable throwable) {
        Window.alert(throwable.getMessage());
    }
});
&lt;/pre&gt;
&lt;p&gt;After making these changes, you should be able to authenticate with Spring Security&apos;s form-based configuration. While this example doesn&apos;t show how to logout, it should be easy enough to do by 1) deleting the JSESSIONID cookie or 2) calling the Logout URL you have configured in your services WAR.&lt;/p&gt;
&lt;p&gt;Hopefully this howto gives you enough information to configure your GWT application to talk to Spring Security
without modifying your existing backend application. It&apos;s entirely possible that Spring Security offers a more GWT-friendly
authentication mechanism. If you know of a better way to integrate GWT with Spring Security, I&apos;d love to hear about it.&lt;/p&gt;
&lt;p id=&quot;update&quot;&gt;&lt;strong&gt;Update on October 7, 2009&lt;/strong&gt;: I did some additional work on this and got Remember Me working when using form-based authentication. I found I didn&apos;t need as much fancy logic in my ProxyServlet and was able to reduce the &quot;followRequests&quot; logic to the following:
&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
if (followRedirects) {
    if (httpMethodProxyRequest.getResponseHeader(&quot;Set-Cookie&quot;) != null) {
        Header[] headers = httpMethodProxyRequest.getResponseHeaders(&quot;Set-Cookie&quot;);
        if (headers.length == 1) {
            extractCookieFromHeader(httpServletResponse, headers[0]);
        } else {
            // ignore the first header since there always seems two jsessionid headers
            // and the 2nd is the valid one
            for (int i = 1; i &lt; headers.length; i++) {
                extractCookieFromHeader(httpServletResponse, headers[i]);
            }
        }
    }
    httpServletResponse.sendRedirect(
            stringLocation.replace(getProxyHostAndPort() + getProxyPath(), stringMyHostName));
    return;
}
&lt;/pre&gt;
&lt;p&gt;I was also able to remove the &lt;em&gt;setProxyRequestCookies()&lt;/em&gt; method completely as it no longer seems necessary.&lt;/p&gt;
&lt;p&gt;Next, I&apos;d like to figure out how to make Spring Security more Ajax-friendly where it can read an authentication token in the request body or header (instead of from a cookie). Also, it&apos;d be sweet if I could convince it to return error codes instead of the login page (for example, when a certain header is present). </content>
    </entry>
    <entry>
        <id>https://raibledesigns.com/rd/entry/how_to_do_cross_domain</id>
        <title type="html">How to do cross-domain GWT RPC with a ProxyServlet</title>
        <author><name>Matt Raible</name></author>
        <link rel="alternate" type="text/html" href="https://raibledesigns.com/rd/entry/how_to_do_cross_domain"/>
        <published>2009-08-05T16:06:12-06:00</published>
        <updated>2014-10-07T19:59:23-06:00</updated> 
        <category term="/Java" label="Java" />
        <category term="servlet" scheme="http://roller.apache.org/ns/tags/" />
        <category term="java" scheme="http://roller.apache.org/ns/tags/" />
        <category term="rpc" scheme="http://roller.apache.org/ns/tags/" />
        <category term="gwt" scheme="http://roller.apache.org/ns/tags/" />
        <content type="html">Last week, I started working on a new project using GWT. On my &lt;a href=&quot;http://raibledesigns.com/rd/entry/enhancing_evite_com_with_gwt&quot;&gt;last project&lt;/a&gt;, we used GWT &lt;a href=&quot;http://code.google.com/docreader/#p=google-web-toolkit-doc-1-5&amp;s=google-web-toolkit-doc-1-5&amp;t=DevGuideHttp&quot;&gt;HTTP Calls&lt;/a&gt; and my new project is using &lt;a href=&quot;http://code.google.com/docreader/#p=google-web-toolkit-doc-1-5&amp;s=google-web-toolkit-doc-1-5&amp;t=DevGuideRemoteProcedureCalls&quot;&gt;RPC&lt;/a&gt;. We&apos;ll likely migrate to a JSON backend eventually, but in the meantime, I wanted to be able to develop in hosted mode (localhost:8888) and call services on another host (localhost:8080), where the services are running in a JSF/Spring webapp.
&lt;/p&gt;
&lt;p&gt;At first, I thought it&apos;d be easy thanks to the handy-dandy ProxyServlet I mentioned in &lt;a href=&quot;http://raibledesigns.com/rd/entry/implementing_oauth_with_gwt&quot;&gt;Implementing OAuth with GWT&lt;/a&gt;. However, when I tried to hook it in and use it, I saw the following error in my server-side logs.&lt;/p&gt;
&lt;pre&gt;
java.lang.NullPointerException
        at javax.servlet.GenericServlet.getServletName(GenericServlet.java:322)
        at javax.servlet.GenericServlet.log(GenericServlet.java:277)
        at com.google.gwt.user.server.rpc.RemoteServiceServlet.doGetSerializationPolicy(RemoteServiceServlet.java:219)
        at com.google.gwt.user.server.rpc.RemoteServiceServlet.getSerializationPolicy(RemoteServiceServlet.java:117)
        at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader.prepareToRead(ServerSerializationStreamReader.java:429)
        at com.google.gwt.user.server.rpc.RPC.decodeRequest(RPC.java:234)
&lt;/pre&gt;
&lt;p&gt;Looking at RemoteServiceServlet.java:219, there&apos;s a logging call that fails for some reason (at least in my application).
&lt;p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
/*
 * Check that the module path must be in the same web app as the servlet
 * itself. If you need to implement a scheme different than this, override
 * this method.
 */
if (modulePath == null || !modulePath.startsWith(contextPath)) {
  String message = &quot;ERROR: The module path requested, &quot;
      + modulePath
      + &quot;, is not in the same web application as this servlet, &quot;
      + contextPath
      + &quot;.  Your module may not be properly configured or your client and server code maybe out of date.&quot;;
  log(message, null);
}
&lt;/pre&gt;&lt;p&gt;In the above code, you might notice that GWT is checking to make sure the client is hosted in the same application as the server. After I figured this out, it was pretty easy to modify my ProxyServlet to trick GWT RPC into thinking the client was in the same web application. In the ProxyServlet&apos;s &lt;em&gt;handleContentPost&lt;/em&gt; method, I added the following code to replace &quot;localhost:8888/&quot; with &quot;localhost:8080/services/&quot; (in the content of the post to the server).
&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
if (contentType.startsWith(&quot;text/x-gwt-rpc&quot;)) {
    String clientHost = httpServletRequest.getLocalName();
    if (clientHost.equals(&quot;127.0.0.1&quot;)) {
        clientHost = &quot;localhost&quot;;
    }

    int clientPort = httpServletRequest.getLocalPort();
    String clientUrl = clientHost + ((clientPort != 80) ? &quot;:&quot; + 
                       clientPort : &quot;&quot;);
    String serverUrl = stringProxyHost + ((intProxyPort != 80) ? &quot;:&quot; + 
                       intProxyPort : &quot;&quot;) + httpServletRequest.getServletPath();
    postContent = postContent.replace(clientUrl , serverUrl);
}
&lt;/pre&gt;&lt;p&gt;After manipulating the posted content, I was successfully able to use GWT RPC cross-domain. 
&lt;/p&gt;
&lt;p&gt;
&lt;em&gt;Woo hoo!&lt;/em&gt; 
&lt;/p&gt;
&lt;p&gt;
For your convenience, the full &lt;em&gt;handleContentPost()&lt;/em&gt; method is listed below.&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
private void handleContentPost(PostMethod postMethodProxyRequest, 
                               HttpServletRequest httpServletRequest) 
            throws IOException, ServletException {
    StringBuilder content = new StringBuilder();
    BufferedReader reader = httpServletRequest.getReader();
    for (;;) {
        String line = reader.readLine();
        if (line == null) break;
        content.append(line);
    }

    String contentType = httpServletRequest.getContentType();
    String postContent = content.toString();

    if (contentType.startsWith(&quot;text/x-gwt-rpc&quot;)) {
        String clientHost = httpServletRequest.getLocalName();
        if (clientHost.equals(&quot;127.0.0.1&quot;)) {
            clientHost = &quot;localhost&quot;;
        }

        int clientPort = httpServletRequest.getLocalPort();
        String clientUrl = clientHost + ((clientPort != 80) ? &quot;:&quot; + 
                           clientPort : &quot;&quot;);
        String serverUrl = stringProxyHost + ((intProxyPort != 80) ? &quot;:&quot; + 
                           intProxyPort : &quot;&quot;) + httpServletRequest.getServletPath();
        postContent = postContent.replace(clientUrl , serverUrl);
    }

    String encoding = httpServletRequest.getCharacterEncoding();
    debug(&quot;POST Content Type: &quot; + contentType + &quot; Encoding: &quot; + encoding,
          &quot;Content: &quot; + postContent);
    StringRequestEntity entity;
    try {
        entity = new StringRequestEntity(postContent, contentType, encoding);
    } catch (UnsupportedEncodingException e) {
        throw new ServletException(e);
    }
    // Set the proxy request POST data
    postMethodProxyRequest.setRequestEntity(entity);
}
&lt;/pre&gt;
&lt;a name=&quot;proxyServlet&quot;&gt;&lt;/a&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; In the comments, Ganesh asked for more details, so I figured it&apos;d be a good idea to post the full source code. First of all, &lt;a href=&quot;http://raibledesigns.com/rd/entry/how_to_do_cross_domain#proxyServlet&quot; onclick=&quot;($(&apos;#proxyServletCode&apos;).is(&apos;:visible&apos;) ? $(&apos;#proxyServletCode&apos;).hide() : $(&apos;#proxyServletCode&apos;).show()); return false&quot;&gt;click here&lt;/a&gt; to see the code for the ProxyServlet:
&lt;/p&gt;
&lt;div style=&quot;display: none&quot; id=&quot;proxyServletCode&quot;&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.zip.GZIPInputStream;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.httpclient.methods.multipart.ByteArrayPartSource;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.StringPart;

/**
 * ProxyServlet from http://edwardstx.net/wiki/attach/HttpProxyServlet/ProxyServlet.java
 * (This seems to be a derivative of Noodle -- http://noodle.tigris.org/)
 * 
 * Patched to skip &quot;Transfer-Encoding: chunked&quot; headers, avoid double slashes
 * in proxied URLs, handle GZip and allow GWT RPC.
 */
public class ProxyServlet extends HttpServlet {

    private static final int FOUR_KB = 4196;

    /**
     * Serialization UID.
     */
    private static final long serialVersionUID = 1L;
    /**
     * Key for redirect location header.
     */
    private static final String STRING_LOCATION_HEADER = &quot;Location&quot;;
    /**
     * Key for content type header.
     */
    private static final String STRING_CONTENT_ENGINE_HEADER_NAME = &quot;Content-Type&quot;;
    /**
     * Key for content length header.
     */
    private static final String STRING_CONTENT_LENGTH_HEADER_NAME = &quot;Content-Length&quot;;
    /**
     * Key for host header
     */
    private static final String STRING_HOST_HEADER_NAME = &quot;Host&quot;;
    /**
     * The directory to use to temporarily store uploaded files
     */
    private static final File FILE_UPLOAD_TEMP_DIRECTORY = new File(System.getProperty(&quot;java.io.tmpdir&quot;));

    // Proxy host params
    /**
     * The host to which we are proxying requests. Default value is &quot;localhost&quot;.
     */
    private String stringProxyHost = &quot;localhost&quot;;
    /**
     * The port on the proxy host to wihch we are proxying requests. Default value is 80.
     */
    private int intProxyPort = 80;
    /**
     * The (optional) path on the proxy host to wihch we are proxying requests. Default value is &quot;&quot;.
     */
    private String stringProxyPath = &quot;&quot;;
    /**
     * Setting that allows removing the initial path from client. Allows specifying /twitter/* as synonym for twitter.com.
     */
    private boolean removePrefix;
    /**
     * The maximum size for uploaded files in bytes. Default value is 5MB.
     */
    private int intMaxFileUploadSize = 5 * 1024 * 1024;
    private boolean isSecure;
    private boolean followRedirects;

    /**
     * Initialize the &amp;lt;code&amp;gt;ProxyServlet&amp;lt;/code&amp;gt;
     * @param servletConfig The Servlet configuration passed in by the servlet container
     */
    public void init(ServletConfig servletConfig) {
        // Get the proxy host
        String stringProxyHostNew = servletConfig.getInitParameter(&quot;proxyHost&quot;);
        if (stringProxyHostNew == null || stringProxyHostNew.length() == 0) {
            throw new IllegalArgumentException(&quot;Proxy host not set, please set init-param &apos;proxyHost&apos; in web.xml&quot;);
        }
        this.setProxyHost(stringProxyHostNew);
        // Get the proxy port if specified
        String stringProxyPortNew = servletConfig.getInitParameter(&quot;proxyPort&quot;);
        if (stringProxyPortNew != null &amp;amp;&amp;amp; stringProxyPortNew.length() &amp;gt; 0) {
            this.setProxyPort(Integer.parseInt(stringProxyPortNew));
        }
        // Get the proxy path if specified
        String stringProxyPathNew = servletConfig.getInitParameter(&quot;proxyPath&quot;);
        if (stringProxyPathNew != null &amp;amp;&amp;amp; stringProxyPathNew.length() &amp;gt; 0) {
            this.setProxyPath(stringProxyPathNew);
        }
        // Get the maximum file upload size if specified
        String stringMaxFileUploadSize = servletConfig.getInitParameter(&quot;maxFileUploadSize&quot;);
        if (stringMaxFileUploadSize != null &amp;amp;&amp;amp; stringMaxFileUploadSize.length() &amp;gt; 0) {
            this.setMaxFileUploadSize(Integer.parseInt(stringMaxFileUploadSize));
        }
    }

    /**
     * Performs an HTTP GET request
     * @param httpServletRequest The {@link HttpServletRequest} object passed
     *                            in by the servlet engine representing the
     *                            client request to be proxied
     * @param httpServletResponse The {@link HttpServletResponse} object by which
     *                             we can send a proxied response to the client
     */
    public void doGet(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
            throws IOException, ServletException {
        // Create a GET request
        String destinationUrl = this.getProxyURL(httpServletRequest);
        debug(&quot;GET Request URL: &quot; + httpServletRequest.getRequestURL(),
              &quot;Destination URL: &quot; + destinationUrl);
        GetMethod getMethodProxyRequest = new GetMethod(destinationUrl);
        // Forward the request headers
        setProxyRequestHeaders(httpServletRequest, getMethodProxyRequest);
        setProxyRequestCookies(httpServletRequest, getMethodProxyRequest);
        // Execute the proxy request
        this.executeProxyRequest(getMethodProxyRequest, httpServletRequest, httpServletResponse);
    }

    /**
     * Performs an HTTP POST request
     * @param httpServletRequest The {@link HttpServletRequest} object passed
     *                            in by the servlet engine representing the
     *                            client request to be proxied
     * @param httpServletResponse The {@link HttpServletResponse} object by which
     *                             we can send a proxied response to the client
     */
    public void doPost(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
            throws IOException, ServletException {
        // Create a standard POST request
        String contentType = httpServletRequest.getContentType();
        String destinationUrl = this.getProxyURL(httpServletRequest);
        debug(&quot;POST Request URL: &quot; + httpServletRequest.getRequestURL(),
              &quot;    Content Type: &quot; + contentType,
              &quot; Destination URL: &quot; + destinationUrl);
        PostMethod postMethodProxyRequest = new PostMethod(destinationUrl);
        // Forward the request headers
        setProxyRequestHeaders(httpServletRequest, postMethodProxyRequest);
        setProxyRequestCookies(httpServletRequest, postMethodProxyRequest);
        // Check if this is a mulitpart (file upload) POST
        if (ServletFileUpload.isMultipartContent(httpServletRequest)) {
            this.handleMultipartPost(postMethodProxyRequest, httpServletRequest);
        } else {
            if (contentType == null || PostMethod.FORM_URL_ENCODED_CONTENT_ENGINE.equals(contentType)) {
                this.handleStandardPost(postMethodProxyRequest, httpServletRequest);
            } else {
                this.handleContentPost(postMethodProxyRequest, httpServletRequest);
            }
        }
        // Execute the proxy request
        this.executeProxyRequest(postMethodProxyRequest, httpServletRequest, httpServletResponse);
    }

    /**
     * Sets up the given {@link PostMethod} to send the same multipart POST
     * data as was sent in the given {@link HttpServletRequest}
     * @param postMethodProxyRequest The {@link PostMethod} that we are
     *                                configuring to send a multipart POST request
     * @param httpServletRequest The {@link HttpServletRequest} that contains
     *                            the mutlipart POST data to be sent via the {@link PostMethod}
     */
    @SuppressWarnings(&quot;unchecked&quot;)
    private void handleMultipartPost(PostMethod postMethodProxyRequest, HttpServletRequest httpServletRequest)
            throws ServletException {
        // Create a factory for disk-based file items
        DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
        // Set factory constraints
        diskFileItemFactory.setSizeThreshold(this.getMaxFileUploadSize());
        diskFileItemFactory.setRepository(FILE_UPLOAD_TEMP_DIRECTORY);
        // Create a new file upload handler
        ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory);
        // Parse the request
        try {
            // Get the multipart items as a list
            List&amp;lt;FileItem&amp;gt; listFileItems = (List&amp;lt;FileItem&amp;gt;) servletFileUpload.parseRequest(httpServletRequest);
            // Create a list to hold all of the parts
            List&amp;lt;Part&amp;gt; listParts = new ArrayList&amp;lt;Part&amp;gt;();
            // Iterate the multipart items list
            for (FileItem fileItemCurrent : listFileItems) {
                // If the current item is a form field, then create a string part
                if (fileItemCurrent.isFormField()) {
                    StringPart stringPart = new StringPart(
                            fileItemCurrent.getFieldName(), // The field name
                            fileItemCurrent.getString() // The field value
                            );
                    // Add the part to the list
                    listParts.add(stringPart);
                } else {
                    // The item is a file upload, so we create a FilePart
                    FilePart filePart = new FilePart(
                            fileItemCurrent.getFieldName(), // The field name
                            new ByteArrayPartSource(
                            fileItemCurrent.getName(), // The uploaded file name
                            fileItemCurrent.get() // The uploaded file contents
                            ));
                    // Add the part to the list
                    listParts.add(filePart);
                }
            }
            MultipartRequestEntity multipartRequestEntity = new MultipartRequestEntity(
                    listParts.toArray(new Part&amp;#91;&amp;#93;{}),
                    postMethodProxyRequest.getParams());
            postMethodProxyRequest.setRequestEntity(multipartRequestEntity);
            // The current content-type header (received from the client) IS of
            // type &quot;multipart/form-data&quot;, but the content-type header also
            // contains the chunk boundary string of the chunks. Currently, this
            // header is using the boundary of the client request, since we
            // blindly copied all headers from the client request to the proxy
            // request. However, we are creating a new request with a new chunk
            // boundary string, so it is necessary that we re-set the
            // content-type string to reflect the new chunk boundary string
            postMethodProxyRequest.setRequestHeader(STRING_CONTENT_ENGINE_HEADER_NAME, multipartRequestEntity.getContentType());
        } catch (FileUploadException fileUploadException) {
            throw new ServletException(fileUploadException);
        }
    }

    /**
     * Sets up the given {@link PostMethod} to send the same standard POST
     * data as was sent in the given {@link HttpServletRequest}
     * @param postMethodProxyRequest The {@link PostMethod} that we are
     *                                configuring to send a standard POST request
     * @param httpServletRequest The {@link HttpServletRequest} that contains
     *                            the POST data to be sent via the {@link PostMethod}
     */
    @SuppressWarnings(&quot;unchecked&quot;)
    private void handleStandardPost(PostMethod postMethodProxyRequest, HttpServletRequest httpServletRequest) {
        // Get the client POST data as a Map
        Map&amp;lt;String, String&amp;#91;&amp;#93;&amp;gt; mapPostParameters = (Map&amp;lt;String, String&amp;#91;&amp;#93;&amp;gt;) httpServletRequest.getParameterMap();
        // Create a List to hold the NameValuePairs to be passed to the PostMethod
        List&amp;lt;NameValuePair&amp;gt; listNameValuePairs = new ArrayList&amp;lt;NameValuePair&amp;gt;();
        // Iterate the parameter names
        for (String stringParameterName : mapPostParameters.keySet()) {
            // Iterate the values for each parameter name
            String&amp;#91;&amp;#93; stringArrayParameterValues = mapPostParameters.get(stringParameterName);
            for (String stringParamterValue : stringArrayParameterValues) {
                // Create a NameValuePair and store in list
                NameValuePair nameValuePair = new NameValuePair(stringParameterName, stringParamterValue);
                listNameValuePairs.add(nameValuePair);
            }
        }
        // Set the proxy request POST data
        postMethodProxyRequest.setRequestBody(listNameValuePairs.toArray(new NameValuePair&amp;#91;&amp;#93;{}));
    }

    /**
     * Sets up the given {@link PostMethod} to send the same content POST
     * data (JSON, XML, etc.) as was sent in the given {@link HttpServletRequest}
     * @param postMethodProxyRequest The {@link PostMethod} that we are
     *                                configuring to send a standard POST request
     * @param httpServletRequest The {@link HttpServletRequest} that contains
     *                            the POST data to be sent via the {@link PostMethod}
     */
    private void handleContentPost(PostMethod postMethodProxyRequest, HttpServletRequest httpServletRequest) throws IOException, ServletException {
        StringBuilder content = new StringBuilder();
        BufferedReader reader = httpServletRequest.getReader();
        for (;;) {
            String line = reader.readLine();
            if (line == null) break;
            content.append(line);
        }

        String contentType = httpServletRequest.getContentType();
        String postContent = content.toString();

        if (contentType.startsWith(&quot;text/x-gwt-rpc&quot;)) {
            String clientHost = httpServletRequest.getLocalName();
            if (clientHost.equals(&quot;127.0.0.1&quot;)) {
                clientHost = &quot;localhost&quot;;
            }

            int clientPort = httpServletRequest.getLocalPort();
            String clientUrl = clientHost + ((clientPort != 80) ? &quot;:&quot; + clientPort : &quot;&quot;);
            String serverUrl = stringProxyHost + ((intProxyPort != 80) ? &quot;:&quot; + intProxyPort : &quot;&quot;) + httpServletRequest.getServletPath();
            //debug(&quot;Replacing client (&quot; + clientUrl + &quot;) with server (&quot; + serverUrl + &quot;)&quot;);
            postContent = postContent.replace(clientUrl , serverUrl);
        }

        String encoding = httpServletRequest.getCharacterEncoding();
        debug(&quot;POST Content Type: &quot; + contentType + &quot; Encoding: &quot; + encoding,
              &quot;Content: &quot; + postContent);
        StringRequestEntity entity;
        try {
            entity = new StringRequestEntity(postContent, contentType, encoding);
        } catch (UnsupportedEncodingException e) {
            throw new ServletException(e);
        }
        // Set the proxy request POST data
        postMethodProxyRequest.setRequestEntity(entity);
    }

    /**
     * Executes the {@link HttpMethod} passed in and sends the proxy response
     * back to the client via the given {@link HttpServletResponse}
     * @param httpMethodProxyRequest An object representing the proxy request to be made
     * @param httpServletResponse An object by which we can send the proxied
     *                             response back to the client
     * @throws IOException Can be thrown by the {@link HttpClient}.executeMethod
     * @throws ServletException Can be thrown to indicate that another error has occurred
     */
    private void executeProxyRequest(
            HttpMethod httpMethodProxyRequest,
            HttpServletRequest httpServletRequest,
            HttpServletResponse httpServletResponse)
            throws IOException, ServletException {
        // Create a default HttpClient
        HttpClient httpClient = new HttpClient();
        httpMethodProxyRequest.setFollowRedirects(false);
        // Execute the request
        int intProxyResponseCode = httpClient.executeMethod(httpMethodProxyRequest);
        String response = httpMethodProxyRequest.getResponseBodyAsString();

        // Check if the proxy response is a redirect
        // The following code is adapted from org.tigris.noodle.filters.CheckForRedirect
        // Hooray for open source software
        if (intProxyResponseCode &amp;gt;= HttpServletResponse.SC_MULTIPLE_CHOICES /* 300 */ &amp;amp;&amp;amp; intProxyResponseCode &amp;lt; HttpServletResponse.SC_NOT_MODIFIED /* 304 */) {
            String stringStatusCode = Integer.toString(intProxyResponseCode);
            String stringLocation = httpMethodProxyRequest.getResponseHeader(STRING_LOCATION_HEADER).getValue();
            if (stringLocation == null) {
                throw new ServletException(&quot;Received status code: &quot; + stringStatusCode + &quot; but no &quot; + STRING_LOCATION_HEADER + &quot; header was found in the response&quot;);
            }
            // Modify the redirect to go to this proxy servlet rather that the proxied host
            String stringMyHostName = httpServletRequest.getServerName();
            if (httpServletRequest.getServerPort() != 80) {
                stringMyHostName += &quot;:&quot; + httpServletRequest.getServerPort();
            }
            stringMyHostName += httpServletRequest.getContextPath();
            if (followRedirects) {
                if (stringLocation.contains(&quot;jsessionid&quot;)) {
                    Cookie cookie = new Cookie(&quot;JSESSIONID&quot;, stringLocation.substring(stringLocation.indexOf(&quot;jsessionid=&quot;) + 11));
                    cookie.setPath(&quot;/&quot;);
                    httpServletResponse.addCookie(cookie);
                    //debug(&quot;redirecting: set jessionid (&quot; + cookie.getValue() + &quot;) cookie from URL&quot;);
                } else if (httpMethodProxyRequest.getResponseHeader(&quot;Set-Cookie&quot;) != null) {
	                Header header = httpMethodProxyRequest.getResponseHeader(&quot;Set-Cookie&quot;);
                    String&amp;#91;&amp;#93; cookieDetails = header.getValue().split(&quot;;&quot;);
					String&amp;#91;&amp;#93; nameValue = cookieDetails&amp;#91;0&amp;#93;.split(&quot;=&quot;);

					Cookie cookie = new Cookie(nameValue&amp;#91;0&amp;#93;, nameValue&amp;#91;1&amp;#93;);
					cookie.setPath(&quot;/&quot;);
					//debug(&quot;redirecting: setting cookie: &quot; + cookie.getName() + &quot;:&quot; + cookie.getValue() + &quot; on &quot; + cookie.getPath());
					httpServletResponse.addCookie(cookie);
                }
                httpServletResponse.sendRedirect(stringLocation.replace(getProxyHostAndPort() + this.getProxyPath(), stringMyHostName));
                return;
            }
        } else if (intProxyResponseCode == HttpServletResponse.SC_NOT_MODIFIED) {
            // 304 needs special handling.  See:
            // http://www.ics.uci.edu/pub/ietf/http/rfc1945.html#Code304
            // We get a 304 whenever passed an &apos;If-Modified-Since&apos;
            // header and the data on disk has not changed; server
            // responds w/ a 304 saying I&apos;m not going to send the
            // body because the file has not changed.
            httpServletResponse.setIntHeader(STRING_CONTENT_LENGTH_HEADER_NAME, 0);
            httpServletResponse.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
            return;
        }

        // Pass the response code back to the client
        httpServletResponse.setStatus(intProxyResponseCode);

        // Pass response headers back to the client
        Header&amp;#91;&amp;#93; headerArrayResponse = httpMethodProxyRequest.getResponseHeaders();
        for (Header header : headerArrayResponse) {
            if (header.getName().equals(&quot;Transfer-Encoding&quot;) &amp;amp;&amp;amp; header.getValue().equals(&quot;chunked&quot;) ||
                    header.getName().equals(&quot;Content-Encoding&quot;) &amp;amp;&amp;amp; header.getValue().equals(&quot;gzip&quot;) || // don&apos;t copy gzip header
                    header.getName().equals(&quot;WWW-Authenticate&quot;)) { // don&apos;t copy WWW-Authenticate header so browser doesn&apos;t prompt on failed basic auth
                // proxy servlet does not support chunked encoding
            } else {
                httpServletResponse.setHeader(header.getName(), header.getValue());
            }
        }

        List&amp;lt;Header&amp;gt; responseHeaders = Arrays.asList(headerArrayResponse);

        if (isBodyParameterGzipped(responseHeaders)) {
            debug(&quot;GZipped: true&quot;);
            if (!followRedirects &amp;amp;&amp;amp; intProxyResponseCode == HttpServletResponse.SC_MOVED_TEMPORARILY) {
                response = httpMethodProxyRequest.getResponseHeader(STRING_LOCATION_HEADER).getValue();
                httpServletResponse.setStatus(HttpServletResponse.SC_OK);
                intProxyResponseCode = HttpServletResponse.SC_OK;
                httpServletResponse.setHeader(STRING_LOCATION_HEADER, response);
            } else {
                response = new String(ungzip(httpMethodProxyRequest.getResponseBody()));
            }
            httpServletResponse.setContentLength(response.length());
        }

        // Send the content to the client
        debug(&quot;Received status code: &quot; + intProxyResponseCode,
              &quot;Response: &quot; + response);

        httpServletResponse.getWriter().write(response);
    }


    /**
     * The response body will be assumed to be gzipped if the GZIP header has been set.
     *
     * @param responseHeaders of response headers
     * @return true if the body is gzipped
     */
    private boolean isBodyParameterGzipped(List&amp;lt;Header&amp;gt; responseHeaders) {
        for (Header header : responseHeaders) {
            if (header.getValue().equals(&quot;gzip&quot;)) {
                return true;
            }
        }
        return false;
    }

    /**
     * A highly performant ungzip implementation. Do not refactor this without taking new timings.
     * See ElementTest in ehcache for timings
     *
     * @param gzipped the gzipped content
     * @return an ungzipped byte&amp;#91;&amp;#93;
     * @throws java.io.IOException when something bad happens
     */
    private byte&amp;#91;&amp;#93; ungzip(final byte&amp;#91;&amp;#93; gzipped) throws IOException {
        final GZIPInputStream inputStream = new GZIPInputStream(new ByteArrayInputStream(gzipped));
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(gzipped.length);
        final byte&amp;#91;&amp;#93; buffer = new byte&amp;#91;FOUR_KB&amp;#93;;
        int bytesRead = 0;
        while (bytesRead != -1) {
            bytesRead = inputStream.read(buffer, 0, FOUR_KB);
            if (bytesRead != -1) {
                byteArrayOutputStream.write(buffer, 0, bytesRead);
            }
        }
        byte&amp;#91;&amp;#93; ungzipped = byteArrayOutputStream.toByteArray();
        inputStream.close();
        byteArrayOutputStream.close();
        return ungzipped;
    }

    public String getServletInfo() {
        return &quot;GWT Proxy Servlet&quot;;
    }

    /**
     * Retrieves all of the headers from the servlet request and sets them on
     * the proxy request
     *
     * @param httpServletRequest The request object representing the client&apos;s
     *                            request to the servlet engine
     * @param httpMethodProxyRequest The request that we are about to send to
     *                                the proxy host
     */
    @SuppressWarnings(&quot;unchecked&quot;)
    private void setProxyRequestHeaders(HttpServletRequest httpServletRequest, HttpMethod httpMethodProxyRequest) {
        // Get an Enumeration of all of the header names sent by the client
        Enumeration enumerationOfHeaderNames = httpServletRequest.getHeaderNames();
        while (enumerationOfHeaderNames.hasMoreElements()) {
            String stringHeaderName = (String) enumerationOfHeaderNames.nextElement();
            if (stringHeaderName.equalsIgnoreCase(STRING_CONTENT_LENGTH_HEADER_NAME)) {
                continue;
            }
            // As per the Java Servlet API 2.5 documentation:
            //		Some headers, such as Accept-Language can be sent by clients
            //		as several headers each with a different value rather than
            //		sending the header as a comma separated list.
            // Thus, we get an Enumeration of the header values sent by the client
            Enumeration enumerationOfHeaderValues = httpServletRequest.getHeaders(stringHeaderName);
            while (enumerationOfHeaderValues.hasMoreElements()) {
                String stringHeaderValue = (String) enumerationOfHeaderValues.nextElement();
                // In case the proxy host is running multiple virtual servers,
                // rewrite the Host header to ensure that we get content from
                // the correct virtual server
                if (stringHeaderName.equalsIgnoreCase(STRING_HOST_HEADER_NAME)) {
                    stringHeaderValue = getProxyHostAndPort();
                }
                Header header = new Header(stringHeaderName, stringHeaderValue);
                // Set the same header on the proxy request
                httpMethodProxyRequest.setRequestHeader(header);
            }
        }
    }

    /**
     * Retrieves all of the cookies from the servlet request and sets them on
     * the proxy request
     *
     * @param httpServletRequest The request object representing the client&apos;s
     *                            request to the servlet engine
     * @param httpMethodProxyRequest The request that we are about to send to
     *                                the proxy host
     */
    @SuppressWarnings(&quot;unchecked&quot;)
    private void setProxyRequestCookies(HttpServletRequest httpServletRequest, HttpMethod httpMethodProxyRequest) {
        // Get an array of all of all the cookies sent by the client
        Cookie&amp;#91;&amp;#93; cookies = httpServletRequest.getCookies();
        if (cookies == null) {
            return;
        }

        for (Cookie cookie : cookies) {
            cookie.setDomain(stringProxyHost);
            cookie.setPath(httpServletRequest.getServletPath());
            httpMethodProxyRequest.setRequestHeader(&quot;Cookie&quot;, cookie.getName() + &quot;=&quot; + cookie.getValue() + &quot;; Path=&quot; + cookie.getPath());
        }
    }

    // Accessors
    private String getProxyURL(HttpServletRequest httpServletRequest) {
        // Set the protocol to HTTP
        String protocol = (isSecure) ? &quot;https://&quot; : &quot;http://&quot;;
        String stringProxyURL = protocol + this.getProxyHostAndPort();

        // simply use whatever servlet path that was part of the request as opposed to getting a preset/configurable proxy path
        if (!removePrefix) {
            stringProxyURL += httpServletRequest.getServletPath();
        }
        stringProxyURL += &quot;/&quot;;
        
        // Handle the path given to the servlet
        String pathInfo = httpServletRequest.getPathInfo();
        if (pathInfo != null &amp;amp;&amp;amp; pathInfo.startsWith(&quot;/&quot;)) {
            if (stringProxyURL != null &amp;amp;&amp;amp; stringProxyURL.endsWith(&quot;/&quot;)) {
                // avoid double &apos;/&apos;
                stringProxyURL += pathInfo.substring(1);
            }
        } else {
            stringProxyURL += httpServletRequest.getPathInfo();
        }
        // Handle the query string
        if (httpServletRequest.getQueryString() != null) {
            stringProxyURL += &quot;?&quot; + httpServletRequest.getQueryString();
        }
        
        return stringProxyURL;
    }

    private String getProxyHostAndPort() {
        if (this.getProxyPort() == 80) {
            return this.getProxyHost();
        } else {
            return this.getProxyHost() + &quot;:&quot; + this.getProxyPort();
        }
    }

    protected String getProxyHost() {
        return this.stringProxyHost;
    }

    protected void setProxyHost(String stringProxyHostNew) {
        this.stringProxyHost = stringProxyHostNew;
    }

    protected int getProxyPort() {
        return this.intProxyPort;
    }

    protected void setSecure(boolean secure) {
        this.isSecure = secure;
    }
    
    protected void setFollowRedirects(boolean followRedirects) {
        this.followRedirects = followRedirects;
    }

    protected void setProxyPort(int intProxyPortNew) {
        this.intProxyPort = intProxyPortNew;
    }

    protected String getProxyPath() {
        return this.stringProxyPath;
    }

    protected void setProxyPath(String stringProxyPathNew) {
        this.stringProxyPath = stringProxyPathNew;
    }

    protected void setRemovePrefix(boolean removePrefix) {
        this.removePrefix = removePrefix;
    }

    protected int getMaxFileUploadSize() {
        return this.intMaxFileUploadSize;
    }

    protected void setMaxFileUploadSize(int intMaxFileUploadSizeNew) {
        this.intMaxFileUploadSize = intMaxFileUploadSizeNew;
    }

    private void debug(String ... msg) {
        for (String m : msg) {
            System.out.println(&quot;&amp;#91;DEBUG&amp;#93; &quot; + m);
        }
    }
}
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;I generally subclass ProxyServlet to provide my own configuration:&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
public class MyProxyServlet extends ProxyServlet {

    @Override
    public void init(ServletConfig servletConfig) {
        setFollowRedirects(true);
        setRemovePrefix(false);
        setProxyPort(8080);
    }
}
&lt;/pre&gt;
&lt;p&gt;Here&apos;s another example that reads configuration settings from web.xml and proxies to a different domain name:&lt;/p&gt;
&lt;pre class=&quot;brush: java&quot;&gt;
public class AlternateHostProxyServlet extends ProxyServlet {

    @Override
    public void init(ServletConfig servletConfig) {

        setProxyHost(servletConfig.getInitParameter(&quot;proxyHost&quot;));

        String secure = servletConfig.getInitParameter(&quot;secure&quot;);
        if (secure != null) {
            setSecure(Boolean.valueOf(secure));
        }

        setFollowRedirects(false);
        setRemovePrefix(true);
        setProxyPort(80);
    }
}
&lt;/pre&gt;
&lt;p&gt;After you&apos;ve added these to your project, simply map the servlet (and its path) in your *.gwt.xml file (if you&apos;re using GWT) and your web.xml.</content>
    </entry>
</feed>

