Friday March 05, 2010

How We Hired a Team of 10 in 2 Months Back in December, I started a new contract with Time Warner Cable (TWC). As part of the terms of that contract, it named the following as one of my deliverables:
Assist in identifying, recruiting and hiring additional full-time Web development staff, emphasizing open-source framework expertise.
- Timeframe: ongoing, throughout the six-month engagement
- Deliverable: targeting 2-3 quality leads/hires
Since this was a local gig and I always like a good challenge, I asked my client to raise the number from 2-3 to 4-5. Shortly after signing that contract, my project began. Almost immediately, I began spreading the word on Twitter.
When TWC hired me, it was just the beginning of a larger initiative. They were making a number of large changes:
- Moving from Waterfall to Agile.
- Restructuring organizationally for functional teams.
- Moving from ColdFusion to JVM technologies.
To help with the move to Agile, I contacted a good friend, Brad Swanson. Brad is the founder of Propero Solutions and has always had a passion for agile coaching and making teams more efficient. At the beginning of the year, we setup 2-day training class in Herndon, VA to kick-off the Agile Initiative. There were 15 existing developers on the team when I started and 40 people showed up to that initial training. Most of these additional folks were from Product and QA. Brad's message of working together quickly resonated with the group and you could see their eyes light up with their new-found knowledge.
After the success of Brad's training, we leveraged his network to help us find some very impressive coaches to assist with our efforts. We hired two Agile Coaches to start working with us at the end of January.
While our agile movement was progressing in January, I started contacting friends, former colleagues and referrals about coming to work for us. For friends and former colleagues, my e-mail simply outlined the positions available, the exciting opportunity of the project and that TWC was willing to pay very competitive salaries for strong engineers. While it didn't happen immediately, I did manage to convince 4 former co-workers to join me, including the team I built at LinkedIn and worked with at Evite.
Following those 4, most of the candidates we interviewed were referrals or folks that contacted me directly after seeing my tweet. I'm amazed that I never had to write a blog post to advertise the positions.
Once we identified potential candidates, we executed the following process:
- Requested a resume (or LinkedIn Profile URL) via e-mail.
- If skills and experience looked like a match, we sent a list of screening questions specific to the position.
- If screening answers were satisfactory, we'd schedule a face-to-face interview.
- We then conducted a face-to-face interview with a list of questions specific to the position.
- If convenient, we took the candidate to lunch to explore their social skills.
- After interviewing, the interviewers would huddle for 5-10 minutes and give thumbs-up/thumbs-down and we'd right up a summary e-mail for our boss.
- If thumbs-up, our boss would contact the candidate, discuss the details and extend an offer.
This process turned out to be a great way to hire a kick-ass team very quickly. You might notice that HR was not involved at all in this process. While we did use them to post jobs and such, we found that our recommendation-based process of identifying high-quality candidates worked much better. HR was able to bring in folks with lots of buzzwords on their resume, but no one knew them or what they were capable of.
Once a person passed the screening questions, our interview focused more on a person's social skills than their technical ability. The first half of the interview was all about their career experiences and what they enjoyed/disliked about employers and projects. The second half consisted of a handful of very technical, hard questions that we expected people to struggle with. If they answered correctly, we were impressed. If they didn't, we examined how they handled explaining they didn't know the answer. It was interesting to see how many people didn't simply answer "I don't know".
One of my most interesting observations of the process was our question about "what was your most enjoyable employment experience and why?" Most folks responded with something very early on in their career, and often it was their first job. This caused me to reflect on our industry and careers as a whole and wonder if people get more miserable as they keep working. It's a shame there's not more folks happy with their current jobs.
By mid-February, we managed to fill most of our open headcounts. We'd successfully hired 2 Agile Coaches and 8 Developers in a little over 2 months. While everyone hasn't started yet, there's several of us now working in my Denver office. We pretty much caught everyone off-guard with our success and we've moved onto our next biggest problem - were do we put everyone? The TWC Broomfield office is building out space for us, but it'll likely take them a few months to complete the project. My office that fits 4 comfortably had 8 of us in their last week. I had to sit on a garbage can when pairing because we'd run out of chairs.
To solve our short-term space constrains, I've successfully negotiated additional space upstairs from our landlady and we've ordered a number of new desks for folks. Our desks arrive Monday and we're setting up pairing stations upstairs next week. All-in-all, it's been a wild ride with a fair amount of stress. Interviewing folks wasn't that stressful, but trying to hire folks while writing code and trying to deliver features for our project was challenging.
We've been emphasizing pair programming and hiring process required a lot of e-mail communication. When we were pairing, we'd ignore our e-mails for most of the day and then have to catch up at night. Once people started on-boarding, we had to figure out the best way to get them started and slinging code. We established an on-boarding plan and we've been able to get everyone running our app on their machines before lunch. We've even had a couple folks committing code by the end of the first day.
This week, we on-boarded 3 of our final 4 developers. I breathed a big sigh of relief that the hiring was over and we could get back to slinging code and making things happen. As luck would have it, I received an e-mail from my boss on Tuesday that the hiring engine is starting up again and we need to hire 6 more developers. While I'm not anxious to start the Hiring Engine again, I am glad to know it works well and it has helped us build a great team. I'm not going to post the positions as part of this blog entry, but there's a good chance you'll hear more about the gigs if you follow me on Twitter. Posted in Java at Mar 05 2010, 12:01:57 PM MST 3 Comments
My Future of Web Frameworks Presentation Earlier this week, I tweeted about a history of web frameworks timeline I created for my upcoming Future of Web Frameworks talk at TSSJS Vegas 2010. I immediately received a lot of feedback and requests for adding new frameworks and releases. The image below is the result of that Twitter conversation. Thanks to everyone who contributed.
Back in November, I wrote about my proposals for TSSJS. I've been thinking a lot about web frameworks lately and I can't help but think we live in a very exciting time. As a Java developer, I've been exposed to one of the most vibrant language ecosystems on the planet. As Tim Bray talks about, the Java Platform has 3 legs: the language, the virtual machine and a huge, immense library of APIs (both in the JDK and in open source libraries). The diagram below is something I created based on Tim's podcast.
Tim says, "One of those legs is replaceable and that's the language." And he's right, there's many Java.next languages that run efficiently on the JVM. This is one of the most exciting parts of being a Java web developer today. There's many proven web frameworks and languages that you can pick to build your next web application.
The best part is many of the best web frameworks run on the JVM. Not only that, but the best code editors are the IDEs that you're familiar with and have grown to love. Furthermore, much of the literature for Java.next languages is written for Java developers. As someone who knows Java, you have wealth of web frameworks and languages just waiting for you to learn them.
To create my presentation on the future of web frameworks, I followed the outline I posted previously. I plan on explaining the evolution and history of web frameworks and how we got to where we are today. From there, I'll be speculating on what web applications we'll be developing in the future. Finally, I'll touch on the necessary features of web frameworks that will allow us to develop these applications.
Of course, I haven't actually presented this talk yet, so it's likely to change in the coming weeks before the conference. The good news is this gives you the opportunity to provide constructive criticism on this presentation and help make it better. I realize that a presentation rarely represents the conversation that takes place during a conference. However, I believe it can portray the jist of my thinking and lead to a meaningful conversation in the comments of this post. Below is the presentation I created - thanks in advance for any feedback.
For those who will be joining me at TSSJS ... it's gonna be a great show. St. Patrick's Day in Vegas, what more could you ask for? 
Update: This article has been re-posted on Javalobby and contains additional community feedback in the comments. Posted in Java at Feb 26 2010, 08:55:39 AM MST 4 Comments
Using JRebel with IntelliJ IDEA on OS X Yesterday afternoon, I figured out how to use JRebel with IntelliJ IDEA. I wrote up some AppFuse documentation for it at Using JRebel with IntelliJ IDEA and figured I'd repost it here for those developers using IDEA 9 and Maven.
- Download and install IntelliJ IDEA 9 Ultimate Edition (in /Applications on OS X).
- Download and install JRebel.
java -jar jrebel-setup.jar- Install the JRebel Plugin for IDEA. Shortcut: File > Settings > Search for plugins and find JRebel.
- On OS X, Modify /etc/launchd.conf and add the following so M2_HOME is available to GUI apps. You'll need to reboot after making this change.
setenv M2_HOME /opt/tools/maven2
More info on this setting is available on Stack Overflow.
- Modify your project's pom.xml to include the JRebel Maven plugin (for generating the configuration of which directories and files to watch for changes).
<plugin> <groupId>org.zeroturnaround</groupId> <artifactId>javarebel-maven-plugin</artifactId> <version>1.0.5</version> <executions> <execution> <id>generate-rebel-xml</id> <phase>process-resources</phase> <goals> <goal>generate</goal> </goals> </execution> </executions> </plugin> - If you're using the Maven Jetty plugin, change your pom.xml so Jetty doesn't reload the app when classes are compiled. Specifically, change scanIntervalSeconds to 0. If you're not using this plugin, you should definitely check it out for Java webapp development.
- Use the JRebel icons to start jetty:run in your IDE.
- Command Line Integration: Set a JREBEL_HOME environment variable that points to your JRebel install (/Applications/ZeroTurnaround/JRebel on OS X) and set your MAVEN_OPTS to use JRebel's settings. For example:
export JAVA_OPTS="-Xmx512M -XX:PermSize=256m -XX:MaxPermSize=512m -Djava.awt.headless=true" export JREBEL_HOME=/Applications/ZeroTurnaround/JRebel export MAVEN_OPTS="$JAVA_OPTS -noverify -javaagent:$JREBEL_HOME/jrebel.jar"
After making these changes, you should able to compile classes in IDEA and refresh your browser. Log messages like the following should show up in your console.
JRebel: Reloading class 'org.appfuse.webapp.action.UserAction'.
To simplify things further, you can map Command+S to compile (instead of Shift+F9). Just look for Keymaps in Settings, rename the default one and search for Compile to remap.
I'm assuming the steps to make things work on Windows and Linux are similar. Please let me know if you have any issues with these instructions. Posted in Java at Feb 02 2010, 10:34:08 AM MST 2 Comments
AppFuse 2.1 Milestone 1 Released
The AppFuse Team is pleased to announce the first milestone release of AppFuse 2.1. This release includes upgrades to all dependencies to bring them up-to-date with their latest releases. Most notable are Hibernate, Spring and Tapestry 5.
What is AppFuse?
AppFuse is an open source project and application that uses open source tools built on the Java platform to help you develop Web applications quickly and efficiently. It was originally developed to eliminate the ramp-up time found when building new web applications for customers. At its core, AppFuse is a project skeleton, similar to the one that's created by your IDE when you click through a wizard to create a new web project.
Release Details
Archetypes now include all the source for the web modules so using jetty:run and your IDE will work much smoother now. The backend is still embedded in JARs, enabling you to choose which persistence framework (Hibernate, iBATIS or JPA) you'd like to use. If you want to modify the source for that, add the core classes to your project or run appfuse:full-source.
In addition, AppFuse Light has been converted to Maven and has archetypes available. AppFuse provides archetypes for JSF, Spring MVC, Struts 2 and Tapestry 5. The light archetypes are available for these frameworks, as well as for Spring MVC + FreeMarker, Stripes and Wicket.
Other notable improvements:
- Added Compass support thanks to a patch from Shay Banon.
- Upgraded from XFire to CXF for Web Services.
- Moved Maven repository to Sonatype's OSS Repository Hosting for snapshots and releasing to Maven Central. There are no longer any AppFuse-specific artifacts, all are available in central. Thanks to Sonatype for this great service and its excellent repository manager.
- Upgraded to Canoo WebTest 3.0. Now if we could just get its Maven Plugin moved to Codehaus.
- Added Ajaxified Body to AppFuse Light archetypes.
- Infrastructure upgrades, including JIRA 4, Confluence 3, FishEye 2, Bamboo 2 and Crowd 1.6. Many thanks to Atlassian and Contegix for their excellent products and services.
- For more details on specific changes see the release notes.
Please note that this release does not contain updates to the documentation. Code generation will work, but it's likely that some content in the tutorials won't match. For example, you can use annotations (vs. XML) for dependency injection and Tapestry is a whole new framework. I'll be working on documentation over the next several weeks in preparation for Milestone 2.
AppFuse is available as several Maven archetypes. For information on creating a new project, please see the QuickStart Guide.
To learn more about AppFuse, please read Ryan Withers' Igniting your applications with AppFuse.
The 2.x series of AppFuse has a minimum requirement of the following specification versions:
- Java Servlet 2.4 and JSP 2.0 (2.1 for JSF)
- Java 5+
If you have questions about AppFuse, please read the FAQ or join the user mailing list. If you find bugs, please create an issue in JIRA.
Thanks to everyone for their help contributing code, writing documentation, posting to the mailing lists, and logging issues. Posted in Java at Nov 19 2009, 07:16:36 AM MST 8 Comments
Consulting, SOFEA, Grails and GWT at next week's Denver JUG Next Wednesday, I'll be at Denver's JUG meeting to talk about Independent Consulting and Building SOFEA Applications with Grails and GWT. The first talk will be a a panel discussion among local independent consultants, including James Goodwill, Matthew McCullough, Tim Berglund and myself.
This session explores the trials and tribulations of an independent consultant. How do you find contracts? Should you setup an LLC, an S-Corp or just be a sole proprietorship? What about health insurance and benefits? Are recruiters helpful or hurtful? Learn lots of tips and tricks to get your dream job and your ideal lifestyle.
The Grails and GWT talk is a preview of a talk I'll be doing at the Rich Web Experience in December. Below is a rewrite of the abstract in first-person.
Earlier this year, I participated in a major enhancement of a high-traffic well-known internet site. The company wanted us to quickly re-architect their site and use a modern Ajax framework to do it with. An Ajax Framework evaluation was done to help the team choose the best framework for their skillset. The application was built with a SOFEA architecture using GWT on the frontend and Grails/REST on the backend.
This talk will cover how Bryan Noll, Scott Nicholls, James Goodwill and I came to choose GWT and Grails, as well as stumbling blocks we encountered along the way. In addition, we'll explore many topics such as raw GWT vs. GXT/SmartGWT, the Maven GWT Plugin, modularizing your code, multiple EntryPoints, MVP, integration testing and JSON parsing with Overlay Types.
If you're in Denver next Wednesday night (November 11th), you should stop by the Denver JUG meeting. It'll be a fun night and there's sure to be a few beers afterward.
Posted in Java
at Nov 05 2009, 10:52:37 PM MST
5 Comments
Testing GWT Libraries with Selenium and Maven On Tuesday, I wrote about Running Hosted Mode in GWT Libraries. Today I added an additional module to our project to run Selenium tests against our GWT library. In the process, I discovered some things I needed to modify in my GWT library's pom.xml. I'm writing this post so others can use this setup to write GWT libraries and package them for testing with Selenium.
First of all, I noticed that when you're using the GWT Maven Plugin with a JAR project, it doesn't automatically run gwt:compile or gwt:test in the compile and test phases. I had to explicitly configure the compile goal to run in the compile phase. I also had to add <webappDirectory> to the configuration to compile the JavaScript files into the war directory.
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>gwt-maven-plugin</artifactId>
<version>1.1</version>
<configuration>
<module>org.appfuse.gwt.core.CoreUI</module>
<runTarget>index.html</runTarget>
<webappDirectory>war</webappDirectory>
</configuration>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
To package the generated JavaScript and index.html in the JAR, I added the following <resources> section to the maven-resources-plugin configuration I mentioned in my previous post.
<resource>
<directory>war</directory>
<includes>
<include>core.ui/**</include>
<include>index.html</include>
</includes>
</resource>
In addition, I discovered some javax.servlet.* classes in my JAR after running "mvn package". I believe this is caused by the GWT plugin sucking these in when it compiles my ProxyServlet. I excluded them by adding the maven-jar-plugin.
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<excludes>
<exclude>javax/servlet/**</exclude>
</excludes>
</configuration>
</plugin>
After doing this, I was able to publish my JAR with all the contents I needed to run Selenium tests against it.
Testing the GWT Library with Selenium
The module that contains the Selenium tests is a WAR project that uses war overlays, Cargo and Selenium RC. You can read about the Maven setup I use for running Selenium tests in Packaging a SOFEA Application for Distribution.
The major difference when testing a JAR (vs. a WAR), is I had to use the maven-dependency-plugin to unpack the JAR so its contents would get included in the WAR for testing. Below is the configuration I used to accomplish this:
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>unpack</id>
<phase>generate-sources</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.appfuse</groupId>
<artifactId>gwt-core</artifactId>
<version>1.0-SNAPSHOT</version>
<type>jar</type>
<overWrite>false</overWrite>
<excludes>META-INF/**,org/**,javax/**</excludes>
</artifactItem>
</artifactItems>
<outputDirectory>
${project.build.directory}/${project.build.finalName}
</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
Hopefully this will help you develop GWT libraries and run Selenium tests against them. If you have any suggestions for simplifying this configuration, please let me know.
NOTE: I did considering a couple of other options for running Selenium tests against our GWT library:
- Add something to the existing project that 1) creates a WAR and 2) fires up Cargo/Selenium in a profile to test it.
- Create the tests in a GWT (war) project that includes widgets from the library.
I decided on the solution documented above because it seemed like the best option.
Posted in Java at Nov 04 2009, 10:09:27 PM MST 2 Comments
Running Hosted Mode in GWT Libraries (when using Maven) Earlier this year, I wrote about Modularizing GWT Applications with GWT-Maven. Fast forward 8 months and I'm still working with GWT and using this same technique. However, this time I'm working with the Maven GWT Plugin from Codehaus. In my last post, I wrote:
The results of modularizing your application are beneficial (shared code) and detrimental (you have to mvn install gwt-core whenever you make changes in shared classes). If you know of a way to configure the gwt-maven plugin to read sources from both gwt-core and gwt-webapp in hosted mode, I'd love to hear about it.
The good news is I found a solution for this, using the Builder Helper Maven Plugin. The GWT Maven Plugin's Productivity tip for multi-project setup has more information on how to configure this (note: we use IntelliJ and Eclipse on my project and did not need to configure this in a profile).
All was fine and dandy with this configuration until I wanted to be able to run hosted mode to develop/test everything in my library before including it in my main project. Luckily, you can still run mvn gwt:run on a JAR project. However, when you configure your pom.xml so sources are included in your JAR, you run into an issue: your *.java files will be copied to war/WEB-INF/classes and hosted mode will use these files as source rather than the ones you're editing in src/main/java.
To solve this, I changed my pom.xml to do two things:
- Only copy resources right before packaging (in the test phase).
- When packaging is complete, delete the *.java files from war/WEB-INF/classes (using Ant).
Below is the XML I used to make this possible. Please let me know if you have a way to simplify this configuration.
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>2.4.1</version>
<executions>
<execution>
<phase>test</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${project.build.outputDirectory}</outputDirectory>
<resources>
<resource>
<directory>src/main/java</directory>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<delete>
<fileset dir="${project.build.outputDirectory}" includes="**/*.java"/>
</delete>
</tasks>
</configuration>
</execution>
</executions>
</plugin>
This solution seems to work pretty well. As far as developing your library in hosted mode, you'll need to configure two *.gwt.xml files, one that doesn't have an <entry-point> defined and one that does. Configure the one with the entry point as the <module> in your gwt-maven-plugin configuration.
As a side note, I found a few issues with the 1.1 version of the Maven GWT Archetype. Below are the steps I used to fix these issues and upgrade to GWT 1.7.0 (I realize 1.7.1 is out, but gwt-dev-1.7.1-mac.jar doesn't exist in Maven central).
First, create a new project by running the following from the command line:
mvn archetype:generate \ -DarchetypeGroupId=org.codehaus.mojo \ -DarchetypeArtifactId=gwt-maven-plugin \ -DarchetypeVersion=1.1 \ -DgroupId=com.yourcompany \ -DartifactId=gwt-project -Dversion=1.0-SNAPSHOT -B
After creating the project, you'll need to modify the pom.xml as follows:
- Change the gwt-maven-plugin's version to 1.1.
- Change the ${gwtVersion} property to 1.7.0.
- Add
<runTarget>Application.html</runTarget>to the <configuration> element of the plugin. - Move Application.html and web.xml so they're under the "war" directory.
- Update Application.html to prepend the GWT module name in the <script> tag.
I hope these instructions help you create modular GWT projects with Maven. This setup is working great on my current project. Posted in Java at Nov 03 2009, 09:37:07 AM MST 3 Comments
What would you like to see at TSSJS 2010?
A couple months ago, I was asked by TheServerSide to speak at next year's TheServerSide Java Symposium in Las Vegas. In addition, they asked me to help them evaluate presentation proposals and suggest topics/speakers.
First of all, I think the biggest thing that TSSJS could do to improve is to host more networking events. With the JavaOne Party being over, I think there's a tremendous opportunity to fill a gap in the networking needs of the Java Community. When I first attended TSSJS in 2006, there were a fair amount of parties and everyone got to interact quite a bit. In 2008, there were no networking events. I believe having a strong networking story would attract a lot more attendees, companies and sponsors.
Secondly, I think it's possible that TSSJS has too many server-side related sessions. IMO, the server-side (and middleware in general) isn't that exciting. TechTarget appears to own TheClientSide, so why not add some more client-side stuff to the mix? For example, I'd love to see a Struts 1 app-makeover using different technologies (for example, Flex, GWT and jQuery). I think HTML5 and Google Wave's Architecture sessions would be interesting too. If adding client-side sessions is too far away from TheServerSide, maybe it should be renamed to TheServerSide JVM Symposium and there can be all kinds of sessions on JVM languages (e.g. Scala, JRuby, Groovy) and all the great things those languages can accomplish.
Lastly, I've been asked to send a couple session proposals. Currently, I'm thinking about a doing GWT vs. Flex Smackdown with James Ward, but I'm open to other ideas. It's been quite awhile since I did a "Comparing Web Frameworks" talk. Maybe "Hot Web Frameworks for 2010" is more appropriate? I also think it'd be interesting to do a somewhat philosophical talk on "The State of Web Frameworks" and where we're headed in the next year.
What would make you want to attend TSSJS next year? Let me know your thoughts and I'll do my best to make them a reality.
Update October 22, 2009: Whoo hoo! It looks like TheClientSide will be a part of TSSJS Vegas next year. Should be a great show. Posted in Java at Oct 12 2009, 11:28:21 AM MDT 3 Comments
Building GWT Applications with MVP and Issues with Overlay Types MVP has recently become a popular strategy for structuring GWT applications. This is largely due to its testability and Ray Ryan's Best Practices For Architecting Your GWT App from this year's Google I/O. GWT, by itself, is simply a widget toolkit and doesn't ship with any sort of MVC (or MVP) framework.
On my current project, we're using GXT, a GWT implementation based on ExtJS. It has its own MVC framework, but it has very little documentation and can be confusing when using it with GWT's History management. At one point, I attempted to make it more understandable by writing a blog entry on GXT's MVC Framework.
One of my initial assignments was to decide if we should use MVP or MVC. Regardless of which one was chosen, I was also tasked with deciding if we should use an existing framework or write our own. After watching Ray Ryan's session on YouTube and recalling my frustration with GXT MVC on my last project, I quickly became convinced MVP was the answer.
To test my "MVP is best for our project" theory, I did a spike to implement it. I used the GWT MVP Example tutorial as a starting point and added the following libraries to my project.
- GWT-Presenter: An implementation of the MVP pattern.
- Google Gin: Dependency Injection based on Google's Guice.
- GWT-Log: A log4j-style logger for GWT.
Implementing the MVP pattern itself was relatively straightforward, but I did encounter a few issues. I'm writing this post to see if anyone has solved these issues.
MVP Implementation Issues
The first issue I ran across was GXT's widgets don't implement standard GWT interfaces. To try and figure out a solution, I posted the following on Twitter:
"Wondering if it's possible to do MVP with GXT since it's buttons don't implement standard GWT interfaces."
The best response I received was from Simon Stewart (founder of the WebDriver project, works for Google):
"Put the GXT buttons in the View. Let that turn DOM events into semantic events."
He also pointed me to his tdd-gwt-gae project which shows many techniques for unit testing (with jMock) and integration testing (with GWTTestCase). Using Simon's examples, I was able to determine an initial strategy for implementing MVP with GXT.
My strategy is instead of getting widgets from the view and adding handlers, you add handlers to to the view and it takes care of adding them to the widgets that should listen for them. This seems to work, but my View interface has a lot of void methods, which is a bit different than standard MVP patterns I've seen.
The 2nd issue I encountered is with unit testing. Unit testing can be be performed on MVP applications by mocking out any dependencies that use JSNI. Classes that use JSNI are not testable with plain ol' JUnit and typically requires you to use GWTTestCase, which can be slow and cumbersome. This isn't to say that GWTTestCase isn't useful, just that it has its place.
When unit testing MVP applications, the recommended practice seems to be you should test presenters and not views. Services fall into a similar "don't unit test" category because they'll need to connect to the server-side, which won't be running in a unit testing environment.
This is where I ran into a major issue that I don't have a solution for.
I'm using Overlay Types (as described in JSON Parsing with JavaScript Overlay Types) to do JSON parsing in callbacks. Since Overlay Types use JSNI, it's not possible to do any JSON parsing in unit tests. The problem with not being able to do any JSON parsing is the callbacks will often call eventBus.fireEvent(GwtEvent) after the JSON parsing has happened. This means I can't fully test the flow of a presenter if event firing happens in a callback.
In attempt to try different mocking techniques for callbacks, I created a test that uses two recommended EasyMock-ing strategies. The first is a "CallbackSuccessMatcher" and is described in more detail in Testing GWT without GwtTestCase. The second technique uses a "CallbackMockSupport" class to allow EasyMock expectations such as expectLastCallAsync() and expectLastCallAsyncSuccess(T). You can read more about this technique in Test driven development for GWT UI code with asynchronous RPC.
Both of these examples use RPC, which typically has callbacks that have an onSuccess(T type) method. It's easy to use these callbacks in unit tests since T is a POJO and the onSuccess() method contains no JSNI code.
Currently, I see a few possible solutions to this problem:
- Figure out a way to detect when unit tests are running and add if/else logic to callbacks.
- Modify presenters and services so a callback can be set that is unit test-friendly.
- Make JSOModel an interface that can be replaced/mocked in tests.
The last solution seems like best one, but I'm also curious to know what others are doing. My hunch is that most GWT apps use RPC and haven't run into this issue. Posted in Java at Sep 22 2009, 01:41:36 PM MDT 12 Comments
How to do cross-domain GWT RPC with a ProxyServlet Last week, I started working on a new project using GWT. On my last project, we used GWT HTTP Calls and my new project is using RPC. We'll likely migrate to a JSON backend eventually, but in the meantime, I wanted to be able to develop in hosted mode (localhost:8888) and call services on another host (localhost:8080), where the services are running in a JSF/Spring webapp.
At first, I thought it'd be easy thanks to the handy-dandy ProxyServlet I mentioned in Implementing OAuth with GWT. However, when I tried to hook it in and use it, I saw the following error in my server-side logs.
java.lang.NullPointerException
at javax.servlet.GenericServlet.getServletName(GenericServlet.java:322)
at javax.servlet.GenericServlet.log(GenericServlet.java:277)
at com.google.gwt.user.server.rpc.RemoteServiceServlet.doGetSerializationPolicy(RemoteServiceServlet.java:219)
at com.google.gwt.user.server.rpc.RemoteServiceServlet.getSerializationPolicy(RemoteServiceServlet.java:117)
at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader.prepareToRead(ServerSerializationStreamReader.java:429)
at com.google.gwt.user.server.rpc.RPC.decodeRequest(RPC.java:234)
Looking at RemoteServiceServlet.java:219, there's a logging call that fails for some reason (at least in my application).
/*
* Check that the module path must be in the same web app as the servlet
* itself. If you need to implement a scheme different than this, override
* this method.
*/
if (modulePath == null || !modulePath.startsWith(contextPath)) {
String message = "ERROR: The module path requested, "
+ modulePath
+ ", is not in the same web application as this servlet, "
+ contextPath
+ ". Your module may not be properly configured or your client and server code maybe out of date.";
log(message, null);
}
In the above code, you might notice that GWT is checking to make sure the client is hosted in the same application as the server. After I figured this out, it was pretty easy to modify my ProxyServlet to trick GWT RPC into thinking the client was in the same web application. In the ProxyServlet's handleContentPost method, I added the following code to replace "localhost:8888/" with "localhost:8080/services/" (in the content of the post to the server).
if (contentType.startsWith("text/x-gwt-rpc")) {
String clientHost = httpServletRequest.getLocalName();
if (clientHost.equals("127.0.0.1")) {
clientHost = "localhost";
}
int clientPort = httpServletRequest.getLocalPort();
String clientUrl = clientHost + ((clientPort != 80) ? ":" +
clientPort : "");
String serverUrl = stringProxyHost + ((intProxyPort != 80) ? ":" +
intProxyPort : "") + httpServletRequest.getServletPath();
postContent = postContent.replace(clientUrl , serverUrl);
}
After manipulating the posted content, I was successfully able to use GWT RPC cross-domain.
Woo hoo!
For your convenience, the full handleContentPost() method is listed below.
private void handleContentPost(PostMethod postMethodProxyRequest,
HttpServletRequest httpServletRequest)
throws IOException, ServletException {
StringBuilder content = new StringBuilder();
BufferedReader reader = httpServletRequest.getReader();
for (;;) {
String line = reader.readLine();
if (line == null) break;
content.append(line);
}
String contentType = httpServletRequest.getContentType();
String postContent = content.toString();
if (contentType.startsWith("text/x-gwt-rpc")) {
String clientHost = httpServletRequest.getLocalName();
if (clientHost.equals("127.0.0.1")) {
clientHost = "localhost";
}
int clientPort = httpServletRequest.getLocalPort();
String clientUrl = clientHost + ((clientPort != 80) ? ":" +
clientPort : "");
String serverUrl = stringProxyHost + ((intProxyPort != 80) ? ":" +
intProxyPort : "") + httpServletRequest.getServletPath();
postContent = postContent.replace(clientUrl , serverUrl);
}
String encoding = httpServletRequest.getCharacterEncoding();
debug("POST Content Type: " + contentType + " Encoding: " + encoding,
"Content: " + postContent);
StringRequestEntity entity;
try {
entity = new StringRequestEntity(postContent, contentType, encoding);
} catch (UnsupportedEncodingException e) {
throw new ServletException(e);
}
// Set the proxy request POST data
postMethodProxyRequest.setRequestEntity(entity);
}
Update: In the comments, Ganesh asked for more details, so I figured it'd be a good idea to post the full source code. First of all, click here to see the code for the ProxyServlet:
I generally subclass ProxyServlet to provide my own configuration:
public class MyProxyServlet extends ProxyServlet {
@Override
public void init(ServletConfig servletConfig) {
setFollowRedirects(true);
setRemovePrefix(false);
setProxyPort(8080);
}
}
Here's another example that reads configuration settings from web.xml and proxies to a different domain name:
public class AlternateHostProxyServlet extends ProxyServlet {
@Override
public void init(ServletConfig servletConfig) {
setProxyHost(servletConfig.getInitParameter("proxyHost"));
String secure = servletConfig.getInitParameter("secure");
if (secure != null) {
setSecure(Boolean.valueOf(secure));
}
setFollowRedirects(false);
setRemovePrefix(true);
setProxyPort(80);
}
}
After you've added these to your project, simply map the servlet (and its path) in your *.gwt.xml file (if you're using GWT) and your web.xml. Posted in Java at Aug 05 2009, 04:06:12 PM MDT 7 Comments
Search This Site
Recent Entries
- Fantastic Fun in Jackson Hole
- How We Hired a Team of 10 in 2 Months
- Jack's Skiing Like A Madman!
- My Future of Web Frameworks Presentation
- My Guest Room Remodel is finished!
- Web Application Testing with Selenium by Jason Huggins
- Web Application Testing with Selenium at Agile Denver next Monday
- 17" MacBook Pro Stolen from Living Room
- Using JRebel with IntelliJ IDEA on OS X
- Reviews for Grails: A Quick-Start Guide and Kanban and Scrum

