Matt RaibleMatt Raible is a writer with a passion for software. 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.

We bought a boat!

I'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've been on many trips with family and friends. However, it wasn't until this summer that I realized it was something I should do more often. It was Trish'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'd done more rafting than any other outdoor activities (mountain biking, camping and even golfing). That's when we decided to buy our own.

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'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 Down River Equipment 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 Pro 140) 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.

Last Friday, we picked up a raft trailer from Trailer Source 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.

We bought a boat! Thanks to Mike and Matt from Down River

Saturday, we took it on its Maiden Voyage on the Colorado River, floating from Radium to Rancho del Rio. According to this page, 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 7 passengers (and 2 dogs) 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.

Abbie with our new boat

Abbie's First Golf Game
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 only child was to take Abbie to play her first real game of golf at Pole Creek. 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.

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.

Nice form kiddo! Great day of golf at Pole Creek. Got a sweet framed set of Abbie pics at the end too!

Abbie frolicking on the golf course

We don't know how many more days of rafting we'll get in this year, but next year should be epic. We're hoping to do multi-day trips on the Green River, the Smith River and fly into Schafer Meadows for a journey through the Bob Marshall Wilderness. I grew up only 10 miles from "The Bob" and I've never been in it. I can't wait! :-D

Posted in General at Sep 05 2012, 11:12:02 AM MDT Add a Comment

Happy Birthday Jack!

Eight years ago today, Jack was born. Today, it's hard to even imagine him as a baby or a toddler. But then again, we all looked quite a bit different back then.

Hiking at Red Rocks Abbie and Jack on the Train

These days, Jack has his own website and prefers to spend his free moments playing with Beyblades or his new Nintendo 3DS. He started 3rd grade this year and has excellent math and reading skills, even though he's the youngest in his class. As he grows older, he's starting to like the same things I do, particularly fishing, skiing and golfing.

Last weekend, we threw a birthday party for Jack at Julie's house. He invited some of his close friends from school and had a Beyblade tournament. Fun was had by all and you couldn't wipe the smile off his face when he went home.

Let it rip!

This fall, Jack will be playing lacrosse again and might even pass up his sister in height. He sure is a handsome kid. Happy Birthday Jack!

Jack and his buddies on his 8th birthday

Related: Jack's 5th, 4th, 3rd and 1st birthdays.

Posted in General at Aug 28 2012, 12:12:46 PM MDT 2 Comments

Refreshing Taleo's UI with HTML5, Twitter Bootstrap and CSS3

Back in December, I wrote about what I've been working on at Taleo. Shortly after finishing up the Profile Picture, Talent Card and Org Chart features for TBE, I spent two weeks doing page speed optimization. By following Web Performance Best Practices, I was able to make the TBE application twice as fast and improve its score into the low 90s.

Next, I started working on a new project - refreshing the UI. Nick, the Lead UX Designer at Taleo (at the time), had developed a number of mockups and presented it to the developers and product folks in early November. I listened to a WebEx of that meeting and learned that everyone thought it'd take 6-9 months to complete the work. They figured they could release the new design in Q3 2012.

Since I like to provide high-value for my clients, I offered to help with the redesign and do a spike to help estimate. They agreed it'd be a good use of my time and I started working on it the week before Christmas. Since I'd used Twitter Bootstrap for my Play More! app, I recommended we use it as a foundation of the redesign. They agreed and I went to work. By the end of the week, I'd made good progress and told them I thought the redesign was possible in 2-3 months (including QA and cross-browser compatibility).

When I came back to work in January, we decided to split the redesign into two phases. Rather than moving elements around and introducing new features, we decided to do that in the 2nd phase. The 1st phase would entail simply re-skinning the existing UI, with minimal HTML changes. I spent a week refining my spike and integrating it into a branch. The next week, I switched images from individual images to CSS sprites. Next, I implemented a new theming system with different colors/icons and got everything looking good in Chrome, Safari and IE8/9.

The result is something I'm quite proud of. IE8 doesn't have the rounded corners (via border-radius), but it still looks good. Forms look much better thanks to Bootstrap's styling and even jQuery UI's widgets look good thanks to jQuery UI Bootstrap. I did have to override quite a few Bootstrap styles in the process, but the result is something that doesn't look too bootstrappy.

One technique I found to be extremely useful during this process was to pair with Nick (the designer) as mentioned in Building Twitter Bootstrap. At one point, when we were trying to refine slight nuances and spacing in the UI, I paired with the Product Manager and found this to be a real time-saving effort as well.

Taleo's UI Refresh project has been a great experience for me in sharpening my CSS skills. I used quite a bit of child and sibling selectors, which work great in all the browser's we're supporting. Also, by using CSS sprites and colors (vs. images), I was able to get the manual theme-creation process down to around 15 minutes. After getting the manual process greatly reduced, I wrote a Theme Generator (based on Ant, LESS and wro4j) and got it down to mere minutes. I found Sprite Cow to be an invaluable resources for working with CSS sprites.

Below are some before and after shots of what we've been able to accomplish in the first quarter of this year.

Old UI - My View Old UI - New Employee

New UI - My View New UI - New Employee

I originally wrote this post at the end of January. We ran into some stumbling blocks shorty after its original composition: Nick (the designer) moved onto greener pastures and Oracle bought Taleo. What I didn't expect when I wrote this was to spend the next two months fixing slight bugs that occurred with spacing, alignment and dependent applications I didn't know about at the time. And then there was IE7. We didn't realize we needed to support it until mid-March. Then it took us around a month to make it all work good enough.

The good news is the UI Refresh was released a few months ago and seems to be humming along just fine. Sure, there were slight nuances and customizations we had conflicts with (clashing CSS classes), but overall it seems to have gone well. I can't thank the Bootstrap developers enough for motivating us to move to HTML5 and CSS3. Also, cheers to the excellent co-workers that helped make this happen: Murray Newton (Product Manager) and Vladimir Bazarsky. I couldn't have done it without you guys.

Posted in The Web at Aug 20 2012, 12:27:21 PM MDT 5 Comments

The First Day of School

The first day of school always marks a big change for my family. We have to start getting up early, start our scouting adventures and figure out some sports to play. This year, it seemed to come faster than ever. I'm sure that had nothing to do with the fact that we were gone most of the summer.

Abbie and Jack's first day of school was yesterday and they couldn't be happier. Abbie is now a 4th grader and Jack is in the 3rd grade. Heck, they even seem to have some fashion sense!

Abbie and Jack on the First Day of School 2012

For fall activities, Abbie is doing horseback riding and Jack will be playing lacrosse at DU. Both are pumped about their sports and Abbie had her first lesson last night. She looks pretty good on a horse if you ask me.

Abbie's first horseback riding lesson

In other life news, our house is a disaster (we're halfway through getting our kitchen remodeled), our deck project is almost done and The Bus will be finished in just a few more weeks.

OK, I made that last part up - one can dream, right? ;)

Related: First Day of School 2010 and First Day of School 2007

Posted in General at Aug 16 2012, 01:01:30 PM MDT 1 Comment

New Look and Feel, Designed by Gillen's Army

As part of my 10-year blogiversary, I was hoping to refresh this site with a new look and feel. A few months ago, I contacted my friend Mark Waggoner to see about getting his design help. We promptly worked out a logo/business card/website deal and Gillen's Army went to work.

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.

  • Upgraded to Roller 5.0.1.
  • Upgraded wro4j to the latest version (1.4.8.1) to workaround using a → (\2192) in CSS.
  • Changed to use jQuery and Lightbox2 for pictures.
  • Upgraded to the latest version (3.0.83) of SyntaxHighlighter. You might notice there is no longer a toolbar in this version. However, you can still double-click on code and easily copy/paste it.

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'll give you a dark theme when the sun is down and a light theme when it's daylight. I used Preston's Hunt's JavaScript Class for Sunrise and Sunset Calculations 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 site.js.

The stylesheet switching doesn't happen as fast as I'd hoped (there's a flash even if using cookies), so I'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 "data-colorscheme" in markup so this further supports moving to the server.

I have other minor adjustments I'd like to make, but more importantly - I wanted to get it out to you all. Tell me what you like and don't like. Among other things, the form inputs for comments and contact 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.

Here's some stats comparing my old andreas08 theme to the new darklight:

Metric andreas08 darklight
Size and Speed 167 requests, 3.6MB, 9.89s 148 requests, 3.2MB, 7.34s
YSlow 76 87
PageSpeed 91 96

Sweet! It looks like this site is faster than ever. Cheers to Mark and Gillen's Army for the new design. I dig it!

Posted in Roller at Aug 14 2012, 10:58:11 PM MDT 6 Comments

10 Years Ago...

Wouldn't you know it, I missed my 10-year blogiversary. 10 years ago yesterday, I wrote my first blog post. This was shortly after reading Dave Johnson's article about Roller. I originally started this blog to share a bunch of tips and tricks I'd learn while doing web development, particularly with Java and Struts. Since then, I've written 3,086 entries and received 13,462 comments.

I found that writing technical blog posts was a great way to remember things and share knowledge. According to Google Analytics (which doesn't track my RSS/Atom feeds), here's my most-visited blog posts since 2006.

I also started it to document my life, so I could remember the details of significant life events and fun family vacations. I wrote about Abbie's and Jack's arrival. I penned a story about growing up at the cabin, twice. In April 2004, I wrote about buying a 1966 21-window VW Bus. Yes, I'm still restoring it. No, I don't know when it'll be done. This year I hope.

I blogged about Abbie and Jack's first day of school, I wrote about almost all of Abbie's birthdays; Jack's 5th, 4th, 3rd and 1st. Some of my favorite comments are on Jack's got a bead stuck in his nose! I blogged about getting a divorce 5 years ago. I wrote about visiting the real Oktoberfest the next year.

I wrote about my Dad's retirement, my sister's wedding and my Mom's retirement.

I met Trish in June of 2010 and mentioned her name for the first time in September 2010. I introduced her to the kids in November and we started traveling the world together. We journeyed to Antwerp/Amsterdam, Fort Lauderdale/Key West, Crested Butte, Alta, Las Vegas, a hut trip in the Rockies and Kraków. And that was just in the first 6 months! We got engaged last November.

I summarized the years several times, in 2005, 2006, 2008, 2009, 2010 and 2011.

Last, but certainly not least, I'd like to thank the ones that made the last 10 years possible:

  • Keith at KGB Internet. Hosted here since day 1, for $20 month*.
  • Dave Johnson, for creating Roller and continuing to maintain it all these years.
  • Java and Tomcat, for your rock-solid stability through the years.
  • The Open Source Movement, for providing so much to write about.
  • My Family, for giving me with so many adventures and memories.

* Thank goodness he doesn't charge me for bandwidth. It's a little heavy on the bits.

Update: As part of this 10-year celebration, I've updated the look-and-feel with help from Gillen's Army.

Posted in Roller at Aug 02 2012, 12:47:19 PM MDT 3 Comments

Summer Vacation 2012 in Montana and Maui

I started writing about my summer vacations in Montana as soon as this blog gave me an opportunity: July 2003. In July 2004, I didn't travel to the cabin for the 4th, but I did in 2005. Then, 5 years ago, I started writing longer blog posts about our trips. 2008 was Raible Road Trip #12. In 2009, I spent a whole month in Montana. In 2010, I visited after my parents moved there full-time. Last year, I took Trish for the first time.

I don't know what Raible Road Trip number this year's trip was, but I'm going to go ahead and assume #17. This year wasn't exactly a vacation for me. It was what I like to call a workation. 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'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't bad either. ;)

Below is our trip itinerary in condensed form, complete with photos from my incredibly talented fiancé.

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 new house 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 Dr. Barton's PhD at his sister's house. Rafted the Middlefork of the Flathead all weekend. Played some more golf and went waterskiing.

Abbie at The Cabin My Dad Happy 4th! Raible Homestead in the Swan Valley Parade
Holland Lake Patio The Girls at Double Arrow Hard to believe I caused this guy to move to Montana in 1997 Holland Lake Sunset Soy Fields in Montana
→ Trish's Montana Summer 2012 Album

On July 14th, we flew to Maui and continued to explore. Our good friend'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's excellent cooking. Flew back to Missoula (Montana), slept for 6 hours and drove 12.5 hours back to Denver on July 27th.

Api, Makao and Keaka. My Hawaiin name is Pualani I'm going to call him Honu! Abbie with the Stingray Wahoo! We did it!
Sunset sail from a catamaran in Maui. Epic! Where we stayed for 2 weeks! Epic Maui Ride Kayaking with Turtles
→ Trish's Maui Summer 2012 Album

Mahalo Montana and Maui. Thank you to our awesome friends: Mom, Dad, Owen, Jason, Ryan and Shawn. It was an incredible adventure.

Posted in General at Aug 01 2012, 09:30:27 AM MDT Add a Comment

Father's Day Weekend at The Oregon Coast

For the 5-year anniversary of our Father's Day Camping Trip, we decided to mix things up a bit. My parents were in Oregon for a friend's wedding, so we decided to fly to meet them there instead of flying them to Colorado. My sister hopped on "Buttercup" (her Harley) and drove to meet us from Washington. We also invited our good friends Clint and Autumn (who you might remember from Costa Rica) and had a great time staying at Gearhart Cottages.

We didn't feel too bad about skipping camping since Trish held a "Boot Camp" with the kids and their friends at Stillwater Campground the week before. You might remember Stillwater from last year's Father's Day trip.

Trish's Uncle John drove out to the coast to meet us and we did a really fun hike along Indian Beach to Ecola State Park. It was a real treat! It was also really fun to meet Clint and Autumn's 9 month old Brodie. As usual, Trish snapped some incredible photos.

View from Ecola State Park Oregon

Raible Family on the Oregon coast Abbie and Jack frolicking in the waves Kalin and Joe on the beach

That was two weeks ago. We returned to Colorado, had some fun at ÜberConf, rocked out at a Def Leppard concert then enjoyed a few Rockies games before hopping in the car and heading to Montana. We spent the night at Roosevelt Lodge Cabins in Yellowstone and saw a plethora of buffalo, a couple bears and even a beaver while driving through.

This morning, we woke up at the cabin I was born in.

The Cabin 2012

We'll be working on my parent's retirement cabin, getting a float ready for the Swan Valley Parade and having a good ol' time with great friends for the next two weeks. After that, we're taking the kids to Hawaii for the first time.

Here's to summer vacations. I hope yours is spent with great people in beautiful places too.

Posted in General at Jul 02 2012, 09:44:00 AM MDT Add a Comment

Play vs. Grails Smackdown at ÜberConf

Play and Grails have been hyped as the most productive JVM Web Frameworks for the last couple of years. That hype has recently grown thanks to both frameworks' 2.0 releases. That's why James Ward and I decided to do a presentation at ÜberConf comparing the two. In April, we proposed the talk to Jay Zimmerman, got accepted and went to work.

How we did it
In the beginning of May, we met at a brewery in LoDo and sketched out the app we wanted to build. We also came up with a schedule for development and a plan for the presentation. We decided to build two different webapps, each with little-to-no Ajax functionality and a few features that we could use to load test and compare the applications.

We started out with the name “Happy Trails” since we both liked trails and happy hours. Later, James found that www.ubertracks.com was available and purchased the domain. We setup the Grails app to be on bike.ubertracks.com and Play/Java to be on hike.ubertracks.com. We managed our source code on GitHub, continuously tested on CloudBees and deployed to Heroku. Two weeks ago, when we were finishing up our apps, we hired a friend (Linsay Shirley) to do QA.

After fixing bugs, I emailed Patrick Lightbody, got some “cloud dollars” for Neustar Web Performance and started running load tests. The Wednesday before last, at 2 in the morning, I recorded a simple browsing regions and routes script and set it to go to 50 users over a 5 minute period and then sustain 50 for another 5 minutes. It was fun to watch the log messages whiz through my console so fast they got blurry. About halfway through testing the Grails app, there was an OOM issue, but it eventually recovered. Limiting db connections to 4 and scaling to 5 Dynos in future tests helped alleviate any issues.

We took our development experience, the load/performance testing data, and a bunch of ecosystem stats and built our smackdown presentation. We used reveal.js, GitHub Files and Google Charts to make things more dynamic.

What we found
We arrived at a number of conclusions after doing our research:

Code

  • From a code perspective, Play 2 and Grails 2 are very similar frameworks.
  • Code authoring was good in both, but lacking IDE support for Play 2's Scala Templates.
  • Grails Plugin Ecosystem is excellent.
  • TDD-Style Development is easy with both.
  • Type-safety in Play 2 was really useful, especially routes.

Statistical Analysis

  • Grails has better support for FEO (YSlow, PageSpeed)
  • Grails has less LOC! (6 lines less, but 40% more files)
  • 1 Dyno - Grails had 2x transactions!
    • Grails experienced OOM about halfway through.
  • Apache Benchmark with 10K requests:
    • Play: ~10% failed requests, Grails: 0
    • Requests per second: {Play: 170, Grails: 198}
    • Requests per second: {Play: 251, Grails: 198}
  • Load Test with 100 Real Users:
    • Grails: 10% more transactions, 0 errors

Ecosystem Analysis

  • "Play" is difficult to search for.
  • Grails is more mature.
  • Play has momentum issues.
  • LinkedIn: more people know Grails than Spring MVC.
  • Play has 3x user mailing list traffic.
  • We had similar experiences with documentation and questions.
  • Outdated documentation is a problem for both.
  • Play has way more hype!

We figured we spent around 100 hours developing the apps, gathering data and creating the presentation. The good news is it's all open source! This means you can clone the project on GitHub (Grails is in the grails2 branch, Play is in the play2_java branch) and help us improve it. The presentation is in the master branch in the preso directory.

All the data we gathered is open for debate and we’d love to tune our apps to handle more requests per second. In fact, we already had a contributor discover an issue and provide a fix for Play that increases its throughput from 170 req/second to 252 req/second!

Regardless of what the stats and pretty graphs say, we both enjoyed our experiences with Play 2 and Grails 2. If you haven't tried them yourself, we encourage you to do so.

Posted in Java at Jun 25 2012, 07:10:57 AM MDT 19 Comments

Migrating to Play 2 and My ÜberConf Presentation

In my last post about migrating to Play 2, I said I'd write another post on the rest of my experience. While I'm not completely finished with migrating to Play 2, I feel like I've done enough to talk about the issues I encountered.

Validation and Displaying Errors
With Play 1, I can't help but think validation was a bit more intuitive. For example, here's how I populated an object from request parameters, converted a value and validated its data was fit to put in a database.

var workout = params.get("workout", classOf[Workout])

// change duration to time
var duration = params.get("workout.duration")
workout.duration = convertWatchToTime(duration)

Validation.valid("workout", workout)

if (Validation.hasErrors) {
  renderArgs.put("template", "Profile/edit")
  edit(id);
  ...
} else { // put into db

With Play Scala 2, you have to define a Form structure 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:

val workoutForm = Form(
  mapping(
    "id" -> ignored(NotAssigned: anorm.Pk[Long]),
    "title" -> text,
    "description" -> text,
    "duration" -> nonEmptyText,
    "distance" -> nonEmptyText,
    "postedAt" -> optional(date),
    "athleteId" -> optional(longNumber)
  )((id, title, description, duration, distance, postedAt, athleteId) =>
    Workout(id, title, description, convertWatchToTime(duration), distance.toDouble, null, 0))
    ((w: Workout) =>
      Some((w.id, w.title, w.description, w.duration.toString, w.distance.toString, null, Some(0))))
)
...
workoutForm.bindFromRequest.fold(
  form => {
    Ok(Scalate("/Profile/edit.jade").render(request, 'errors -> form.errors))
  },
  workout => { // put into db

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 do it in one line whereas the Scala version does not. Also, I was unable to figure out how to get the data from my "form" back into the request so I could refill input fields. I'll admit, I didn't spend a lot of time trying to figure it out, but it did fail the 10 minute test. Note to self: use HTML5's required attribute to reduce the need for server-side validation on modern browsers.

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's Scala Templates:

-import controllers._

form(method="post" class="form-stacked" id="workoutForm"
  action={routes.Profile.postWorkout(workout.map(_.id.get))})
  input(type="hidden" name="id" value="#{workout.map(_.id)}")

Secure Social
After getting most of my UI working, I started looking at the Secure Social Module for Play 2. Below are the steps I had to go through to install it:

  1. Cloned GitHub project to my hard drive.
  2. Copied module-code/* into my projects' modules/securesocial directory.
  3. Modified project/Build.scala to add secureSocial and dependsOn to my project.
    val secureSocial = PlayProject(
      appName + "-securesocial", appVersion, mainLang = SCALA, path = file("modules/securesocial")
    )
    
    val main = PlayProject(appName, appVersion, appDependencies, mainLang = SCALA).settings(
      // Add your own project settings here
    ).dependsOn(secureSocial).aggregate(secureSocial)
    
  4. Added a conf/securesocial.conf and included it in my application.conf with the following line:
    include "securesocial.conf"
    
  5. Added a conf/play.plugins with the following to get Twitter to load as a provider:
    10000:securesocial.core.providers.TwitterProvider
    
  6. Created an InMemoryUserService.scala and referenced it in my play.plugins file:
    9999:services.InMemoryUserService
    
  7. Added Secure Social's routes to my conf/routes file.

Once I'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.

Heroku
The next thing I did was attempt to deploy my app to Heroku. I knew there might be some issues with Scalate after reading Jan Helwich's blog post about Scalate on Heroku. The first things I encountered were 1) a successful startup and 2) an error in my browser.

Action not found

I was able to reproduce this issue locally by running "play clean stage" and starting the app with "target/start". 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.

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 /] ->
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.(FileOutputStream.java:209) ~[na:1.6.0_20]
2012-06-21T07:07:12+00:00 app[web.1]:   at java.io.FileOutputStream.(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]

I tried his suggestion (removing the first slash on my Scalate paths) but it didn't work. I tried adding in Scalate pre-compilation, but that didn't solve the problem either. The good news is I did solve it this afternoon by changing my Scalate object to use a canonical path instead of an absolute one.

iPhone App
In addition to the changes mentioned here, I re-wrote the iPhone app for Play More. I upgraded it to PhoneGap 1.8.1, used jQTouch, developed with AppCode (instead of Xcode) and had a pretty good experience. The only issue I ran into was with the jqt.bars extension from DataZombies. 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!

Presentation and Source Code
I presented all of these findings and told my story at ÜberConf this morning. In addition, I announced that the code is now open source and available on GitHub. You can view my presentation below or on Slideshare.

Conclusion
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'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.

I found Anorm and Scalate to be huge time sinks and don't know if I'd recommend using either one in a Play 2 project. I'm sure Scalate will be easier to use as its Play 2 integration gets more refined, but I don't know if there's any hope for a JDBC abstraction that doesn't produce error messages when things go south.

On the upside, my experience with HTML5 and CoffeeScript was wonderful. They did what I asked them to do and didn't cause much pain. When a browser-based webapp couldn't handle geo running in the background, PhoneGap came to the rescue.

I plan on continuing to develop Play More! If you'd like to help, checkout the open issues and viva la open source!

Posted in Java at Jun 21 2012, 04:09:22 PM MDT 1 Comment