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.

Saturday, January 9, 2010

Validating User Emails

First step in adding user email addresses is pretty basic.

We need to add an email field to the User object, and add it to the ProfileServlet form and processing, so that the user can enter the email address. We'll also need a field that tells us if the email has been validated or not. That field won't be user editable via the profile form.

I'll leave the first step as an exercise for the reader (it uses only techniques we've already seen), and move on to email verification.

The basic strategy with email verification is threefold:

1) The user enters their email address in their profile

2) The web site sends an email to that email address, with a link to click

3) The user clicks that link, taking them back to the web site

That way, we know that the user entered a valid email address they control.

Step 1 is pretty basic and uses techniques we've already covered. Step 2 requires sending email, which we'll look at. Step 3 requires having a page the link in the email goes to, where we can mark the email as verified.

Tying together step 2 and step 3 means we'll have to generate a unique code for this particular email verification. This unique code needs to be impossible to predict, so users entering false email addresses cannot simply guess the verification code. It also needs to be something that can be used as a query parameter in a URL. An MD5 hash is a good candidate, and we can check the database to make sure it's unique before sending the email.

Okay, lots to do!

1) Create a class to send the activation email

2) Create a servlet to receive the click from the activation email. Either mark the email as verified, or tell the user the verification email cannot be found

3) Change ProfileServlet to automatically send the activation email when a new email is entered, and to set the verified status to false

4) Change ProfileServlet to provide a link to resend the activation email (only display when the email isn't verified)

Also, the GAE Mail API allows an application to receive email. The email gets transformed into an HTTP request to a servlet, which can then save the email text in the datastore or take some other action based on the email. So you can do some pretty sophisticated email driven functionality (such as allowing people to send an email to your app to get a listing of contests in their area). For now, we'll use this feature just to handle when the verification email bounces.

Sending Emails

Since this is something we might have to do often, I'll write a MailUtil class that will hide a little of the complexity. Error handling will still be exposed, since we'll need to display an error message on the web page. Here's what the only method so far in the MailUtil class will look like:


public static void sendOne (String subject, String body, String name, String address, String fromName, String fromAddress) throws Exception
{
Properties props = new Properties();
Session session = Session.getDefaultInstance(props, null);

Message msg = new MimeMessage(session);

msg.setFrom(new InternetAddress(fromAddress, fromName));
msg.addRecipient(Message.RecipientType.TO,
new InternetAddress(address, name));
msg.setSubject(subject);
msg.setText(body);
Transport.send(msg);
}


To put multiple lines in your message body, use the standard \n escape in the body text.

Note that running this in Eclipse doesn't seem to actually send email. Before this will work, you'll have to get an actual application created on Google App Engine for Java, and deploy the application. But you can safely call the method to send email when running it in Eclipse...it just won't send the email.

Verification Servlet

It might seem odd to create the servlet to respond to the user clicking the link in the email when we haven't written the code that generates that link yet. But it all has to be done in some order, so I'll do the processing of the click first.

I'll start by copying FrontPage.java and calling it ValidateEmail. I'll also add in web.xml mappings so that /validate-email calls the ValidateEmail servlet.

To recap the changes to User to support all this, I added in an email field (a String), an emailVerified field (a boolean that starts out as false), and a verificationCode field (String).

In the verification servlet, I want to look at the verification code passed in as a query parameter, and use it to look up the matching User in the datastore. The verification codes will be unique, so I'll only get zero or one User matching it.

If it's one User, I'll mark the email as verified. Otherwise, I display an error message. Here's a portion of that code.


Map root = new HashMap();

String code = req.getParameter("code");

query = pm.newQuery("select from omega.server.User where verificationCode == codeParam");
query.declareParameters("String codeParam");

try
{
results = (List<User>) query.execute(code);

if (results.size () == 0)
root.put ("errorMessage", "Verification code not found. Try resending the verification email.");
else
{
user = results.get(0);
user.setEmailVerified(true);
pm.makePersistent(user);
root.put ("errorMessage", "Email verified!");
}
}
finally
{
query.closeAll();
pm.close();
}

root.put("heading", "Email Verification Results");
root.put("loggedIn", loggedIn);
root.put("name", name);
root.put("userLevel", userLevel);
root.put("title", "GamesByTeens Email Verification");

Template temp = config.getTemplate("error.ftl");

All of that is added toward the bottom of the copy of the FrontPage servlet (the root.put statements show where the overlap is.

To test this I first load the page with a bogus verification code, to make sure it displays the error. Then I would need to force a valid verification code into the datastore. I can do this up at the start of the verification servlet itself, to make sure that the code I wrote works. Just don't forget to remove your testing code after you're done (or at least comment it out in case you need to use it again later).

This highlights an important part of any development, but web development especially. Make sure your code works on its own before you try to hook it to other parts of your site.

That's enough for one post. In the next I'll come back to the last two steps, both of which are modifications to the ProfileServlet.

Friday, January 8, 2010

Unique Display Names, Part 2

Okay, now we finally get to implementing unique display names.

In RPXResults.java, we have a section where we see if the user is already in the system. There are three possibilities now that we want unique display names:

1) The user is in the system, and has a unique display name.

2) The user is in the system, but hasn't updated their profile with a unique display name

3) The user is not in the system yet

For option 1, we can just do what we're already doing, redirect to the appropriate destination URL.

For option 2 and 3, we need to instead redirect to the profile page so they can enter their unique display name. Once they have entered a unique display name, we want to continue to the original destination URL.

That requires a change to RPXResults.java to redirect to the profile page in appropriate circumstances, and a change to ProfileServlet.java to redirect to the destination URL.

Note that we have a slight conflict here, because ProfileServlet.java wants to set a destination URL cookie, too, which will overwrite the one set by whatever page they were on when they clicked Sign In. Rather than complicate things by having multiple cookies for destination URLs, we'll just make ProfileServlet hide the complexity. It'll set its own cookie only when it is displaying its error template (which is the only time the Sign In link will show).

So we have these changes to make:

1) RPXResults must redirect to the profile page when appropriate

2) ProfileServlet must redirect to itself only when displaying the error template

3) ProfileServlet must require the user name be unique

4) ProfileServlet must, on a successful save of a unique user name, redirect to the destination URL

I won't show the code for 2, since that's just moving the two Cookie related lines into the if statement for showing the error template.

We'll start with the RPXResults changes. Right now we have logic like this:


if (results.size() == 0)
{
// create new user and store it
}
else
{
// Existing user, update needed values
}


We're going to change to something like this:


if (results.size() == 0)
{
// create new user and store it
// Redirect to /profile
}
else
{
if (user name is not set)
// Existing user no name, redirect to /profile
else
// Existing user name okay
}


For the purposes of making the code I post as simple as possible, I'm going to strip out the else portion where the existing user name is okay. The only reason to put code there is to update user records from previous versions during development, and now that we're trending more toward having the user enter their profile information rather than getting it from RPX, we won't need that as much.

I'll move the declaration of String destination = null up above the try block in RPXServlet, so that I can set an appropriate destination page manually if needed.

Here's the changed code for the case where the user doesn't exist in the system yet:


if (results.size() == 0)
{
user = new User ();
user.setIdentifier(identifier);
user.setName("");
user.setUserLevel (0);
pm.makePersistent(user);
destination = "/profile";
}


Note that I don't set the user name from the RPX data now. I'll let the user type in whatever they want in their profile.

The else part of that if is for when they're already in the system, and we just need to make sure that they already have a unique user name set:


else
{
user = results.get(0);

if (user.getName().equals(""))
destination = "/profile";
}


This assumes, of course, that ProfileServlet won't let them set a user name unless it's unique. So let's make that change now.

In ProfileServlet, we have code like this:


if (req.getParameter("submit") != null)
{
if (req.getParameter("name").trim().length () == 0)
message = "You must enter a name!";
else
{
user.setName(req.getParameter("name").trim());
pm.makePersistent(user);
}
}


We're going to add some more error checking, so that pm.makePersistent is only called when the user name is both non-empty and unique. To see if the name is unique, we need to build a new query based on the name.

Inside the else from above, I'll put:


Query nameQuery = pm.newQuery("select from omega.server.User where name == nameParam");
nameQuery.declareParameters("String nameParam");
List<User> nameResults = (List<User>) nameQuery.execute(req.getParameter("name").trim());

if (nameResults.size () != 0)


If the nameResults has some records, that still doesn't mean that the name is not unique. After all, maybe they pressed Submit without changing their name...then we'd get exactly one result, and it would be them. So we need to make sure the result we're getting isn't them.


if (nameResults.size () != 0)
{
if (nameResults.get(0).getId() != user.getId())
message = "That name is already taken, please try another";
}

if (message.equals(""))
{
user.setName(req.getParameter("name").trim());
pm.makePersistent(user);
}


And finally save the user name is it turns out that none of our error checking found problems.

That still leaves us with needing ProfileServlet to redirect appropriately. There's actually a bit of complexity here. We have a few cases to deal with:

1) They clicked on the Profile link, so our destination URL cookie is set to /profile

2) They clicked on Sign In and got here via RPXResults, so our destination URL cookie is set to something else, but their name wasn't unique so we need to stay here for now

3) They clicked on Sign In and got here via RPXResults, so our destination URL cookie is set to something else, and their name is okay so we can redirect

I'll focus my code on the else, just after the call to pm.makePersistent. It won't be pretty, but it'll work (this, by the way, is how unmanageable code evolves, with ongoing changes to existing code...it's a good idea to stop and refactor the code now and then to clean it up).

Just after the call to pm.makePersistent, I'll get the destination URL from the cookie and see if it's /profile. If it isn't, I'll redirect to it. It'll look something like this:


if (message.equals(""))
{
user.setName(req.getParameter("name").trim());
pm.makePersistent(user);

String destination = null;

Cookie [] cookies = req.getCookies();

if (destination == null && cookies != null)
{
for(int i=0; i<cookies.length; i++)
{
Cookie cookie = cookies[i];

if ("destinationURL".equals(cookie.getName()))
destination = cookie.getValue();
}
}

if (destination != null && ! destination.equals ("/profile"))
{
resp.sendRedirect (destination);
return;
}
}


Be prepared for there to be bugs with this, since we have enough cases that you should be worried about the stability of the code. Before going much farther, you really should redesign ProfileServlet to be more straightforward.

But, it should mostly work and shows the techniques to create the same sort of experience you're used to on well done membership sites.

Next up, I think, will be adding an email address to the user's profile, and requiring that their email address be verified before it's used by the system. This will introduce us to the Mail API.

Unique Display Names

I'm going to focus on new techniques in the coming posts, because the semester is starting.

I've decided to make display names unique in the system. I'm not especially happy with this, since it leads to funky display names like Dave5386. But, if I'm going to provide some sort of forum interaction eventually, I also need a way to differentiate between people who might decide to use the same display name.

I could simply require and display some context to the display name, such as "Jay, Muskingum University". But, providing information online for kids that might be used to locate them is not a good idea.

So, I'll go with unique display names. This means I need to change what I already have a bit.

RPXResults.java is what first puts the user into the datastore. Right now it's getting the user name from the RPX records. That isn't guaranteed to be unique. I could auto-generate a unique name, but then they might not go back and edit it.

So instead, RPXResults.java will, when a user logs in for the first time, redirect to the page for editing their profile. They will be required to update their profile with a unique name before doing using the rest of the site as a logged in user.

While I'm at it, I also want to modify RPXResults.java so that instead of redirecting to /front when a user logs in and they do have their profile info set, it redirects to the page they were trying to access. So if they save a bookmark on My Contests, and try to go to that after the session has expired, it'll take them to the login page and then to My Contests after they log in. That's more user-friendly, and best to do now before I have too many of those sorts of pages to edit.

Redirect to Target Page

We'll do the redirect to the target page first. This requires me to pass into /rpxresults the target page. Here's a page at RPXNow about passing query parameters through the token URL.

Unfortunately, that didn't work for me. The extra query parameter simply never came through to RPXResults.java.

Luckily we do have another option. We can store the destination URL as a cookie on the local machine and fetch it from RPXResults.java.

We'll do FrontPage.java first as an example of how to modify the servlets. Here's the code to save the destination URL as a cookie:


Cookie cookie = new Cookie ("destinationURL", "/front");
resp.addCookie(cookie);


I added this to just before the template is displayed. Now we need to modify RPXResults.java to use the value of this cookie as the destination URL. Here's the code that would replace the resp.redirect ("/front") line:


String destination = null;
Cookie [] cookies = req.getCookies();

if (destination == null && cookies != null)
{
for(int i=0; i<cookies.length; i++)
{
Cookie cookie = cookies[i];

if ("destinationURL".equals(cookie.getName()))
destination = cookie.getValue();
}
}

if (destination == null)
destination = "/front";

resp.sendRedirect(destination);


Now that RPXResults.java is set up, I can go back and modify the ProfileServlet and AdminServlet classes to add the cookie in the same way that the FrontPage servlet does.

Now if I remember to do that for every servlet I add after this, the user will always get to the page they wanted to get to, even if they have to log in first.

That's probably long enough for one post, so I'll do unique user names in the next.

Wednesday, January 6, 2010

Template Updates

A quick post about some template updates.

Following the pattern I used for navigation.ftl, I created header.ftl and moved everything up to and including the opening body tag into it. I replaced the page title with a Freemarker variable, so that each servlet could specify the page title (I then modified each servlet to set that page title). Every other template then included header.ftl at the top.

I did the same with footer.ftl, putting into it just the closing body and html tags for now. Later I'll have more to put there, and will have a central place to put it.

As an example of the end result, here's what my error.ftl looks like now:


<#include "header.ftl">

<h1>${heading}</h1>

<#include "navigation.ftl">

<p>${errorMessage}</p>

<#include "footer.ftl">


Nothing complicated here, just taking a moment to set the templates up for easier modification later on.

Monday, December 21, 2009

Contest Work Flow

As I started thinking about the Create Contest page, I realized that I hadn't thought enough yet about the contest work flow. How does a prospective contest admin request that a contest be created? Who can see the help information we'll make available for contest admins? How does a contest admin get selected?

So, in the interests of having as few major changes later as possible, I'm going to take a moment to lay out how I think it'll work.

Step One

A prospective contest admin views the site as a visitor. There will be a page they can access that will have some basic info on what's required to run contests, a "Want To Run A Contest?" sort of page.

That page will have a link form they can fill out to be made a contest admin. They will need to be logged in as a member to view the form and submit it. That way their user name is tagged to the request.

We'll require enough info on that form to discourage tire kickers.

Step Two

The site administrator will have a spot where requests to be made contest admins can be approved.

Step Three

Once a member is made a contest admin (this will be a field in the User object), they will be able to see on their "My Contests" page a link to request a contest be created. They'll fill out the basic information for the contest, and also include some budget info (so we know they've thought enough about the contest to make it work).

Until their request is processed, it shows up as Pending on their My Contests page.

Step Four

The site administrator will be able to see and approve or reject these requests. An approved request shows up as active on the contest admin's My Contests page. A rejected request shows up as such on their My Contests page.

Step Five

The contest admin can then fill out the rest of the contest information (deadlines, etc), and start to take applications for the contest.

That seems like a reasonable work flow for the process, and gives me a number of pages I need to create.

The first would be the "Want To Run A Contest?" page. There won't be much on that right now since it'll be mostly information, but there will be a link to the form to request to be made a contest admin.

That's the second page, the form.

The third page would be the one that lets the site administrator see those requests, and approve or reject them. I'll also need a mechanism to notify the member that their request was approved or rejected.

The fourth page would be the one that lets the contest admin request a contest be created.

The fifth page would be the site adminstrator's way to approve or reject those requests.

The sixth page would be the contest admin's My Contests page.

The seventh page would be the one to let the contest admin edit the contest info.

That's a fair amount to work through, but each one is doable based on what we already know. Other things I will want to do before too long include:

o) Make user names unique in the system, so no two members have the same user name.

o) Add an email address to the user's profile, and require that the email be verified in order to receive site notifications

o) And/or, set up a site based messaging system that stores messages on the site and possibly emails notifications of the messages.

o) Change the "you are not logged in" page so that clicking the Sign In link on it will make sure they get to where they were trying to go when they finish signing in. So if they're trying to go to the form to request being a contest admin, they get there instead of the front page after they sign in.

Those will each require some new techniques.

Creating The Admin Page

Before we can create a contest, we need to create the Admin page. This is where the site administrator can do things that only site administrators can do. Right now that's only creating a contest, but later on we'll have a variety of admin only tasks.

Again, because I'm not a web designer, this page is going to be only a page of links to the admin only pages. I will add on some extra security...not only does someone have to be logged on to see this page, but they have to also be a site administrator.

I'll start with the web template. Here's what will go between the body and /body tags in my new siteAdmin.ftl:


<h1>Site Administration Options</h1>

<#include "navigation.ftl">

<a href="/newContest">Create A Contest</a>


I also need a template for when they don't have enough permission to view the page. I already have one for when they're not logged in. Rather than create a template for each possible error condition when the only difference in those conditions is the text displayed, I'll do some refactoring here and create an error.ftl template.

This will be similar to profileNotLoggedIn.ftl, except I'll put ${heading} between the h1 and /h1 tags, and modify ProfileServlet.java to pass in "User Profile" in the data model as the heading when they're not logged in. I'll also replace the "not logged in" text with ${errorMessage} and modify ProfileServlet.java to add that to the data model with the not logged in message. ProfileServlet.java will also be modified to use error.ftl instead of profileNotLoggedIn.ftl.

Okay, back to creating AdminServlet.java. I'll use ProfileServlet.java as a basis, since it does something similar. I'll change the successful template to siteAdmin.ftl, the error message to "You do not have permissions to view this page", and the heading to "Site Administration Options".

I'll also remove the bits that check for the submit button and update the user name, since those are profile specific. Along with that, I can get rid of the message field from the data model.

The last modification is to require that they not only be logged in, but that they actually be site administrators. The bits inside the try block should now read:


List<User> results = (List<User>) query.execute(userid);
User user = results.get(0);

Integer userLevel = user.getUserLevel ();
String name = user.getName ();
root.put("userLevel", userLevel);
root.put("name", name);
root.put("heading", "Site Admistration Options");

if (userLevel > 0)
{
template = "siteAdmin.ftl";
}


I then need to change the error handling a bit, so that the error template is shown no matter what went wrong. I'll take the else out, and replace it with:


if (template.equals("error.ftl"))


That way, the error template displays if the user wasn't logged in, if they aren't a site administrator, or if something weird happened with fetching from the data store.

Because the user might be logged in, I also need to take the root.put("loggedIn", false) in that error block, and move it to just after declaring the root variable. Then, under the if (session != null) line, I'll use root.put("loggedIn", true) to change it to true. This ensures that the navigation.ftl has the loggedIn variable set correctly for displaying the right navigation bar.

I'll also go back and make that same else change in ProfileServlet, since it's more robust.

Oh, and I also need to modify web.xml to use AdminServlet for calls to /admin. Note that the only way to test the error handling in the admin panel is to manually type in the URL for the admin panel into the browser. If our navigation template is working correctly, we won't see links to the admin section.

I left some of the code changes deliberately vague. You should be able to follow what needs to be done, and experimenting to get it right will help you figure out how everything fits together.

All of that was just to get our page for the admin options. We still need to create the page for making a new contest.

Friday, December 18, 2009

Next Steps

We've gone about as far as we can with the simple data store we have right now. To add more functionality to the site will need more data identified.

For example, I know the site needs to track contests. What sort of data is kept for each contest? What sort of data is kept for a contestant? For a judge? And so on.

This will be a lot like designing a relational database. The App Engine data store doesn't support the same set of features as an SQL compliant relational database, but you should really design your data store thinking that way. Figuring out the relationships between the entities in your data store, the cardinality, etc, all helps in the design process.

I'm a big fan of starting small and working my way up in database design. I've seen too much time spent on hugely detailed database designs, only to find something unexpected that requires the entire thing to be redesigned.

So, start small.

Contests

The site allows contests to be run, so obviously I'm going to have a Contest object that stores data about a contest. The data elements I come up with here define how I look at contests.

For example, is a contest something that has dates associated with it? If so, one date or more? Is there a deadline? Is there a starting date? Is there a limited number of participants? Can there be more than one contest administrator?

Some of these questions can be answered later, but as soon as I put a field into my Contest object, I'm starting to answer questions about contests.

So again, I'll start small so I don't back myself into a corner.

Here are the basic fields I'll store for contests: name, description, contest administrator, application date, starting date, and deadline.

I'm sure I'll add more later, but for now that will get me started. The basic idea is that a contest can be created, but members cannot apply to be a contestant until the application date. The contest doesn't actually start until the starting date (this might affect availability of the contest forum, for example). Submissions can be made from the starting date to the deadline.

A contest is created by the site administrator, who assigns a contest administrator to it. The contest administrator can then edit the contest data, approve applications, etc.

This is a simple start, but will allow us to explore some more new techniques. As we go we'll add in applications for contests, submissions, judging, etc, expanding the Contest object as needed.

Handling Profile Updates

We have a profile page with a form that allows logged in users to update their name. Now we need to add handling to ProfileServlet that will update the data store when the Save Changes button is clicked.

The trick is to know if the servlet is being called by clicking on a My Profile link, or as a result of the Save Changes button being clicked. The secret to that is to look at the form submission data provided to every servlet.

The HTTPServletRequest object (passed into the doPost and doGet methods by the web server) contains a method called getParameter. Pass it the name of a form element, and it will give you back the values associated with that element. If the element doesn't exist, you'll get null back.

So code like this:


if (req.getParameter("submit") == null)
{
// Servlet called because of link click
}
else
{
// Servlet called because of button click
}


will allow you to decide which situation is calling the servlet, so you can make the appropriate response.

In the case of the profile page, I still want to display the form after they click submit, but I want to update the data store, too. So I'm going to have code like this just after I fetch the user record from the data store, but just before I get any values out of it:


User user = results.get(0);

if (req.getParameter("submit") != null)
{
user.setName(req.getParameter("name").trim());
pm.makePersistent(user);
}

String name = user.getName ();



This completes the form processing round trip. We really should add some error checking and error messages, too. For example, someone shouldn't be able to save a blank name. Add appropriate error handling and messages to your own projects.

Following this same pattern, you can add however many profile fields you need. Just remember to add them to the User object, too, if they aren't already in there.

Removing Duplication From Templates

A common issue with web design is that you have the same content on multiple pages. Top navigation bars, for example, are often identical across every page on the site. You certainly don't want the navigation bar to actually be in every page, because then modifying it is a nightmare of changing multiple files.

Instead, you want to put the navigation bar into a single file, and include that file at the appropriate point in your other pages.

Here's how to do that in Freemarker. Create a template, for example navigation.ftl. Copy the contents of Front.ftl over to it, and then remove everything above the first Freemarker #if statement. Go down and remove everything below the last </script> statement. What you're left with is just the navigation bar and the RPXNow script to make the sign in work.

Save navigation.ftl and edit Front.ftl.

Remove the navigation bar and script block from Front.ftl, and replace them with this line:


<#include "navigation.ftl">


Do the same thing with profileLoggedIn.ftl and profileNotLoggedIn.ftl. Now, any future pages you create that need the top navigation link, just use the include statement.

Use the same technique with any HTML or FreeMarker code that is identical between more than one page.

Now that our digression on reducing duplication is over, back to form processing.

Wednesday, December 16, 2009

Adding The Profile Page

I want the user to be able to edit their profile page, so they can change their name to something other than what RPX reported. And so they can add information not given in RPX (for example, in my contest site I might want their birthday so I can calculate their age, and so I can display a Happy Birthday message on their birthday).

In the FreeMarker template for the front page I put the link for the profile page as /profile. That will have to map to a Java servlet that displays the profile editing page for the currently logged in user, and saves changes to the data store.

This will also let us set up a pattern for pages that require the user to be logged in to view, as well as a pattern for pages that do form processing.

So let's add a handler for the profile page, and start out by requiring the user to be logged in to view it. If they aren't logged in, they'll get a message and a Sign In link.

If you don't remember how to add a new servlet, read my original post on Java Servlets. Add a new servlet mapped to /profile. I'll call mine ProfileServlet, so the code for it will be in ProfileServlet.java in my project.server package.

In ProfileServlet.java, I'll use code like this to show one page if they're logged in, and another if they're not:


Map root = new HashMap();
String template = "profileNotLoggedIn.ftl";

if (session != null)
{
// Get the User's name
Long userid = (Long) session.getAttribute ("userid");
PersistenceManager pm = PMF.get().getPersistenceManager();

Query query = pm.newQuery("select from omega.server.User where id == idParam");
query.declareParameters("Long idParam");

try
{
List<User> results = (List<User>) query.execute(userid);
User user = results.get(0);
String name = user.getName ();
Integer userLevel = user.getUserLevel ();
root.put("loggedIn", true);
root.put("name", name);
root.put("userLevel", userLevel);
template = "profileLoggedIn.ftl";
}
finally
{
query.closeAll();
pm.close();
}
}
else
{
root.put("loggedIn", false);
}

Template temp = config.getTemplate(template);

PrintWriter out = resp.getWriter();

try
{
temp.process(root, out);
out.flush();
} catch (TemplateException e)
{
e.printStackTrace(out);
}


Notice how the FreeMarker template we use depends on whether the user is logged in or not. This allows us to separate out those pages for web design purposes, rather than forcing the web designer to put logic into the pages.

I'll need to create both FreeMarker templates. profileNotLoggedIn.ftl will just be a simple error page with the standard links across the top. profileLoggedIn.ftl will display a form, populating the fields with the data from the data model.

I'll create those templates by copying over my Front.ftl file and adding the extra bits. Next post we'll see how to centralize some of the common navigation elements so we aren't duplicating them between templates.

Here's the form from profileLoggedIn.ftl:


<form action="/profile" method="POST">
<input type="text" name="name" value="${name}">
<input type="submit" name="submit" value="Save Changes">
</form>


This is the simplest form possible that will do the job. It really should be a bit fancier, providing at least a label for the edit field.

Right now clicking Save Changes just redisplays the form. After we remove the duplication among the templates, we'll add in handling of the form to actually update the data store.

Creating The Front Page

Okay, now to create the front page template in FreeMarker.

Keep in mind that I'm not a web designer, so I'm not going to attempt to make these pages look pretty. That's part of the advantage of using a templating system...as long as I put the data into the data model for the template, I can create whatever ugly page I want and later have someone else rewrite the template to look nice. It'll still all work.

We've seen before that a FreeMarker template is just HTML with some FreeMarker code mixed in. See my previous post on Creating A FreeMarker Template if you missed it.

I'm going to start with that template, since it was designed to be the front page, and already has a Sign In/Sign Out link on it.

I want to have a set of links across the top that show based on the level of the user. Some of this is context dependent. A member might be a contestant in one contest, a judge in another, etc. Site administrators are special, though, so we'll add a field to the User object to account for them. I'll call this field userLevel, and a 0 in that field will indicate a member (who might be a contestant, judge, or contest admin) and a 1 in that field will indicate a site admin.

The user level will need to be added to the data model for the front page. I'll leave that as an exercise for the reader...it follows the same pattern we've done before. Be sure to also add it to RPXResults.java so it's set similar to the user name. If you get a NullPointerException when running this, it's probably because you forgot to update users already in the data store to set their user level.

So the links across the top will be: Sign In/Sign Out, My Profile, My Contests, Admin. I'll leave off the forums for now and we'll handle those later.

Here's the FreeMarker template code for this:


<#if loggedIn>
<a href="/signout">Sign Out, ${name}</a>
  
<a href="/profile">My Profile</a>
  
<a href="/contests">My Contests</a>
<#if (userLevel > 0)>
  
<a href="/admin">Admin</a>
</#if>
<#else>
<a class="rpxnow" onclick="return false;"
href="https://omega.rpxnow.com/openid/v2/signin?token_url=http://localhost:8080/rpxresults">
Sign In
</a>
</#if>


This just builds on what we already did. Note, though, the embedded if statement to see if we should show site admin level links. The parentheses around the condition are important, otherwise FreeMarker will see the greater than sign as the end of the tag, not as a condition.

Also note that the URLs given for the links don't go anywhere yet. We'll have to add those pages.

I also want some text explaining what the site is about, and a list of current activity. The list of current activity will have to wait until we have more of the site done, and the text explaining what the site is about isn't very interesting in terms of programming, so we'll stop here.

The next step is to allow the user to edit their profile. This will get rid of the ugly display name and replace it with one of the user's choosing.

Creating The Front Page Data Model

Okay, so I know I need a value called name in my front page data model. How do I get that?

It's stored in the User datastore, so I need to look it up there. In my FrontPage.java, I already look at the session to see if the user is logged in or not. I'd said before we could also look up the User record in the datastore...now's the time we do that.


HttpSession session = req.getSession(false);
boolean loggedIn = false;
String name = "";

if (session != null)
{
loggedIn = true;

// Get the User's name
Long userid = (Long) session.getAttribute ("userid");
PersistenceManager pm = PMF.get().getPersistenceManager();

Query query = pm.newQuery("select from project.server.User where id == idParam");
query.declareParameters("Long idParam");

try
{
List results = (List) query.execute(userid);
User user = results.get(0);
name = user.getName ();
}
finally
{
query.closeAll();
pm.close();
}
}

Map root = new HashMap();
root.put("loggedIn", loggedIn);
root.put("name", name);


Note that we're looking up the User record by id this time. That's the automatically generated id the datastore assigned when we created the User record, and that's what we stored in the session. We can use it to pull the entire User record out whenever we need it.

Now let's update the FreeMarker template to just display this new name, so we know that we've gotten it correctly from RPX. I just changed my current front.ftl like this:


<#if loggedIn>
<a href="/signout">Sign Out, ${name}</a>
<#else>


Unfortunately, when we run this and sign in, we can see that the DisplayName isn't always what we want. For example, if you sign in with a Gmail account, the DisplayName comes up as the user id, not the name.

We could continue working with the RPX results to see what fields might fit what we want, but ultimately those are dependent on the particular OpenID provider. So we'll go ahead and make ourselves independent of those, and create a user profile that we ask all new users to fill out. This will include their name, so we know exactly what they want to be called.

Note that I will still try to populate the user's profile from their RPX results. This is a convenience for the user...if the RPX results provide the right info, they don't have to enter it themselves.

Making The User Datastore More Robust

On the front page, I want to expand the data model for the template. I want to be able to display a nice "Welcome back, Jay" sort of message, incorporating the member's name.

This means I have to get the user's name from the RPX results. While I'm at it, I want to make the User records more robust...for example, let's say that I have a hundred people sign up on the site, and later decide I want to start pulling their middle name from the RPX results.

The way the code is written now, I only pull from RPX results when the user first signs in to the site. I want to change that so that on future sign ins, if I discover that the datastore is missing user information I want, I fill that in from the RPX results.

The RPX results has a field called DisplayName that supposedly contains a version of the user's name suitable for display. I want to add that to my user record. I already have a field called Name in the User object, but so far I haven't set it. Here's how I'll set it from within RPXResults.java.

(By the way, I'm going to stop formatting the code for indentation manually in HTML. It's been too much of a pain, and shouldn't affect your ability to understand the code.)


String identifier = info.getElementsByTagName("identifier").item(0).getTextContent();
String name = info.getElementsByTagName("displayName").item(0).getTextContent();

PersistenceManager pm = PMF.get().getPersistenceManager();

Query query = pm.newQuery("select from project.server.User where identifier == identifierParam");
query.declareParameters("String identifierParam");

try
{
List results = (List) query.execute(identifier);
User user = null;

if (results.size() == 0)
{
user = new User ();
user.setIdentifier(identifier);
user.setName(name);
pm.makePersistent(user);
}
else
{
boolean changed = false;

user = results.get(0);

if (user.getName () == null)
{
user.setName (name);
changed = true;
}

if (changed)
pm.makePersistent(user);
}

HttpSession session = req.getSession();
session.setAttribute("userid", user.getId());
}
finally
{
query.closeAll();
pm.close();
}


Note how I set the name when the User already exists, but there's nothing in the name field. That allows me to add new RPX fields that I'm interested in later, and have existing Users pick up those fields.