public int serve(HTTPServer.Request req, HTTPServer.Response resp) throws IOException {
        synchronized (mApp) { // Only allow one thread to touch app at a time.
            mApp.setRequest(new WikiRequest(req, mContainerPrefix));
            try {
                WikiContext context = mApp.getContext();
                if (req.getMethod().equals("POST")) {
                    // Require form password for all posts, like toadlet framework.
                    context.checkFormPassword();
                }
                ChildContainerResult appResult = mApp.handle(context);
                // NOTE: We don't have to worry about the meta refresh here.
                //       That is done for us in by HtmlResultFactory.
                resp.sendHeaders(200,
                                 appResult.getData().length,
                                 0, // Send current date for Last-Modified header.
                                 null, // Don't send Etag header.
                                 appResult.getMimeType(),
                                 null /* No range. i.e. send everything. */);
                OutputStream body = resp.getBody();
                try {
                    body.write(appResult.getData());
                } finally {
                    body.close();
                }
                return 0;
            } catch(AccessDeniedException accessDenied) {
                resp.sendError(403, accessDenied.getMessage());
                return 0;
            } catch(NotFoundException notFound) {
                resp.sendError(404, notFound.getMessage());
                return 0;
            } catch(RedirectException redirected) {
                resp.redirect(redirected.getLocation(), false);
                return 0;
            } catch(DownloadException forceDownload) {
                try {
                    resp.getHeaders().add("Content-disposition",
                                          String.format("attachment; filename=%s", forceDownload.mFilename));
                    resp.sendHeaders(200, forceDownload.mData.length, -1,
                                     null, forceDownload.mMimeType, null);
                    OutputStream body = resp.getBody();
                    if (body == null) {
                        return 0; // hmmm... getBody() can return null.
                    }
                    try {
                        body.write(forceDownload.mData);
                    } finally {
                        body.close();
                    }
                    return 0;
                } catch (IOException ioe) {
                    // Totally hosed. We already sent the headers so we can't send a response.
                    ioe.printStackTrace();
                    return 0;
                }
            } catch(ChildContainerException serverError) {
                // This also handles ServerErrorException.
                resp.sendError(500, serverError.getMessage());
                return 0;
            }
        }
    }