Wednesday, February 17, 2010

GWT Integration

This is the time when I want to start covering GWT integration.

I could start using GWT in small ways in the mainly servlet based application I have, but I figured I'd swing to the other extreme and start a new application based entirely on GWT. The techniques I show will still be applicable to a mainly servlet based application, but I'll also get to cover some GWT specific techniques (such as logging in via GWT).

So for this next series of posts, I'm creating a new web application in Eclipse. Everything from this point on until I post otherwise will be in reference to that new application.

It's worth looking at that basic application the plugin creates to understand a little about how GWT works.

The basic idea is that GWT widgets run as Javascript in the browser, and instead of loading a new web page to show results to the user you instead make a Javascript call to the server and then update only specific elements of the web page you're currently viewing.

Look in the project.server package and you'll find the server side of the GWT sample application. It looks a little different than what we're used to in terms of servlets. Instead of extending HttpServlet, it extends RemoteServiceServlet and implements GreetingService.

If the implements keyword is new to you, it means that the class implements an interface. An interface is (mostly) just a set of methods that the class must implement to be considered an implementation of the interface.

You can see the GreetingService interface in the project.client package. Look at that, and you'll see that the interface is just one method that the server object must implement.

GWT uses interfaces to define the set of possible calls that can go from client to server. In the sample application, there's only one possible call: greetServer that passes in a String. In a real application you'll have a larger number of possible calls in various interfaces.

Back to GreetingServiceImpl in the server package. It implements the greetServer method, and takes in the String the interface said it would need to take. This method looks a lot like a regular Java method, not a servlet. It does some error checking and throws an exception if there's a problem (you'll see in the interface that the exception is listed as being thrown). When there is no error, it simply returns a String, again as the interface specified.

There's no HttpServletRequest, no HttpServletResponse, just some odd calls to get information about the user's browser and the server itself.

At its most basic, the server implementation of interface methods must simply provide information back to the client. The display of that information is then up to the client code.

Now let's look at the project.html in the /war directory of your project. Skip down to the bottom of the file to see the actual HTML. Notice that it's a table with three table cells: one for them to type in a name, one for a submit button, and one for an error message to be displayed. But the table cells are empty!

If you run the project as a web application, you'll see an edit box and submit button. If you click submit without entering a name, you'll get an error displayed.

Where are these things coming from, if not the HTML?

Back in the project.client package, look at the file that's named the same as your project. Go down to the onModuleLoad method. This is the method that integrates the Java code with the HTML.

If you've done any Java Swing programming, the code here should look familiar. You create widgets in GWT the same way you do in Swing. The widgets themselves may be different, but the basic model of using them is the same.

Notice the line that reads:


RootPanel.get("nameFieldContainer").add(nameField);


That puts the widget represented by the variable nameField into the HTML in the HTML element that has the id nameFieldContainer. If you check the HTML file, that's one of those empty table cells. This is how the empty cells are filled with widgets.

Notice also that GWT uses the Swing methodology of creating widgets and adding them to the application, but then defining methods for what happens when the user interacts with those widgets.

If you haven't read Swing code before, this might seem pretty complicated. You've got some inner classes being defined, one of them anonymous. Without Swing experience, you might not even know how that all works.

I'd recommend finding a good Java Swing tutorial to get the basics down before trying to use GWT. Ultimately, though, it's the code defined for the submit button click that will make the call to the server greetServer method and receive the response back.

Tuesday, February 9, 2010

Encrypting User Passwords

The basic idea behind password encryption is that the encryption must be one-way. You should be able to turn plain text into encrypted text, but there should be no way to get back to the plain text only given the encrypted text. This ensures the security of the user's password on your system. To see if the password they've provided matches their original password, you encrypt the password and see if the encrypted versions are the same.

For more on this process, read this excellent post on How To Encrypt User Passwords. The post is great on concepts and rationale, but light on actual code, so I'll present some possible code here.

The basic steps:

1) Generate a string to encrypt by combining the password with a random number

2) Encrypt the string using a digest mechanism (e.g. like we did for the email verification code)

3) Store the encrypted password and the random number in the datastore

#3 should be familiar to you, so I'll provide a sample for 1 and 2. This isn't exactly what the web site I linked talks about, but builds on our previous MD5 hashing code for email verification. In fact, I'm going to retrofit the more secure mechanism back into the email verification code so that I have only one implementation of encrypting in a utility class.

I'll leave out some of the bits that we've already covered with email verification:


//Create the MD5 MessageDigest instance
Copy the code from previous examples here

// Get the random number
SecureRandom random = SecureRandom.getInstance ("SHA1PRNG");
int number = random.nextInt ();

// Build the string to encrypt and normalize it
String passAndNumber = "" + number + password;
passAndNumber = Normalizer.normalize (passAndNumber);

// Encrypt it
Use the MD5 digest and conversion to hex code from previous examples


What you'll end up with is a hex code string suitable for storing in the datastore as an encrypted password. You'll also store the random number you generated.

When the user tries to login, you'll do a similar process to see if they entered the correct password:


// Read the encrypted password and the random number from the data store
String encryptedPass = ....
int number = ....

// Create the MD5 MessageDigest instance
Already shown in email verification example

// Combine the password they entered with the random number from the data store
password = "" + number + password;

// Normalize the password they entered
password = Normalizer.normalize (password);

// Encrypt the password
Use MD5 and conversion to hex code shown before

// Compare
if (code.equals (encryptedPassword))
// Correct password
else
// Incorrect password


Note that I haven't tested this yet. There may very well be details to work out, but they shouldn't be major. The Normalizer class is only available in Java 1.6, so if you're using Java 1.5 you'll need to upgrade.

Also note that to be safe, when you call the String's getBytes method as part of creating the digest, pass in "UTF-8" as the encoding scheme so you know you'll be getting something consistent. This is different from what we did for the email verification code; I recommend refitting the email verification code to use the same mechanism as password encryption. This means you'll need to store two random numbers in the datastore...the one for the password itself, and the one for the email verification code.

Wednesday, February 3, 2010

Deploying To App Engine

It's a good idea to deploy your code to the actual AppEngine servers to test. Most features work the same between the local development environment and the servers, but now and then you'll run into something that doesn't.

Deploying is fairly simple, although it does require that you finally pick your application name on appspot.com, if you haven't already.

Step 1

Create your application. Go to the Google AppEngine site and log into your account. Click "Create an Application".

Type in the Application Identifier. This doubles as part of the URL for accessing your application at appspot.com, so it has to be unique and will be publicly visible.
Also enter the Application Title, and click Save.

Step 2

Back in Eclipse, we need to hook your project up to the application you just created. Right click on the project and go into Properties. Expand the Google section and click on App Engine. Under Application ID, type in the identifier you entered when you created your application.

Close out of the Preferences.

Step 3

Deploy! Right click on the project, go down to the Google menu and choose Deploy to App Engine. Enter the email address and password you used when you signed up for Google AppEngine, and click Deploy.

It should happen automatically, now you can open a web browser and go to your application id .appspot.com to see your application live on Google's servers.

Step 4

Everything blows up!

Well, maybe not. But if you're using RPXNow for authentication, you won't be able to get logged in unless you followed my advice back in the post on validating user emails. The reason is that part of the HTML in your sign in link is the location of your RPX results servlet. Right now, that probably still is what you had when you were developing it...e.g. http://localhost:8080/rpxresults.

Your application needs to change the http://localhost:8080 part based on whether it's running in development mode or in production mode. With a recent version of AppEngine, you'd use something like this:


if (SystemProperty.environment.get().contains("Development"))
  hostName = "localhost:8080";
else
  hostName = "gamesbyteens.appspot.com";


at an appropriate point where you fetch the host name for inclusion into the data model for your pages.