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.

New Site Design

Here's a rough design of the new site. Keep in mind this is just to give me an idea what pages need to exist, and what features I want. I reserve the right to change any of this if it makes sense later.

Front Page

First time visitors still need an explanation of what the site is. That'll be in the text that initially shows in the body.

Returning visitors need a way to see what's new. There should be a list of current and recent contests somewhere, so that a visitor can quickly go to a specific contest.

Visitors will fall into one of several classes: non-members(1), members(2), contestants(3), judges(4), contest administrators(5), site administrators(6). The numbers in parentheses will be used to show which features are accessible to which class of visitor. In general, higher classes can access whatever lower classes can, so if I mark something as (1) to show it's accessible to non-members, then everyone else can also see it.

The front page should have the following links: Sign In (1), Sign Out (2), My Profile (2), My Contests (2), Admin (4), Forums (2).

My Profile

Each member will have some profile information they can fill out. Things like their name, email address, etc. This is private information that can be seen by contest administrators and site administrators only.

My Contests

This page will list all the contests the currently logged in user is in. This could be none.

If the member is participating in more than one contest, this will display a list of the contests with a link to click on to see the contest page. If the member is part of only one contest, the contest page will automatically show.

Contest administrators count as participating in a contest, so they'll see the contests they are administering on this page, too.

Admin

This is the site administration area. Things the site administrator can do here include: creating new contests, modifying user info, seeing site stats, etc. I'll leave this deliberately vague until I see what I actually need in the way of site administration features.

Forums

I may or may not have public forums for the site. Each contest will have a forum specific for it. Due to the site working with kids under 18, the contest sites might be restricted to contestants of that contest. For that same reason, the site-wide forums may be restricted to contestants from any contest.

Contest Site

Each contest will have a site of its own. Each contest site will allow other contestants to see the blogs for each contestant, a snapshot of current work, etc. Each contest site will have a results page that is publicly accessible, that will show final results and links to download games when the contest is done.

Judges will have a section for putting in their ratings for each game in the judging categories. The contest administrator will be able to set judging categories. Winners will be calculated automatically based on judging data.

Each contest site will have a link for applying to be part of that contest. Contest administrators must accept applications before the person becomes a contestant. Contest administrators can specify what information an applicant must provide.

Member Flow

With so many different types of members, it's worth talking about how someone gets to be each type.

A visitor is simply someone visiting the site who hasn't logged in yet. This is the lowest access level.

A member is someone who has created an account on the site, but is not involved in a contest. Someone who is a member in relation to one contest might be a contestant in another, so this is somewhat context dependent. A member has the same access to contest sites as visitors, but may be able to post in public forum areas.

A contestant is a member who has been accepted into a contest. Contestants can see other contestants' blogs and can see the contest specific forum.

A judge is someone who has been invited to judge a contest. They can see all the contest site and the contest specific forum. They also get access to the judging forms for each contestant.

A contest administrator has total control over the contests they run. They can accept applicants, invite judges, etc.

A site administrator can create new contests and assign contest administrators, and has total control over the entire site.

Friday, December 11, 2009

Change Of Direction

It's been a while since my last post...fall semester got in the way.

Progress will continue during the Christmas break, but with a change in direction. The original concept for a community oriented site allowed me to use a lot of cool features I wanted to use. But I've since come up with an actual need for a membership based site to support game creation contests I run for high school students.

So I'll be creating that site here, in the context of showing how to create sites using GWT and App Engine. I may not get to use all the cool features I'd intended, but I'll work as many of them in as makes sense.

What I've done up to this point is extremely generic, and will still apply to the new site concept. The only bit that needs redone is the last post, which is the initial design of the site.

I'll do that next, and then get back on track with the technical bits.

Tuesday, June 16, 2009

Designing The Site

It's time to take a step back, now that we've gotten a set of tools in place to allow a user to log in and to allow us to write templates for dynamic web pages.

We need to decide what features we want on the site. What pages will there be, what links will be on the pages, etc. Doing a rough design now will make things move much faster in the next steps.

A web designer might work through this by actually designing web pages. I'm not a web designer, so I'm going to do it by thinking about the different ways people might use the site, and then just listing out the functionality that each page should have.

If you remember, the project I'm doing will be a community events site. I've generalized it a bit since that post, into more of a community portal. That allows me to add lists of recommended restaurants, etc, not just events.

People will be able to use the site either as a guest, or logged in. Some features will only be available to logged in users. I'll mark those features with (*) in the lists below.

Now keep in mind that this is an initial design of the site. I reserve the right to change this around at any point during future development, if it seems like a good idea. Designs are absolutely necessary for organizing your thoughts...but don't let them become straitjackets.

Front Page

First time visitors to the site need a brief explanation of what it is, while returning visitors probably want to see a list of upcoming events (or other recent activity on the site).

The features I'd want on the front page include: list of recent activity (this will be generated dynamically), a Sign In link, a Sign Out link (*), an Edit Profile link (*), a Create Event link (*), a Create Recommendation link (*), a Create Group link (*), an Events link, a Recommendations link, a Groups link, and a Members link.

There will be more eventually, but right now those are the main ones. Groups will represent organization in the community...churches, schools, businesses, government offices, etc.

The Members link will go to a list of members and the ability to view their profiles.

Edit Profile Page

This allows the logged in users to edit their profile information. I don't really know yet what all profile information I'll have, so for now this will just be the user's Name.

Create Event Page

This page will allow a logged in user to create a new event. Some of the info that will be needed: event name, description, time, date, location, and group affiliations. Maybe more later, but that's enough for now.

The location will allow us to generate maps to the event, and the location and date/time will allow us to pick up weather forecasts for the event.

Eventually we might want to categorize events based on age range, cost, etc, to allow users to search on those fields. But we'll save that for later.

Events Page

This page will list the most recent events, and allow browsing/searching events. A logged in user will be able to mark themselves as attending an event.

Recommendations Page

This page will list the most recent recommendations, and allow browsing/searching recommendations.

Groups Page

Allows the user to browse groups and join them.

Members Page

Allows the user to browse member profiles. Might need to be logged in for this, for privacy reasons.

Create Recommendation Page

Allows the logged in user to create a new recommendation. We need a good search algorithm to make sure we don't get duplicate recommendations (e.g. one for "Johnsons" and one for "Johnson's".

Create Group Page

Allows the logged in user to create a new group. Need the group name, description, address, etc.

That's enough to get started. The next several posts will deal with creating FreeMarker templates for those pages and designing the data model needed for each. By the time we're done with this we'll have a much better idea of what entities we'll need in the datastore.

Monday, June 15, 2009

Executing A FreeMarker Template

In our front page servlet, we need to execute the FreeMarker template we created.

We start by creating a Configuration instance. This only needs to be done once per application, so we can use a singleton pattern (remember the PMF class?) Only this time we don't have a utility class provided to us, so you'll have to write your own. Follow the PMF pattern and create a singleton for getting Configuration instances.

You'll end up with something like this (I've left off the package and import statements to save space):


public class ConfigFactory
{
  private static Configuration configInstance = null;
  private ConfigFactory() {}
  public static Configuration get(ServletContext context)
  {
   if (configInstance == null)
   {
    configInstance = new Configuration();
    configInstance.setServletContextForTemplateLoading(context, "WEB-INF/templates");
    configInstance.setObjectWrapper(new DefaultObjectWrapper());
   }

   return configInstance;
  }
}


Note that I have it looking for templates in WEB-INF/templates. That gets templates out of the way, which is generally a good idea to keep things tidy. Now would be a good time to right click on WEB-INF and create a new folder named templates, and to drag Front.ftl into that new directory.

In our front page servlet, we can now use the singleton class to get a Configuration instance.


Configuration config = ConfigFactory.get(getServletContext ());


Now we need to create the data model for the template. In the case of our simplified front page, the data model just has a boolean named loggedIn. Before we can do that we need to know if they're logged in or not.

If they are logged in, we'll have their user id in the session. So let's get the session and check.


HttpSession session = req.getSession(false);


I pass false into getSession this time because if there isn't already a session, I don't want to create one. If no session already existed, getSession(false) returns null.


boolean loggedIn = false;

if (session != null)
  loggedIn = true;


We could very well have used getAttribute to get the actual user id and looked up the User record in the datastore, but this will do for the moment. Now that we have our loggedIn variable, let's create the data model:


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


That's it. A data model is nothing more than a map of values. You can nest maps inside the map to create objects with fields for the template to use.

Now we need to get the template:


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


If you named your template something other than Front, you'll want to modify that. Now that we have the template, let's execute it:


PrintWriter out = resp.getWriter();

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


Note that printing the stack trace to the user's web browser isn't the best way to handle errors. We'll want to do something better than that later, but for now it'll serve.

We have one last piece before this will ever show "Sign Out". That's to cause the RPXResults servlet to redirect the user's browser back to the front page after the session has been set.

At the bottom of doPost in RPXResults, this should do it:


resp.sendRedirect("/front");


Assuming that /front is what you put in for the url mapping for your front page servlet.

You should now be able to run the application, click Sign In, go through the RPX process and end up back at the front page with the Sign Out link showing. This shows a very simple example of using templates to display pages with dynamic content. If our content is going to be updated according to user action, we'll use GWT widgets, but for simple content that stays the same once it's generated on the page, templates are a great option.

Creating FreeMarker Templates

Before we can execute a template, we have to create one.

A template is basically just an HTML file with some extra bits for making minor decisions and displaying collections of data. When we create the template we decide what kind of data we'll need, but we don't know exactly what values will be assigned. That happens later, when the servlet gives the template values for the data it needs.

For the front page, let's say that we just need to know if the user is logged in or not. Eventually we'll want their name, too, but remember that we haven't put that into the datastore yet. So we'll settle for knowing if they're logged in.

In Eclipse, right click on the war directory in your project and choose New->File. Name it something like Front.ftl. The extension must be .ftl for the FreeMarker plugin to operate on it.

Now let's copy over the HTML from the project.html file, and remove the bits we don't need. We'll end up with something like this:


<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<link type="text/css" rel="stylesheet" href="Omega.css">
<title>Web Application Starter Project</title>
</head>

<body>

<h1>Web Application Starter Project</h1>

<!-- Put your RPX Sign In link here -->

<!-- Put your RPX Javascript here -->
</body>
</html>


Except that you would have actually copied your RPX Sign In link and Javascript over from the project.html file.

Now picture that if the user is signed in, we want to display a Sign Out link instead of the Sign In link. Let's just go ahead and put a Sign Out link in there, just before the Sign In link.


<a href="/signout">Sign Out</a>


We don't have a signout servlet yet, but that's okay for now. We can use an if statement inside a FreeMarker template, similar to in Java. Let's pretend that we have a variable called "loggedIn" that's true if the user is logged in.

If we do something like this:


<#if loggedIn>
<!-- Put Sign Out link here -->
<#else>
<!-- Put Sign In link here -->
</#if>


Replacing, of course, the comments with the appropriate HTML, then the template will only display one of the links, depending on the value of loggedIn.

So how does the value of loggedIn get set? That's over in our front page servlet, and the next post.

Installing FreeMarker

There are two pieces to install with FreeMarker. One is the server side code needed to allow the servlets to run template files. The other is the Eclipse plugin that provides support for writing template files.

Let's start with the plugin (you remember installing all those plugins earlier, right?). Go to this page and copy the link for the version of Eclipse you have (use the Stable Updates links). Install just the FreeMarker IDE from that site.

Restart Eclipse at the end, and we'll move on to the FreeMarker server libraries. Download the latest stable version at the FreeMarker download page. Unzip the file onto your desktop, and look for lib/freemarker.jar (the rest of the zip file is documentation that you may want to keep).

Copy freemarker.jar into the war/WEB-INF/lib directory of your project in Eclipse. Right click on the file, choose Build Path->Add to Build Path.

That's it, we can now both create template files and execute them from servlets. We'll see how in the next few posts.

UPDATE: If you're using more recent versions of GAE and Freemarker than are installed in the labs (very likely if you've installed the latest versions at home), there might be a conflict between them. If you get an error message when compiling code that uses Freemarker templates about TreeNode being a restricted class, then you have the versions that are not compatible.

There's a workaround here. The workaround is to to create a TextBlock.java file in the freemarker.core package (the thread linked to gives the code you put into that file). If you got it right, you should see, under your src directory in Eclipse, a freemarker.core package (at the same level as your project.server and project.client packages).

Note that to successfully copy the code from the thread, you need to click the Read More link at the bottom of that message (since not all the code shows in the thread view). Then, after you paste the code into Eclipse, you'll get some errors because of line wrapping in the thread. Just back the offending lines onto the end of the previous line to remove the wrapping, and it'll compile.

Eventually, this will be fixed in one way or another (probably with a Freemarker update), and your version of TextBlock.java can be removed.

A One Page Web Site

We'll now create a one-page web site that properly reflects the user's logged in status. Where our current first page has the "Sign In" link, that will be "Sign In" if the user is not logged in, or "Sign Out" if the user is logged in.

Our RPXResults servlet will do its thing silently, and then redirect back to the first page. We'll have to change the first page to be a servlet rather than a static HTML page, so that we can dynamically change it based on the user's logged in status.

Note that we could do the same thing with a static HTML page that included GWT components. We could in a GWT pane display the "Sign In" or "Sign Out" links, and use GWT calls to the server to see if the user is logged in or not. Eventually we'll get around to that sort of thing, but for now I'd like to keep it as simple as possible.

First step, change the main page of the site to be a servlet. Right click on war/WEB-INF/web.xml and choose Open With->Text Editor.

Enter the info for a new servlet (you remember how, right?) to be used as the main page. You don't need to have a servlet mapping for this servlet, but it might come in handy later to have it map to something like /front. Then copy the welcome-file-list section down to just before /web-app, and change the welcome file to be the name you gave to the servlet.

Now create the servlet (which you also remember, right?). Go ahead and put in the code to create the PrintWriter.

Let me describe the easy way (at least in the short term) to do this, which makes the code very ugly and the HTML almost impossible to maintain. Then we'll look at the hard way to do it, which makes the code nicer and the HTML easier to maintain.

The easy way is to output the HTML using our PrintWriter:


writer.println ("<html>");
writer.println ("<head>");
writer.println ("<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">");


Etc, printing the entire HTML file using the PrintWriter. Notice the need to escape the double quotes inside the output using a backslash. This method is easy, because we don't have to work with anything but the servlet, but it makes it nearly impossible to easily change the HTML later.

The harder way (in the short run at least) is to use a templating engine. A templating engine is server side code that allows us to write the HTML in a file of its own, with a mini-programming language for making decisions. The servlet will execute that template file and provide any information it needs to make decisions. The result is the HTML that should be sent to the client.

There are lots of templating engines out there. I'm going to use FreeMarker, which integrates well with Java servlets and Eclipse.

So before we can use FreeMarker, we have to install it and the Eclipse plugin for it.

Session Handling

Normal web pages are "stateless". This means that every time they're loaded is like the first. They don't keep any information around about the user loading them.

This doesn't work for web applications, where we do want to keep information about the user around from page to page. To carry information over from page load to page load, we need to create something called a "session".

A session is basically an id that gets sent to the browser and stored in a cookie. Future page loads on the same domain will send that session id along with the page request. The server then uses that session id as the primary key in a datastore lookup to load in any information you wanted to carry over from page load to page load.

By default, session support in the Java version of App Engine is not enabled. Right click on war/WEB-INF/appengine-web.xml and choose Open With->Text Editor. Just before the ending /appengine-web-app tag, put this line:


<sessions-enabled>true</sessions-enabled>


This makes the class HttpSession available to your servlets. The HttpSession class is how you'll set data on one page and get it on another page. Inside RPXResults.java, after we have the User instance, we can get the HttpSession instance like this:


HttpSession session = req.getSession ();


The HttpSession class then has methods for getting to data you've previously set, and for setting data. Think of it like a map between a property name and a property value. Use the setAttribute method to set property values, and the getAttribute method to get property values.

Another useful method on HttpSession is setMaxInactiveInterval, where you specify the number of seconds the session will live without activity (e.g. the timeout). Pass in a negative value to make the session live forever (or at least until the user clears their cookies).

What do we need to store in the session? Not much, really. The datastore already will have our User records (and other entity records we'll add later), so in the session we just need enough to get to the User in the datastore.

So let's store the user's primary key in the session. From there we can look up the rest of their fields.


HttpSession session = req.getSession();
session.setAttribute("userid", user.getId());


And that's it. In other code we can use getAttribute to fetch the user's id, and then use the datastore to fetch the full User instance for that id.

So the user is officially logged in at this point, although we don't have much of a web site yet to demonstrate that fact. So that's next, to develop a very simple web site that will reflect the logged in status of the user.

User Datastore Interaction

Okay, here's the complete code (in RPXResults.java) for creating users only if they don't already exist:


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

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

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

 if (results.size() == 0)
 {
  User user = new User ();
  user.setIdentifier(identifier);
  pm.makePersistent(user);
 }
 else
 {
  User user = results.get(0);
 }
}
finally
{
 query.closeAll();
 pm.close();
}


We could do more about populating User fields from the RPX profile, but this is reasonably complete otherwise. Does this mean the user is logged in?

Unfortunately, no.

By the end of the code, we have loaded the User instance from the datastore (or created it). To get the other pages in the application, and the GWT widgets, to know about the user we have to start a session.

Next post, sessions.

Searching For A User

Searching for a user in the datastore uses the Query interface. We get a Query by aking for one from a PerstistenceManager. There are several ways to populate a Query...I'll use the SQL like form:


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


Note the use of the project.server.User notation to say which class you want to load. Change the project to be the name of your project.

If your first thought on seeing that wasn't, "What about SQL injection attacks?", then you should read this tutorial.

In this particular instance, the likelihood of suffering an SQL injection attack is low, since the data is coming from a request to RPX. Nobody can just load up the RPXResults servlet in a browser and inject an arbitrary identifier.

But, it never hurts to be safe, so let's use a parameter to lessen the risk:


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


Note that parameters may lessen your risk of an SQL injection attack, but it depends on the implementation of JDO. I'd like to think Google is on top of its game with App Engine security, but we should be wary of relying on that for pages that are susceptible to SQL injection. When we get to a page that's more vulnerable, we'll look at ways to protect the page.

Now that we have a Query, we need to execute it and provide the value for the parameter:


try
{
  List results = (List) query.execute(identifier);
}
finally
{
  query.closeAll();
}


We can then use normal List methods for iterating over elements of the list of Users that has been returned. Note that since our identifier field is not a unique field, the List could theoretically have more than one element. But we'll write the code so that we only have one User per identifier, so the List will either have zero elements (for a new user) or one element.

We'll put the pieces together in the next post.

Creating A New User

Back in RPXResults.java, we've gotten our identifier and need to create a User. With JDO, that's a two-step process. First we create the User instance and populate it. Second we store that data to the datastore.

Creating the User is nothing new:


String identifier = info.getElementsByTagName("identifier").item(0).getTextContent();
User user = new User ();
user.setIdentifier(identifier);


We could search through the profile data to see if any of the various name fields were provided and use those to pass to user.setName, but I'll opt for the simple route of asking them for their name later. For now we'll leave it null.

Now for storing the User to the datastore. We need to get an instance of a PerstistenceManager from the factory:


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


Then we call makePersistent:


try
{
  pm.makePersistent(user);
}
finally
{
  pm.close();
}


The odd try/finally usage is worth a comment. There's no catch block needed, because there are no checked exceptions thrown by makePersistent. So why a try block in the first place? I'd guess it's because makePersistent can throw some runtime exceptions, and the finally block is a way to ensure the manager gets closed even if a runtime exception is thrown.

That saves the User to the datastore. In the next post we'll add in querying the datastore to see if the user already exists, so we only create the user if this is their first time using our application.

Storing Data In App Engine

My students will have had a database course by now, so they'll be familiar with (or at least exposed to) SQL and relational database design.

The bad news is that App Engine doesn't support SQL and it isn't a relational database. Instead it supports an object oriented database and you use Java Data Objects to interact with it.

The good news is that this means you don't have to fool with a database design, you just write Java classes in a certain way, and App Engine takes care of the rest.

The bad news is that Java Data Objects use annotations, a Java feature you won't have seen yet. Annotations allow you to add features to a Java class without using inheritance.

The good news is that JDO does allow an SQL like syntax for querying, so that database course will come in handy (or maybe that's more bad news).

Enough of the good news/bad news bit. Let's dive into JDO.

An object that is stored into the database is called an entity. Entities have properties and a unique key (which can be autogenerated by App Engine, if you like). Oh, and we'll start to use the JDO terminology for database, which is datastore.

To specify a class as an entity, use the annotation @PersistenceCapable on the line before the class definition. For example, we'll need an entity to store information about a user (create this class in Eclipse in your project.server package):


@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class User
{
}


That marks User as an entity that can be stored in the datastore. We also have to mark each field in User that we want stored in the datastore as @Persistent, and one of them as a @PrimaryKey.

I know that I'll need an autogenerated user id as a primary key (because the identifier returned by RPX is too long to be suitable as a primary key), and I'll need some other basic info. Here's what I came up with as a first pass:


@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class User
{
  @PrimaryKey
  @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
  private Long id;
  
  @Persistent
  private String identifier;
  
  @Persistent
  private String name;
}


The valueStrategy argument on the id means it'll be automatically generated for me when I create new Users. Since the fields are private, I'll need public get methods for them, and set methods for the name and identifier. I won't show those here, but go ahead and add them to your version (this is a good time to figure out how to use Eclipse's Refactor->Encapsulate Field option to save yourself the typing).

To work with the datastore itself, we need an instance of PersistenceManagerFactory. The Google docs suggest using a singleton pattern for this, since the class is expensive to instantiate. Go ahead and use their PMF class as is in your project.server package.

In our RPXResults servlet, we'll need to use the identifier RPX provides to see if we already have the user in the datastore. If so, we'll load the existing data. Otherwise, we need to create the User instance and store it to the datastore.

We'll start in the next post with creating a new User and storing it to the datastore.

Tuesday, June 9, 2009

Fetching User's Email From RPX

The next step in the login sequence is to fetch the user's information from RPX. This will include their email, which will be used as our unique identifier for a user.

Now that we have the token from RPX in RXPServlet, we need to load a page from RPX at the URL given in step 2 in the RPX Integration Guide. As of right now, that's https://rpxnow.com/api/v2/auth_info

That page will send back the info we need about the user. Simple, right?

Well, it could be involved. We'd need to use a couple of Java classes to get the data from the URL, and then some other Java classes to unpack the data (it's formatted as XML, a common data interchange format). We'd need to figure out how to use these classes, and then how to format the HTML request to the auth_info page.

Luckily, RPX has provided a Java helper class that does this for us. Follow that link and copy the Java class into a new class in your project.server package. Add a "package project.server;" line at the top, and compile. It should pass with no problems, but will give some warnings. Ignore them.

Now, in your doPost method of your RPXResults servlet, we want to use that class to get the user's info from RPX. Create an instance like this:


Rpx rpx = new Rpx ("PutYourApiKeyHere", "https://rpxnow.com");


Don't forget to change PutYourApiKeyHere to the actual API key listed in your RPX Integration Guide. Then make a call to authInfo, passing in your token (that you should already have in the variable token, from the last post):


Element info = rpx.authInfo (token);


Note that when you try to find an import for Element, you'll get a big long list. The one you want is:


import org.w3c.dom.Element;


That's an XML Element as it's being used by the RPX helper class. Now you have the info from RPX!

Except that it's buried in the XML data structure represented by that Element instance. So we have to do a little bit extra to get it out of there.

First off, though, the data returned by RPX can include different fields depending on which third-party provider the user chose to use (Google, Facebook, Yahoo, etc). The RPX help has a list of possible fields.

You're guaranteed to get the identifier and providerName fields. The identifier is what we're supposed to use to uniquely identify this user in our database. So right now that's what we're interested in getting. Later we may use some of the other fields to prepopulate our user record.

Using the Element that was returned, we call getElementsByTagName and use the name of the profile field we want.


NodeList list = info.getElementsByTagName("identifier");


That returns a list of nodes, because you can use wild cards in the field name. We know we didn't, so there will be exactly one node in the list. We call getTextContent on that node to get the identifier.


writer.println (list.item(0).getTextContent ());


That prints the identifier out to the web page. Not what we'll want to do with it eventually, but at least we can verify that it's working. Run it now and sign in using whatever provider you want, and you should see your identifier come back.

It's a pretty ugly identifier, and too long to use as a primary key. So our next step is going to be to design a sane database structure to use for storing user records. While we're at it we'll structure it so a user can use more than one third-party provider for the same account on our site.

We'll start on that in the next post.

Starting RPXResults Servlet

To create the RPXResults servlet, navigate in Eclipse to your project.server package. Right click on that and choose New->Class. It should prepopulate the package name for you.

For the name of the class, use RPXResults (or whatever you used in your web.xml file in the previous steps). For the superclass, use javax.servlet.http.HttpServlet.

When the class comes up, right click on it and choose Source->Override/Implement Methods. Choose doGet and doPost and click OK.

The doGet method is what is called when the servlet is loaded via an HTTP GET method. That's what RPXResults will do. Just in case they change to POST later, we'll handle that, too. In your doGet method, replace what's there with a call to doPost, looking like this:

doPost (req, resp);
Now in the doPost, we want to just make sure that we're getting a result from RXP at first. We'll worry later about putting in the processing that's needed. Put something like this in doPost:

String token = req.getParameter("token");
PrintWriter writer = resp.getWriter ();

writer.print("RPX sent this: " + token);
The getParameter call on the request gets the token paramter passed by RPX to the servlet. We then use the PrintWriter to just output the token value as our HTML page. The user will see the token value come up after a successful login.

You can test this by running the web application and logging in via RPX. You should see the token value come up in the page after you log in.

Note that this servlet nicely highlights the way servlets work. A request for a web page starts the servlet running (via a call to doGet or doPost), and the servlet simply outputs the HTML that the user should see. In our case, we didn't output HTML, but we could have.

Next we'll put in the code to complete the login processing and set up a session.

Friday, June 5, 2009

Java Servlets

Using Google's App Engine for Java, the server side coding is done using Java Servlets.

Servlets are just Java classes that get run on the server when a web browser requests a URL. A servlet can handle just one specific URL, or a range of URLs using wildcards. You specify which URLs a servlet handles using a URL mapping. The Java class when run must output something that will get sent back to the user's web browser...typically HTML, but in the case of GWT the servlet may return data that gets parsed on the client side.

Open up your web application project in Eclipse, and open war/WEB-INF/web.xml. Eclipse parses out the XML and shows you a structured editor for it. Click on the plus sign beside web-app. The servlet section defines what servlets are available, and the servlet-mapping section defines the mappings between those servlets and URLs.

The sample application has just a single servlet, and a single mapping for that servlet. We need a servlet to handle the RPX results, and we need that to map to /rpxresults. We could just replace the sample servlet, but we need to add new ones eventually, so let's go ahead and add a new one.

You could fool around with Eclipse's XML editor to do this, but I prefer to work in text. Right click on the web.xml file in the project list, and choose Open With->Text Editor. Copy the <servlet> and <servlet-mapping> sections and paste them at the bottom of the file, just after </servlet-mapping>.

Change the servlet-name to some name that you'll use to refer to this servlet in web.xml. Change the servlet-class to the name of the servlet class (we haven't written that yet, so just make this what you plan to name that class). Follow the same package naming that the sample servlet uses, typicall "project.server.classname".

In the servlet-mapping section, change the servlet-name to match what you used above, and change the url-pattern to /rpxresults. That should make the server aware of the servlet and have that servlet invoked when anyone tries to go to /rpxresults.

Now we just need to write the actual servlet class.

Tuesday, June 2, 2009

User Authentication With RPX

The first step is to sign up with RPX.

Nicely enough, they use their own service, so you'll sign in to RPX using a third-party user ID. Pick whichever one you like. Then agree to their terms of service, and create an application. Name it whatever you want, and leave domains as localhost.

The Integration Guide link under Next Steps (lower right after you create your application) will give you the code you need to paste into those HTML pages where you want users to be able to log in.

Now, any sort of user authentication system isn't going to be a two-minute implementation. RPX makes it far easier than it would be to implement ourselves, but there's still some work involved. Plus, I'm assuming you know very little about creating web sites or web development, so we'll take it step by step.

How RPX Works

You'll put some code RPX gives you onto your login page. That gives you a nice widget in your web page that prompts the user to log in via Google, Yahoo, etc. We can even add sites like Facebook and Twitter later.

When the user clicks on one of those links and signs in whereever they chose, RPX is taking care of all the details. When RPX determines that they've successfully signed in, the RPX widget (in your user's web browser) will open up another page on your site.

This other page, called by RPX the token_url, will be sent information by RPX. That page must include server side code that connects to the RPX server to finalize the login and fetch the user's email address.

So the parts we write are:

1) A login HTML page that includes the widget code from RPX

2) A login results page that includes server side code for fetching the user's email from RPX

That login results page also needs to start a session and perhaps create a user account on our site for the user, and no doubt a number of other things we'll discover later. But that's all about our site's book keeping, and not about RPX. For now, we'll just have that page start a session.

Creating The Login Page

The sample project created by the Google Plugin for Eclipse already has a main HTML page. We'll just edit that and create our login page. So start up Eclipse and open up your project. In the project, go to the war directory and look for the HTML file that's named the same as your project.

Double click that to edit it, and let's see what we have to work with. Now go to RPX and log in and go to the Integration Guide.

You'll see some Javascript that's supposed to go at the bottom of the login page. Copy that and paste it just before the closing body tag in the HTML file. You need to change the token_url to the correct URL of the page that will handle post-login processing.

Since you haven't created that yet, let's just make up a name for it. Here's what I ended up with for that line in the Javascript:

RPXNOW.token_url = "http://localhost:8080/rpxresults";

Going back to the RPX Integration Guide, you also need to put a link where you want the Sign In link to appear. I'll paste mine after the H1 heading in the page. Don't forget to change the token_url in this link to match what you put in the Javascript!

Note for later: you should have looked twice at the use of "localhost:8080" in the token_url. That works for when we're running the Google App Engine development server through Eclipse, but not when we deploy the pages to the production server. We'll want to remember to change that before deploying!

Save the HTML file, and run the web application. You'll see the Sign In link. Click it, and you'll see RPX's list of third-party login providers.

That's step #1. Step #2 is to have the server side processing needed to finalize the login once RPX is done with the user. To do that we'll have to get into Java servlets, so that'll be the next post.

Thursday, May 21, 2009

User Authentication

Since we know our application will need to have registered users, I figured I'd start by implementing the user authentication framework.

There are several options, some of which can be used together.

Use OpenID

OpenID is a pretty cool mechanism that allows you to register on one web site, and use that id and password on another web site. So instead of registering with each web site, you only register the once. It's a lot like Google Accounts, but there's no centralized control over where you register. You can register on any OpenID enabled site, and that site then becomes your home site.

The problem with OpenID is that it's still very much a geeky sort of thing to use. I don't want to force non-technical community members to figure out OpenID in order to use my application. It'd be cool to allow OpenID for those who already have those IDs, but I don't want to require it.

On the other hand, the geeks at places like Google and Yahoo allow their ids to be used as OpenID ids, so people might already have an OpenID and not know it.

Use Google's Authentication

App Engine makes it nearly trivial to hitch on to the authentication Google uses for Gmail and other Google services. No code needs written to force someone to log in before seeing a specific page, and only a little code is needed to get the email of the currently logged in user. That email can then be used as a key into an application specific data store.

One problem is that people have to register for a Google Account if they don't have one...that isn't all that serious, since people would have to register on the web application anyway.

Another issue is that this doesn't support any OpenID account, just Google Accounts (which can also be used as OpenID accounts). So an OpenID user who used a different OpenID provider than Google would still have to register for a Google Account.

Maintain My Own Database

The traditional way of handling a web application is for that web application to maintain a list of registered users. A new user must register with the web application, and their user id and password is specific to that web application.

The advantage here is that I can write this so it plays well with OpenID, and have both. Since a Google Account also works as an OpenID account, I'd be automatically supporting those, too.

The disadvantage is that there's quite a bit of code to write, compared to the nearly zero effort of just supporting Google Accounts via App Engine.

What To Do?

Having talked about three options, I think I'll use a fourth.

RPX is a website that allows a site to piggy back on logins people already have, such as OpenID, Google, Yahoo, Facebook, etc. So someone could sign onto my site with their Facebook ID. The basic service is free, so no worries there.

The disadvantage to this is that I'm now dependent on a third party site for processing my user authentication. No doubt this opens up all sorts of security problems, but for the purposes of this course I can accept that. I'll talk at some point during the course about security concerns with web applications, and how to reduce security issues.

For a company, or a site that expected to have a huge user base, RPX might not be the best option (at least not the free version...but then a company could afford the paid version). But to get something up and running easily that allows access to a fairly large number of people, I'll go with RPX.

We'll start to work through the details of this in the next post.

Wednesday, May 20, 2009

Examining The Sample App

When we created our project in Eclipse, we got a sample GWT/App Engine application for free. It doesn't do much, but it's a good way of seeing the sorts of things we'll have to deal with in our own application.

App Engine works by means of Java Servlets. These are Java classes that are associated on the server with a specific URL. When a web browser asks the server for that URL, the Java class is run and it returns data back to the browser. That data may be a complete web page, or it may be smaller amounts of data.

GWT will call servlets in the server by asking for specific URLs. GWT can then parse the data sent back to figure out what to change on the current page.

In the Eclipse project, look under war/WEB-INF and open web.xml. Expand web-app and then servlet, and you'll see that the servlet named greetServlet maps to the Java class omega.server.GreetingServiceImpl. Expand servlet-mapping and you'll see that the servlet named greetServlet also maps to the URL /omega/greet (yours will have project names different from omega).

For servlet based projects, the web.xml file manages the mapping between URLs and Java classes.

You should also see an HTML file at the same level as WEB-INF. That's the HTML file that provides the container for the GWT app. If you look at it, you'll see that there are a couple of empty divs for the name field and the send button. The GWT code in Omega.java fills those in wtih an edit field and a button, and calls the server when the button is pressed.

Now look at Omega.java (again, the name of yours will depend on your project name). In the onModuleLoad method, you'll see what looks like Java Swing initialization. Note though the RootPanel.get lines. Those are actually adding user interface elements to the HTML page itself, in the divs with those specific ids.

Scroll farther down to the MyHandler nested class. This handles the click on the button, or the pressing of the Enter key. Both call sendNameToServer, which is just below them in the file. The line that starts greetingService.greet makes a call to the server. The onFailure method is run if that call fails for some reason. The onSuccess method is run if the call succeeds. On success a dialog box is shown that contains the HTML sent back by the server.

Now look at GreetingServiceImpl.java in the server folder. This is the class that gets called by the client. You can see it just returns some HTML with info about what browser they're running.

That's a quick tour through the sample app. Studying this will give you a better handle on what we'll be doing as we add our own functionality.

How GWT Works & Our First Design Decision

GWT allows you to write Java code, which then gets compiled down into Javascript suitable to run in any major web browser. For Java programmers, this means using your familiar language to create the client side of a web application. Hooray!

A GWT application does run inside of a web page; it's important not to forget that we aren't actually writing Java. We're writing Javascript that runs in a browser, so not everything is the way we're used to it.

Our first major design decision is how to integrate the GWT application into the web page. Here are the options:

HTML First

In this option, the HTML files control the layout and look and feel of the site, while the GWT application will fill in specific areas in the page with their own information. A web designer would create the page, and leave in blank divs for the GWT application to use.

The advantage of this is that the web designer doesn't have to understand anything about GWT. They just have to give their divs appropriate ids.

Pure GWT

In this approach, the basic HTML file is empty, and the entire user interface is built using GWT. The advantage of this is that you have, basically, only one page in the entire application. It all loads, and after that asynchronous calls are made to fetch data. The GWT code can change the components on the page all it wants.

The disadvantage is that it looks like an application, not a pretty web page. You can take alook at Gmail to see what this would look like.

Another disadvantage is that a pure GWT site won't index well in search engines. This isn't a problem for member-only areas, but is for the public areas of the site.

And The Answer Is?

Ultimately, I think the HTML First option is the way to go. It allows you to leverage talented web designers to create a very nice looking site, and allows the GWT programmers to focus on just the logic of the application and not on how it looks. We can also get pages indexed.

Unfortunately for my students, if they choose that option it means they'll have to learn some HTML. If they've escaped it so far, that isn't such a bad thing in this day and age. But it does mean extra work learning something we won't cover in class.

I'll be using that option for my sample application. I'm not a great web designer (just the opposite, in fact), but I hope to be able to find someone to make it pretty later.

Teaching & Development Philosophy

Okay, one more post before I start writing some code.

My general teaching philosophy with this course is that we are learning how to develop web applications. We are not just learning GWT (which is the client side), and we are not just learning App Engine (which is the server side).

We're learning how those two tools (and a host of others) hook together to form a complete web application.

In these posts, I'm not going to cover the basics. If you're not in the course, and you're reading this, and you haven't done any Java Swing work before, head for a tutorial. That'll be important for understanding how to create a user interface in GWT.

There will probably be other things that I won't cover as well. I'll try to post tutorial links when I remember to do so.

Another important aspect of my development philosophy is that I favor having a working application at all times. I'm not going to write tons of code in GWT for the screens, and then move on to the server work. At every stage we will have a working application and full client/server interaction for the features we've implemented so far.

It may take a few blogs posts to get through a particular feature, but I believe strongly that having a working application is the best way to know if you're heading in the wrong direction early.

The first step of this project is going to be user authentication. At the end of that step I'll have a web server that can accept logins and registrations, even if it doesn't do anything else.

Okay, enough talking, the next post will start the technical bits. Promise.

What Project?

Okay, I think I'm at the point where I finally have to decide on a project to do.

Sure, I could put it off for a bit longer...after all, any web project will need a login and authentication infrastructure, but last night my wife suggested doing a community events site, where people and organizations could post their events. I liked the idea, so I don't have any need to keep procrastinating.

What makes a community events site a good candidate for this project? Aren't there tons of those out there? It's true that there are a lot of sites that attempt to provide this functionality, but if people from the community don't use them they have no value. I think a site developed by a member of the community might get better buy in from the community itself.

And the technical aspects of the project are great. I get a typical web model, where visitors don't need to be logged in to see events, but do need to be logged in to generate or discuss events.

I get the opportunity to do some cool mashups with Google Maps and weather data (for directions to the event and the weather forecast for the event).

The project is complicated enough to make for a good sample, but not so complicated that most of my time is going to be spent on logic not related to the technology.

So, that's it...I'm not sure what it'll be called yet, but it'll be a community events site. Now I can start writing some code!

(Note to my students...I'm going to be designing as I go, and refactoring as needed. This works for me because of my experience in developing software, and because I do a lot of head-work design while walking to work. I'd strongly suggest that you do some on paper design work to show what you want the screens on your site to look like. That'll help get your head around what has to happen on the server.)

Using SVN Productively

Most of my students won't have used a version control system before, so I thought I'd better write on the best way for them to use it.

First off, it takes a bit of discipline to use SVN productively. If you do it right, then you'll be able to work on your project at home, at school, on your laptop, and always be sure that you have the most current version of your code.

Do it wrong, and you'll find yourself with three different versions of the code and faced with the problem of merging the changes.

So I'll lay out some general rules for using SVN:

1) Before you start to work on your code in a particular location, right click on the project name in Eclipse and select Team->Update To HEAD. This downloads the most recent version of the files from the SVN repository, making sure that you'll see any changes you made on other machines.

2) When you're done with a particular change in a file (e.g. you're satisfied you fixed the bug, or the functionality you've added works), right click on the file and select Team->Commit and then enter a description of what you changed.

3) Before you stop working on a particular machine, Commit anything you've changed, even if you're not done with it yet. This makes sure it'll be available on other machines when you update them.

If you keep to these rules, you shouldn't have any trouble with needing to merge different changes to a file, or possibly losing changes to a file. At least not with a single developer project, which is what you'll be doing in the course.

Tuesday, May 19, 2009

Why GWT & App Engine?

I said last post that we didn't have any code written yet, and that's technically true.

We haven't written any code yet, but by creating a Web Application Project using GWT and App Engine, we do have a skeleton project that shows communication between a GWT client and an App Engine server. And it'll all run right on our machine from within Eclipse.

Start Eclipse and click on the project name for your project in the upper left pane. Then click on the toolbar icon that looks like a red suitcase with a G on it. On the next screen click Compile. That will take the GWT code and compile it down into Javascript.

Now if you right click on the project name and choose Run As->Web Application, a local web server will start with Java App Engine running, and the web page defined by our GWT application will open. Type in your name and click Send to see a full round trip to the server and back.

It's important to understand that all this is running on your local machine. We haven't signed up for App Engine space yet (and won't until we figure out what the project is!), and we haven't had to transfer anything to a web server. It's all local, and we can keep it local until we're ready for it to go online.

Okay, so on to the question, Why GWT? There are many reasons, but here are the important ones for this course.

1) My students already know Java, so they don't need to learn Javascript.

2) GWT takes care of browser compatibility

3) GWT limits the security concerns of web apps (to at least a manageable level)

4) It all works in Eclipse, which my students are already used to using

Why App Engine? We could write GWT apps that communicate with any server language, including PHP, Python, etc, hosted on any shared hosting account. We're using App Engine because:

1) With Java App Engine and Eclipse, it all integrates beautifully for testing locally

2) There's no cost for App Engine hosting at the basic level

3) If a student's project takes off, App Engine can scale with their success

Sure, I could have used a Javascript library on the client and PHP on the server, but I'd have lost at least some of the advantages of GWT and App Engine in doing so. This way, I'll know that students who complete the course will have a leg up on their colleagues who don't have this sort of experience.

Subversion Server

Since I want to do this project as a sample that will work for anyone reading it, not just the students in my class, I'm not going to set up a local Subversion repository for the code.

Instead, I'll use a site that offers free Subversion hosting. XP-Dev.com has both free and paid modes. One of the things I liked about their free mode is that there's no limit on the number of users. So if I decide later to take on collaborators, they can get access to the code.

Another free service that looked good was Unfuddle.com. They limited their free version to just a couple of users, though, and didn't provide as much storage space.

So I created an account over at XP-Dev.com. First step there is to create a Subversion repository. It'd be helpful at this point to know what my project was going to be about, to name the repository appropriately. Since I don't, I'll just pick a working code name for the repository.

Click on Subversion in the list on the right after you're logged on to XP-Dev.com, and then click on New Repository. Put in a name of some sort and click the Save button.

The next page that shows gives you the addresses you'll need to configure Subclipse. Copy the No SSL address and start Eclipse.

Create a new project in Eclipse (File->New->Web Application Project). Name the project, and give it a package name, too (that can be the same as the project name). Make sure that it's checked to use the latest versions of GWT and App Engine. Click the Finish button.

Open Window->Open Perspective->Other..., and choose SVN Repository Exploring. In the upper left pane, choose the little icon that has a + and SVN on it to add your new repository. Paste in the URL you copied from XP-Dev.com after you created your SVN repository, and click the Finish button.

Enter your XP-Dev.com user id and password when asked. You should then see your repository listed in the upper left pane.

Now let's hook that repository up to the project you created. Get back to the Java perspective (Window->Open Perspective->Java) and right click on the name of the project in the upper left pane. Choose Team->Share Project..., choose SVN and click the Next button. Use Existing Repository Location should already be selected, and you'll see your XP-Dev repository listed. Make sure it's selected, too, and click the Next button. On the next screen it asks for the folder name, I just left the default selected and clicked Next.

On the next screen click Finish, and it'll ask if it should open the Synchronize View perspective. Click Yes, and the Team Synchronizing perspective opens. This is where you can add files to the repository so that they're under version control. To do this we right click on a file and choose Add To Version Control.

We can do the same thing from the Java perspective, by right clicking on a file and choosing Team->Add To Version Control.

Let's go ahead and add everything using the Team Synchronizing perspective. Right click on the name of the project and choose Expand All. Select all the actual files (but not the directories) by holding down the control key as you click on each file.

Be sure to select all the .jar files from war/WEB-INF/lib. You haven't written those files, but including them makes it easier to recreate the project from SVN if you have a computer or disk crash.

Right click on one of the selected files and choose Add To Version Control. Then right click on one of the files and choose Commit. You'll now be able to enter a comment to be attached to this particular version of the files in the repository. I entered "Initial version" and clicked OK.

Subclipse will now upload those files to the repository. If you log in via the web to XP-Dev.com you can browse the repository and see the files there.

We'll need to remember to add new files we create to version control, and to Commit changes to version controlled files.

More on that as we go along, and other Subversion related topics.

Installing Eclipse

The IDE I'll be using for this project is Eclipse.

For the course, the computer labs will already have Eclipse installed and configured. Most of the students will also already have Eclipse installed on their home machines and/or laptops. But, they probably have the Eclipse IDE for Java Developers, not the one for Java EE Developers. We're moving into web programming now, so let's move to the version of Eclipse that has tools specifically for web programming (at this point, I have no idea if we'll need the extra tools or not...that's how software development goes when you're trying something new, you seem wise only after you've made your mistakes).

Head over to the Eclipse downloads page, and download the latest Eclipse IDE for Java EE Developers. Have some coffee, or take a walk in the sun, while you wait for the download to complete.

What the download gets you is a zip file. If you've never installed Eclipse before, just unzip this file somewhere (the root of your C: drive is traditional, but if you have another version of Eclipse there and don't want to overwrite it, anywhere will do). You'll end up with an Eclipse directory with a whole bunch of files and directories under it. One of those files immediately under it will be Eclipse.exe. Make a shortcut to that somewhere handy, like on your Desktop.

Start Eclipse, and wait for a bit more. When it asks you about the workspace, you can accept the default or change it. You should know where your workspace is if you aren't going to be using a hosted version control system, so you can regularly back up your code.

Now, for the plugins. Note that everything but the Google Plugin was part of the Pro Web 2.0 toolset, so credit goes to Jeff Dwyer for choosing them for his project. I'm just following his lead.

Google Plugin

This is an important one, since it takes what used to be a bunch of command line typing for managing GWT and App Engine projects and instead gives us Eclipse menus. See the Google page about installing this plugin. To find out what version of Eclipse you have (if you've forgotten what you downloaded), in Eclipse look at Help->About Eclipse Platform.

Once you figure out which of the URLs you need, copy that and then go into Eclipse's Help->Software Updates menu. Click on the Available Software tab, and then on the Add Site button. Paste in the URL from you copied from Google's page and click OK.

You should see now the option to download not only the plugin, but also the latest versions of GWT and Java App Engine. Go ahead and check all three and click the Install button. It'll take a few seconds to calculate dependencies, and then will display an Install wizard. Go ahead and click Next, and then accept the terms of the license agreements. Click the Finish button, and it'll actually do the install. Click Yes when it asks if you want to restart Eclipse.

We now have the Google Eclipse plugin, plus the GWT and App Engine files we need to develop those sorts of applications.

Subclipse Plugin

This plugin allows Eclipse to work with a Subversion repository (a particular type of online version control). That's the sort used by Google Code, so if you're making your project Open Source then you'll be able to use Google Code to host it. If you aren't making it Open Source, then you'll have to find another Subversion server to use (do a Google Search on free subversion hosting and you'll find some that are suitable for single person projects).

To install Subclipse, go to the Subclipse web site and click the Download and Install link on the left. Scroll down a bit to where it lists the links for the most recent version. Copy the link that's labeled Eclipse Update Site URL.

In Eclipse, go back to Help->Software Updates, and back to the Available Software tab to Add Site with the URL for Subclipse. Click the box next to that URL when it shows up in the list to install everything from that site. Click Install, Next, agree to the license agreements, and Finish.

Restart Eclipse again.

Find Bugs Plugin

Install the Find Bugs plugin. This plugin will look for some common causes of errors in Java programs. These sorts of errors will pass the compiler, but cause you problems. There's a huge variety of bugs that are looked for as part of this. Most you won't ever see, but if it saves you the time tracking down even one obscure bug, it'll have been worth installing.

That's it for Eclipse.