Matt RaibleMatt Raible is a Web Developer and Java Champion. Connect with him on LinkedIn.

The Angular Mini-Book The Angular Mini-Book is a guide to getting started with Angular. You'll learn how to develop a bare-bones application, test it, and deploy it. Then you'll move on to adding Bootstrap, Angular Material, continuous integration, and authentication.

Spring Boot is a popular framework for building REST APIs. You'll learn how to integrate Angular with Spring Boot and use security best practices like HTTPS and a content security policy.

For book updates, follow @angular_book on Twitter.

The JHipster Mini-Book The JHipster Mini-Book is a guide to getting started with hip technologies today: Angular, Bootstrap, and Spring Boot. All of these frameworks are wrapped up in an easy-to-use project called JHipster.

This book shows you how to build an app with JHipster, and guides you through the plethora of tools, techniques and options you can use. Furthermore, it explains the UI and API building blocks so you understand the underpinnings of your great application.

For book updates, follow @jhipster-book on Twitter.

10+ YEARS


Over 10 years ago, I wrote my first blog post. Since then, I've authored books, had kids, traveled the world, found Trish and blogged about it all.

The Digital Diet

Digital Diet I don't remember the exact cause of my desire to throw the boy in the lake, but I remember the aftermath well. He did something that inspired me to rip off my shirt, empty my pockets and rush after him to give him a good solid dunking in the cold Montana water. It was obvious he deserved it and there was lots of laughs afterward that proved it was a good idea on my part. Shortly after getting dressed and refilling my pockets, I walked to my car to grab something out of the back. As I pulled my keys out to unlock it with the remote, I realized my keys were wet. This made me double-check the contents of my pockets and realize my iPhone was wet too. Apparently, when I emptied my pockets, all I'd removed was my wallet. The date was July 2nd, 2011.

It took me a couple days to confirm my iPhone was hosed. It was an iPhone 4 that I got on the day it was released, on the night before I left for my Montana Summer Vacation last year. It was also the night I met the lovely Trish McGinity. Since I knew the iPhone 5 was coming out in the next couple of months, I decided that if Apple wouldn't replace it, I'd go without a phone until the new iPhone came out. When I arrived back in Denver, I took it to the Apple Store and they confirmed it had "liquid damage." They said they'd replace it for $200. This was $200 less than I expected, but I decided to stay firm with my no-phone plan.

On that same trip in Montana, I decided to leave my car at The Cabin since I ride my bike to work everyday.

So I spent the last 2 months without a phone and without a car. I know, it's definitely a First World Problem. Poor me, I had to live without my Cadillac and iPhone for a whole 60 days. Regardless, I believe it was an interesting experiment and one that many technologists like myself might enjoy (or find it impossible to do).

It was interesting reverting to "old times" (the 90s) where you had to setup meeting times/places with friends and hope everyone met up on time. It was awesome that no one could contact me immediately and I could go about my day with little interruption. I found I had a lot more time because I wasn't constantly checking my Twitter or Facebook for updates. I found I had less stress because I never felt the urge to check for new emails.

Other than that, my life didn't change a whole lot. I rode my bike a lot more because of the no-car situation, logging 251 miles in July (with 2 weeks off for vacation) and 461 miles in August. I used Gmail to make phone calls, and checked my voice mail once a week to see if I was missing anything. I was able to plug in my water-logged phone and retrieve text messages every week or two (it'd stay on for about 30 seconds before dying again).

My parents arrived from Montana with my truck last night and I re-activated my iPhone 3G before driving to meet them in the mountains. I'm thankful for having my luxury items back, but I also realize that I shouldn't check my phone as much as I used to.

It wasn't until after I started this experiment that I discovered The Digital Diet and connected with the idea that Technology Is The New Smoking. I hope to stop being that guy that brings out his phone and checks for updates at family gatherings, when talking to my kids or when having lunch with friends. If you're addicted to your smart phone, I encourage you to try The Digital Diet. It's not that hard and there seems to be far more benefits than consequences.

Posted in General at Sep 02 2011, 04:14:46 PM MDT 1 Comment

Oregon, Cape Cod and Fun in Winter Park

Silver Falls State Park I'm proud to say it's been almost a month since my last blog entry. This can only mean one thing → I've been having too much fun to blog. The good news is I finally found time to write about our recent adventures, so grab yourself a tasty beverage and read on.

Within hours of my last blog post, Trish and I hopped on a plane to visit my good friends, Clint and Autumn, in Oregon. We flew into Portland and spent the weekend hiking in Silver Falls and drinking good beer at the Oregon Brewers Festival and Edgefield. Edgefield is one my favorite places on Earth, encapsulating a farm/resort environment with 27 different features. The features range from a soaking pool to a winery to a pool hall to scotch tasting in a shack next to a garden.

Thirsty Field of Flowers

The weekend was especially fun since Autumn was pregnant and it was two weeks before the due date of their first child. Broderick Jordan Wilburn Foster was born yesterday and I hear the whole family is doing well.

Trish and I spent the night at Edgefield on Saturday and caught an early flight from Portland to Boston the next morning. Her brother (also named Matt) lives near Boston and we drove our rental Volvo convertible (to fit in with the New Englanders) to his house Sunday night. On the Hertz Bus from the airport met Nigel Parry, a professional photographer. Not just anybody, but someone that's shot pictures of some very famous people. Trish is also a photographer, so they had a long conversation and it inspired her to resign from her sales job last week. Actually, it wasn't that meeting, she's been wanting to be a famous photographer since she was in high school. Now that dream shall happen. She's awesome.

On Monday afternoon, we picked up Abbie and Jack from the Boston airport. They flew up from West Palm Beach on their first unaccompanied minors flight. We drove them to Cape Cod and settled into Trish's parents house near the water. The rest of the week, we had a blast with our friends Chris and Julie and the entire McGinity clan. We boated, grilled, flew down the water slides, played miniature golf and toured Martha's Vineyard. We had a pool party, played some pool and sang our hearts out at karaoke. Trish's brother's kids are the same age as mine, so you couldn't wipe the smiles off our kids' faces, especially when we had the tunes cranked with the top down on the convertible.

Wheeee!! Hercules! Michael and Jack

Monkey Shadows

For more pictures of this super-fun trip, see Summer Fun in Oregon and Cape Cod part 1 and 2.

After returning from Boston, we had a week without kids and lived it to the fullest with a John Butler Trio concert at Red Rocks and the Winter Park Beer Festival. Trish's friend Joanna and my good friend, "The Professor" joined us for a weekend of mountain biking and sweet mountain views. We even got to hang out with James Ward and his wife Jenny at the beer festival. Sunday, we hiked up the beautiful Columbine Lake Trail. Trish's dog, Sagan (named after Carl) chased sticks and was his crazy self as usual.

Cheers! Crazy Sagan!

We drove back Monday morning to be there for the kids first day of school. They sure start early these days don't they?

First Day of 2nd and 3rd Grade

I rode my bike 168 miles to and from work throughout the following week. The next weekend, we took the kids up to the Ski Shack for their first time. I hired a guy to make queen-size bunk beds for them, so they had a blast in their new roomy beds. On Saturday, we had a garage sale since the previous owners left a bunch of beds and couches and we didn't need all that furniture. The turnout was great and we got to hang out with Jodi and James, Suzie, and The Lamonts. All of these folks are old friends that just happened to be in the mountains for the weekend. One of Trish's best friends, Chris, has a place less than a mile from us and we had a blast with her on Saturday night. Thanks for the fun memories Chris!

On Sunday, we took the kids the Winter Park Base Area where they have Disneyland in the Mountains as I like to call it. An alpine slide, miniature golf, a maze, a climbing wall and a Chili Cook-Off to keep Daddy happy. We met our good friend Suzie there and had a lot of fun with Chris, Brice and their crazy kids. Enjoying an Avery White Rascal at the Cheeky Monk's happy hour was the perfect way to end the day.

Abbie and Jack on the Cabriolet Chili Cook Off! Abbie on the Trampoline

Nice flip Jack!

Sunday night, we were graced with the presence of a beautiful sunset.

Sunset Views from the shack

Happy Kids Sunday Sunset

That pretty much catches you up-to-date with the goings on in my life. As you can tell, it's been a great summer so far. I'm really looking forward to the fall too. Broncos season, a trip to Hawaii, learning new technologies, talking about them at Devoxx and a new gig.

I've decided to leave Overstock because I'm a die-hard Broncos fan and I can't work for a company that supports the Raiders during football season. There's a good chance I'll be back after the season, just in time for The Greatest Snow on Earth. ;)

This weekend, my parents are meeting us in Winter Park for Labor Day weekend. We plan on fishing, hiking, smiling and enjoying each other's company a whole lot. Tomorrow is Jack's birthday. I gotta run ... it's time to go buy a bike, play a little golf, hit the pool and go to the BBQ before tonight's CD Release Party.

Posted in General at Aug 27 2011, 01:50:52 PM MDT 1 Comment

How do you get started in programming?

I recently received the email below from someone asking how he might get started in programming. I think this is a popular topic, especially given the current economic situation in the US (unemployment is high, but not in the tech industry). For that reason, I figured I'd post my response here and allow others to chime in with their advice.

I read about you on LinkedIn, forgive my intrusion. Since you seem like an expert in the field of designing websites I wanted to know your thoughts on switching into this field late in life. I am 41 and looking to make the move from an unrelated field (finance) to programming. So far I have learned HTML, CSS and some Javascript. I have taken classes on C and Java. I have made some basic Android phone apps.

What languages do you think I should focus on? What is the fastest way to get up to speed to make a career of it? Classes? Take a entry level job? Study on my own?

Thanks for any insights….

My reply:

It's interesting that you're switching from finance to programming. I did the same thing early on in my career, but I was fortunate enough to do it in college (I have degrees in Russian, International Business and Finance) and therefore able to audit some CS classes before I graduated.

I think the most valuable skills these days are front-end skills (HTML, CSS and JavaScript). If you can combine those skills with the ability to design websites, you'll go along way. I've taken a different approach where I have excellent front-end skills, but also know a lot about the backend.

While it helps to have a Java background these days, the real sweat spot is the JVM and the containers that run on it like Tomcat and Jetty. A lot of Java developers are learning Groovy and Scala, but unfortunately a lot of their documentation/books are targeted towards Java developers.

The fastest way to get up-to-speed on it is to start your own project (if you can't get a company to hire you to do it). I'd suggest creating a webapp that solves a problem that you have, makes your life easier, etc. If you open source it and build a community around it, that's just as good as working for a company as far as experience goes. Combine this with studying on your own and you can likely come up to speed very quickly.

As a programmer, what advice do you have for someone looking to switch careers, or get into our industry fresh out of college?

Posted in Java at Jul 28 2011, 11:12:09 AM MDT 8 Comments

Another Dream Realized: Mountain Views

A few years back, I developed a few 5-year goals. The first was to build a sauna in my basement, the second was to get a condo in the mountains and the third was to restore my old '66 21-window VW Bus. I finished the sauna earlier this year and I'm proud to say my 2nd goal has been achieved as of last week.

Views from Waterside West in Fraser, CO

As far as the last one, that's still a work in progress. If not this year, hopefully I'll be driving the bus around next spring. In the meantime, it's gonna be one helluva ski season. ;-)

Posted in General at Jul 28 2011, 05:33:21 AM MDT 4 Comments

4th of July Adventures in Montana

Montana is one of my favorite places on earth, especially the The Cabin. Like last year and the year before, I spent a couple weeks at the cabin for this year's 4th of July. Last year was interesting because I met Trish two days before my vacation. This year, she joined me for the 2nd week, along with her Dad. It was also the "90s Reunion" for the Seeley Swan High School, which I attended my freshman and sophomore year.

My parents have lived at the cabin for a little over a year now, having retired there last year. It now looks like it did when we were kids. That is, it feels like home rather than just a cabin in the woods. Their retirement cabin is coming along nicely and the finished bathroom with a toilet + shower provides a nice break from the outhouse and sauna. It was interesting to see all the logging being done on The Homestead. My parents hired someone to clean things up and it looks great so far. They're not clear-cutting, just thinning the forest to reduce fire danger and give more resources to the big trees.

Abbie, Mimi, Jack and Joleen The Logging Operation Happy Kids at Seeley Lake Lookout at Sunset

There were several highlights of the trip, but one of my favorites was our Float in the Swan Valley Parade. Abbie came up with the idea (Happy Birthday America) and we decorated an old trailer with streamers, balloons and a birthday cake to celebrate. Abbie made up a song and sang it throughout the entire parade (with a portable microphone and amp). We all got dunked afterwards, drank some cheap beer and had lots of fun fiddlin' and swinging. The fireworks that night were epic and Trish and I were amazed late night when we smiled on a crowded campfire with many of my parents' old friends.

Abbie singing "Happy Birthday America" Abbie before getting dunked Jack and Joe Fiddlin' Trish

The next day, we woke up, recovered a bit and then headed for Glacier National Park. We spent the afternoon at Big Sky Waterpark and had a great night's rest at Glacier Mountain Lodge. Trish woke up early the next morning for some sunrise shots at Lake McDonald and we enjoyed a short hike to Avalanche Lake that afternoon. Unfortunately, Going-to-the-Sun Road was closed due to snow so we were unable to drive to the top. 2011 is the latest they've ever opened.

Happy Abbie Thirsty Boy Paddleboating Lake McDonald, Glacier National Park

The rest of our vacation was spent doing early morning airport runs, riding the 4-wheeler, golfing and hanging out with old friends. For the trip home, we took the scenic route, driving through Yellowstone and Grand Teton. We stopped along the way to spend the night at Jackson Lake Lodge, shortly after marveling at a Grizzly Bear and her two cubs along the highway. Trish snapped some some magnificent sunset and sunrise photos and we packed up the car for the final journey home. After being gone for 3 weeks, I've never seen the kids happier to cross the Colorado State Line.

As far as vacation destinations go, Montana is one of my favorites. This year was special because I had the pleasure of sharing it with Trish. She's always wanted to photograph Glacier National Park and the Grand Tetons and we were lucky enough to visit both. I also got to share a lot of smiles and memories with old classmates and friends. I experienced the joy of seeing my parents back home, living as Montanans once again and loving every minute of it. I smiled proudly as Abbie thought up the "Happy Birthday America" float and then sang on it the whole way through the parade. I simply loved spending so much time with my family in such a beautiful place.

Finally, I'd like to share a picture montage that Trish put together of the trip. Below that are some of her best shots from our adventures. As always, you can find see my latest photos on Flickr.

The Raible Cabin

Sunrise at Jackson Lake by Trish McGinity Abbie at The Tetons by Trish McGinity

Path to Tetons with wildflowers

Posted in General at Jul 19 2011, 09:24:54 AM MDT 2 Comments

Installing OpenJDK 7 on OS X

Last week, I scanned an article and saw there was a Java 7 Webinar. At first, I thought Java 7 was released, but soon after realized it was a Developer Preview. Unfortunately, the download page doesn't have support for OS X. Since it took me a bit of work to figure out how to install OpenJDK 7 on OS X (I'm running Snow Leopard 10.6.7), I figured I'd write down how I did it.

I started off by downloading "OpenJDK 1.7 universal (32/64 bits) from Mac OS/X branch" from the openjdk-osx-build project's downloads (direct link). After downloading, I installed the dmg as normal.

Update Jan 27, 2012:
After installing the dmg, add the following to your ~/.profile and you should be good to go. Thanks to Mark Beaty for the tip.

function setjdk() { if [ $# -ne 0 ];then export JAVA_HOME=`/usr/libexec/java_home -v $@`; fi; java -version; }

Continue with the instructions below if you don't like this technique for some reason.

I don't use Java Preferences to set my JDK, instead I use David Blevin's handy setjdk script. To make this script work with JDK 7 on OS X, I had to make one minor change. On line 40, I added "Contents" to the path for JAVA_HOME:

export JAVA_HOME=$vmdir/$ver/Contents/Home

Update Jan 27, 2012: You no longer need to make this change.

From there, I had to setup some symlinks so everything would work as expected:

cd /System/Library/Java/JavaVirtualMachines/
sudo ln -s /Library/Java/JavaVirtualMachines/1.7.0.jdk

Update Jan 27, 2012: The latest version installs at a different location so the symlink command above should be changed to:

sudo ln -s /Library/Java/JavaVirtualMachines/1.7.0u.jdk 1.7.0.jdk

Lastly, I had my JAVA_HOME set to "/System/Library/Frameworks/JavaVM.framework/Home". I like the shorter (and seemingly more common) "/Library/Java/Home", so I set it back to that in my ~/.profile:

export JAVA_HOME=/Library/Java/Home

On my system, /Library/Java/Home had a symlink to /System/Library/Frameworks/JavaVM.framework/Home, so I changed it to the CurrentJDK that Java Preferences and setjdk use.

cd /Library/Java
rm Home
ln -s /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Contents/Home

Then I had to add a symlink for 1.7 in the Versions directory.

cd /System/Library/Frameworks/JavaVM.framework/Versions
sudo ln -s /System/Library/Java/JavaVirtualMachines/1.7.0.jdk/Contents 1.7

After making these changes, I was able to switch to JDK 7 easily.

$ setjdk 1.7
Setting this terminal's JDK to 1.7 ... openjdk version "1.7.0-internal"
OpenJDK Runtime Environment (build 1.7.0-internal-b00)
OpenJDK 64-Bit Server VM (build 21.0-b17, mixed mode)

I was also able to switch back to JDK 6.

$ setjdk 1.6
Setting this terminal's JDK to 1.6 ... java version "1.6.0_26"
Java(TM) SE Runtime Environment (build 1.6.0_26-b03-384-10M3425)
Java HotSpot(TM) 64-Bit Server VM (build 20.1-b02-384, mixed mode)

Maven Issues
Next, I tried using JDK 7 to build AppFuse. I ran into two issues when I tried to do this. The first was caused by the native2ascii plugin, which has been known to cause issues on non-Mac platforms. Adding the following profile seemed to solve the problem.

<profile>
    <activation>
        <jdk>1.7</jdk>
    </activation>
    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>native2ascii-maven-plugin</artifactId>
                <dependencies>
                    <dependency>
                      <groupId>com.sun</groupId>
                      <artifactId>tools</artifactId>
                      <version>1.7.0</version>
                      <scope>system</scope>
                      <systemPath>${java.home}/../lib/tools.jar</systemPath>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>
</profile>

The next issue was with Enunciate and its maven-enunciate-cxf-plugin.

[INFO] ------------------------------------------------------------------------
[ERROR] FATAL ERROR
[INFO] ------------------------------------------------------------------------
[INFO] com/sun/mirror/apt/AnnotationProcessorFactory
com.sun.mirror.apt.AnnotationProcessorFactory
[INFO] ------------------------------------------------------------------------
[INFO] Trace
java.lang.NoClassDefFoundError: com/sun/mirror/apt/AnnotationProcessorFactory

It seemed like adding a profile that included tools.jar would solve this, but it doesn't. When I add the dependency directly to the plugin itself, I get the following error:

warning: The apt tool and its associated API are planned to be
removed in the next major JDK release.  These features have been
superseded by javac and the standardized annotation processing API,
javax.annotation.processing and javax.lang.model.  Users are
recommended to migrate to the annotation processing features of
javac; see the javac man page for more information.
[WARNING] Validation result has errors.
error: [core] java.lang.StackTraceElement: A TypeDefinition must have a public no-arg constructor or be annotated with a factory method.
1 error
[INFO] ------------------------------------------------------------------------
[ERROR] BUILD ERROR
[INFO] ------------------------------------------------------------------------

Hopefully this article helps you get started with Java 7 on OS X. If you have any additional tips, please leave a comment.

Posted in Java at Jul 12 2011, 02:11:44 PM MDT 9 Comments

Integrating OAuth with AppFuse and its REST API

One of the new features in AppFuse 2.1 is an appfuse-ws archetype. This archetype leverages Enunciate and CXF 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 SOFEA (a.k.a. modern) applications.

Back in March, Ryan Heaton published a nice article on Securing Web Services 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'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.

Create a New AppFuse WS Project
To begin, I visited the Create AppFuse Archetypes page and created a new application using the "Web Services Only" option in the Web Framework dropdown. Below is the command I used to create the "appfuse-oauth" project.

mvn archetype:generate -B -DarchetypeGroupId=org.appfuse.archetypes \
-DarchetypeArtifactId=appfuse-ws-archetype -DarchetypeVersion=2.1.0 \
-DgroupId=org.appfuse.example -DartifactId=appfuse-oauth 

After doing this, I started the app using mvn jetty:run and confirmed it started OK. At this point, I was able to view the generated documentation for the application at http://localhost:8080. The screenshot below shows what the app looks like at this point.

AppFuse WS Homepage

NOTE: You might notice the REST endpoint of /{username}. This is a bug in AppFuse 2.1.0 and has been fixed in SVN. It does not affect this tutorial.

Integrate Spring Security and OAuth
I originally tried to integrate Spring Security with Enunciate's Securing Web Services Tutorial. However, it only secures endpoints and doesn't do enough filtering for OAuth support, so I ended up using a custom web.xml. I put this file in src/main/resources and loaded it in my enunciate.xml file. I also upgraded Spring Security and imported my security.xml file.

  <?xml version="1.0"?>
  <enunciate xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:noNamespaceSchemaLocation="http://enunciate.codehaus.org/schemas/enunciate-1.22.xsd">
      ...
      <webapp mergeWebXML="src/main/resources/web.xml"/>
      <modules>
      ...
          <spring-app disabled="false" springVersion="3.0.5.RELEASE">
              <springImport uri="classpath:/applicationContext-resources.xml"/>
              <springImport uri="classpath:/applicationContext-dao.xml"/>
              <springImport uri="classpath:/applicationContext-service.xml"/>
              <springImport uri="classpath:/applicationContext.xml"/>
              <springImport uri="classpath:/security.xml"/>
          </spring-app>
      </modules>
  </enunciate>

Then I created src/main/resources/web.xml with a filter for Spring Security and a DispatcherServlet for OAuth support.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0">

    <filter>
        <filter-name>securityFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <init-param>
            <param-name>targetBeanName</param-name>
            <param-value>springSecurityFilterChain</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>securityFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <servlet>
        <servlet-name>appfuse-oauth</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>appfuse-oauth</servlet-name>
        <url-pattern>/oauth/*</url-pattern>
    </servlet-mapping>
</web-app>

Next, I created a src/main/resources/security.xml and used it to secure my API, specify a login page, supply the users and integrate OAuth (see the last 4 beans below).

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:beans="http://www.springframework.org/schema/beans"
             xmlns:oauth="http://www.springframework.org/schema/security/oauth2"
             xsi:schemaLocation="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">

    <http auto-config="true">
        <intercept-url pattern="/api/**" access="ROLE_USER"/>
        <intercept-url pattern="/oauth/**" access="ROLE_USER"/>
        <intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
        <form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?error=true"
                    login-processing-url="/j_security_check"/>
    </http>

    <authentication-manager>
        <authentication-provider>
            <user-service>
                <user name="admin" password="admin" authorities="ROLE_USER,ROLE_ADMIN"/>
                <user name="user" password="user" authorities="ROLE_USER"/>
            </user-service>
        </authentication-provider>
    </authentication-manager>

    <!--hook up the spring security filter chain-->
    <beans:alias name="springSecurityFilterChain" alias="securityFilter"/>

    <beans:bean id="tokenServices"
                class="org.springframework.security.oauth2.provider.token.InMemoryOAuth2ProviderTokenServices">
        <beans:property name="supportRefreshToken" value="true"/>
    </beans:bean>

    <oauth:provider client-details-service-ref="clientDetails" token-services-ref="tokenServices">
        <oauth:verification-code user-approval-page="/oauth/confirm_access"/>
    </oauth:provider>

    <oauth:client-details-service id="clientDetails">
        <!--<oauth:client clientId="my-trusted-client" authorizedGrantTypes="password,authorization_code,refresh_token"/>
        <oauth:client clientId="my-trusted-client-with-secret"
                      authorizedGrantTypes="password,authorization_code,refresh_token" secret="somesecret"/>
        <oauth:client clientId="my-less-trusted-client" authorizedGrantTypes="authorization_code"/>-->
        <oauth:client clientId="ajax-login" authorizedGrantTypes="authorization_code"/>
    </oauth:client-details-service>
</beans:beans>

I used the OAuth for Spring Security sample apps to figure this out. In this example, I used authorizedGrantTypes="authorization_code", but you can see from the commented <oauth:client> elements above that there's a few different options. You should also note that the clientId is hard-coded to "ajax-login", signifying I only want to allow a single application to authenticate.

At this point, I'd like to give a shoutout to Ryan Heaton for creating both Enunciate and Spring Security's OAuth support. Nice work Ryan!

At this point, I needed to do a number of additional tasks to finish integrating oauth. The first was to modify the Jetty Plugin's configuration to 1) run on port 9000, 2) load my custom files and 3) allow jetty:run to recognize Enunciate's generated files. Below is the final configuration in my pom.xml.

<plugin>
    <groupId>org.mortbay.jetty</groupId>
    <artifactId>maven-jetty-plugin</artifactId>
    <version>6.1.26</version>
    <configuration>
        <connectors>
            <connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
                <port>9000</port>
                <maxIdleTime>60000</maxIdleTime>
            </connector>
        </connectors>
        <webAppConfig>
            <baseResource implementation="org.mortbay.resource.ResourceCollection">
                <resourcesAsCSV>
                    ${basedir}/src/main/webapp,
                    ${project.build.directory}/${project.build.finalName}
                </resourcesAsCSV>
            </baseResource>
            <contextPath>/appfuse-oauth</contextPath>
        </webAppConfig>
        <webXml>${project.build.directory}/${project.build.finalName}/WEB-INF/web.xml</webXml>
    </configuration>
</plugin>

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's milestone repo too.

<repository>
    <id>spring-milestone</id>
    <url>http://s3.amazonaws.com/maven.springframework.org/milestone</url>
</repository>
...
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-taglibs</artifactId>
    <version>${spring.version}</version>
    <exclusions>
        <exclusion>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.springframework</groupId>
            <artifactId>spring-support</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.security.oauth</groupId>
    <artifactId>spring-security-oauth</artifactId>
    <version>1.0.0.M3</version>
    <exclusions>
        <exclusion>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>${spring.version}</version>
</dependency>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>servlet-api</artifactId>
    <version>2.5</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
    <version>1.1.2</version>
</dependency>
<dependency>
    <groupId>taglibs</groupId>
    <artifactId>standard</artifactId>
    <version>1.1.2</version>
</dependency>

Since I named my DispatcherServlet "appfuse-oauth" in web.xml, I created a src/main/webapp/WEB-INF/appfuse-oauth-servlet.xml to configure Spring MVC. I had to create the src/main/webapp/WEB-INF directory.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="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">

    <!-- Scans the classpath of this application for @Components to deploy as beans -->
    <context:component-scan base-package="org.appfuse.examples.webapp"/>

    <!-- Configures the @Controller programming model -->
    <mvc:annotation-driven/>

    <!-- Resolves view names to protected .jsp resources within the /WEB-INF/views directory -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
        <property name="prefix" value="/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

In order to show the OAuth confirmation page, I needed to create src/main/java/org/appfuse/examples/webapp/AccessConfirmationController.java and map it to /oauth/confirm_access. I copied this from one of the sample projects and modified to use Spring's annotations.

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("/confirm_access")
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("No client authentication request to authorize.");
        }

        TreeMap<String, Object> model = new TreeMap<String, Object>();
        ClientDetails client = clientDetailsService.loadClientByClientId(clientAuth.getClientId());
        model.put("auth_request", clientAuth);
        model.put("client", client);

        return new ModelAndView("access_confirmation", model);
    }
}

This controller delegates to src/main/webapp/access_confirmation.jsp. I created this file and filled it with code to display Accept and Deny buttons.

<%@ page import="org.springframework.security.core.AuthenticationException" %>
<%@ page import="org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException" %>
<%@ page import="org.springframework.security.oauth2.provider.verification.BasicUserApprovalFilter" %>
<%@ page import="org.springframework.security.oauth2.provider.verification.VerificationCodeFilter" %>
<%@ page import="org.springframework.security.web.WebAttributes" %>
<%@ taglib prefix="authz" uri="http://www.springframework.org/security/tags" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
    <title>Confirm Access</title>
    <link rel="stylesheet" type="text/css" media="all"
          href="http://demo.appfuse.org/appfuse-struts/styles/simplicity/theme.css"/>
    <style type="text/css">
        h1 {
            margin-left: -300px;
            margin-top: 50px
        }
    </style>
</head>

<body>

<h1>Confirm Access</h1>

<div id="content">

    <% if (session.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION) != null && 
                 !(session.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION) instanceof UnapprovedClientAuthenticationException)) { %>
    <div class="error">
        <h2>Woops!</h2>

        <p>Access could not be granted.
            (<%= ((AuthenticationException) session.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION)).getMessage() %>)</p>
    </div>
    <% } %>
    <c:remove scope="session" var="SPRING_SECURITY_LAST_EXCEPTION"/>

    <authz:authorize ifAnyGranted="ROLE_USER,ROLE_ADMIN">
        <h2>Please Confirm</h2>

        <p>You hereby authorize "<c:out value="${client.clientId}" escapeXml="true"/>" to access your protected resources.</p>

        <form id="confirmationForm" name="confirmationForm"
              action="<%=request.getContextPath() + VerificationCodeFilter.DEFAULT_PROCESSING_URL%>" method="POST">
            <input name="<%=BasicUserApprovalFilter.DEFAULT_APPROVAL_REQUEST_PARAMETER%>"
                   value="<%=BasicUserApprovalFilter.DEFAULT_APPROVAL_PARAMETER_VALUE%>" type="hidden"/>
            <label><input name="authorize" value="Authorize" type="submit"></label>
        </form>
        <form id="denialForm" name="denialForm"
              action="<%=request.getContextPath() + VerificationCodeFilter.DEFAULT_PROCESSING_URL%>" method="POST">
            <input name="<%=BasicUserApprovalFilter.DEFAULT_APPROVAL_REQUEST_PARAMETER%>"
                   value="not_<%=BasicUserApprovalFilter.DEFAULT_APPROVAL_PARAMETER_VALUE%>" type="hidden"/>
            <label><input name="deny" value="Deny" type="submit"></label>
        </form>
    </authz:authorize>
</div>
</body>
</html>

Finally, I needed to create src/main/webapp/login.jsp to allow users to login.

<%@ page language="java" pageEncoding="UTF-8" contentType="text/html;charset=utf-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<html>
<head>
    <title>Login</title>
    <link rel="stylesheet" type="text/css" media="all"
          href="http://demo.appfuse.org/appfuse-struts/styles/simplicity/theme.css"/>
    <style type="text/css">
        h1 {
            margin-left: -300px;
            margin-top: 50px
        }
    </style>
</head>
<body>
<h1>Login</h1>

<form method="post" id="loginForm" action="<c:url value='/j_security_check'/>">
    <fieldset style="padding-bottom: 0">
        <ul>
            <c:if test="${param.error != null}">
                <li class="error">
                    ${sessionScope.SPRING_SECURITY_LAST_EXCEPTION.message}
                </li>
            </c:if>
            <li>
                <label for="j_username" class="required desc">
                    Username <span class="req">*</span>
                </label>
                <input type="text" class="text medium" name="j_username"
                       id="j_username" tabindex="1"/>
            </li>

            <li>
                <label for="j_password" class="required desc">
                    Password <span class="req">*</span>
                </label>
                <input type="password" class="text medium" name="j_password"
                       id="j_password" tabindex="2"/>
            </li>
            <li>
                <input type="submit" class="button" name="login" value="Login"
                       tabindex="3"/>
            </li>
        </ul>
    </fieldset>
</form>
</body>
</html>

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'm also considering adding it by default to the next version of AppFuse. Now let's look at integrating OAuth into a client to authenticate and retrieve data from this application.

Authenticate and Retrieve Data with Client
I originally thought my GWT OAuth 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 Ajax Login application I've been using in my recent security tutorials.

In this example, I used OAuth2RestTemplate from Spring Security OAuth. While this works, and works well, I'd still like to get things working with GWT (or jQuery) to demonstrate how to do it from a pure client-side perspective.

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:

<repository>
    <id>spring-milestone</id>
    <url>http://s3.amazonaws.com/maven.springframework.org/milestone</url>
</repository>
...
<dependency>
    <groupId>org.springframework.security.oauth</groupId>
    <artifactId>spring-security-oauth</artifactId>
    <version>1.0.0.M3</version>
    <exclusions>
        <exclusion>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>

Then I modified src/main/webapp/WEB-INF/security.xml and added an OAuth Token Service and defined the location of the OAuth server.

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:beans="http://www.springframework.org/schema/beans"
             xmlns:oauth="http://www.springframework.org/schema/security/oauth2"
             xsi:schemaLocation="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">

...
    <oauth:client token-services-ref="oauth2TokenServices"/>

    <beans:bean id="oauth2TokenServices"
                class="org.springframework.security.oauth2.consumer.token.InMemoryOAuth2ClientTokenServices"/>

    <oauth:resource id="appfuse" type="authorization_code" clientId="ajax-login"
                    accessTokenUri="http://localhost:9000/appfuse-oauth/oauth/authorize"
                    userAuthorizationUri="http://localhost:9000/appfuse-oauth/oauth/user/authorize"/>

Next, I created a Controller that uses OAuth2RestTemplate to make the request and get the data from the AppFuse OAuth application's API. I created src/main/java/org/appfuse/examples/webapp/oauth/UsersApiController.java and filled it with the following code:

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("/appfuse/users")
@Controller
public class UsersApiController {

    private OAuth2RestTemplate apiRestTemplate;
    @Autowired
    private OAuth2ClientTokenServices tokenServices;

    private static final String REMOTE_DATA_URL = "http://localhost:9000/appfuse-oauth/api/users";

    @Autowired
    public UsersApiController(OAuth2ProtectedResourceDetails resourceDetails) {
        this.apiRestTemplate = new OAuth2RestTemplate(resourceDetails);
    }

    @RequestMapping(method = RequestMethod.GET)
    @ResponseBody
    public List<User> getUsers() {
        try {
            List users = apiRestTemplate.getForObject(REMOTE_DATA_URL, List.class);
            return new ArrayList<User>(users);
        } catch (InvalidTokenException badToken) {
            //we've got a bad token, probably because it'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't remove the 'code=' 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);
        }
    }
}

At this point, I thought everything would work and I spent quite some time banging my head against the wall when it didn'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.

<rule>
    <from>/app/**</from>
    <to last="true" type="redirect">%{context-path}/$1</to>
</rule>

To fix this, I added use-query-string="true" to the root element in src/main/webapp/WEB-INF/urlrewrite.xml:

<urlrewrite default-match-type="wildcard" use-query-string="true">

After making all these changes, I ran mvn jetty:run on both apps and opened http://localhost:8080/appfuse/users in my browser. It all worked and a smile crept across my face. I've checked in the client changes into ajax-login on GitHub and the appfuse-oauth example into AppFuse Demos on Google Code. If you'd like to see this example in action, I'd encourage you to checkout both projects and let me know if you find any issues.

Posted in Java at Jul 05 2011, 10:56:48 AM MDT 4 Comments

Agile Hiring Book Review

Agile Hiring While working for Time Warner Cable last year, I experienced a unique challenge: building a team of strong developers in a short amount of time. With the help of colleagues and my network, we were able to hire a team of 10 in 2 months. The reason for our success was largely because we experienced a number of strong applicants and we didn't have to reach beyond a couple Twitter posts to find anyone. I realize we were lucky in our pursuits to find strong people. We didn't have much of an interview process and we relied on friends' recommendations moreso than interviewing and evaluating candidates.

My contract ended with TWC in December. I could've converted to a full-time employee, but my boss recommended against it since it would've been a 50% salary reduction and lots of TWC politics if I moved into management. So I moved onto a great opportunity I had at Overstock.com. I started talking with Overstock shortly before I left for Devoxx and scheduled an interview in mid-December. My interview at Overstock was incredibly enjoyable and I quickly agreed to work for them.

A couple weeks ago, I received a copy of Agile Hiring from Sean Landis, one of the architects at Overstock. Sean was part of the team that interviewed me and joined a few of us for the 2nd part of my interview, a beautiful day of skiing at Snowbird. As I flipped through the pages of Agile Hiring, I realized they might've used some techniques on me, hence the reason I liked the interview so much and agreed to work there.

One of the things I liked best about Agile Hiring is that it emphasizes one of the guiding principles of Agile Development: people over process. It recommends you adjust your interview tactics for each candidate, rather than using a boilerplate approach for all candidates. It stresses the importance of a Tracking System (Overstock uses JIRA) to record notes during the process and notify those involved. One thing that became readily apparent as I read this book: if you want to interview right (and hire strong people), it's going to take a lot of time investment from your company. I don't think this is a bad thing, but it seems like a lot of companies viewing hiring as a nuisance, not an essential part of their business.

I chuckled a bit when I read that Sean recommended not hiring contractors (I am a contractor at Overstock), but loved his honesty when talking about the Google Effect during phone interviews. He also mentions a bunch of real-world issues with candidates, such as the Dunning-Kruger effect or getting candidates to admit they don't know something they've listed on their resumes. Also, he has great advice for getting straight answers from candidates when they keep skirting around the question. He emphasizes ending the interview process as soon as possible when the candidate doesn't fit in order to reduce costs and time investment. This is another agile recommendation: fail fast.

Section 3 on Reviewing Resumes has excruciating detail on what you should look for and how you should perform reviews. It also explains how many resumes will have truth stretching, but shouldn't have outright dishonesty. I think it's great how Sean recognizes the skills section of a resume will typically be stuffed with a list of buzzwords that add very little value. He says "the skills section is written with the purpose of getting the resume through filtering software." He also recognizes that those with certifications don't always have the most knowledge: "Unless you have good reason to act otherwise, treat certifications with skepticism."

The Phone Interview and On-Site Interview sections are great as they emphasize the importance of being a respectful interviewer and doing your best to sell your company. My favorite line is on page 176 when Sean talks about giving candidates a quick tour of your facility.

If you feel embarrassed to give candidates tours, put down this book and figure out why. Some salespeople can sell anything, but the best salespeople are selling products they believe in.

You can tell that folks at Overstock are proud of what they've built, especially when they post recruitment videos like their recent Looking for Java Developers.

The last chapter on Closing the Deal gives some unique insights to the interviewee on how companies might negotiate. Sean recommends giving fair offers, never lowballing and refusing to negotiate on salary (instead using one-time costs like signing bonuses and relocation packages to sweeten the deal).

I think Sean does a great job in showing how you can apply agile principles (people over process, tracking tools, failing fast, continuous improvement, constant feedback) to improve your hiring process. As a person that interviews often, I think it also gives great insights into how companies interview and what they're looking for. I've often thought that being honest about my skills and what I'm looking for is a good tactic and this book seems to confirm that. One things for sure, if I'm ever in the position to hire folks again, I'll have Agile Hiring by my side.

One of the things this book doesn't cover is how to source candidates or advertise positions. If you're looking for advice on that, I think A Vision for the Future of Recruitment: Recruitment 3.0 is a good start. This article has good advice, especially in that it points out the best candidates aren't looking for jobs. Posting on job boards, CV searching, etc. are only going to find candidates that are searching, and it's probably not the best talent pool.

Posted in Java at Jun 30 2011, 08:58:57 AM MDT 1 Comment

A Sweet Trip to Switzerland for Jazoon 2011

I've always enjoyed speaking at conferences, especially those in exotic locations. Earlier this year, I saw Jazoon's call for papers and thought, "I've never been to Switzerland, sounds fun!" I proceeded to submit a talk, got accepted and booked a trip with my girlfriend, Trish. We left Denver last Tuesday and arrived in Zürich on Wednesday around noon. After settling into our awesome accommodations at Hotel Opera, we journeyed to the conference location, which was held at a movie theater.

We were quickly impressed when we learned the conference had reserved a theater for an evening showing of The Hangover 2. We grabbed some drinks from the bar and settled in for a couple hours of giggling and barely staying awake from jetlag.

Zürich quickly made an impression on us the first day. We experienced its excellent transportation system (trains, trams, buses and boats) and saw a number of fancy cars (a Bently, a Lamborghini and a Ferrari). Prices were high, but the food was excellent.

Zürich Airport Sunset in Zürich Zurich Shopping District Ferrari Ferrari

On Thursday, we woke up and Trish realized she'd left her expensive Nikon lenses on our flight from Atlanta to Amsterdam. This realization hit her hard and she scrambled to try and find them by contacting the airlines. I rushed off to the conference to catch Frank Kim's What Every Developer Should Know About Application Security. This was a great session and I especially enjoyed that he selected Apache Roller as the application to hack. After that, I attended Ed Burns' Hyperproductive JSF and published my notes.

Then I met Trish for lunch at the conference and we went to the room for my talk. We had some technical difficulties to start (my brand-new Mini-DVI connector was bad) and I started about 10 minutes late. During this mad scramble to fix things, I started having some stomach issues, but ignored them thinking I could make it through my 50 minute talk. As I was doing demos and sweating profusely, I realized I couldn't make it and ran out of the room with 10 minutes left. The whole experience shook me up a bit. It was definitely not my best performance, but it could've been a lot worse. ;-)

Friday, we tried to sleep off our jet lag and woke up in the early afternoon. Trish had three places she wanted to visit and we managed to see them all in the same day. We climbed the tower at Grossmünster, rode the train to Rhine Falls and celebrated our anniversary at Gruelich, the highest-rated restaurant on TripAdvisor. We arrived at Guelich around 10pm and were very happy when they agreed to serve us. We did the 4-course meal and had the Sommelier choose our wines. It wasn't cheap, but definitely worth the experience.

View from Great Church Happy Couple in Zürich Riding the Train Rhine Falls!

Whoa! Rhine Falls Flowers

Olives and Sprouts Scallops and Tomato Dessert at Greulich Greulich's Backyard Trees

After dinner, we took the 8 Tram back to our hotel. However, it somehow turned into the 9 and we ended up on the opposite side of the lake. This turned out to be a nice diversion as we had a nice walk around the lake with some nice swans and lights on the water.

Swans on Lake Zürich Cool Lights Night Colors of Lake Zürich

Mmmmm, breakfast! Saturday, we woke up early for a big day of travel to the Swiss Alps. We enjoyed a scrumptious breakfast at the hotel and headed for the central train station. My good friend, Fernand, recommended we visit Grindelwald, while one of Trish's best friends thought we'd love the quaintness of Gimmelwald. We gulped at the price (500 USD) of the train ticket to both locations, but were impressed that you could buy all the tickets we needed (including the bus and trolley ride) at one place. We both quickly forgot the cost as we passed through the beautiful town of Interlaken and arrived in Grindelwald. An hour later we were hiking through Gletscherschlucht (Glacier Gorge) and marveling at the craftsmanship of the trail.

Grindelwald! Grindelwald, Switzerland Grindelwald from Gletscherschlucht Cue Water Rushing Sound

After enjoying a beer on a patio with a view, we boarded the train for Gimmelwald. A transfer, bus ride and cable car ride later, we were standing in the picturesque town, marveling that it had a lower elevation than Denver and perplexed that everyone seemed to be American. We strolled around, took a number of cool photos and had a scrumptious dinner at the Pension Gimmelwald.

Gimmelwald Gimmelwald Flora Path in Gimmelwald The Swiss Alps

Purple in Gimmelwald Trish as Julie Andrews Yodeling Cheers from Gimmelwald!

On our train ride home, we missed our connection in Interlaken. This was no surprise as we regularly encountered very short layovers between trains. The Swiss are incredibly efficient and our brief stop in a store (that had a line at the cashier) was enough to miss the next train. The hour layover turned into a nice opportunity to stroll around town and snap a few last pictures. I especially like the sunset below, taken around 9:30 at night.

Interlaken Sunset

Trish and I both were super-impressed with Zürich, the friendliness of the Swiss and especially the Swiss Alps. Yes, it was definitely expensive, but everything was top notch and beautiful. There's a very good chance we'll visit again. Thanks to the Jazoon Organizers for having us and providing us the opportunity to explore their gorgeous country.

Posted in General at Jun 28 2011, 12:52:33 PM MDT 4 Comments

My Java Web Application Security Presentation from Jazoon 2011

Yesterday I delivered my Java Web Application Security talk at Jazoon. The presentation I gave was similar to the one I delivered at Utah JUG, but contains a few more slides about penetration testing and securing REST APIs. I also opted not to embed the screencasts in the presentation on SlideShare since you can click on the links to view them. Lastly, I included a great quote from Erlend Oftedal, who left a great comment on my last post.

"Security is a quality, and as all other quality, it is important that we build it into our apps while we are developing them, not patching it on afterwards like many people do."

If you'd like to download a PDF of this presentation, you can do it from Slideshare or from my presentations page.

Like most conferences in the last year, I brought the lovely Trish McGinity with me. As of today, it's been one year since I saw her switch from a Martini to a Guinness and thought "I need to talk to that girl!" It's been a heckuva a ride ever since and I'm sure the future will be just as much fun. To celebrate, we're going to explore Rhine Falls and have dinner at Greulich. Happy Anniversary Trish!

Posted in Java at Jun 24 2011, 06:25:55 AM MDT 1 Comment