Friday, April 8, 2011

GWT Server-side Internationalization With Hermes

Greetings interwebs! My name is Matt Bertolini and I am an engineer here at Travel Tripper. I thought it was about time I made an appearance on our tech blog. I want to talk about a new project called Hermes that I have recently finished and am sharing with the world.



A Little Backstory



Our application, RezTrip, is in the process of being internationalized. Since it is a GWT based application, we chose to use the GWT static string internationalization features. Unfortunately, these features only work on the GWT client-side and we have lots of other client-facing strings that need to be internationalized (client emails, legacy HTML pages, etc.).

The gwt-i18n-server Library

A quick Google search on the subject of "server-side GWT i18n" found a library called gwt-i18n-server. This library gives the server-side support for the GWT static string i18n interfaces and properties files. We quickly incorporated the library into RezTrip and found it was pretty buggy. Worse yet, development on the library had stagnated and hadn't been updated in over a year. This is where I come in. Not satisfied with admitting defeat and implementing a separate i18n system just for server-side content, I decided to fork the gwt-i18n-server library and fix the bugs.

Enter Hermes

Hermes is the name of my fork of the gwt-i18n-server library. It is named after the Greek god Hermes (the god of language, and the great messenger of the gods). While technically a fork, every line of code has been re-written to improve compatibility with GWT and fix bugs. Here are the list of features and supported technologies found in the library:
  • GWT Constants, ConstantsWithLookup, and Messages interface support

  • Plural forms support
  • Date and number sub-formatting

  • UTF-8 properties file support
Using Hermes is very simple. All you need to do is pass the your GWT i18n class and the desired language tag to Hermes and it will create the necessary proxy class and load the appropriate properties file. Then all you need to do is call the methods on your class just like you do on the GWT client-side. Here is a small example to get you started:
ExampleMessages messages = Hermes.get(ExampleMessages.class, "en-us");
messages.sampleString();
Hermes also has a complete suite of unit tests to make sure the library is functioning as intended. The best part of Hermes it is completely free and open source, licensed under the same LGPL license as the gwt-i18n-server library. I encourage anyone using GWT to check out the library. To grab a copy or download/view the source, visit the Hermes page on GitHub. If you do use Hermes, feel free to let us know how it goes.

Thursday, March 24, 2011

Racing Gmail Superstars

The Problem
Something happened in the last few weeks and my Gmail accounts started locking up and becoming totally unresponsive. All of a sudden:
  • Some keyboard commands worked (the "/" to go to the search bar, "g-l" to go to the search bar pre-populated with "label:" --> actually entering a label and hitting return didn't work though)
  • opening messages failed
  • navigating message lists with the keys (j/k) wasn't showing the cursor moving
  • using the "g-" combo keyboard commands (to navigate to other labels, inbox, buzz, etc.) wasn't working,
  • using the mouse to click around didn't work at all
The Hunt for the Cure

At first I thought it might be connection problems, or something specific to one of my accounts or browser. But it was happening in both Chrome and Firefox, and with multiple Google accounts... so I knew something was really wrong.

I loaded up a Gmail in Firefox and turned on Firebug. Sure enough, after the lock-up, when clicking around, I was getting script errors.

I tried posting to the Gmail Help forum, but the specifics of the bug were so odd and vague that (understandably) nobody was interested in looking into the issue.

So, all that was left to me was to try to hone in on the bug myself. After hours and hours of careful observation, I finally figured out a sequence that was reliably causing the problem.

Reproducing The Bug
1) Navigate to a label or inbox (any list of multiple messages)



2) Use the "j" and "k" keys to navigate to the 2nd message in the list (note: this step is probably not strictly necessary)



3. Use the "s" shortcut to unstar the message



4. Use the "x" key to select the just-unstarred message



5. Use the "y" key to archive the message



6. Try clicking on one of the other two emails, or using the "o" key to open and see script error:



The Conclusion


My hunch was that the problem was probably related to a labs feature (or it would have already been reported).

I tried disabling the Superstars labs feature and repeating the above steps.... no script error!

Postlude: Race Condition (Out of my league?)


Having figured this out, I noticed a couple of other things:

1) If I perform the above steps slowly, I don't get the lockup.
2) Starring and Refreshing:
  • Fast: If I use the keyboard to star a bunch of messages and then immediately hit F5, Gmail refreshes, and the messages I'd starred are not starred.
  • Medium: If I use the keyboard to star a bunch of messages, wait a few seconds, then hit F5, Chrome warns me that Gmail is working and that I should stay on the page.
  • Slow: If I use the keyboard to star a bunch of messages and then wait a while, then hit F5, Gmail refreshes, and the messages I'd starred are starred.
My guess from the above is that on un-starring the message, an AJAX call goes out... but performing an action on the message before the response comes back causes an inconsistency, which causes the lock up.

PostScript:

27/3 - Bug now seems to be fixed. :-)

Tuesday, March 8, 2011

Styling/Skinning Our Application

The styling of our application was a unique design/architectural challenge for the team at TravelTripper.... read all about it in our guest post on the Google's GWT Blog:

At TravelTripper, we make hotel reservation software. Our main product is a "booking engine" called RezTrip, a web based application that allows visitors of a hotel’s website to directly book a stay with that hotel.



As GWT applications go, we think RezTrip, when it comes to the question of styling, presents an interesting departure from traditional development. As a "white label" application, we needed to create our app in such a way that allows our hotel clients the ability to customize not only the "frame" around the application, but also the internal style of the application itself, such as fonts, colors, etc.



In other words, each hotel needs the ability to create their own custom header, footer, or sidebar and have it wrap the booking "application" portion of the page. Furthermore, each hotel needs to be able to change all the colors, fonts, and even some icons within the application.



The desired end result is a single booking engine application, running on multiple web sites, but always mimicking the look and feel of each individual hotel site.


Read the whole article at: Styling and Skinning Your Apps With GWT

Monday, January 17, 2011

Today's Twitter WTF moment (or, put a "?" in your invalid url)

We learned something very interesting over at Travel Tripper these last few days while using Twitter's sharing API. Erik and I spent a good amount of time scratching our heads because every time we tried to share one of our RESTful URLs, Twitter would hit us with a "URL Required" error and would complain that the "url parameter does not contain a valid URL".


The even stranger thing was that when we manually replaced the url parameter in the twitter share query string with one from our demo server, it would work!

At first I was convinced that Twitter did some sort of pingback to our url to do it's URL shortening bit, making all calls to http://localhost#key1=value1 invalid, but even if we used demo.testdomain.com, we had a 50/50 chance of success. Sip coffee, look at each other in frustration, rinse and repeat.


In a moment of desperation, I noticed that our demo server was adding a query parameter to our RESTful url, making it look something like http://demo.testdomain.com?lang=en_us#key1=value1, and for some odd reason Twitter liked it (for the record, our RESTful URLs use everything after the "#" symbol, so having a URL with "#" and no "?" was perfectly valid). In between fits of cursing at Twitter and some key pounding, we managed to insert a "%3F" (the url encoding for "?") in between our domain and our "#" symbol and VOILA!

For the record, I still have no idea why it would matter to Twitter whether or not we included the query string identifier, but it's most likely an issue with their regex expression. Lots of URLs have "#" to denote the anchor on the current page, so we're not quite sure why their URL shortener would throw up whenever it came across this format. BUT, if you are having issues with an invalid URL parameter on Twitter, you might want to throw a "%3F" in the URL parameter value between your domain and the "#" symbol.


The final url format before url encoding would look like this:

http://demo.testdomain.com?#key1=value1

Hope this helps.

Friday, December 31, 2010

GWT Temp Directory

The other day, my all-star colleague was sifting through his hard drive and found that his temp directory had become horribly bloated.




Looking through the folder, he found a ton of GWT-generated files. It turns out that in both debug and compile, GWT is creating temporary files and using the default system temp directory to store them. He brought this to my attention.

me: Doesn't seem unreasonable.

colleague: But it's not cleaning them up.

me: Hmm..

colleague: ...and it's like >1gb of files!!


Ok! That could lead to some performance issues. Worth trying see if we can fix it.

A little bit of hunting, and he found Development Mode Options and GWT Compiler Options sections on the Compile & Debug page of the official GWT online documentation.

In both sections, it lists:

-workDir         The compiler's working directory for internal use (must be writeable; defaults to a system temp dir)


Turns out that's the one that if left blank causes our default temp directory[1] to get full of crap.

Solution:

In our Eclipse Debug Configuration and in our Ant build script, we specify a new directory within the project's /build directory that we'll reserve exclusively for GWT's temp files.

Since we already clean the /build directory with every launch, we're sure to clean out these GWT temp files as well --Problem Solved!

Not Quite:

Compiling:
Here, when we specify the -workDir argument, we do see the new gwt-specific temp directory get created and populated with generated files.

However, if we open our system temp directory and monitor it during the compile, we see that GWT is actually still using the system temp dir for some other temporary files. Drat!

When the compiler finishes, it does clean itself up nicely. Phew!

If, however, you stop things mid-compile[2], those temp files will be orphaned. :-( Good to know.

Debug:
Adding a -workDir specification in our Debug Mode Configuration didn't seem to do anything at all. The specified directory remained empty before, during, and after the debug run. Meanwhile, our temp directory continued to get filled with temp files, which are not removed on exit from debug mode:



Conclusion:

In the end, it seems unclear whether or not it's worth the effort of setting the -workDir option in your debug and compile configurations.

As far as we could tell, GWT uses the system temp directory no matter what.

Depending on whether you are running principally in debug or full compile/deploy mode, you may see fewer files piling up there, but either way...

After a couple months of concentrated GWT work, you probably want to go in and do a little manual housekeeping on your system's temp directory!




[1] In Windows 7, the directory seems to be c:\Users\USER_NAME\AppData\Local\Temp

[2] With a decent-sized project, you'll eventually get to where the compile phase is upwards of 3 minutes, in which case you're almost sure to change your mind mid-compile from time to time.

Tuesday, April 27, 2010

We are Looking for Java/J2EE Developers and a Technology Team Leader

If you're a talented programmer and/or team leader living in the NYC metro area looking to work for a cool up-and-coming company where the sky is the limit, we want to hear from you!

Required Experience:

  • at least 6 years Java development experience in a team environment
  • team leadership experience
  • experience with J2EE and either Servlets or JSPs, the more the better
  • developed at least one multi-tier application that connects to a relational DB using JDBC
  • experience using an IDE (ideally Eclipse, but IntelliJ or other is fine)
  • at least basic comfort working with HTML/CSS
  • comfortable working as a user on both Windows XP and at least one linux distribution or unix OS
  • 4-year computer science bachelors degree, or equivalent
  • experience using a source-code-control tool (CVS, SVN, etc.)
  • strong oral and written communication skills
  • experience with a widely-used relational database, and comfort writing SQL queries by hand (ideally MySQL, but PostgreSQL, Oracle, SQL Server also ok)



Desired Experience (the more the better):

  • J2EE application servers (especially Tomcat, JBoss) and EJBs
  • core java (multi-threading, performance analysis/optimization, JVM configuration)
  • messaging systems and SOA architectures (TCP/IP-level protocols, web services)
  • design patterns for OO development
  • comfort with UML and/or ability to write and understand high-level software design documents
  • experience with the MVC architectural pattern and at least one Java-based MVC framework (Struts, Spring, etc.)
  • an object-data binding framework for Java (Hibernate, JDO, etc.)
  • sysadmin experience on a linux distribution or other unix OS
  • general networking experience
  • experience with production web application environments and web servers, especially Apache
  • javascript experience, particularly experience with AJAX toolkits
  • experience supporting a live web application in production



No recruiters - we already work with ones we want to work with. I repeat no recruiters or head-hunters etc. Only candidates should respond.

Please send a cover letter, resume and approximate desired salary to: careers@traveltripper.com

For more info on the company and the position, see the full job posting here:
http://www.meetup.com/ny-tech/messages/boards/thread/9017120/#35357662

Friday, April 16, 2010

Generics in UiBinder



Generics!

One of the most exciting things about GWT is that you get to use Java. And one of the most exciting things about Java is that you get to use Generics.

 




UiBinder!

Another great thing about GWT is the UiBinder, a tool that makes it really easy to create and (most importantly) modify and maintain ui layouts.

 




For a long time, though, I was stuck, because I was creating a bunch of fancy UI widgets that used Generics, but I didn't know how to specify them in my .ui.xml files.

There wasn't any mention of generics in the official google ui:binder tutorial, so I just sort of assumed it was impossible. As a workaround, I created <g:SimplePanel> instances in the .ui.xml files, and then filled them dynamically in the corresponding java files when calling createAndBindUi().

The other day, though, I was surfing through our app's DOM via Firebug (thank you OOPHM!) and I was getting a little annoyed at all the extra DIVs that were being created due to the wrapping SimplePanel instances.

So I looked a little deeper and I found a hit on the "google-web-toolkit" google code page entitled GWT UIBinder Use Cases.



Comment by rjrjr@google.com, Dec 13, 2009

@johan.rydberg, generics support is anemic, trying only not to break things. You can't specify generics in your ui.xml file, and you'll need to instantiate your Table in your owner class.

I'm pretty sure this should work (making some presumptions about your typed Table class):

 ui:field='rowTypeTable' />
@UiField(provided = true)

final Table<RowType> rowTypeTable = new Table<RowType>() {}




Very happily, I don't have a single thing to add here, the solution above works perfectly, no modifications needed.

Probably would be a good addition to the UiBinder tutorial page on the gwt homepage, but in the meantime, I figure putting it up here might help others who hit this same problem.