<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-4925441287181613986</id><updated>2012-02-16T19:56:22.499-08:00</updated><category term='xml'/><category term='openid'/><category term='java'/><category term='google maps'/><category term='authentication'/><category term='cookies'/><category term='books'/><category term='javamail'/><category term='jdo'/><category term='gwt'/><category term='freemarker'/><category term='mashups'/><category term='query'/><category term='filter'/><category term='form'/><category term='software development'/><category term='encryption'/><category term='session handling'/><category term='web2.0'/><category term='rpxnow'/><category term='servlet'/><category term='design'/><category term='eclipse'/><category term='app engine'/><category term='password'/><category term='web design'/><category term='svn'/><category term='subversion'/><title type='text'>GWT &amp; App Engine Project</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>47</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-8083459824818746113</id><published>2010-03-03T05:23:00.000-08:00</published><updated>2010-03-03T05:43:37.058-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gwt'/><title type='text'>GWT Basics</title><content type='html'>Okay, let's do a simple GWT interaction to see how it works.  Note that this will depend on data being in the datastore, data that the site doesn't have yet.  I can just create a simple GWT call to populate the datastore so what we're developing can return some results.  &lt;br /&gt;&lt;br /&gt;The servlet model was a page load model.  Each page load called a servlet, which returned the HTML to display.  So when designing your servlet based site, you thought in terms of the pages the site needs to display.  &lt;br /&gt;&lt;br /&gt;The GWT model is a remote procedure call model.  Picture the client and server both containing objects that talk to each other (primarily the client objects making calls on the server objects to get information).  The application user interface itself is contained in the client, and looks more like a regular Java application than a web page.  &lt;br /&gt;&lt;br /&gt;So we have to shift our thinking from the servlet model.  To identify GWT interactions, think about the sorts of information the client might need to get from the server.  &lt;br /&gt;&lt;br /&gt;I'll start with something simple.  I want the front page of the site to display a list of contests.  So that list of contests is information the client needs to get from the server.&lt;br /&gt;&lt;br /&gt;Think of that client/server interaction as a method call between objects.  Let's say that I just want that call to return the names of the current contests.  I might write the method interface like this:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;String[] getCurrentContests ();&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;To convert that into Java code for your project, click on the project.client package and choose File-&gt;New-&gt;Interface.  Next to "Extended interfaces", click the Add button.  Type in RemoteService and choose to add that interface.&lt;br /&gt;&lt;br /&gt;Don't forget to give your interface a name.  I'll name my interface something like Contests.  &lt;br /&gt;&lt;br /&gt;Click Finish and the interface stub is created.  It should look something like this:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;import com.google.gwt.user.client.rpc.RemoteService;&lt;br /&gt;&lt;br /&gt;public interface Contests extends RemoteService &lt;br /&gt;{&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;We want to add an annotation before the interface line to say what the URL path is on the server.  It'll end up like this:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;import com.google.gwt.user.client.rpc.RemoteService;&lt;br /&gt;import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;&lt;br /&gt;&lt;br /&gt;@RemoteServiceRelativePath("getContests")&lt;br /&gt;public interface Contests extends RemoteService &lt;br /&gt;{&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Now we need to add in the actual method interface:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;import com.google.gwt.user.client.rpc.RemoteService;&lt;br /&gt;import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;&lt;br /&gt;&lt;br /&gt;@RemoteServiceRelativePath("getContests")&lt;br /&gt;public interface Contests extends RemoteService &lt;br /&gt;{&lt;br /&gt;    String[] getCurrentContests ();&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;That's it for creating the interface.  What we've defined is the interface between the client and server.&lt;br /&gt;&lt;br /&gt;What we still need to define is what the client does to call this interface, and what the server does to implement it.&lt;br /&gt;&lt;br /&gt;More next post.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-8083459824818746113?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/8083459824818746113/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2010/03/gwt-basics.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/8083459824818746113'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/8083459824818746113'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2010/03/gwt-basics.html' title='GWT Basics'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-7585132015561641801</id><published>2010-02-17T07:25:00.000-08:00</published><updated>2010-02-17T07:56:04.376-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gwt'/><title type='text'>GWT Integration</title><content type='html'>This is the time when I want to start covering GWT integration.  &lt;br /&gt;&lt;br /&gt;I could start using GWT in small ways in the mainly servlet based application I have, but I figured I'd swing to the other extreme and start a new application based entirely on GWT.  The techniques I show will still be applicable to a mainly servlet based application, but I'll also get to cover some GWT specific techniques (such as logging in via GWT).  &lt;br /&gt;&lt;br /&gt;So for this next series of posts, I'm creating a new web application in Eclipse.  Everything from this point on until I post otherwise will be in reference to that new application.&lt;br /&gt;&lt;br /&gt;It's worth looking at that basic application the plugin creates to understand a little about how GWT works.  &lt;br /&gt;&lt;br /&gt;The basic idea is that GWT widgets run as Javascript in the browser, and instead of loading a new web page to show results to the user you instead make a Javascript call to the server and then update only specific elements of the web page you're currently viewing.  &lt;br /&gt;&lt;br /&gt;Look in the project.server package and you'll find the server side of the GWT sample application.  It looks a little different than what we're used to in terms of servlets.  Instead of extending HttpServlet, it extends RemoteServiceServlet and implements GreetingService.&lt;br /&gt;&lt;br /&gt;If the implements keyword is new to you, it means that the class implements an interface.  An interface is (mostly) just a set of methods that the class must implement to be considered an implementation of the interface.  &lt;br /&gt;&lt;br /&gt;You can see the GreetingService interface in the project.client package. Look at that, and you'll see that the interface is just one method that the server object must implement.&lt;br /&gt;&lt;br /&gt;GWT uses interfaces to define the set of possible calls that can go from client to server.  In the sample application, there's only one possible call: greetServer that passes in a String.  In a real application you'll have a larger number of possible calls in various interfaces.&lt;br /&gt;&lt;br /&gt;Back to GreetingServiceImpl in the server package.  It implements the greetServer method, and takes in the String the interface said it would need to take.  This method looks a lot like a regular Java method, not a servlet.  It does some error checking and throws an exception if there's a problem (you'll see in the interface that the exception is listed as being thrown).  When there is no error, it simply returns a String, again as the interface specified.&lt;br /&gt;&lt;br /&gt;There's no HttpServletRequest, no HttpServletResponse, just some odd calls to get information about the user's browser and the server itself.  &lt;br /&gt;&lt;br /&gt;At its most basic, the server implementation of interface methods must simply provide information back to the client.  The display of that information is then up to the client code.  &lt;br /&gt;&lt;br /&gt;Now let's look at the project.html in the /war directory of your project.  Skip down to the bottom of the file to see the actual HTML.  Notice that it's a table with three table cells: one for them to type in a name, one for a submit button, and one for an error message to be displayed.  But the table cells are empty!&lt;br /&gt;&lt;br /&gt;If you run the project as a web application, you'll see an edit box and submit button.  If you click submit without entering a name, you'll get an error displayed.&lt;br /&gt;&lt;br /&gt;Where are these things coming from, if not the HTML?&lt;br /&gt;&lt;br /&gt;Back in the project.client package, look at the file that's named the same as your project.  Go down to the onModuleLoad method.  This is the method that integrates the Java code with the HTML.  &lt;br /&gt;&lt;br /&gt;If you've done any Java Swing programming, the code here should look familiar.  You create widgets in GWT the same way you do in Swing.  The widgets themselves may be different, but the basic model of using them is the same.  &lt;br /&gt;&lt;br /&gt;Notice the line that reads:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;RootPanel.get("nameFieldContainer").add(nameField);&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;That puts the widget represented by the variable nameField into the HTML in the HTML element that has the id nameFieldContainer.  If you check the HTML file, that's one of those empty table cells.  This is how the empty cells are filled with widgets.&lt;br /&gt;&lt;br /&gt;Notice also that GWT uses the Swing methodology of creating widgets and adding them to the application, but then defining methods for what happens when the user interacts with those widgets.  &lt;br /&gt;&lt;br /&gt;If you haven't read Swing code before, this might seem pretty complicated.  You've got some inner classes being defined, one of them anonymous.  Without Swing experience, you might not even know how that all works.  &lt;br /&gt;&lt;br /&gt;I'd recommend finding a good Java Swing tutorial to get the basics down before trying to use GWT.  Ultimately, though, it's the code defined for the submit button click that will make the call to the server greetServer method and receive the response back.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-7585132015561641801?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/7585132015561641801/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2010/02/gwt-integration.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/7585132015561641801'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/7585132015561641801'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2010/02/gwt-integration.html' title='GWT Integration'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-9109300760142608833</id><published>2010-02-09T05:54:00.001-08:00</published><updated>2010-02-09T06:28:57.759-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='password'/><category scheme='http://www.blogger.com/atom/ns#' term='encryption'/><title type='text'>Encrypting User Passwords</title><content type='html'>The basic idea behind password encryption is that the encryption must be one-way.  You should be able to turn plain text into encrypted text, but there should be no way to get back to the plain text only given the encrypted text.  This ensures the security of the user's password on your system.  To see if the password they've provided matches their original password, you encrypt the password and see if the encrypted versions are the same.&lt;br /&gt;&lt;br /&gt;For more on this process, read this excellent post on &lt;a href="http://www.jasypt.org/howtoencryptuserpasswords.html"&gt;How To Encrypt User Passwords&lt;/a&gt;.  The post is great on concepts and rationale, but light on actual code, so I'll present some possible code here.&lt;br /&gt;&lt;br /&gt;The basic steps:&lt;br /&gt;&lt;br /&gt;1) Generate a string to encrypt by combining the password with a random number&lt;br /&gt;&lt;br /&gt;2) Encrypt the string using a digest mechanism (e.g. like we did for the email verification code)&lt;br /&gt;&lt;br /&gt;3) Store the encrypted password and the random number in the datastore&lt;br /&gt;&lt;br /&gt;#3 should be familiar to you, so I'll provide a sample for 1 and 2.  This isn't exactly what the web site I linked talks about, but builds on our previous MD5 hashing code for email verification.  In fact, I'm going to retrofit the more secure mechanism back into the email verification code so that I have only one implementation of encrypting in a utility class.&lt;br /&gt;&lt;br /&gt;I'll leave out some of the bits that we've already covered with email verification:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;//Create the MD5 MessageDigest instance&lt;br /&gt;Copy the code from previous examples here&lt;br /&gt;&lt;br /&gt;// Get the random number&lt;br /&gt;SecureRandom random = SecureRandom.getInstance ("SHA1PRNG");&lt;br /&gt;int number = random.nextInt ();&lt;br /&gt;&lt;br /&gt;// Build the string to encrypt and normalize it&lt;br /&gt;String passAndNumber = "" + number + password;&lt;br /&gt;passAndNumber = Normalizer.normalize (passAndNumber);&lt;br /&gt;&lt;br /&gt;// Encrypt it&lt;br /&gt;Use the MD5 digest and conversion to hex code from previous examples&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;What you'll end up with is a hex code string suitable for storing in the datastore as an encrypted password.  You'll also store the random number you generated.  &lt;br /&gt;&lt;br /&gt;When the user tries to login, you'll do a similar process to see if they entered the correct password:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;// Read the encrypted password and the random number from the data store&lt;br /&gt;String encryptedPass = ....&lt;br /&gt;int number = ....&lt;br /&gt;&lt;br /&gt;// Create the MD5 MessageDigest instance&lt;br /&gt;Already shown in email verification example&lt;br /&gt;&lt;br /&gt;// Combine the password they entered with the random number from the data store&lt;br /&gt;password = "" + number + password;&lt;br /&gt;&lt;br /&gt;// Normalize the password they entered&lt;br /&gt;password = Normalizer.normalize (password);&lt;br /&gt;&lt;br /&gt;// Encrypt the password&lt;br /&gt;Use MD5 and conversion to hex code shown before&lt;br /&gt;&lt;br /&gt;// Compare&lt;br /&gt;if (code.equals (encryptedPassword))&lt;br /&gt;// Correct password&lt;br /&gt;else&lt;br /&gt;// Incorrect password&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Note that I haven't tested this yet.  There may very well be details to work out, but they shouldn't be major.  The Normalizer class is only available in Java 1.6, so if you're using Java 1.5 you'll need to upgrade.&lt;br /&gt;&lt;br /&gt;Also note that to be safe, when you call the String's getBytes method as part of creating the digest, pass in "UTF-8" as the encoding scheme so you know you'll be getting something consistent.  This is different from what we did for the email verification code; I recommend refitting the email verification code to use the same mechanism as password encryption.  This means you'll need to store two random numbers in the datastore...the one for the password itself, and the one for the email verification code.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-9109300760142608833?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/9109300760142608833/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2010/02/encrypting-user-passwords.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/9109300760142608833'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/9109300760142608833'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2010/02/encrypting-user-passwords.html' title='Encrypting User Passwords'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-4703483559352721594</id><published>2010-02-03T05:45:00.000-08:00</published><updated>2010-02-09T06:55:07.920-08:00</updated><title type='text'>Deploying To App Engine</title><content type='html'>It's a good idea to deploy your code to the actual AppEngine servers to test.  Most features work the same between the local development environment and the servers, but now and then you'll run into something that doesn't.  &lt;br /&gt;&lt;br /&gt;Deploying is fairly simple, although it does require that you finally pick your application name on appspot.com, if you haven't already.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Step 1&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Create your application.  Go to the &lt;a href="http://code.google.com/appengine/"&gt;Google AppEngine&lt;/a&gt; site and log into your account.  Click "Create an Application".  &lt;br /&gt;&lt;br /&gt;Type in the Application Identifier.  This doubles as part of the URL for accessing your application at appspot.com, so it has to be unique and will be publicly visible.  &lt;br /&gt;Also enter the Application Title, and click Save.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Step 2&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Back in Eclipse, we need to hook your project up to the application you just created.  Right click on the project and go into Properties.  Expand the Google section and click on App Engine.  Under Application ID, type in the identifier you entered when you created your application.&lt;br /&gt;&lt;br /&gt;Close out of the Preferences.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Step 3&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Deploy!  Right click on the project, go down to the Google menu and choose Deploy to App Engine.  Enter the email address and password you used when you signed up for Google AppEngine, and click Deploy.&lt;br /&gt;&lt;br /&gt;It should happen automatically, now you can open a web browser and go to your application id .appspot.com to see your application live on Google's servers.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Step 4&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Everything blows up!  &lt;br /&gt;&lt;br /&gt;Well, maybe not.  But if you're using RPXNow for authentication, you won't be able to get logged in unless you followed my advice back in the post on &lt;a href="http://gwtappengine.blogspot.com/2010/01/validating-user-emails-part-2.html"&gt;validating user emails&lt;/a&gt;.  The reason is that part of the HTML in your sign in link is the location of your RPX results servlet.  Right now, that probably still is what you had when you were developing it...e.g. http://localhost:8080/rpxresults.  &lt;br /&gt;&lt;br /&gt;Your application needs to change the http://localhost:8080 part based on whether it's running in development mode or in production mode.  With a recent version of AppEngine, you'd use something like this:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;if (SystemProperty.environment.get().contains("Development"))&lt;br /&gt;&amp;nbsp;&amp;nbsp;hostName = "localhost:8080";&lt;br /&gt;else&lt;br /&gt;&amp;nbsp;&amp;nbsp;hostName = "gamesbyteens.appspot.com";&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;at an appropriate point where you fetch the host name for inclusion into the data model for your pages.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-4703483559352721594?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/4703483559352721594/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2010/02/deploying-to-app-engine.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/4703483559352721594'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/4703483559352721594'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2010/02/deploying-to-app-engine.html' title='Deploying To App Engine'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-8219308351352031972</id><published>2010-01-22T07:30:00.000-08:00</published><updated>2010-02-09T05:54:16.592-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='filter'/><category scheme='http://www.blogger.com/atom/ns#' term='servlet'/><category scheme='http://www.blogger.com/atom/ns#' term='authentication'/><title type='text'>Implementing User Authentication</title><content type='html'>Earlier in the blog, I showed how to use a 3rd party OpenId service to allow people to login to your site using their Google account, their Facebook account, etc.  &lt;br /&gt;&lt;br /&gt;That isn't appropriate for every type of site, so now I'll show how to allow people to register at your site and create a login and password specific for your site.  &lt;br /&gt;&lt;br /&gt;I'd planned on using Spring Security for this, so that the authentication would be robust.  But, Spring Security has some dependencies on the Spring framework.  Supposedly getting it to work without Spring is possible, but not knowing either product it probably won't happen in the limited amount of time I have available (sorry for those of you searching for how to do just that).&lt;br /&gt;&lt;br /&gt;I do, by the way, highly recommend Spring for industrial strength applications built on Java servlets.  It just isn't appropriate for the class that I'm teaching.  So, the authentication we'll roll here won't be industrial grade.  But it'll be a start.&lt;br /&gt;&lt;br /&gt;Let's talk for a moment about the characteristics of a good authentication mechanism.  &lt;br /&gt;&lt;br /&gt;First, it must be impossible to fool.  For example, an authentication mechanism that simply avoided displaying members only links to non-members would not prevent non-members from typing those links in directly.  The server must authenticate on any restricted page load.&lt;br /&gt;&lt;br /&gt;Second, it must be easy to use.  The more places we have to touch to implement authentication for a page, the higher the chance that we'll get it wrong.  In particular, if we have to write Java code for each page we want protected, the chances are good that we'll get it wrong sooner or later.&lt;br /&gt;&lt;br /&gt;We'll use Java Servlet filters to accomplish this.  A filter is basically a piece of Java code that is run before a servlet is run.  The filter can allow the servlet to run, or it can display a page itself and block the servlet.  This happens without adding code to the servlet itself.  &lt;br /&gt;&lt;br /&gt;First step is to set up our web.xml file to use the filter (okay, so we haven't written the filter yet, but editing web.xml is the easy part so we'll get it out of the way).  Add something like the following to your web.xml:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;  &amp;lt;filter&amp;gt;&lt;br /&gt;    &amp;lt;filter-name&amp;gt;AuthenticationFilter&amp;lt;/filter-name&amp;gt;&lt;br /&gt;    &amp;lt;filter-class&amp;gt;omega.server.AuthenticationFilter&amp;lt;/filter-class&amp;gt;&lt;br /&gt;  &amp;lt;/filter&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;filter-mapping&amp;gt;&lt;br /&gt;    &amp;lt;filter-name&amp;gt;AuthenticationFilter&amp;lt;/filter-name&amp;gt;&lt;br /&gt;    &amp;lt;url-pattern&amp;gt;/members/*&amp;lt;/url-pattern&amp;gt;&lt;br /&gt;  &amp;lt;/filter-mapping&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;This is like registering a servlet, in that you specify a URL pattern that triggers the filter.  In my case, I used /members/*.  This means that any URL under the members directory will trigger the filter.  This will require changing my servlet URL mappings around a bit, so that any page that should be restricted to logged in users is mapped under /members, and any page that should be visible to anyone is not.  So far for me, that's basically just the ProfileServlet, but I'll make a mental note to put future members only pages under /members.&lt;br /&gt;&lt;br /&gt;Now we have to write the AuthenticationFilter class.  This is similar to writing a servlet.  Create a new class in your server package.  There's no superclass for this one, but you will implement an interface.  So in the new class dialog, click on the Add button to the right of the (empty) interface list, and type in Filter.  You want the javax.servlet.Filter interface.  &lt;br /&gt;&lt;br /&gt;You'll get some Java code that leaves stubs for three methods: init, destroy, and doFilter.  The primary purpose of the init is to save off the FilterConfig passed in, so create a private data member for doing that:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;  private FilterConfig filterConfig = null;&lt;br /&gt;&lt;br /&gt; public void init(FilterConfig arg0) throws ServletException {&lt;br /&gt;  filterConfig = arg0;&lt;br /&gt;&lt;br /&gt; }&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;You may not need that FilterConfig instance, but it can be used to get the ServletContext of the servlet that you're filtering.  &lt;br /&gt;&lt;br /&gt;The actual work of authentication will be done in the doFilter method.  Here's the basic idea (not working code, but algorithm):&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;if (user authenticates correctly)&lt;br /&gt;&amp;nbsp;&amp;nbsp;chain.doFilter(request, response);&lt;br /&gt;else&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;User the response argument to generate error output, or redirect to a login form&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;So calling chain.doFilter passes the request on to the next filter in the chain, meaning that the AuthenticationFilter is happy with the request.  If authentication fails, do something other than calling chain.doFilter.  &lt;br /&gt;&lt;br /&gt;The details of pulling a user's password from the datastore and comparing it to one passed into the HTTP request we've already covered.  You should also be able to write the registration form and servlet to allow users to create their user account in the first place (and you can do verification of their email, too).  You should also know what you need to know to have a "I forgot my password" link that will email a new password to them.&lt;br /&gt;&lt;br /&gt;The one bit that we do still need to cover is storing passwords in an encrypted form.  Storing user's passwords in plain text is bad form, so we won't do it.&lt;br /&gt;&lt;br /&gt;Next post, encrypting passwords.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-8219308351352031972?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/8219308351352031972/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2010/01/implementing-user-authentication.html#comment-form' title='12 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/8219308351352031972'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/8219308351352031972'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2010/01/implementing-user-authentication.html' title='Implementing User Authentication'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>12</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-8371048757885640008</id><published>2010-01-19T10:02:00.001-08:00</published><updated>2010-01-22T07:25:46.941-08:00</updated><title type='text'>Signing A User Out</title><content type='html'>I realized that I haven't shown how to sign a user out yet.  I have a link to /signout in my navigation template, but nothing mapped to that URL.  This is where having a project plan (or at least a to-do list) would help!  &lt;br /&gt;&lt;br /&gt;So, I'll create a Signout servlet mapped to /signout.  In that servlet, I need to get the session object and call the invalidate method on it.  Afterward, I'll redirect the user to the front page.  &lt;br /&gt;&lt;br /&gt;It'll look something like this:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;  HttpSession session = req.getSession(false);&lt;br /&gt;  &lt;br /&gt;  if (session != null)&lt;br /&gt;   session.invalidate();&lt;br /&gt;&lt;br /&gt;  resp.sendRedirect("/front");&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;and that's it.  Not a big deal, but something we needed to implement.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-8371048757885640008?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/8371048757885640008/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2010/01/signing-user-out.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/8371048757885640008'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/8371048757885640008'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2010/01/signing-user-out.html' title='Signing A User Out'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-4760044775301742738</id><published>2010-01-11T14:50:00.000-08:00</published><updated>2010-01-19T10:48:38.175-08:00</updated><title type='text'>Validating User Emails, part 2</title><content type='html'>The last two steps of validating user emails involved changing the profile servlet to display the link to resend the verification email when they already have an unverified email, and to send the verification email when they change their email. &lt;br /&gt;&lt;br /&gt;Since both of those require sending the verification email, I'll put that in a utility class.  I already have MailUtil, and this is a special sort of email, so I'll stick it there.  &lt;br /&gt;&lt;br /&gt;The main problem so solve before I can send the verification email is generating the unique verification code.  I mentioned that MD5 is fine to use here, so we'll use that.  The MessageDigest class in Java is what we use to generate the MD5 value, like this:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;  MessageDigest md5 = null;&lt;br /&gt;  &lt;br /&gt;  try &lt;br /&gt;  {&lt;br /&gt;   md5 = MessageDigest.getInstance("md5");&lt;br /&gt;  } catch (NoSuchAlgorithmException e) &lt;br /&gt;  {&lt;br /&gt;   // Should never happen, but just in case&lt;br /&gt;   return "Unable to access md5 digest algorithm";&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  byte [] digest = md5.digest(user.getEmail ().getBytes());&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Note the exception handling...that exception should never happen, because MD5 is a standard digest algorithm.  But since the exception can be thrown, Java requires we handle it.  This assumes the method returns a String that's an error message if something went wrong.  &lt;br /&gt;&lt;br /&gt;The byte array we get from the MessageDigest is not suitable for putting into an email yet.  We need to convert it into hexadecimal first, like this:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;  String code = "";&lt;br /&gt;  &lt;br /&gt;  for (byte element : digest)&lt;br /&gt;   code += Integer.toHexString(element &amp; 0xFF);&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;If you're wondering about the use of "&amp; 0xFF", it's because of the way that integral values are stored in Java.  Think in terms of twos-complement sign extending from 1 byte to 4 bytes, and getting a bunch of extra 1s.  We strip off anything extra by anding it with a single byte of 1s.  &lt;br /&gt;&lt;br /&gt;And if none of that makes sense, just believe me that you need it (or don't believe me, and remove it, and you'll see the difference in the hexadecimal generated).  &lt;br /&gt;&lt;br /&gt;So what I end up with is a hexadecimal string suitable for use as a verification code.  &lt;br /&gt;&lt;br /&gt;I also want to store the email I'll send as a Freemarker template, rather than have it embedded in Java code.  This means a couple of things: I'll need to pass the Template object into the MailUtil method, and when I process the template I'll need to use a StringWriter so I can get the results as a String.  The StringWriter serves in place of how we've been using PrintWriter for the templates.&lt;br /&gt;&lt;br /&gt;I'll leave that, and finishing off the send verification email method as an exercise for the reader.  &lt;br /&gt;&lt;br /&gt;The last bit I'll mention here is that we'll need to include a link to the servlet we wrote last post that handles the verification clicks in the email.  In the template we'll use something like this in the href arg of an a tag:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;http://localhost:8080//validate-email?code=${code}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;We'll use localhost for testing, but will want to change that when we're deploying the application.  For those keeping track, we now have two places where we've embedded localhost and made notes to change it when we deploy.  &lt;br /&gt;&lt;br /&gt;Might be a very good idea to include a programmatic way to switch this depending on whether the application is running on the server or on the local machine.  You can check that like this:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;if (SystemProperty.environment.get().contains("Development")) &lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;That if statement is true when you're running on the local host, and false otherwise. Use that if statement at appropriate points to change the host name used in URLs.  The SystemProperty class used there is an App Engine provided one.&lt;br /&gt;&lt;br /&gt;(Update: I just realized that the SystemProperty class doesn't exist on the App Engine version installed in the computer lab.  So we'll likely update to the most recent version at some point, to keep things in sync with what students would have at home.)&lt;br /&gt;&lt;br /&gt;Okay, so if you've done the exercises left for the reader, you have a MailUtil method that will send a verification email.  We need to invoke that method in the profile servlet when the user changes their email, using code like this:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt; private String sendVerificationEmail (User user) throws IOException&lt;br /&gt; {&lt;br /&gt;  Configuration config = ConfigFactory.get(getServletContext ());&lt;br /&gt;  Template template = config.getTemplate("emailVerify.ftl");&lt;br /&gt;  &lt;br /&gt;  try&lt;br /&gt;  {&lt;br /&gt;   MailUtil.sendVerificationEmail(user, template);&lt;br /&gt;  }&lt;br /&gt;  catch (Exception e)&lt;br /&gt;  {&lt;br /&gt;   return e.getMessage ();&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  return "";&lt;br /&gt; }&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;This is in the profile servlet, and will be called from the code that updates the email in the User. I'm not showing those parts, because they're basically the same as we did for updating the name.  &lt;br /&gt;&lt;br /&gt;The last bit we need is to display the link to resend the verification email when the user is editing their profile and their email isn't verified.  &lt;br /&gt;&lt;br /&gt;I'm going to add a query parameter to the ProfileServlet that will tell it to resend the verification email.  It'll be something like this:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;if (req.getParameter("resend") != null)&lt;br /&gt;{&lt;br /&gt;// Only resend if the email isn't verified already&lt;br /&gt;if (! user.getEmailVerified())&lt;br /&gt;{&lt;br /&gt;message = sendVerificationEmail (user);&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Now that the profile servlet will handle that query parameter, put the link in when the email isn't verified.  I'll add the link to the ProfileLoggedIn template, just as a variable, e.g. ${resendLink}.  Then in the profile servlet, when the email isn't empty but isn't verified, I'll set that variable to the resend link in the data model.  When the email is empty, or verified, I'll set that variable to an empty string.&lt;br /&gt;&lt;br /&gt;Since that uses only techniques we've already seen, I'll leave that as an exercise for the reader.  Note also that I haven't tested the above, so it might very well blow up.  When I get a chance to test it I'll update this post to reflect any fixes needed.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-4760044775301742738?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/4760044775301742738/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2010/01/validating-user-emails-part-2.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/4760044775301742738'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/4760044775301742738'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2010/01/validating-user-emails-part-2.html' title='Validating User Emails, part 2'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-2105445782991935694</id><published>2010-01-09T07:15:00.000-08:00</published><updated>2010-01-10T17:02:39.458-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javamail'/><title type='text'>Validating User Emails</title><content type='html'>First step in adding user email addresses is pretty basic.&lt;br /&gt;&lt;br /&gt;We need to add an email field to the User object, and add it to the ProfileServlet form and processing, so that the user can enter the email address.  We'll also need a field that tells us if the email has been validated or not.  That field won't be user editable via the profile form.&lt;br /&gt;&lt;br /&gt;I'll leave the first step as an exercise for the reader (it uses only techniques we've already seen), and move on to email verification.&lt;br /&gt;&lt;br /&gt;The basic strategy with email verification is threefold:&lt;br /&gt;&lt;br /&gt;1) The user enters their email address in their profile&lt;br /&gt;&lt;br /&gt;2) The web site sends an email to that email address, with a link to click&lt;br /&gt;&lt;br /&gt;3) The user clicks that link, taking them back to the web site&lt;br /&gt;&lt;br /&gt;That way, we know that the user entered a valid email address they control. &lt;br /&gt;&lt;br /&gt;Step 1 is pretty basic and uses techniques we've already covered.  Step 2 requires sending email, which we'll look at.  Step 3 requires having a page the link in the email goes to, where we can mark the email as verified.  &lt;br /&gt;&lt;br /&gt;Tying together step 2 and step 3 means we'll have to generate a unique code for this particular email verification.  This unique code needs to be impossible to predict, so users entering false email addresses cannot simply guess the verification code.  It also needs to be something that can be used as a query parameter in a URL.  An MD5 hash is a good candidate, and we can check the database to make sure it's unique before sending the email.&lt;br /&gt;&lt;br /&gt;Okay, lots to do!  &lt;br /&gt;&lt;br /&gt;1) Create a class to send the activation email&lt;br /&gt;&lt;br /&gt;2) Create a servlet to receive the click from the activation email.  Either mark the email as verified, or tell the user the verification email cannot be found&lt;br /&gt;&lt;br /&gt;3) Change ProfileServlet to automatically send the activation email when a new email is entered, and to set the verified status to false&lt;br /&gt;&lt;br /&gt;4) Change ProfileServlet to provide a link to resend the activation email (only display when the email isn't verified)&lt;br /&gt;&lt;br /&gt;Also, the GAE Mail API allows an application to receive email.  The email gets transformed into an HTTP request to a servlet, which can then save the email text in the datastore or take some other action based on the email.  So you can do some pretty sophisticated email driven functionality (such as allowing people to send an email to your app to get a listing of contests in their area).  For now, we'll use this feature just to handle when the verification email bounces.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Sending Emails&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Since this is something we might have to do often, I'll write a MailUtil class that will hide a little of the complexity.  Error handling will still be exposed, since we'll need to display an error message on the web page.  Here's what the only method so far in the MailUtil class will look like:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt; public static void sendOne (String subject, String body, String name, String address, String fromName, String fromAddress) throws Exception&lt;br /&gt; {&lt;br /&gt;  Properties props = new Properties();&lt;br /&gt;        Session session = Session.getDefaultInstance(props, null);&lt;br /&gt;&lt;br /&gt;        Message msg = new MimeMessage(session);&lt;br /&gt;        &lt;br /&gt;        msg.setFrom(new InternetAddress(fromAddress, fromName));&lt;br /&gt;        msg.addRecipient(Message.RecipientType.TO,&lt;br /&gt;                         new InternetAddress(address, name));&lt;br /&gt;        msg.setSubject(subject);&lt;br /&gt;        msg.setText(body);&lt;br /&gt;        Transport.send(msg);&lt;br /&gt; }&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;To put multiple lines in your message body, use the standard \n escape in the body text.&lt;br /&gt;&lt;br /&gt;Note that running this in Eclipse doesn't seem to actually send email.  Before this will work, you'll have to get an actual application created on &lt;a href="http://code.google.com/appengine/"&gt;Google App Engine for Java&lt;/a&gt;, and deploy the application.  But you can safely call the method to send email when running it in Eclipse...it just won't send the email.  &lt;br /&gt;&lt;br /&gt;&lt;i&gt;Verification Servlet&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;It might seem odd to create the servlet to respond to the user clicking the link in the email when we haven't written the code that generates that link yet.  But it all has to be done in some order, so I'll do the processing of the click first.  &lt;br /&gt;&lt;br /&gt;I'll start by copying FrontPage.java and calling it ValidateEmail.  I'll also add in web.xml mappings so that /validate-email calls the ValidateEmail servlet.&lt;br /&gt;&lt;br /&gt;To recap the changes to User to support all this, I added in an email field (a String), an emailVerified field (a boolean that starts out as false), and a verificationCode field (String).  &lt;br /&gt;&lt;br /&gt;In the verification servlet, I want to look at the verification code passed in as a query parameter, and use it to look up the matching User in the datastore.  The verification codes will be unique, so I'll only get zero or one User matching it.  &lt;br /&gt;&lt;br /&gt;If it's one User, I'll mark the email as verified.  Otherwise, I display an error message. Here's a portion of that code. &lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;  Map root = new HashMap();&lt;br /&gt;&lt;br /&gt;  String code = req.getParameter("code");&lt;br /&gt;   &lt;br /&gt;  query = pm.newQuery("select from omega.server.User where verificationCode == codeParam");&lt;br /&gt;  query.declareParameters("String codeParam");&lt;br /&gt;   &lt;br /&gt;  try&lt;br /&gt;  {&lt;br /&gt;   results = (List&amp;lt;User&amp;gt;) query.execute(code);&lt;br /&gt;   &lt;br /&gt;   if (results.size () == 0)&lt;br /&gt;    root.put ("errorMessage", "Verification code not found.  Try resending the verification email.");&lt;br /&gt;   else&lt;br /&gt;   {&lt;br /&gt;    user = results.get(0);&lt;br /&gt;    user.setEmailVerified(true);&lt;br /&gt;    pm.makePersistent(user);&lt;br /&gt;    root.put ("errorMessage", "Email verified!");&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  finally&lt;br /&gt;  {&lt;br /&gt;   query.closeAll();&lt;br /&gt;   pm.close();        &lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  root.put("heading", "Email Verification Results");&lt;br /&gt;  root.put("loggedIn", loggedIn);&lt;br /&gt;  root.put("name", name);&lt;br /&gt;  root.put("userLevel", userLevel);&lt;br /&gt;  root.put("title", "GamesByTeens Email Verification");&lt;br /&gt;  &lt;br /&gt;  Template temp = config.getTemplate("error.ftl");&lt;br /&gt;&lt;/code&lt;br /&gt;&lt;br /&gt;All of that is added toward the bottom of the copy of the FrontPage servlet (the root.put statements show where the overlap is.  &lt;br /&gt;&lt;br /&gt;To test this I first load the page with a bogus verification code, to make sure it displays the error.  Then I would need to force a valid verification code into the datastore.  I can do this up at the start of the verification servlet itself, to make sure that the code I wrote works.  Just don't forget to remove your testing code after you're done (or at least comment it out in case you need to use it again later).&lt;br /&gt;&lt;br /&gt;This highlights an important part of any development, but web development especially.  Make sure your code works on its own before you try to hook it to other parts of your site.&lt;br /&gt;&lt;br /&gt;That's enough for one post.  In the next I'll come back to the last two steps, both of which are modifications to the ProfileServlet.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-2105445782991935694?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/2105445782991935694/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2010/01/validating-user-emails.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/2105445782991935694'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/2105445782991935694'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2010/01/validating-user-emails.html' title='Validating User Emails'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-8967820029635920301</id><published>2010-01-08T11:35:00.000-08:00</published><updated>2010-01-08T12:36:03.410-08:00</updated><title type='text'>Unique Display Names, Part 2</title><content type='html'>Okay, now we finally get to implementing unique display names.&lt;br /&gt;&lt;br /&gt;In RPXResults.java, we have a section where we see if the user is already in the system.  There are three possibilities now that we want unique display names:&lt;br /&gt;&lt;br /&gt;1) The user is in the system, and has a unique display name.  &lt;br /&gt;&lt;br /&gt;2) The user is in the system, but hasn't updated their profile with a unique display name&lt;br /&gt;&lt;br /&gt;3) The user is not in the system yet&lt;br /&gt;&lt;br /&gt;For option 1, we can just do what we're already doing, redirect to the appropriate destination URL.  &lt;br /&gt;&lt;br /&gt;For option 2 and 3, we need to instead redirect to the profile page so they can enter their unique display name.  Once they have entered a unique display name, we want to continue to the original destination URL.&lt;br /&gt;&lt;br /&gt;That requires a change to RPXResults.java to redirect to the profile page in appropriate circumstances, and a change to ProfileServlet.java to redirect to the destination URL.&lt;br /&gt;&lt;br /&gt;Note that we have a slight conflict here, because ProfileServlet.java wants to set a destination URL cookie, too, which will overwrite the one set by whatever page they were on when they clicked Sign In.  Rather than complicate things by having multiple cookies for destination URLs, we'll just make ProfileServlet hide the complexity.  It'll set its own cookie only when it is displaying its error template (which is the only time the Sign In link will show).  &lt;br /&gt;&lt;br /&gt;So we have these changes to make:&lt;br /&gt;&lt;br /&gt;1) RPXResults must redirect to the profile page when appropriate&lt;br /&gt;&lt;br /&gt;2) ProfileServlet must redirect to itself only when displaying the error template&lt;br /&gt;&lt;br /&gt;3) ProfileServlet must require the user name be unique&lt;br /&gt;&lt;br /&gt;4) ProfileServlet must, on a successful save of a unique user name, redirect to the destination URL&lt;br /&gt;&lt;br /&gt;I won't show the code for 2, since that's just moving the two Cookie related lines into the if statement for showing the error template.&lt;br /&gt;&lt;br /&gt;We'll start with the RPXResults changes.  Right now we have logic like this:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;if (results.size() == 0)&lt;br /&gt;{&lt;br /&gt;// create new  user and store it&lt;br /&gt;}&lt;br /&gt;else&lt;br /&gt;{&lt;br /&gt;// Existing user, update needed values&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;We're going to change to something like this:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;if (results.size() == 0)&lt;br /&gt;{&lt;br /&gt;// create new  user and store it&lt;br /&gt;// Redirect to /profile&lt;br /&gt;}&lt;br /&gt;else&lt;br /&gt;{&lt;br /&gt;if (user name is not set)&lt;br /&gt;// Existing user no name, redirect to /profile&lt;br /&gt;else&lt;br /&gt;// Existing user name okay&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;For the purposes of making the code I post as simple as possible, I'm going to strip out the else portion where the existing user name is okay.  The only reason to put code there is to update user records from previous versions during development, and now that we're trending more toward having the user enter their profile information rather than getting it from RPX, we won't need that as much.&lt;br /&gt;&lt;br /&gt;I'll move the declaration of String destination = null up above the try block in RPXServlet, so that I can set an appropriate destination page manually if needed.&lt;br /&gt;&lt;br /&gt;Here's the changed code for the case where the user doesn't exist in the system yet:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;   if (results.size() == 0)&lt;br /&gt;   {&lt;br /&gt;    user = new User ();&lt;br /&gt;    user.setIdentifier(identifier);&lt;br /&gt;    user.setName("");&lt;br /&gt;    user.setUserLevel (0);&lt;br /&gt;    pm.makePersistent(user);&lt;br /&gt;    destination = "/profile";&lt;br /&gt;   }&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Note that I don't set the user name from the RPX data now.  I'll let the user type in whatever they want in their profile.&lt;br /&gt;&lt;br /&gt;The else part of that if is for when they're already in the system, and we just need to make sure that they already have a unique user name set:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;   else&lt;br /&gt;   {&lt;br /&gt;    user = results.get(0);&lt;br /&gt;    &lt;br /&gt;    if (user.getName().equals(""))&lt;br /&gt;     destination = "/profile";&lt;br /&gt;   }&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;This assumes, of course, that ProfileServlet won't let them set a user name unless it's unique.  So let's make that change now.&lt;br /&gt;&lt;br /&gt;In ProfileServlet, we have code like this:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;    if (req.getParameter("submit") != null)&lt;br /&gt;    {&lt;br /&gt;     if (req.getParameter("name").trim().length () == 0)&lt;br /&gt;      message = "You must enter a name!";&lt;br /&gt;     else&lt;br /&gt;     {&lt;br /&gt;      user.setName(req.getParameter("name").trim());&lt;br /&gt;      pm.makePersistent(user);&lt;br /&gt;     }&lt;br /&gt;    }&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;We're going to add some more error checking, so that pm.makePersistent is only called when the user name is both non-empty and unique.  To see if the name is unique, we need to build a new query based on the name.  &lt;br /&gt;&lt;br /&gt;Inside the else from above, I'll put:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;      Query nameQuery = pm.newQuery("select from omega.server.User where name == nameParam");&lt;br /&gt;      nameQuery.declareParameters("String nameParam");&lt;br /&gt;      List&amp;lt;User&amp;gt; nameResults = (List&amp;lt;User&amp;gt;) nameQuery.execute(req.getParameter("name").trim());&lt;br /&gt;      &lt;br /&gt;      if (nameResults.size () != 0)&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;If the nameResults has some records, that still doesn't mean that the name is not unique.  After all, maybe they pressed Submit without changing their name...then we'd get exactly one result, and it would be them.  So we need to make sure the result we're getting isn't them.  &lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;      if (nameResults.size () != 0)&lt;br /&gt;      {&lt;br /&gt;       if (nameResults.get(0).getId() != user.getId())&lt;br /&gt;        message = "That name is already taken, please try another";&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      if (message.equals(""))&lt;br /&gt;      {&lt;br /&gt;       user.setName(req.getParameter("name").trim());&lt;br /&gt;       pm.makePersistent(user);&lt;br /&gt;      }&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;And finally save the user name is it turns out that none of our error checking found problems.&lt;br /&gt;&lt;br /&gt;That still leaves us with needing ProfileServlet to redirect appropriately.  There's actually a bit of complexity here.  We have a few cases to deal with:&lt;br /&gt;&lt;br /&gt;1) They clicked on the Profile link, so our destination URL cookie is set to /profile&lt;br /&gt;&lt;br /&gt;2) They clicked on Sign In and got here via RPXResults, so our destination URL cookie is set to something else, but their name wasn't unique so we need to stay here for now&lt;br /&gt;&lt;br /&gt;3) They clicked on Sign In and got here via RPXResults, so our destination URL cookie is set to something else, and their name is okay so we can redirect&lt;br /&gt;&lt;br /&gt;I'll focus my code on the else, just after the call to pm.makePersistent.  It won't be pretty, but it'll work (this, by the way, is how unmanageable code evolves, with ongoing changes to existing code...it's a good idea to stop and refactor the code now and then to clean it up).  &lt;br /&gt;&lt;br /&gt;Just after the call to pm.makePersistent, I'll get the destination URL from the cookie and see if it's /profile.  If it isn't, I'll redirect to it.  It'll look something like this:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;      if (message.equals(""))&lt;br /&gt;      {&lt;br /&gt;       user.setName(req.getParameter("name").trim());&lt;br /&gt;       pm.makePersistent(user);&lt;br /&gt;&lt;br /&gt;       String destination = null;&lt;br /&gt;       &lt;br /&gt;       Cookie [] cookies = req.getCookies();&lt;br /&gt;&lt;br /&gt;       if (destination == null &amp;&amp; cookies != null)&lt;br /&gt;       {&lt;br /&gt;        for(int i=0; i&amp;lt;cookies.length; i++) &lt;br /&gt;        {&lt;br /&gt;              Cookie cookie = cookies[i];&lt;br /&gt;              &lt;br /&gt;              if ("destinationURL".equals(cookie.getName()))&lt;br /&gt;                destination = cookie.getValue();&lt;br /&gt;           }&lt;br /&gt;       }&lt;br /&gt;       &lt;br /&gt;       if (destination != null &amp;&amp; ! destination.equals ("/profile"))&lt;br /&gt;       {&lt;br /&gt;        resp.sendRedirect (destination);&lt;br /&gt;        return;&lt;br /&gt;       }&lt;br /&gt;      }&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Be prepared for there to be bugs with this, since we have enough cases that you should be worried about the stability of the code.  Before going much farther, you really should redesign ProfileServlet to be more straightforward.  &lt;br /&gt;&lt;br /&gt;But, it should mostly work and shows the techniques to create the same sort of experience you're used to on well done membership sites.&lt;br /&gt;&lt;br /&gt;Next up, I think, will be adding an email address to the user's profile, and requiring that their email address be verified before it's used by the system.  This will introduce us to the Mail API.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-8967820029635920301?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/8967820029635920301/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2010/01/unique-display-names-part-2.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/8967820029635920301'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/8967820029635920301'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2010/01/unique-display-names-part-2.html' title='Unique Display Names, Part 2'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-2435040083829761367</id><published>2010-01-08T10:17:00.001-08:00</published><updated>2010-01-08T12:13:21.002-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='cookies'/><category scheme='http://www.blogger.com/atom/ns#' term='servlet'/><category scheme='http://www.blogger.com/atom/ns#' term='rpxnow'/><title type='text'>Unique Display Names</title><content type='html'>I'm going to focus on new techniques in the coming posts, because the semester is starting.  &lt;br /&gt;&lt;br /&gt;I've decided to make display names unique in the system.  I'm not especially happy with this, since it leads to funky display names like Dave5386.  But, if I'm going to provide some sort of forum interaction eventually, I also need a way to differentiate between people who might decide to use the same display name.&lt;br /&gt;&lt;br /&gt;I could simply require and display some context to the display name, such as "Jay, Muskingum University".  But, providing information online for kids that might be used to locate them is not a good idea.  &lt;br /&gt;&lt;br /&gt;So, I'll go with unique display names.  This means I need to change what I already have a bit.&lt;br /&gt;&lt;br /&gt;RPXResults.java is what first puts the user into the datastore.  Right now it's getting the user name from the RPX records.  That isn't guaranteed to be unique.  I could auto-generate a unique name, but then they might not go back and edit it. &lt;br /&gt;&lt;br /&gt;So instead, RPXResults.java will, when a user logs in for the first time, redirect to the page for editing their profile.  They will be required to update their profile with a unique name before doing using the rest of the site as a logged in user.  &lt;br /&gt;&lt;br /&gt;While I'm at it, I also want to modify RPXResults.java so that instead of redirecting to /front when a user logs in and they do have their profile info set, it redirects to the page they were trying to access.  So if they save a bookmark on My Contests, and try to go to that after the session has expired, it'll take them to the login page and then to My Contests after they log in.  That's more user-friendly, and best to do now before I have too many of those sorts of pages to edit.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Redirect to Target Page&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;We'll do the redirect to the target page first.  This requires me to pass into /rpxresults the target page.  Here's a page at &lt;a href="http://rpxwiki.com/Passing-state-through-RPX"&gt;RPXNow about passing query parameters&lt;/a&gt; through the token URL.&lt;br /&gt;&lt;br /&gt;Unfortunately, that didn't work for me.  The extra query parameter simply never came through to RPXResults.java.&lt;br /&gt;&lt;br /&gt;Luckily we do have another option.  We can store the destination URL as a cookie on the local machine and fetch it from RPXResults.java.&lt;br /&gt;&lt;br /&gt;We'll do FrontPage.java first as an example of how to modify the servlets.  Here's the code to save the destination URL as a cookie:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;Cookie cookie = new Cookie ("destinationURL", "/front");&lt;br /&gt;resp.addCookie(cookie);&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;I added this to just before the template is displayed.  Now we need to modify RPXResults.java to use the value of this cookie as the destination URL.  Here's the code that would replace the resp.redirect ("/front") line:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;String destination = null;&lt;br /&gt;Cookie [] cookies = req.getCookies();&lt;br /&gt;&lt;br /&gt;if (destination == null &amp;&amp; cookies != null)&lt;br /&gt;{&lt;br /&gt; for(int i=0; i&amp;lt;cookies.length; i++) &lt;br /&gt; {&lt;br /&gt;       Cookie cookie = cookies[i];&lt;br /&gt;       &lt;br /&gt;       if ("destinationURL".equals(cookie.getName()))&lt;br /&gt;         destination = cookie.getValue();&lt;br /&gt;        }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;if (destination == null)&lt;br /&gt;  destination = "/front";&lt;br /&gt;  &lt;br /&gt;resp.sendRedirect(destination);&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Now that RPXResults.java is set up, I can go back and modify the ProfileServlet and AdminServlet classes to add the cookie in the same way that the FrontPage servlet does.  &lt;br /&gt;&lt;br /&gt;Now if I remember to do that for every servlet I add after this, the user will always get to the page they wanted to get to, even if they have to log in first.&lt;br /&gt;&lt;br /&gt;That's probably long enough for one post, so I'll do unique user names in the next.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-2435040083829761367?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/2435040083829761367/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2010/01/unique-display-names.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/2435040083829761367'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/2435040083829761367'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2010/01/unique-display-names.html' title='Unique Display Names'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-1166039301843599763</id><published>2010-01-06T13:32:00.000-08:00</published><updated>2010-01-06T13:36:46.836-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='freemarker'/><title type='text'>Template Updates</title><content type='html'>A quick post about some template updates.  &lt;br /&gt;&lt;br /&gt;Following the pattern I used for navigation.ftl, I created header.ftl and moved everything up to and including the opening body tag into it.  I replaced the page title with a Freemarker variable, so that each servlet could specify the page title (I then modified each servlet to set that page title).  Every other template then included header.ftl at the top. &lt;br /&gt;&lt;br /&gt;I did the same with footer.ftl, putting into it just the closing body and html tags for now.  Later I'll  have more to put there, and will have a central place to put it.  &lt;br /&gt;&lt;br /&gt;As an example of the end result, here's what my error.ftl looks like now:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;#include "header.ftl"&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;h1&amp;gt;${heading}&amp;lt;/h1&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;#include "navigation.ftl"&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;p&amp;gt;${errorMessage}&amp;lt;/p&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;#include "footer.ftl"&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Nothing complicated here, just taking a moment to set the templates up for easier modification later on.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-1166039301843599763?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/1166039301843599763/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2010/01/template-updates.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/1166039301843599763'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/1166039301843599763'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2010/01/template-updates.html' title='Template Updates'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-5890831890234657425</id><published>2009-12-21T12:14:00.000-08:00</published><updated>2009-12-21T12:58:24.590-08:00</updated><title type='text'>Contest Work Flow</title><content type='html'>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?&lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;&lt;i&gt;Step One&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;We'll require enough info on that form to discourage tire kickers.  &lt;br /&gt;&lt;br /&gt;&lt;i&gt;Step Two&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;The site administrator will have a spot where requests to be made contest admins can be approved.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Step Three&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;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).  &lt;br /&gt;&lt;br /&gt;Until their request is processed, it shows up as Pending on their My Contests page.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Step Four&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;&lt;i&gt;Step Five&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;The contest admin can then fill out the rest of the contest information (deadlines, etc), and start to take applications for the contest.&lt;br /&gt;&lt;br /&gt;That seems like a reasonable work flow for the process, and gives me a number of pages I need to create.  &lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;That's the second page, the form.  &lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;The fourth page would be the one that lets the contest admin request a contest be created.&lt;br /&gt;&lt;br /&gt;The fifth page would be the site adminstrator's way to approve or reject those requests.  &lt;br /&gt;&lt;br /&gt;The sixth page would be the contest admin's My Contests page.&lt;br /&gt;&lt;br /&gt;The seventh page would be the one to let the contest admin edit the contest info.&lt;br /&gt;&lt;br /&gt;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:&lt;br /&gt;&lt;br /&gt;o) Make user names unique in the system, so no two members have the same user name. &lt;br /&gt; &lt;br /&gt;o) Add an email address to the user's profile, and require that the email be verified in order to receive site notifications&lt;br /&gt;&lt;br /&gt;o) And/or, set up a site based messaging system that stores messages on the site and possibly emails notifications of the messages.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;Those will each require some new techniques.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-5890831890234657425?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/5890831890234657425/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2009/12/contest-work-flow.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/5890831890234657425'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/5890831890234657425'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2009/12/contest-work-flow.html' title='Contest Work Flow'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-4199647946936852820</id><published>2009-12-21T11:10:00.001-08:00</published><updated>2009-12-21T12:09:06.557-08:00</updated><title type='text'>Creating The Admin Page</title><content type='html'>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.  &lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;I'll start with the web template.  Here's what will go between the body and /body tags in my new siteAdmin.ftl:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;h1&amp;gt;Site Administration Options&amp;lt;/h1&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;#include "navigation.ftl"&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;a href="/newContest"&amp;gt;Create A Contest&amp;lt;/a&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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".&lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;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:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;    List&amp;lt;User&amp;gt; results = (List&amp;lt;User&amp;gt;) query.execute(userid);&lt;br /&gt;    User user = results.get(0);&lt;br /&gt;   &lt;br /&gt;    Integer userLevel = user.getUserLevel ();&lt;br /&gt;    String name = user.getName ();&lt;br /&gt;    root.put("userLevel", userLevel);&lt;br /&gt;    root.put("name", name);&lt;br /&gt;    root.put("heading", "Site Admistration Options");&lt;br /&gt;    &lt;br /&gt;    if (userLevel &amp;gt; 0)&lt;br /&gt;    {&lt;br /&gt;     template = "siteAdmin.ftl";&lt;br /&gt;    }&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;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:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;if (template.equals("error.ftl"))&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;I'll also go back and make that same else change in ProfileServlet, since it's more robust.  &lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-4199647946936852820?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/4199647946936852820/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2009/12/creating-admin-page.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/4199647946936852820'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/4199647946936852820'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2009/12/creating-admin-page.html' title='Creating The Admin Page'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-8962032809178214311</id><published>2009-12-18T11:22:00.000-08:00</published><updated>2009-12-18T11:58:18.954-08:00</updated><title type='text'>Next Steps</title><content type='html'>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.  &lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;So, start small.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Contests&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;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?  &lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;So again, I'll start small so I don't back myself into a corner.&lt;br /&gt;&lt;br /&gt;Here are the basic fields I'll store for contests: name, description, contest administrator, application date, starting date, and deadline.  &lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;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.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-8962032809178214311?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/8962032809178214311/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2009/12/next-steps.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/8962032809178214311'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/8962032809178214311'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2009/12/next-steps.html' title='Next Steps'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-5564372732834547817</id><published>2009-12-18T10:47:00.000-08:00</published><updated>2009-12-18T11:32:01.678-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='form'/><category scheme='http://www.blogger.com/atom/ns#' term='servlet'/><title type='text'>Handling Profile Updates</title><content type='html'>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.  &lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;So code like this:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;if (req.getParameter("submit") == null)&lt;br /&gt;{&lt;br /&gt;// Servlet called because of link click&lt;br /&gt;}&lt;br /&gt;else&lt;br /&gt;{&lt;br /&gt;// Servlet called because of button click&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;will allow you to decide which situation is calling the servlet, so you can make the appropriate response.&lt;br /&gt;&lt;br /&gt;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:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;    User user = results.get(0);&lt;br /&gt;&lt;br /&gt;    if (req.getParameter("submit") != null)&lt;br /&gt;    {&lt;br /&gt;     user.setName(req.getParameter("name").trim());&lt;br /&gt;     pm.makePersistent(user);&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    String name = user.getName ();&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-5564372732834547817?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/5564372732834547817/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2009/12/handling-profile-updates.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/5564372732834547817'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/5564372732834547817'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2009/12/handling-profile-updates.html' title='Handling Profile Updates'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-5786609520066465171</id><published>2009-12-18T10:38:00.000-08:00</published><updated>2009-12-18T10:47:18.877-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='freemarker'/><title type='text'>Removing Duplication From Templates</title><content type='html'>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.  &lt;br /&gt;&lt;br /&gt;Instead, you want to put the navigation bar into a single file, and include that file at the appropriate point in your other pages.  &lt;br /&gt;&lt;br /&gt;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 &amp;lt;/script&gt; statement.  What you're left with is just the navigation bar and the RPXNow script to make the sign in work.  &lt;br /&gt;&lt;br /&gt;Save navigation.ftl and edit Front.ftl.  &lt;br /&gt;&lt;br /&gt;Remove the navigation bar and script block from Front.ftl, and replace them with this line:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;#include "navigation.ftl"&gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;Use the same technique with any HTML or FreeMarker code that is identical between more than one page.&lt;br /&gt;&lt;br /&gt;Now that our digression on reducing duplication is over, back to form processing.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-5786609520066465171?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/5786609520066465171/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2009/12/removing-duplication-from-templates.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/5786609520066465171'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/5786609520066465171'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2009/12/removing-duplication-from-templates.html' title='Removing Duplication From Templates'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-243747006961379990</id><published>2009-12-16T09:54:00.001-08:00</published><updated>2009-12-18T11:35:07.142-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='freemarker'/><category scheme='http://www.blogger.com/atom/ns#' term='form'/><category scheme='http://www.blogger.com/atom/ns#' term='servlet'/><title type='text'>Adding The Profile Page</title><content type='html'>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).&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;If you don't remember how to add a new servlet, read my original post on &lt;a href="http://gwtappengine.blogspot.com/2009/06/java-servlets.html"&gt;Java Servlets&lt;/a&gt;.  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.&lt;br /&gt;&lt;br /&gt;In ProfileServlet.java, I'll use code like this to show one page if they're logged in, and another if they're not:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;        Map root = new HashMap();&lt;br /&gt;        String template = "profileNotLoggedIn.ftl";&lt;br /&gt;       &lt;br /&gt;        if (session != null)&lt;br /&gt;        {&lt;br /&gt;            // Get the User's name&lt;br /&gt;            Long userid = (Long) session.getAttribute ("userid");&lt;br /&gt;            PersistenceManager pm = PMF.get().getPersistenceManager();&lt;br /&gt;           &lt;br /&gt;            Query query = pm.newQuery("select from omega.server.User where id == idParam");&lt;br /&gt;            query.declareParameters("Long idParam");&lt;br /&gt;           &lt;br /&gt;            try&lt;br /&gt;            {&lt;br /&gt;                List&amp;lt;User&gt; results = (List&amp;lt;User&gt;) query.execute(userid);&lt;br /&gt;                User user = results.get(0);&lt;br /&gt;                String name = user.getName ();&lt;br /&gt;                Integer userLevel = user.getUserLevel ();&lt;br /&gt;                root.put("loggedIn", true);&lt;br /&gt;                root.put("name", name);&lt;br /&gt;                root.put("userLevel", userLevel);&lt;br /&gt;                template = "profileLoggedIn.ftl";&lt;br /&gt;            }&lt;br /&gt;            finally&lt;br /&gt;            {&lt;br /&gt;                query.closeAll();&lt;br /&gt;                pm.close();       &lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        else&lt;br /&gt;        {&lt;br /&gt;            root.put("loggedIn", false);&lt;br /&gt;        }&lt;br /&gt;       &lt;br /&gt;        Template temp = config.getTemplate(template);         &lt;br /&gt;&lt;br /&gt;        PrintWriter out = resp.getWriter();&lt;br /&gt;       &lt;br /&gt;        try&lt;br /&gt;        {&lt;br /&gt;            temp.process(root, out);&lt;br /&gt;            out.flush();&lt;br /&gt;        } catch (TemplateException e)&lt;br /&gt;        {&lt;br /&gt;            e.printStackTrace(out);&lt;br /&gt;        }&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;Here's the form from profileLoggedIn.ftl:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;form action="/profile" method="POST"&gt;&lt;br /&gt;&amp;lt;input type="text" name="name" value="${name}"&gt;&lt;br /&gt;&amp;lt;input type="submit" name="submit" value="Save Changes"&gt;&lt;br /&gt;&amp;lt;/form&gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-243747006961379990?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/243747006961379990/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2009/12/adding-profile-page.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/243747006961379990'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/243747006961379990'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2009/12/adding-profile-page.html' title='Adding The Profile Page'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-2462971755764244503</id><published>2009-12-16T09:49:00.001-08:00</published><updated>2009-12-16T09:51:58.797-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='freemarker'/><title type='text'>Creating The Front Page</title><content type='html'>Okay, now to create the front page template in FreeMarker.&lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;We've seen before that a FreeMarker template is just HTML with some FreeMarker code mixed in.  See my previous post on &lt;a href="http://gwtappengine.blogspot.com/2009/06/creating-freemarker-templates.html"&gt;Creating A FreeMarker Template&lt;/a&gt; if you missed it.&lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;Here's the FreeMarker template code for this:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;#if loggedIn&amp;gt;&lt;br /&gt;&amp;lt;a href="/signout"&amp;gt;Sign Out, ${name}&amp;lt;/a&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;lt;a href="/profile"&amp;gt;My Profile&amp;lt;/a&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;lt;a href="/contests"&amp;gt;My Contests&amp;lt;/a&amp;gt;&lt;br /&gt;&amp;lt;#if (userLevel &amp;gt; 0)&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;lt;a href="/admin"&amp;gt;Admin&amp;lt;/a&amp;gt;&lt;br /&gt;&amp;lt;/#if&amp;gt;&lt;br /&gt;&amp;lt;#else&amp;gt;&lt;br /&gt;&amp;lt;a class="rpxnow" onclick="return false;"&lt;br /&gt;   href="https://omega.rpxnow.com/openid/v2/signin?token_url=http://localhost:8080/rpxresults"&amp;gt;&lt;br /&gt;  Sign In&lt;br /&gt;&amp;lt;/a&amp;gt;&lt;br /&gt;&amp;lt;/#if&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;Also note that the URLs given for the links don't go anywhere yet.  We'll have to add those pages.&lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;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.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-2462971755764244503?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/2462971755764244503/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2009/12/creating-front-page_16.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/2462971755764244503'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/2462971755764244503'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2009/12/creating-front-page_16.html' title='Creating The Front Page'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-132133695965306914</id><published>2009-12-16T07:37:00.000-08:00</published><updated>2009-12-16T08:01:02.856-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='freemarker'/><title type='text'>Creating The Front Page Data Model</title><content type='html'>Okay, so I know I need a value called name in my front page data model.  How do I get that?&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;  HttpSession session = req.getSession(false);&lt;br /&gt;  boolean loggedIn = false;&lt;br /&gt;  String name = "";&lt;br /&gt;  &lt;br /&gt;  if (session != null)&lt;br /&gt;  {&lt;br /&gt;   loggedIn = true;&lt;br /&gt;  &lt;br /&gt;   // Get the User's name&lt;br /&gt;   Long userid = (Long) session.getAttribute ("userid");&lt;br /&gt;   PersistenceManager pm = PMF.get().getPersistenceManager();&lt;br /&gt;   &lt;br /&gt;   Query query = pm.newQuery("select from project.server.User where id == idParam");&lt;br /&gt;   query.declareParameters("Long idParam");&lt;br /&gt;   &lt;br /&gt;   try&lt;br /&gt;   {&lt;br /&gt;    List&lt;User&gt; results = (List&lt;User&gt;) query.execute(userid);&lt;br /&gt;    User user = results.get(0);&lt;br /&gt;    name = user.getName ();&lt;br /&gt;   }&lt;br /&gt;   finally&lt;br /&gt;   {&lt;br /&gt;    query.closeAll();&lt;br /&gt;    pm.close();        &lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  Map root = new HashMap();&lt;br /&gt;  root.put("loggedIn", loggedIn);&lt;br /&gt;  root.put("name", name);&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;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:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;#if loggedIn&amp;gt;&lt;br /&gt;&amp;lt;a href="/signout"&amp;gt;Sign Out, ${name}&amp;lt;/a&amp;gt;&lt;br /&gt;&amp;lt;#else&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-132133695965306914?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/132133695965306914/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2009/12/creating-front-page-data-model.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/132133695965306914'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/132133695965306914'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2009/12/creating-front-page-data-model.html' title='Creating The Front Page Data Model'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-5301250856827739412</id><published>2009-12-16T07:30:00.000-08:00</published><updated>2009-12-16T07:54:45.338-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rpxnow'/><title type='text'>Making The User Datastore More Robust</title><content type='html'>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.  &lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;(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.)&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;  String identifier = info.getElementsByTagName("identifier").item(0).getTextContent(); &lt;br /&gt;  String name = info.getElementsByTagName("displayName").item(0).getTextContent();&lt;br /&gt;  &lt;br /&gt;  PersistenceManager pm = PMF.get().getPersistenceManager();&lt;br /&gt;  &lt;br /&gt;  Query query = pm.newQuery("select from project.server.User where identifier == identifierParam");&lt;br /&gt;  query.declareParameters("String identifierParam");&lt;br /&gt;  &lt;br /&gt;  try&lt;br /&gt;  {&lt;br /&gt;   List&lt;User&gt; results = (List&lt;User&gt;) query.execute(identifier);&lt;br /&gt;   User user = null;&lt;br /&gt;   &lt;br /&gt;   if (results.size() == 0)&lt;br /&gt;   {&lt;br /&gt;    user = new User ();&lt;br /&gt;    user.setIdentifier(identifier);&lt;br /&gt;    user.setName(name);&lt;br /&gt;    pm.makePersistent(user);&lt;br /&gt;   }&lt;br /&gt;   else&lt;br /&gt;   {&lt;br /&gt;    boolean changed = false;&lt;br /&gt;    &lt;br /&gt;    user = results.get(0);&lt;br /&gt;    &lt;br /&gt;    if (user.getName () == null)&lt;br /&gt;    {&lt;br /&gt;     user.setName (name);&lt;br /&gt;     changed = true;&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    if (changed)&lt;br /&gt;     pm.makePersistent(user);&lt;br /&gt;   }&lt;br /&gt;   &lt;br /&gt;   HttpSession session = req.getSession();&lt;br /&gt;   session.setAttribute("userid", user.getId());&lt;br /&gt;  }&lt;br /&gt;  finally&lt;br /&gt;  {&lt;br /&gt;   query.closeAll();&lt;br /&gt;   pm.close();        &lt;br /&gt;  }&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;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.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-5301250856827739412?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/5301250856827739412/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2009/12/making-user-datastore-more-robust.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/5301250856827739412'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/5301250856827739412'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2009/12/making-user-datastore-more-robust.html' title='Making The User Datastore More Robust'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-453667490499445410</id><published>2009-12-16T06:36:00.000-08:00</published><updated>2009-12-16T07:01:36.151-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web design'/><title type='text'>New Site Design</title><content type='html'>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.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Front Page&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;First time visitors still need an explanation of what the site is.  That'll be in the text that initially shows in the body.  &lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;The front page should have the following links: Sign In (1), Sign Out (2), My Profile (2), My Contests (2), Admin (4), Forums (2).  &lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;My Profile&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;My Contests&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This page will list all the contests the currently logged in user is in.  This could be none.  &lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;Contest administrators count as participating in a contest, so they'll see the contests they are administering on this page, too.  &lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Admin&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Forums&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Contest Site&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Member Flow&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;With so many different types of members, it's worth talking about how someone gets to be each type.  &lt;br /&gt;&lt;br /&gt;A visitor is simply someone visiting the site who hasn't logged in yet.  This is the lowest access level.  &lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;A contest administrator has total control over the contests they run.  They can accept applicants, invite judges, etc.  &lt;br /&gt;&lt;br /&gt;A site administrator can create new contests and assign contest administrators, and has total control over the entire site.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-453667490499445410?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/453667490499445410/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2009/12/new-site-design.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/453667490499445410'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/453667490499445410'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2009/12/new-site-design.html' title='New Site Design'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-2957371794795182298</id><published>2009-12-11T06:45:00.000-08:00</published><updated>2009-12-11T06:50:19.245-08:00</updated><title type='text'>Change Of Direction</title><content type='html'>It's been a while since my last post...fall semester got in the way.&lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;I'll do that next, and then get back on track with the technical bits.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-2957371794795182298?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/2957371794795182298/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2009/12/change-of-direction.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/2957371794795182298'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/2957371794795182298'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2009/12/change-of-direction.html' title='Change Of Direction'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-22250825293623047</id><published>2009-06-16T07:55:00.000-07:00</published><updated>2009-06-16T09:10:29.581-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='design'/><title type='text'>Designing The Site</title><content type='html'>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.  &lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;If you remember, the project I'm doing will be a &lt;a href="http://gwtappengine.blogspot.com/2009/05/what-project.html"&gt;community events site&lt;/a&gt;.  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.  &lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Front Page&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;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).  &lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;The Members link will go to a list of members and the ability to view their profiles.  &lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Edit Profile Page&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Create Event Page&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Events Page&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Recommendations Page&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This page will list the most recent recommendations, and allow browsing/searching recommendations.  &lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Groups Page&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Allows the user to browse groups and join them.  &lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Members Page&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Allows the user to browse member profiles.  Might need to be logged in for this, for privacy reasons.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Create Recommendation Page&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;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".  &lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Create Group Page&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Allows the logged in user to create a new group.  Need the group name, description, address, etc.  &lt;br /&gt;&lt;br /&gt;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.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-22250825293623047?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/22250825293623047/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2009/06/designing-site.html#comment-form' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/22250825293623047'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/22250825293623047'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2009/06/designing-site.html' title='Designing The Site'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-6257095601803501343</id><published>2009-06-15T12:56:00.000-07:00</published><updated>2009-06-16T07:55:11.223-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='app engine'/><category scheme='http://www.blogger.com/atom/ns#' term='freemarker'/><category scheme='http://www.blogger.com/atom/ns#' term='servlet'/><title type='text'>Executing A FreeMarker Template</title><content type='html'>In our front page servlet, we need to execute the FreeMarker template we created.  &lt;br /&gt;&lt;br /&gt;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 &lt;a href="http://code.google.com/appengine/docs/java/datastore/usingjdo.html"&gt;PMF class&lt;/a&gt;?)  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.  &lt;br /&gt;&lt;br /&gt;You'll end up with something like this (I've left off the package and import statements to save space):&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public class ConfigFactory&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;    private static Configuration configInstance = null; &lt;br /&gt;&amp;nbsp;    private ConfigFactory() {}    &lt;br /&gt;&amp;nbsp;    public static Configuration get(ServletContext context) &lt;br /&gt;&amp;nbsp;    {        &lt;br /&gt;&amp;nbsp;&amp;nbsp;     if (configInstance == null)&lt;br /&gt;&amp;nbsp;&amp;nbsp;     {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;      configInstance = new Configuration();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;      configInstance.setServletContextForTemplateLoading(context, "WEB-INF/templates");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;      configInstance.setObjectWrapper(new DefaultObjectWrapper());       &lt;br /&gt;&amp;nbsp;&amp;nbsp;     }&lt;br /&gt;     &lt;br /&gt;&amp;nbsp;&amp;nbsp;     return configInstance;    &lt;br /&gt;&amp;nbsp;    }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;In our front page servlet, we can now use the singleton class to get a Configuration instance.  &lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;Configuration config = ConfigFactory.get(getServletContext ());&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;If they are logged in, we'll have their user id in the session.  So let's get the session and check.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;HttpSession session = req.getSession(false);&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;boolean loggedIn = false;&lt;br /&gt;&lt;br /&gt;if (session != null)&lt;br /&gt;&amp;nbsp;&amp;nbsp;loggedIn = true;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;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:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;Map root = new HashMap();&lt;br /&gt;root.put("loggedIn", loggedIn);&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;Now we need to get the template:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;Template temp = config.getTemplate("Front.ftl");&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;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:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;PrintWriter out = resp.getWriter();&lt;br /&gt;  &lt;br /&gt;try&lt;br /&gt;{&lt;br /&gt;&amp;nbsp; temp.process(root, out);&lt;br /&gt;&amp;nbsp; out.flush();&lt;br /&gt;} catch (TemplateException e)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp; e.printStackTrace(out);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;At the bottom of doPost in RPXResults, this should do it:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;resp.sendRedirect("/front");&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Assuming that /front is what you put in for the url mapping for your front page servlet.&lt;br /&gt;&lt;br /&gt;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.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-6257095601803501343?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/6257095601803501343/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2009/06/executing-freemarker-template.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/6257095601803501343'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/6257095601803501343'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2009/06/executing-freemarker-template.html' title='Executing A FreeMarker Template'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-1690309935491107299</id><published>2009-06-15T12:16:00.000-07:00</published><updated>2009-06-15T12:54:45.649-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='freemarker'/><title type='text'>Creating FreeMarker Templates</title><content type='html'>Before we can execute a template, we have to create one.  &lt;br /&gt;&lt;br /&gt;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 &lt;i&gt;kind&lt;/i&gt; of data we'll need, but we don't know exactly what &lt;i&gt;values&lt;/i&gt; will be assigned.  That happens later, when the servlet gives the template values for the data it needs.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;In Eclipse, right click on the war directory in your project and choose New-&gt;File.  Name it something like Front.ftl.  The extension must be .ftl for the FreeMarker plugin to operate on it.  &lt;br /&gt;&lt;br /&gt;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:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;html&amp;gt;&lt;br /&gt;  &amp;lt;head&amp;gt;&lt;br /&gt;    &amp;lt;meta http-equiv="content-type" content="text/html; charset=UTF-8"&amp;gt;&lt;br /&gt;    &amp;lt;link type="text/css" rel="stylesheet" href="Omega.css"&amp;gt;&lt;br /&gt;    &amp;lt;title&amp;gt;Web Application Starter Project&amp;lt;/title&amp;gt;&lt;br /&gt;  &amp;lt;/head&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;body&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;h1&amp;gt;Web Application Starter Project&amp;lt;/h1&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;!-- Put your RPX Sign In link here --&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;!-- Put your RPX Javascript here --&amp;gt;&lt;br /&gt;  &amp;lt;/body&amp;gt;&lt;br /&gt;&amp;lt;/html&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Except that you would have actually copied your RPX Sign In link and Javascript over from the project.html file.  &lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;a href="/signout"&amp;gt;Sign Out&amp;lt;/a&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;If we do something like this:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;#if loggedIn&amp;gt;&lt;br /&gt;  &amp;lt;!-- Put Sign Out link here --&amp;gt;&lt;br /&gt;&amp;lt;#else&amp;gt;&lt;br /&gt;  &amp;lt;!-- Put Sign In link here --&amp;gt;&lt;br /&gt;&amp;lt;/#if&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;So how does the value of loggedIn get set?  That's over in our front page servlet, and the next post.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-1690309935491107299?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/1690309935491107299/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2009/06/creating-freemarker-templates.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/1690309935491107299'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/1690309935491107299'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2009/06/creating-freemarker-templates.html' title='Creating FreeMarker Templates'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-2007554346894260092</id><published>2009-06-15T12:01:00.001-07:00</published><updated>2010-01-06T13:15:41.046-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='app engine'/><category scheme='http://www.blogger.com/atom/ns#' term='eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='freemarker'/><title type='text'>Installing FreeMarker</title><content type='html'>There are two pieces to install with &lt;a href="http://freemarker.sourceforge.net/"&gt;FreeMarker&lt;/a&gt;.  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.  &lt;br /&gt;&lt;br /&gt;Let's start with the plugin (you remember &lt;a href="http://gwtappengine.blogspot.com/2009/05/installing-eclipse.html"&gt;installing all those plugins earlier&lt;/a&gt;, right?).  &lt;a href="http://www.jboss.org/tools/download/"&gt;Go to this page&lt;/a&gt; and copy the link for the version of Eclipse you have (use the Stable Updates links).  Install just the FreeMarker IDE from that site.&lt;br /&gt;&lt;br /&gt;Restart Eclipse at the end, and we'll move on to the FreeMarker server libraries.  Download the latest stable version at the &lt;a href="http://freemarker.sourceforge.net/freemarkerdownload.html"&gt;FreeMarker download page&lt;/a&gt;.  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).&lt;br /&gt;&lt;br /&gt;Copy freemarker.jar into the war/WEB-INF/lib directory of your project in Eclipse.  Right click on the file, choose Build Path-&gt;Add to Build Path.&lt;br /&gt;&lt;br /&gt;That's it, we can now both create template files and execute them from servlets.  We'll see how in the next few posts.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;There's a &lt;a href="http://groups.google.com/group/google-appengine-java/browse_thread/thread/dd84e44f604498c4"&gt;workaround here&lt;/a&gt;.  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).&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;Eventually, this will be fixed in one way or another (probably with a Freemarker update), and your version of TextBlock.java can be removed.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-2007554346894260092?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/2007554346894260092/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2009/06/installing-freemarker.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/2007554346894260092'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/2007554346894260092'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2009/06/installing-freemarker.html' title='Installing FreeMarker'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-9191391220353545310</id><published>2009-06-15T11:13:00.001-07:00</published><updated>2009-06-15T12:07:52.178-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='app engine'/><category scheme='http://www.blogger.com/atom/ns#' term='freemarker'/><category scheme='http://www.blogger.com/atom/ns#' term='servlet'/><title type='text'>A One Page Web Site</title><content type='html'>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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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-&gt;Text Editor.&lt;br /&gt;&lt;br /&gt;Enter the info for a new servlet (you &lt;a href="http://gwtappengine.blogspot.com/2009/06/java-servlets.html"&gt;remember how&lt;/a&gt;, 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.&lt;br /&gt;&lt;br /&gt;Now create the servlet (which you &lt;a href="http://gwtappengine.blogspot.com/2009/06/starting-rpxresults-servlet.html"&gt;also remember&lt;/a&gt;, right?). Go ahead and put in the code to create the PrintWriter.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;The easy way is to output the HTML using our PrintWriter:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;writer.println ("&amp;lt;html&amp;gt;");&lt;br /&gt;writer.println ("&amp;lt;head&amp;gt;");&lt;br /&gt;writer.println ("&amp;lt;meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\"&amp;gt;");&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;There are lots of templating engines out there.  I'm going to use &lt;a href="http://freemarker.sourceforge.net/"&gt;FreeMarker&lt;/a&gt;, which integrates well with Java servlets and Eclipse.  &lt;br /&gt;&lt;br /&gt;So before we can use FreeMarker, we have to install it and the Eclipse plugin for it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-9191391220353545310?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/9191391220353545310/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2009/06/one-page-web-site.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/9191391220353545310'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/9191391220353545310'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2009/06/one-page-web-site.html' title='A One Page Web Site'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-9183188883575944773</id><published>2009-06-15T10:21:00.000-07:00</published><updated>2009-06-15T11:08:32.497-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='app engine'/><category scheme='http://www.blogger.com/atom/ns#' term='servlet'/><category scheme='http://www.blogger.com/atom/ns#' term='session handling'/><title type='text'>Session Handling</title><content type='html'>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.&lt;br /&gt;&lt;br /&gt;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".&lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;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-&gt;Text Editor.  Just before the ending /appengine-web-app tag, put this line:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;sessions-enabled&amp;gt;true&amp;lt;/sessions-enabled&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;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:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;HttpSession session = req.getSession ();&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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).&lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;So let's store the user's primary key in the session.  From there we can look up the rest of their fields.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;HttpSession session = req.getSession();&lt;br /&gt;session.setAttribute("userid", user.getId());&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;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.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-9183188883575944773?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/9183188883575944773/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2009/06/session-handling.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/9183188883575944773'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/9183188883575944773'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2009/06/session-handling.html' title='Session Handling'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-6882251192419082066</id><published>2009-06-15T07:48:00.000-07:00</published><updated>2009-12-16T07:28:08.928-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='app engine'/><category scheme='http://www.blogger.com/atom/ns#' term='jdo'/><title type='text'>User Datastore Interaction</title><content type='html'>Okay, here's the complete code (in RPXResults.java) for creating users only if they don't already exist:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;PersistenceManager pm = PMF.get().getPersistenceManager();&lt;br /&gt;  &lt;br /&gt;Query query = pm.newQuery("select from omega.server.User where identifier == identifierParam");&lt;br /&gt;query.declareParameters("String identifierParam");&lt;br /&gt;  &lt;br /&gt;try&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;List&lt;User&gt; results = (List&lt;User&gt;) query.execute(identifier);&lt;br /&gt;   &lt;br /&gt;&amp;nbsp;if (results.size() == 0)&lt;br /&gt;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;User user = new User ();&lt;br /&gt;&amp;nbsp;&amp;nbsp;user.setIdentifier(identifier);&lt;br /&gt;&amp;nbsp;&amp;nbsp;pm.makePersistent(user);&lt;br /&gt;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;else&lt;br /&gt;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;User user = results.get(0);&lt;br /&gt;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;finally&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;query.closeAll();&lt;br /&gt;&amp;nbsp;pm.close();        &lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;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?&lt;br /&gt;&lt;br /&gt;Unfortunately, no.&lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;Next post, sessions.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-6882251192419082066?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/6882251192419082066/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2009/06/user-datastore-interaction.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/6882251192419082066'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/6882251192419082066'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2009/06/user-datastore-interaction.html' title='User Datastore Interaction'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-3013743956494983708</id><published>2009-06-15T06:46:00.001-07:00</published><updated>2009-06-15T10:13:35.387-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='app engine'/><category scheme='http://www.blogger.com/atom/ns#' term='query'/><category scheme='http://www.blogger.com/atom/ns#' term='jdo'/><title type='text'>Searching For A User</title><content type='html'>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:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;Query query = pm.newQuery("select from project.server.User where identifier == " + identifier);&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;i&gt;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.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;If your first thought on seeing that wasn't, "What about SQL injection attacks?", then you should &lt;a href="http://www.fortify.com/vulncat/en/vulncat/java/sql_injection_jdo.html"&gt;read this tutorial&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;But, it never hurts to be safe, so let's use a parameter to lessen the risk:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;Query query = pm.newQuery("select from project.server.User where identifier == identifierParam");&lt;br /&gt;query.declareParameters("String identifierParam");&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Note that parameters &lt;i&gt;may&lt;/i&gt; 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.&lt;br /&gt;&lt;br /&gt;Now that we have a Query, we need to execute it and provide the value for the parameter:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;try&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;List&lt;User&gt; results = (List&lt;User&gt;) query.execute(identifier);&lt;br /&gt;}&lt;br /&gt;finally&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;query.closeAll();&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;  &lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;We'll put the pieces together in the next post.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-3013743956494983708?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/3013743956494983708/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2009/06/searching-for-user.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/3013743956494983708'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/3013743956494983708'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2009/06/searching-for-user.html' title='Searching For A User'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-473552499125266234</id><published>2009-06-15T06:15:00.000-07:00</published><updated>2009-06-15T06:57:49.301-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='app engine'/><category scheme='http://www.blogger.com/atom/ns#' term='jdo'/><title type='text'>Creating A New User</title><content type='html'>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.  &lt;br /&gt;&lt;br /&gt;Creating the User is nothing new:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;String identifier = info.getElementsByTagName("identifier").item(0).getTextContent();&lt;br /&gt;User user = new User ();&lt;br /&gt;user.setIdentifier(identifier);&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;Now for storing the User to the datastore.  We need to get an instance of a PerstistenceManager from the factory:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;PersistenceManager pm = PMF.get().getPersistenceManager();&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Then we call makePersistent:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;try &lt;br /&gt;{            &lt;br /&gt;&amp;nbsp;&amp;nbsp;pm.makePersistent(user);        &lt;br /&gt;} &lt;br /&gt;finally &lt;br /&gt;{            &lt;br /&gt;&amp;nbsp;&amp;nbsp;pm.close();        &lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-473552499125266234?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/473552499125266234/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2009/06/creating-new-user.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/473552499125266234'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/473552499125266234'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2009/06/creating-new-user.html' title='Creating A New User'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-3798243577402177204</id><published>2009-06-15T05:24:00.001-07:00</published><updated>2009-06-15T09:59:47.956-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='app engine'/><category scheme='http://www.blogger.com/atom/ns#' term='jdo'/><title type='text'>Storing Data In App Engine</title><content type='html'>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.  &lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;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).&lt;br /&gt;&lt;br /&gt;Enough of the good news/bad news bit.  Let's dive into JDO.  &lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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):&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;@PersistenceCapable(identityType = IdentityType.APPLICATION)&lt;br /&gt;public class User&lt;br /&gt;{&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;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:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;@PersistenceCapable(identityType = IdentityType.APPLICATION)&lt;br /&gt;public class User&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;@PrimaryKey&lt;br /&gt;&amp;nbsp;&amp;nbsp;@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)&lt;br /&gt;&amp;nbsp;&amp;nbsp;private Long id;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;@Persistent&lt;br /&gt;&amp;nbsp;&amp;nbsp;private String identifier;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;@Persistent&lt;br /&gt;&amp;nbsp;&amp;nbsp;private String name;&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;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-&gt;Encapsulate Field option to save yourself the typing).&lt;br /&gt;&lt;br /&gt;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 &lt;a href="http://code.google.com/appengine/docs/java/datastore/usingjdo.html"&gt;use their PMF class&lt;/a&gt; as is in your project.server package.&lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;We'll start in the next post with creating a new User and storing it to the datastore.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-3798243577402177204?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/3798243577402177204/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2009/06/storing-data-in-app-engine.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/3798243577402177204'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/3798243577402177204'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2009/06/storing-data-in-app-engine.html' title='Storing Data In App Engine'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-4133221140127177626</id><published>2009-06-09T10:25:00.000-07:00</published><updated>2009-08-28T12:57:15.800-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gwt'/><category scheme='http://www.blogger.com/atom/ns#' term='xml'/><category scheme='http://www.blogger.com/atom/ns#' term='rpxnow'/><title type='text'>Fetching User's Email From RPX</title><content type='html'>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.&lt;br /&gt;&lt;br /&gt;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 &lt;span style=";font-family:courier new;font-size:100%;"  &gt;&lt;code&gt;https://rpxnow.com/api/v2/auth_info&lt;/code&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;That page will send back the info we need about the user.  Simple, right?&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;Luckily, RPX has provided a &lt;a href="http://rpxwiki.com/Java-Helper-Class"&gt;Java helper class&lt;/a&gt; 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.&lt;br /&gt;&lt;br /&gt;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:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;Rpx rpx = new Rpx ("PutYourApiKeyHere", "https://rpxnow.com");&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;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):&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;Element info = rpx.authInfo (token);&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Note that when you try to find an import for Element, you'll get a big long list.  The one you want is:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;import org.w3c.dom.Element;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;That's an XML Element as it's being used by the RPX helper class.  Now you have the info from RPX!&lt;br /&gt;&lt;br /&gt;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. &lt;br /&gt;&lt;br /&gt;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 &lt;a href="https://rpxnow.com/docs#profile_data"&gt;list of possible fields&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;Using the Element that was returned, we call getElementsByTagName and use the name of the profile field we want.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;NodeList list = info.getElementsByTagName("identifier");&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;writer.println (list.item(0).getTextContent ());&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;We'll start on that in the next post.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-4133221140127177626?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/4133221140127177626/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2009/06/fetching-users-email-from-rpx.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/4133221140127177626'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/4133221140127177626'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2009/06/fetching-users-email-from-rpx.html' title='Fetching User&apos;s Email From RPX'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-2482133051481557803</id><published>2009-06-09T07:24:00.001-07:00</published><updated>2009-06-09T09:34:25.750-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gwt'/><category scheme='http://www.blogger.com/atom/ns#' term='rpxnow'/><title type='text'>Starting RPXResults Servlet</title><content type='html'>To create the RPXResults servlet, navigate in Eclipse to your project.server package.  Right click on that and choose New-&gt;Class.  It should prepopulate the package name for you.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;When the class comes up, right click on it and choose Source-&gt;Override/Implement Methods.  Choose doGet and doPost and click OK.&lt;br /&gt;&lt;br /&gt;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:&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;   doPost (req, resp);&lt;/span&gt;&lt;/blockquote&gt;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:&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;        String token = req.getParameter("token");&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;        PrintWriter writer = resp.getWriter ();&lt;/span&gt;&lt;br /&gt;     &lt;br /&gt;&lt;span style="font-family:courier new;"&gt;        writer.print("RPX sent this: " + token);&lt;/span&gt;&lt;/blockquote&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;Next we'll put in the code to complete the login processing and set up a session.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-2482133051481557803?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/2482133051481557803/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2009/06/starting-rpxresults-servlet.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/2482133051481557803'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/2482133051481557803'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2009/06/starting-rpxresults-servlet.html' title='Starting RPXResults Servlet'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-6294146086310759859</id><published>2009-06-05T08:23:00.000-07:00</published><updated>2009-06-15T11:28:20.059-07:00</updated><title type='text'>Java Servlets</title><content type='html'>Using Google's App Engine for Java, the server side coding is done using Java Servlets.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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-&gt;Text Editor.  Copy the &amp;lt;servlet&amp;gt; and &amp;lt;servlet-mapping&amp;gt; sections and paste them at the bottom of the file, just after &amp;lt;/servlet-mapping&amp;gt;.&lt;br /&gt;&lt;br /&gt;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".&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;Now we just need to write the actual servlet class.&lt;/servlet&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-6294146086310759859?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/6294146086310759859/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2009/06/java-servlets.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/6294146086310759859'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/6294146086310759859'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2009/06/java-servlets.html' title='Java Servlets'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-7231663970051458346</id><published>2009-06-02T12:58:00.000-07:00</published><updated>2009-06-09T07:11:06.148-07:00</updated><title type='text'>User Authentication With RPX</title><content type='html'>The first step is to sign up with &lt;a href="https://rpxnow.com/"&gt;RPX&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;How RPX Works&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;So the parts we write are:&lt;br /&gt;&lt;br /&gt;1) A login HTML page that includes the widget code from RPX&lt;br /&gt;&lt;br /&gt;2) A login results page that includes server side code for fetching the user's email from RPX&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Creating The Login Page&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;Double click that to edit it, and let's see what we have to work with.  Now go to &lt;a href="http://rpxnow.com/"&gt;RPX &lt;/a&gt;and log in and go to the Integration Guide.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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:&lt;br /&gt;&lt;br /&gt;      RPXNOW.token_url = "http://localhost:8080/rpxresults";&lt;br /&gt;&lt;br /&gt;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!&lt;br /&gt;&lt;br /&gt;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!&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-7231663970051458346?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/7231663970051458346/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2009/06/user-authentication-with-rpx.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/7231663970051458346'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/7231663970051458346'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2009/06/user-authentication-with-rpx.html' title='User Authentication With RPX'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-2731146126277162120</id><published>2009-05-21T11:04:00.000-07:00</published><updated>2009-06-03T06:25:36.970-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='openid'/><category scheme='http://www.blogger.com/atom/ns#' term='app engine'/><category scheme='http://www.blogger.com/atom/ns#' term='gwt'/><title type='text'>User Authentication</title><content type='html'>Since we know our application will need to have registered users, I figured I'd start by implementing the user authentication framework.&lt;br /&gt;&lt;br /&gt;There are several options, some of which can be used together.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Use OpenID&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Use Google's Authentication&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;&lt;/span&gt;&lt;span style="font-style: italic;"&gt;Maintain My Own Database&lt;br /&gt;&lt;span style="font-style: italic;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;What To Do?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Having talked about three options, I think I'll use a fourth.&lt;br /&gt;&lt;br /&gt;&lt;a href="https://rpxnow.com/"&gt;RPX&lt;/a&gt; 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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;We'll start to work through the details of this in the next post.&lt;br /&gt;&lt;span style="font-style: italic;"&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-2731146126277162120?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/2731146126277162120/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2009/05/user-authentication.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/2731146126277162120'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/2731146126277162120'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2009/05/user-authentication.html' title='User Authentication'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-8163087203712037583</id><published>2009-05-20T15:42:00.001-07:00</published><updated>2009-05-20T16:01:35.510-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='app engine'/><category scheme='http://www.blogger.com/atom/ns#' term='gwt'/><title type='text'>Examining The Sample App</title><content type='html'>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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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).&lt;br /&gt;&lt;br /&gt;For servlet based projects, the web.xml file manages the mapping between URLs and Java classes. &lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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. &lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-8163087203712037583?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/8163087203712037583/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2009/05/examining-sample-app.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/8163087203712037583'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/8163087203712037583'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2009/05/examining-sample-app.html' title='Examining The Sample App'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-6526951777645305117</id><published>2009-05-20T14:58:00.000-07:00</published><updated>2009-06-02T11:21:23.507-07:00</updated><title type='text'>How GWT Works &amp; Our First Design Decision</title><content type='html'>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!&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;Our first major design decision is how to integrate the GWT application into the web page.  Here are the options:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;HTML First&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Pure GWT&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.  &lt;br /&gt;&lt;br /&gt;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. &lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;And The Answer Is?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;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. &lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-6526951777645305117?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/6526951777645305117/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2009/05/how-gwt-works-our-first-design-decision.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/6526951777645305117'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/6526951777645305117'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2009/05/how-gwt-works-our-first-design-decision.html' title='How GWT Works &amp; Our First Design Decision'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-310816270999880312</id><published>2009-05-20T07:27:00.001-07:00</published><updated>2009-05-20T07:37:34.307-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='app engine'/><category scheme='http://www.blogger.com/atom/ns#' term='gwt'/><category scheme='http://www.blogger.com/atom/ns#' term='software development'/><title type='text'>Teaching &amp; Development Philosophy</title><content type='html'>Okay, one more post before I start writing some code. &lt;br /&gt;&lt;br /&gt;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).&lt;br /&gt;&lt;br /&gt;We're learning how those two tools (and a host of others) hook together to form a complete web application. &lt;br /&gt;&lt;br /&gt;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, &lt;a href="http://java.sun.com/docs/books/tutorial/uiswing/"&gt;head for a tutorial&lt;/a&gt;.  That'll be important for understanding how to create a user interface in GWT. &lt;br /&gt;&lt;br /&gt;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. &lt;br /&gt;&lt;br /&gt;Another important aspect of my development philosophy is that I favor &lt;span style="font-style: italic;"&gt;having a working application at all times&lt;/span&gt;.  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.&lt;br /&gt;&lt;br /&gt;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. &lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;Okay, enough talking, the next post will start the technical bits.  Promise.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-310816270999880312?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/310816270999880312/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2009/05/teaching-development-philosophy.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/310816270999880312'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/310816270999880312'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2009/05/teaching-development-philosophy.html' title='Teaching &amp; Development Philosophy'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-3758592428063056595</id><published>2009-05-20T06:58:00.000-07:00</published><updated>2009-05-20T07:07:33.605-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mashups'/><category scheme='http://www.blogger.com/atom/ns#' term='web2.0'/><category scheme='http://www.blogger.com/atom/ns#' term='google maps'/><title type='text'>What Project?</title><content type='html'>Okay, I think I'm at the point where I finally have to decide on a project to do.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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. &lt;br /&gt;&lt;br /&gt;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). &lt;br /&gt;&lt;br /&gt;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. &lt;br /&gt;&lt;br /&gt;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!&lt;br /&gt;&lt;br /&gt;(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.)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-3758592428063056595?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/3758592428063056595/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2009/05/what-project.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/3758592428063056595'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/3758592428063056595'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2009/05/what-project.html' title='What Project?'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-8534281772344073267</id><published>2009-05-20T06:47:00.000-07:00</published><updated>2009-05-20T06:58:10.934-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='svn'/><title type='text'>Using SVN Productively</title><content type='html'>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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;Do it wrong, and you'll find yourself with three different versions of the code and faced with the problem of merging the changes.&lt;br /&gt;&lt;br /&gt;So I'll lay out some general rules for using SVN:&lt;br /&gt;&lt;br /&gt;1) Before you start to work on your code in a particular location, right click on the project name in Eclipse and select Team-&gt;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.&lt;br /&gt;&lt;br /&gt;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-&gt;Commit and then enter a description of what you changed. &lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-8534281772344073267?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/8534281772344073267/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2009/05/using-svn-productively.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/8534281772344073267'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/8534281772344073267'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2009/05/using-svn-productively.html' title='Using SVN Productively'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-7010084686824218331</id><published>2009-05-19T13:15:00.000-07:00</published><updated>2009-05-19T13:29:06.732-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='app engine'/><category scheme='http://www.blogger.com/atom/ns#' term='gwt'/><title type='text'>Why GWT &amp; App Engine?</title><content type='html'>I said last post that we didn't have any code written yet, and that's technically true.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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. &lt;br /&gt;&lt;br /&gt;Now if you right click on the project name and choose Run As-&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;Okay, so on to the question, Why GWT?  There are many reasons, but here are the important ones for this course.&lt;br /&gt;&lt;br /&gt;1) My students already know Java, so they don't need to learn Javascript.&lt;br /&gt;&lt;br /&gt;2) GWT takes care of browser compatibility&lt;br /&gt;&lt;br /&gt;3) GWT limits the security concerns of web apps (to at least a manageable level)&lt;br /&gt;&lt;br /&gt;4) It all works in Eclipse, which my students are already used to using&lt;br /&gt;&lt;br /&gt;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:&lt;br /&gt;&lt;br /&gt;1) With Java App Engine and Eclipse, it all integrates beautifully for testing locally&lt;br /&gt;&lt;br /&gt;2) There's no cost for App Engine hosting at the basic level&lt;br /&gt;&lt;br /&gt;3) If a student's project takes off, App Engine can scale with their success&lt;br /&gt;&lt;br /&gt;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.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-7010084686824218331?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/7010084686824218331/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2009/05/why-gwt-app-engine.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/7010084686824218331'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/7010084686824218331'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2009/05/why-gwt-app-engine.html' title='Why GWT &amp; App Engine?'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-6019545492882395253</id><published>2009-05-19T10:20:00.000-07:00</published><updated>2009-06-02T12:44:41.814-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='subversion'/><category scheme='http://www.blogger.com/atom/ns#' term='svn'/><title type='text'>Subversion Server</title><content type='html'>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.&lt;br /&gt;&lt;br /&gt;Instead, I'll use a site that offers &lt;a href="http://www.xp-dev.com/"&gt;free Subversion hosting&lt;/a&gt;.  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.&lt;br /&gt;&lt;br /&gt;Another free service that looked good was &lt;a href="http://unfuddle.com/"&gt;Unfuddle.com&lt;/a&gt;.  They limited their free version to just a couple of users, though, and didn't provide as much storage space.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;The next page that shows gives you the addresses you'll need to configure Subclipse.  Copy the No SSL address and start Eclipse.&lt;br /&gt;&lt;br /&gt;Create a new project in Eclipse (File-&gt;New-&gt;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.&lt;br /&gt;&lt;br /&gt;Open Window-&gt;Open Perspective-&gt;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.&lt;br /&gt;&lt;br /&gt;Enter your XP-Dev.com user id and password when asked.  You should then see your repository listed in the upper left pane.&lt;br /&gt;&lt;br /&gt;Now let's hook that repository up to the project you created.  Get back to the Java perspective (Window-&gt;Open Perspective-&gt;Java) and right click on the name of the project in the upper left pane.  Choose Team-&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;We can do the same thing from the Java perspective, by right clicking on a file and choosing Team-&gt;Add To Version Control.&lt;br /&gt;&lt;br /&gt;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. &lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;We'll need to remember to add new files we create to version control, and to Commit changes to version controlled files.&lt;br /&gt;&lt;br /&gt;More on that as we go along, and other Subversion related topics.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-6019545492882395253?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/6019545492882395253/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2009/05/subversion-server.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/6019545492882395253'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/6019545492882395253'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2009/05/subversion-server.html' title='Subversion Server'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-3863661261765299829</id><published>2009-05-19T07:48:00.000-07:00</published><updated>2009-06-10T19:49:31.068-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='app engine'/><category scheme='http://www.blogger.com/atom/ns#' term='gwt'/><category scheme='http://www.blogger.com/atom/ns#' term='eclipse'/><title type='text'>Installing Eclipse</title><content type='html'>The IDE I'll be using for this project is Eclipse. &lt;br /&gt;&lt;br /&gt;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). &lt;br /&gt;&lt;br /&gt;Head over to the &lt;a href="http://www.eclipse.org/downloads/"&gt;Eclipse downloads page&lt;/a&gt;, 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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Google Plugin&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;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 &lt;a href="http://code.google.com/eclipse/docs/getting_started.html"&gt;Google page about installing this plugin&lt;/a&gt;.  To find out what version of Eclipse you have (if you've forgotten what you downloaded), in Eclipse look at Help-&gt;About Eclipse Platform. &lt;br /&gt;&lt;br /&gt;Once you figure out which of the URLs you need, copy that and then go into Eclipse's Help-&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;We now have the Google Eclipse plugin, plus the GWT and App Engine files we need to develop those sorts of applications. &lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Subclipse Plugin&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;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 &lt;a href="http://www.google.com/search?q=free+subversion+hosting&amp;amp;btnG=Search"&gt;Google Search on free subversion hosting&lt;/a&gt; and you'll find some that are suitable for single person projects). &lt;br /&gt;&lt;br /&gt;To install Subclipse, go to the &lt;a href="http://subclipse.tigris.org/"&gt;Subclipse web site&lt;/a&gt; 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. &lt;br /&gt;&lt;br /&gt;In Eclipse, go back to Help-&gt;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.&lt;br /&gt;&lt;br /&gt;Restart Eclipse again. &lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Find Bugs Plugin&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Install the &lt;a href="http://findbugs.sourceforge.net/manual/eclipse.html"&gt;Find Bugs plugin&lt;/a&gt;.  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.&lt;br /&gt;&lt;br /&gt;That's it for Eclipse.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-3863661261765299829?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/3863661261765299829/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2009/05/installing-eclipse.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/3863661261765299829'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/3863661261765299829'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2009/05/installing-eclipse.html' title='Installing Eclipse'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-7613212721451540855</id><published>2009-05-19T07:28:00.001-07:00</published><updated>2009-05-19T07:41:39.206-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gwt'/><category scheme='http://www.blogger.com/atom/ns#' term='books'/><title type='text'>Possible GWT Books</title><content type='html'>Part of the problem with teaching something that's still changing as quickly as GWT and App Engine is finding a suitable text book.  GWT is at version 1.6 right now, and the books I've seen haven't caught up yet.&lt;br /&gt;&lt;br /&gt;So far, I've received evaluation copies of Pro Web 2.0 Application Development with GWT, and Accelerated GWT.  Kudos to &lt;a href="http://www.apress.com/"&gt;Apress &lt;/a&gt;for getting those out to me very quickly!  I'm still waiting to hear from &lt;a href="http://www.manning.com/"&gt;Manning Publications&lt;/a&gt; on a review copy of their GWT In Practice. &lt;br /&gt;&lt;br /&gt;Which book I end up using will depend in part on how this sample project goes.  So far, I'm very impressed with the topics covered in Pro Web 2.0 Application Development with GWT.  The author, Jeff Dwyer, takes you through the development cycle for a full web 2.0 application.  Along the way he talks about how he solved those issues that always come up when you start to move away from toy sample applications.  In particular, his discussions of real web 2.0 security concerns are excellent.&lt;br /&gt;&lt;br /&gt;But, the tool stack Dwyer chose for his project may not match what I'll end up using.  I'll give some of the tools he chose a try, but since I'm using App Engine for the server and he didn't, not everything will match.  So I'm not sure it'll be a good text for the course, even though I consider it to be an invaluable reference for web 2.0 development issues. &lt;br /&gt;&lt;br /&gt;Accelerated GWT looks very nice, although it's more of a general book, and not focused on developing one specific application.  It covers GWT 1.4, though, and the version I'll be using will be 1.6 (it's hard to tell what it'll be up to by spring 2010, when the course runs). &lt;br /&gt;&lt;br /&gt;Even so, it seems like it would function better as a general text, since it nicely covers server integration but doesn't rely on a particular tool stack. &lt;br /&gt;&lt;br /&gt;We'll see what other books I can come up with to review, but I'm not going to wait on picking the text to start development.  My free time is pretty limited, so it'll take most of the summer to put this together.&lt;br /&gt;&lt;br /&gt;If I only knew what I was going to develop...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-7613212721451540855?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/7613212721451540855/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2009/05/possible-gwt-books.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/7613212721451540855'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/7613212721451540855'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2009/05/possible-gwt-books.html' title='Possible GWT Books'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4925441287181613986.post-6602361589179876810</id><published>2009-05-18T11:36:00.001-07:00</published><updated>2009-06-15T10:20:08.213-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='app engine'/><category scheme='http://www.blogger.com/atom/ns#' term='gwt'/><title type='text'>Hello World</title><content type='html'>This blog exists for one purpose only, to document my sample GWT &amp;amp; Google App Engine project.  This will be used as a guide for students in a Web Applications class to be held Spring 2010.  I'll start by assuming you know what GWT and App Engine are, because otherwise the other posts are going to be either boring or mystifying, or both. &lt;br /&gt;&lt;br /&gt;I haven't decided what the project will be yet; I'm leaning toward something more complicated than a typical to-do list sample, because that'll give my students a better guide (along with giving them the odd laugh or two as they see my mistakes along the way). &lt;br /&gt;&lt;br /&gt;I'll be using GWT for the client side because my students already know Java.  Not all of them will have had Swing programming yet, but I'm confident they'll come up to speed on that quickly enough.  Which wouldn't be the case if I decided to go with Java Script for the client side.&lt;br /&gt;&lt;br /&gt;For the server side I'd really like to use Python, and would if it were just my own project.  But again, my students will already know Java so it's hard to justify switching languages on the server.  Especially since App Engine is available in a Java version that plays well with GWT Serialization. &lt;br /&gt;&lt;br /&gt;So, GWT on the client, and Java App Engine on the server.  The next few posts will be about getting the environment set up.  We'll be using Eclipse with some relevant plugins for both client and server code. &lt;br /&gt;&lt;br /&gt;I'm going to be using SVN for managing my project.  My students are welcome to follow along and do that, too.  That isn't required, but if you've ever deleted a big Java file from your hard drive accidentally, you know the motivation for having every version saved somewhere safe. &lt;br /&gt;&lt;br /&gt;That's it for the first post.  Hopefully by the time I've done all the setup posts, I'll have figured out what this project is!&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Important Note&lt;/i&gt;:  I'll be posting Java code in many of the posts.  Code does not format well on the blog posts, so don't expect to be able to simply copy and paste the code into your files.  Treat the code as a text book example...type it in yourself to get a feel for what it's doing, rather than copying and pasting.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4925441287181613986-6602361589179876810?l=gwtappengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gwtappengine.blogspot.com/feeds/6602361589179876810/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://gwtappengine.blogspot.com/2009/05/hello-world.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/6602361589179876810'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4925441287181613986/posts/default/6602361589179876810'/><link rel='alternate' type='text/html' href='http://gwtappengine.blogspot.com/2009/05/hello-world.html' title='Hello World'/><author><name>Jay</name><uri>http://www.blogger.com/profile/02060167708095988408</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://2.bp.blogspot.com/__e6ZOJzL02s/SjfFoLVI2UI/AAAAAAAAABU/jVA6mZJup4k/S220/jay_head.jpg'/></author><thr:total>0</thr:total></entry></feed>
