Google Code offered in: English - Español - 日本語 - 한국어 - Português - Pусский - 中文(简体) - 中文(繁體)
While we could output the HTML for our user interface directly from the Java servlet code, this would be difficult to maintain as the HTML gets complicated. It's better to use a template system, with the user interface designed and implemented in separate files with placeholders and logic to insert data provided by the application. There are many template systems available for Java, any of which would work with App Engine.
For this tutorial, we'll use JavaServer Pages (JSPs) to implement the user interface for the guest book. JSPs are part of the servlet standard. App Engine compiles JSP files in the application's WAR automatically, and maps them to URL paths.
Our guest book app writes strings to an output stream, but this could also be written as a JSP. Let's begin by porting the latest version of the example to a JSP.
In the directory war/
, create a file named guestbook.jsp
with the following contents:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page import="com.google.appengine.api.users.User" %> <%@ page import="com.google.appengine.api.users.UserService" %> <%@ page import="com.google.appengine.api.users.UserServiceFactory" %> <html> <body> <% UserService userService = UserServiceFactory.getUserService(); User user = userService.getCurrentUser(); if (user != null) { %> <p>Hello, <%= user.getNickname() %>! (You can <a href="<%= userService.createLogoutURL(request.getRequestURI()) %>">sign out</a>.)</p> <% } else { %> <p>Hello! <a href="<%= userService.createLoginURL(request.getRequestURI()) %>">Sign in</a> to include your name with greetings you post.</p> <% } %> </body> </html>
By default, any file in war/
or a subdirectory (other than WEB-INF/
) whose name ends in .jsp
is automatically mapped to a URL path. The URL path is the path to the .jsp
file, including the filename. This JSP will be mapped automatically to the URL /guestbook.jsp
.
For the guest book app, we want this to be the application's home page, displayed when someone accesses the URL /
. An easy way to do this is to declare in web.xml
that guestbook.jsp
is the "welcome" servlet for that path.
Edit war/WEB-INF/web.xml
and replace the current <welcome-file>
element in the <welcome-file-list>
. Be sure to remove index.html
from the list, as static files take precedence over JSP and servlets.
<welcome-file-list> <welcome-file>guestbook.jsp</welcome-file> </welcome-file-list>
Tip: If you are using Eclipse, the editor may open this file in "Design" mode. To edit this file as XML, select the "Source" tab at the bottom of the frame.
Stop then start the development server. Visit the following URL:
The app displays the contents of guestbook.jsp
, including the user nickname if the user is signed in.
When you load a JSP for the first time, the development server converts the JSP into Java source code, then compiles the Java source into Java bytecode. The Java source and the compiled class are saved to a temporary directory. The development server regenerates and compiles JSPs automatically if the original JSP files change.
When you upload your application to App Engine, the SDK compiles all JSPs to bytecode, and only uploads the bytecode. When your app is running on App Engine, it uses the compiled JSP classes.
Our guest book application will need a web form so the user can post a new greeting, and a way to process that form. The HTML of the form will go into the JSP. The destination of the form will be a new URL, /sign
, to be handled by a new servlet class, SignGuestbookServlet
. SignGuestbookServlet
will process the form, then redirect the user's browser back to /guestbook.jsp
. For now, the new servlet will just write the posted message to the log.
Edit guestbook.jsp
, and put the following lines just above the closing </body>
tag:
... <form action="/sign" method="post"> <div><textarea name="content" rows="3" cols="60"></textarea></div> <div><input type="submit" value="Post Greeting" /></div> </form> </body> </html>
Create a new class named SignGuestbookServlet
in the package guestbook
. (Non-Eclipse users, create the file SignGuestbookServlet.java
in the directory src/guestbook/
.) Give the source file the following contents:
package guestbook; import java.io.IOException; import java.util.logging.Logger; import javax.servlet.http.*; import com.google.appengine.api.users.User; import com.google.appengine.api.users.UserService; import com.google.appengine.api.users.UserServiceFactory; public class SignGuestbookServlet extends HttpServlet { private static final Logger log = Logger.getLogger(SignGuestbookServlet.class.getName()); public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { UserService userService = UserServiceFactory.getUserService(); User user = userService.getCurrentUser(); String content = req.getParameter("content"); if (content == null) { content = "(No greeting)"; } if (user != null) { log.info("Greeting posted by user " + user.getNickname() + ": " + content); } else { log.info("Greeting posted anonymously: " + content); } resp.sendRedirect("/guestbook.jsp"); } }
Edit war/WEB-INF/web.xml
and add the following lines to declare the SignGuestbookServlet
servlet and map it to the the /sign
URL:
<web-app xmlns="http://java.sun.com/xml/ns/javaee" version="2.5"> ... <servlet> <servlet-name>sign</servlet-name> <servlet-class>guestbook.SignGuestbookServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>sign</servlet-name> <url-pattern>/sign</url-pattern> </servlet-mapping> ... </web-app>
This new servlet uses the java.util.logging.Logger
class to write messages to the log. You can control the behavior of this class using a logging.properties
file, and a system property set in the app's appengine-web.xml
file. If you are using Eclipse, your app was created with a default version of this file in your app's src/
and the appropriate system property.
If you are not using Eclipse, you must set up the Logger configuration file manually. Copy the example file from the SDK appengine-java-sdk/config/user/logging.properties
to your app's war/WEB-INF/
directory. Then edit the app's war/WEB-INF/appengine-web.xml
file as indicated:
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0"> ... <system-properties> <property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/> </system-properties> </appengine-web-app>
The servlet logs messages using the INFO
log level (using log.info()
). The default log level is WARNING
, which suppresses INFO
messages from the output. To change the log level for all classes in the guestbook
package, edit the logging.properties
file and add an entry for guestbook.level
, as follows:
.level = WARNING guestbook.level = INFO ...
Tip: When your app logs messages using the java.util.logging.Logger API while running on App Engine, App Engine records the messages and makes them available for browsing in the Admin Console, and available for downloading using the AppCfg tool. The Admin Console lets you browse messages by log level.
Rebuild and restart, then test http://localhost:8888/. The form displays. Enter some text in the form, and submit. The browser sends the form to the app, then redirects back to the empty form. The greeting data you entered is logged to the console by the server.
We have a user interface for prompting the user to enter a greeting. Now we need a way to remember the greetings users post, and show them to other visitors. For that, we'll use the App Engine datastore.
Continue to Using the Datastore.