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.
Tuesday, June 16, 2009
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):
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.
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.
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.
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:
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:
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:
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:
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.
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:
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.
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:
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.
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.
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:
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.
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:
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:
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.
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.
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:
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.
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:
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:
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:
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.
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:
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:
Then we call makePersistent:
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.
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):
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:
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.
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
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:
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):
Note that when you try to find an import for Element, you'll get a big long list. The one you want is:
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.
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.
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.
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:
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.
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:
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:
doPost (req, resp);
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.
String token = req.getParameter("token");
PrintWriter writer = resp.getWriter ();
writer.print("RPX sent this: " + token);
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.
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.
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.
Subscribe to:
Posts (Atom)