Wednesday, March 3, 2010

GWT Basics

Okay, let's do a simple GWT interaction to see how it works. Note that this will depend on data being in the datastore, data that the site doesn't have yet. I can just create a simple GWT call to populate the datastore so what we're developing can return some results.

The servlet model was a page load model. Each page load called a servlet, which returned the HTML to display. So when designing your servlet based site, you thought in terms of the pages the site needs to display.

The GWT model is a remote procedure call model. Picture the client and server both containing objects that talk to each other (primarily the client objects making calls on the server objects to get information). The application user interface itself is contained in the client, and looks more like a regular Java application than a web page.

So we have to shift our thinking from the servlet model. To identify GWT interactions, think about the sorts of information the client might need to get from the server.

I'll start with something simple. I want the front page of the site to display a list of contests. So that list of contests is information the client needs to get from the server.

Think of that client/server interaction as a method call between objects. Let's say that I just want that call to return the names of the current contests. I might write the method interface like this:


String[] getCurrentContests ();


To convert that into Java code for your project, click on the project.client package and choose File->New->Interface. Next to "Extended interfaces", click the Add button. Type in RemoteService and choose to add that interface.

Don't forget to give your interface a name. I'll name my interface something like Contests.

Click Finish and the interface stub is created. It should look something like this:


import com.google.gwt.user.client.rpc.RemoteService;

public interface Contests extends RemoteService
{

}


We want to add an annotation before the interface line to say what the URL path is on the server. It'll end up like this:


import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;

@RemoteServiceRelativePath("getContests")
public interface Contests extends RemoteService
{

}


Now we need to add in the actual method interface:


import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;

@RemoteServiceRelativePath("getContests")
public interface Contests extends RemoteService
{
String[] getCurrentContests ();
}


That's it for creating the interface. What we've defined is the interface between the client and server.

What we still need to define is what the client does to call this interface, and what the server does to implement it.

More next post.

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.

Friday, January 22, 2010

Implementing User Authentication

Earlier in the blog, I showed how to use a 3rd party OpenId service to allow people to login to your site using their Google account, their Facebook account, etc.

That isn't appropriate for every type of site, so now I'll show how to allow people to register at your site and create a login and password specific for your site.

I'd planned on using Spring Security for this, so that the authentication would be robust. But, Spring Security has some dependencies on the Spring framework. Supposedly getting it to work without Spring is possible, but not knowing either product it probably won't happen in the limited amount of time I have available (sorry for those of you searching for how to do just that).

I do, by the way, highly recommend Spring for industrial strength applications built on Java servlets. It just isn't appropriate for the class that I'm teaching. So, the authentication we'll roll here won't be industrial grade. But it'll be a start.

Let's talk for a moment about the characteristics of a good authentication mechanism.

First, it must be impossible to fool. For example, an authentication mechanism that simply avoided displaying members only links to non-members would not prevent non-members from typing those links in directly. The server must authenticate on any restricted page load.

Second, it must be easy to use. The more places we have to touch to implement authentication for a page, the higher the chance that we'll get it wrong. In particular, if we have to write Java code for each page we want protected, the chances are good that we'll get it wrong sooner or later.

We'll use Java Servlet filters to accomplish this. A filter is basically a piece of Java code that is run before a servlet is run. The filter can allow the servlet to run, or it can display a page itself and block the servlet. This happens without adding code to the servlet itself.

First step is to set up our web.xml file to use the filter (okay, so we haven't written the filter yet, but editing web.xml is the easy part so we'll get it out of the way). Add something like the following to your web.xml:


<filter>
<filter-name>AuthenticationFilter</filter-name>
<filter-class>omega.server.AuthenticationFilter</filter-class>
</filter>

<filter-mapping>
<filter-name>AuthenticationFilter</filter-name>
<url-pattern>/members/*</url-pattern>
</filter-mapping>


This is like registering a servlet, in that you specify a URL pattern that triggers the filter. In my case, I used /members/*. This means that any URL under the members directory will trigger the filter. This will require changing my servlet URL mappings around a bit, so that any page that should be restricted to logged in users is mapped under /members, and any page that should be visible to anyone is not. So far for me, that's basically just the ProfileServlet, but I'll make a mental note to put future members only pages under /members.

Now we have to write the AuthenticationFilter class. This is similar to writing a servlet. Create a new class in your server package. There's no superclass for this one, but you will implement an interface. So in the new class dialog, click on the Add button to the right of the (empty) interface list, and type in Filter. You want the javax.servlet.Filter interface.

You'll get some Java code that leaves stubs for three methods: init, destroy, and doFilter. The primary purpose of the init is to save off the FilterConfig passed in, so create a private data member for doing that:


private FilterConfig filterConfig = null;

public void init(FilterConfig arg0) throws ServletException {
filterConfig = arg0;

}


You may not need that FilterConfig instance, but it can be used to get the ServletContext of the servlet that you're filtering.

The actual work of authentication will be done in the doFilter method. Here's the basic idea (not working code, but algorithm):


if (user authenticates correctly)
  chain.doFilter(request, response);
else
{
  User the response argument to generate error output, or redirect to a login form
}


So calling chain.doFilter passes the request on to the next filter in the chain, meaning that the AuthenticationFilter is happy with the request. If authentication fails, do something other than calling chain.doFilter.

The details of pulling a user's password from the datastore and comparing it to one passed into the HTTP request we've already covered. You should also be able to write the registration form and servlet to allow users to create their user account in the first place (and you can do verification of their email, too). You should also know what you need to know to have a "I forgot my password" link that will email a new password to them.

The one bit that we do still need to cover is storing passwords in an encrypted form. Storing user's passwords in plain text is bad form, so we won't do it.

Next post, encrypting passwords.

Tuesday, January 19, 2010

Signing A User Out

I realized that I haven't shown how to sign a user out yet. I have a link to /signout in my navigation template, but nothing mapped to that URL. This is where having a project plan (or at least a to-do list) would help!

So, I'll create a Signout servlet mapped to /signout. In that servlet, I need to get the session object and call the invalidate method on it. Afterward, I'll redirect the user to the front page.

It'll look something like this:


HttpSession session = req.getSession(false);

if (session != null)
session.invalidate();

resp.sendRedirect("/front");


and that's it. Not a big deal, but something we needed to implement.

Monday, January 11, 2010

Validating User Emails, part 2

The last two steps of validating user emails involved changing the profile servlet to display the link to resend the verification email when they already have an unverified email, and to send the verification email when they change their email.

Since both of those require sending the verification email, I'll put that in a utility class. I already have MailUtil, and this is a special sort of email, so I'll stick it there.

The main problem so solve before I can send the verification email is generating the unique verification code. I mentioned that MD5 is fine to use here, so we'll use that. The MessageDigest class in Java is what we use to generate the MD5 value, like this:


MessageDigest md5 = null;

try
{
md5 = MessageDigest.getInstance("md5");
} catch (NoSuchAlgorithmException e)
{
// Should never happen, but just in case
return "Unable to access md5 digest algorithm";
}

byte [] digest = md5.digest(user.getEmail ().getBytes());


Note the exception handling...that exception should never happen, because MD5 is a standard digest algorithm. But since the exception can be thrown, Java requires we handle it. This assumes the method returns a String that's an error message if something went wrong.

The byte array we get from the MessageDigest is not suitable for putting into an email yet. We need to convert it into hexadecimal first, like this:


String code = "";

for (byte element : digest)
code += Integer.toHexString(element & 0xFF);


If you're wondering about the use of "& 0xFF", it's because of the way that integral values are stored in Java. Think in terms of twos-complement sign extending from 1 byte to 4 bytes, and getting a bunch of extra 1s. We strip off anything extra by anding it with a single byte of 1s.

And if none of that makes sense, just believe me that you need it (or don't believe me, and remove it, and you'll see the difference in the hexadecimal generated).

So what I end up with is a hexadecimal string suitable for use as a verification code.

I also want to store the email I'll send as a Freemarker template, rather than have it embedded in Java code. This means a couple of things: I'll need to pass the Template object into the MailUtil method, and when I process the template I'll need to use a StringWriter so I can get the results as a String. The StringWriter serves in place of how we've been using PrintWriter for the templates.

I'll leave that, and finishing off the send verification email method as an exercise for the reader.

The last bit I'll mention here is that we'll need to include a link to the servlet we wrote last post that handles the verification clicks in the email. In the template we'll use something like this in the href arg of an a tag:


http://localhost:8080//validate-email?code=${code}


We'll use localhost for testing, but will want to change that when we're deploying the application. For those keeping track, we now have two places where we've embedded localhost and made notes to change it when we deploy.

Might be a very good idea to include a programmatic way to switch this depending on whether the application is running on the server or on the local machine. You can check that like this:


if (SystemProperty.environment.get().contains("Development"))


That if statement is true when you're running on the local host, and false otherwise. Use that if statement at appropriate points to change the host name used in URLs. The SystemProperty class used there is an App Engine provided one.

(Update: I just realized that the SystemProperty class doesn't exist on the App Engine version installed in the computer lab. So we'll likely update to the most recent version at some point, to keep things in sync with what students would have at home.)

Okay, so if you've done the exercises left for the reader, you have a MailUtil method that will send a verification email. We need to invoke that method in the profile servlet when the user changes their email, using code like this:


private String sendVerificationEmail (User user) throws IOException
{
Configuration config = ConfigFactory.get(getServletContext ());
Template template = config.getTemplate("emailVerify.ftl");

try
{
MailUtil.sendVerificationEmail(user, template);
}
catch (Exception e)
{
return e.getMessage ();
}

return "";
}


This is in the profile servlet, and will be called from the code that updates the email in the User. I'm not showing those parts, because they're basically the same as we did for updating the name.

The last bit we need is to display the link to resend the verification email when the user is editing their profile and their email isn't verified.

I'm going to add a query parameter to the ProfileServlet that will tell it to resend the verification email. It'll be something like this:


if (req.getParameter("resend") != null)
{
// Only resend if the email isn't verified already
if (! user.getEmailVerified())
{
message = sendVerificationEmail (user);
}
}


Now that the profile servlet will handle that query parameter, put the link in when the email isn't verified. I'll add the link to the ProfileLoggedIn template, just as a variable, e.g. ${resendLink}. Then in the profile servlet, when the email isn't empty but isn't verified, I'll set that variable to the resend link in the data model. When the email is empty, or verified, I'll set that variable to an empty string.

Since that uses only techniques we've already seen, I'll leave that as an exercise for the reader. Note also that I haven't tested the above, so it might very well blow up. When I get a chance to test it I'll update this post to reflect any fixes needed.