Browse Source

Merge branch 'fortune' of gitlab.techempower.com:techempower/frameworkbenchmarks into fortune

Patrick Falls 12 years ago
parent
commit
771b552cf4

+ 20 - 0
config/create-fortunes.sql

@@ -0,0 +1,20 @@
+DROP TABLE IF EXISTS Fortune;
+CREATE TABLE  Fortune (
+  id int(10) unsigned NOT NULL auto_increment,
+  message varchar(2048) CHARACTER SET 'utf8' NOT NULL,
+  PRIMARY KEY  (id)
+)
+ENGINE=INNODB;
+
+INSERT INTO fortune (message) VALUES ('fortune: No such file or directory');
+INSERT INTO fortune (message) VALUES ('A computer scientist is someone who fixes things that aren''t broken.');
+INSERT INTO fortune (message) VALUES ('After enough decimal places, nobody gives a damn.');
+INSERT INTO fortune (message) VALUES ('A bad random number generator: 1, 1, 1, 1, 1, 4.33e+67, 1, 1, 1');
+INSERT INTO fortune (message) VALUES ('A computer program does what you tell it to do, not what you want it to do.');
+INSERT INTO fortune (message) VALUES ('Emacs is a nice operating system, but I prefer UNIX. — Tom Christaensen');
+INSERT INTO fortune (message) VALUES ('Any program that runs right is obsolete.');
+INSERT INTO fortune (message) VALUES ('A list is only as strong as its weakest link. — Donald Knuth');
+INSERT INTO fortune (message) VALUES ('Feature: A bug with seniority.');
+INSERT INTO fortune (message) VALUES ('Computers make very fast, very accurate mistakes.');
+INSERT INTO fortune (message) VALUES ('<script>alert("This should not be displayed in a browser alert box.");</script>');
+INSERT INTO fortune (message) VALUES ('フレームワークのベンチマーク');

+ 1 - 1
gemini/.project

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <projectDescription>
-	<name>GeminiHello</name>
+	<name>FrameworkBenchmarks-Gemini</name>
 	<comment></comment>
 	<projects>
 		<project>TechEmpower</project>

+ 0 - 15
gemini/Docroot/WEB-INF/GeminiHello-Base.conf

@@ -414,21 +414,6 @@ MailServerCount = 1
 
 EmailerThreadsDaemon = yes
 
-# StartupMailAuthor
-#   Specifies the email address to use when sending out the startup
-#   email.  This just needs to be specified so that the mail server
-#   won't reject it due to anti-spam measures.
-
-StartupMailAuthor = [email protected]
-
-# StartupMailRecipients
-#   When this Gemini application starts, Gemini will send an e-mail notice
-#   to the provided comma-separated list of recipients.  This notice can
-#   be useful for tracking when the site has been restarted.  If set to
-#   empty string, this functionality is disabled.
-
-StartupMailRecipients =
-
 # Mail Server blocks (where 'X' is a sequential ID of the mail servers
 # used by the application).
 #

+ 0 - 7
gemini/Docroot/WEB-INF/GeminiHello-Dev.conf

@@ -10,9 +10,6 @@
 # Extend the baseline configuration.
 Extends = GeminiHello-Base.conf
 
-# TODO: Edit these settings according to the particulars of the
-# Development environments.
-
 DeploymentDescription = Development/${Servlet.MachineName}
 
 # Database connectivity for Development.
@@ -23,8 +20,4 @@ db.LoginPass = root
 # Disable outbound e-mail from the Development environment.
 OutboundMailEnabled = no
 
-# You may want to disable the last login update on Development.
-#BasicSecurity.UpdateLastLogin = no
-
-
 

+ 5 - 3
gemini/Docroot/WEB-INF/GeminiHello-Inverness.conf

@@ -9,7 +9,9 @@
 
 # Extend the development configuration, which in turn extends the
 # baseline configuration.
-Extends = GeminiHello-Dev.conf
+Extends = GeminiHello-Prod.conf
+
+DeploymentDescription = Production/${Servlet.MachineName}
 
 # Now set any attributes that are specific to this machine.
 
@@ -18,5 +20,5 @@ db.ConnectString = 172.16.98.98:3306/hello_world?jdbcCompliantTruncation=false&e
 Log.Console.On = yes
 Log.Console.LogDebugThreshold = 50
 
-#db.Driver.Pooling = 30
-#db.Driver.MaxPooling = 30
+db.Driver.Pooling = 30
+db.Driver.MaxPooling = 30

+ 2 - 16
gemini/Docroot/WEB-INF/GeminiHello-Prod.conf

@@ -10,9 +10,6 @@
 # Extend the baseline configuration.
 Extends = GeminiHello-Base.conf
 
-# TODO: Edit these settings according to the particulars of the
-# Production environment.
-
 DeploymentDescription = Production/${Servlet.MachineName}
 
 # Database connectivity for Production.
@@ -20,19 +17,8 @@ db.ConnectString = localhost:3306/gemini?jdbcCompliantTruncation=false&cachePrep
 db.LoginName = hello
 db.LoginPass = hello
 
-# Mail server definition for the production environment.  TODO: Most
-# likely you shouldn't be using the TechEmpower mail server in 
-# Production, so change this.
-MailServerCount = 1
-MailServer1.ServerAddress = mail.techempower.com
-MailServer1.SmtpPort = 25
-MailServer1.PopPort = 110
-MailServer1.Username = mhixson
-MailServer1.Password = password
-MailServer1.ServerRole = Outbound
-
-# In production, we'll want to have the email exception handler enabled.
-EmailExceptionHandler.Enabled = true
+# For this application, we are not sending any outbound e-mail.
+OutboundMailEnabled = no
 
 # In production, refer to all the static assets via URLs with version strings to
 # allow us to perform aggressive caching.

+ 0 - 15
gemini/Docroot/WEB-INF/GeminiHello.conf

@@ -413,21 +413,6 @@ MailServerCount = 1
 
 EmailerThreadsDaemon = yes
 
-# StartupMailAuthor
-#   Specifies the email address to use when sending out the startup
-#   email.  This just needs to be specified so that the mail server
-#   won't reject it due to anti-spam measures.
-
-StartupMailAuthor = [email protected]
-
-# StartupMailRecipients
-#   When this Gemini application starts, Gemini will send an e-mail notice
-#   to the provided comma-separated list of recipients.  This notice can
-#   be useful for tracking when the site has been restarted.  If set to
-#   empty string, this functionality is disabled.
-
-StartupMailRecipients =
-
 # Mail Server blocks (where 'X' is a sequential ID of the mail servers
 # used by the application).
 #

BIN
gemini/Docroot/WEB-INF/lib/techempower.jar


+ 16 - 0
gemini/Docroot/WEB-INF/mustache/fortunes.mustache

@@ -0,0 +1,16 @@
+{{<layout}}
+{{$body}}
+<table>
+<tr>
+<th>id</th>
+<th>message</th>
+</tr>
+{{#.}}
+<tr>
+<td>{{id}}</td>
+<td>{{message}}</td>
+</tr>
+{{/.}}
+</table>
+{{/body}}
+{{/layout}}

+ 9 - 0
gemini/Docroot/WEB-INF/mustache/layout.mustache

@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Fortunes</title>
+</head>
+<body>
+{{$body}}{{/body}}
+</body>
+</html>

+ 0 - 10
gemini/Docroot/WEB-INF/web.xml

@@ -50,14 +50,4 @@
   <!-- Use UTF-8 for everything. -->
   <character-encoding>UTF-8</character-encoding>
 
-  <!-- Enable GZIP compression. -->
-  <!--
-  <filter filter-name="gzip" filter-class="com.caucho.filters.GzipFilter">
-    <init>
-      <use-vary>true</use-vary>
-    </init>
-  </filter>
-  <filter-mapping url-pattern='*' filter-name="gzip" />
-  -->
-
 </web-app>

+ 1 - 1
gemini/Source/hello/GhForm.java

@@ -59,7 +59,7 @@ public class GhForm
   @Override
   protected void onValidlySubmitted()
   {
-    // Does nothing.    
+    // Does nothing.
   }
 
 }   // End GhForm.

+ 1 - 1
gemini/Source/hello/GhStore.java

@@ -5,7 +5,6 @@ import hello.home.entity.*;
 import com.techempower.*;
 import com.techempower.cache.*;
 import com.techempower.data.*;
-import com.techempower.gemini.cluster.client.handler.*;
 import com.techempower.log.*;
 
 /**
@@ -48,6 +47,7 @@ public class GhStore
     
     // Use EntityGroup rather than CacheGroup to ensure World entities are not cached.
     register(EntityGroup.of(World.class));
+    register(EntityGroup.of(Fortune.class));
 
     // Register relationships.
     // We have no relationships in this application.

+ 65 - 0
gemini/Source/hello/home/entity/Fortune.java

@@ -0,0 +1,65 @@
+package hello.home.entity;
+
+import com.techempower.js.*;
+
+import hello.*;
+
+/**
+ * A fortune entity.
+ */
+public class   Fortune
+    extends    GhDataEntity
+    implements Comparable<Fortune>
+{
+
+  private String message;
+  
+  /**
+   * Default Constructor.
+   */
+  public Fortune()
+  {
+    // Does nothing.
+  }
+  
+  /**
+   * Set the message.  
+   */
+  public Fortune setMessage(String message)
+  {
+    this.message = message;
+    return this;
+  }
+  
+  /**
+   * Get the message.
+   */
+  public String getMessage()
+  {
+    return this.message;
+  }
+
+  /**
+   * A visitor factory used to map this class to JSON.
+   */
+  public static final VisitorFactory<Fortune> VISITOR_FACTORY = new VisitorFactory<Fortune>()
+  {
+    @Override
+    public Visitor visitor(Fortune fortune)
+    {
+      return Visitors.map(
+          "id", fortune.getId(),
+          "message", fortune.getMessage());
+    }
+  };
+
+  /**
+   * For our purposes, Fortunes sort by their message text. 
+   */
+  @Override
+  public int compareTo(Fortune other)
+  {
+    return getMessage().compareTo(other.getMessage());
+  }
+  
+}

+ 14 - 0
gemini/Source/hello/home/handler/HelloHandler.java

@@ -59,5 +59,19 @@ public class HelloHandler
     
     return json(worlds);
   }
+  
+  /**
+   * Fetch the full list of Fortunes from the database, sort them by the
+   * fortune message text, and then render the results to simple HTML using a 
+   * server-side template.
+   */
+  @PathSegment
+  public boolean fortunes()
+  {
+    final List<Fortune> fortunes = store.list(Fortune.class);
+    fortunes.add(new Fortune().setMessage("Additional fortune added at request time."));
+    Collections.sort(fortunes);
+    return mustache("fortunes", fortunes);
+  }
 
 }

+ 9 - 6
servlet/.classpath

@@ -1,10 +1,13 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
-	<classpathentry kind="src" path="src"/>
-	<classpathentry kind="lib" path="docroot/WEB-INF/lib/jackson-annotations-2.1.1.jar"/>
-	<classpathentry kind="lib" path="docroot/WEB-INF/lib/jackson-core-2.1.1.jar"/>
-	<classpathentry kind="lib" path="docroot/WEB-INF/lib/jackson-databind-2.1.1.jar"/>
+	<classpathentry including="**/*.java" kind="src" path="src/main/java"/>
+	<classpathentry kind="var" path="M2_REPO/javax/inject/javax.inject/1/javax.inject-1.jar"/>
+	<classpathentry kind="var" path="M2_REPO/javax/servlet/servlet-api/2.5/servlet-api-2.5.jar"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry kind="var" path="Resin4"/>
-	<classpathentry kind="output" path="docroot/WEB-INF/classes"/>
+	<classpathentry kind="var" path="M2_REPO/mysql/mysql-connector-java/5.1.23/mysql-connector-java-5.1.23.jar"/>
+	<classpathentry kind="var" path="M2_REPO/com/fasterxml/jackson/core/jackson-databind/2.1.2/jackson-databind-2.1.2.jar"/>
+	<classpathentry kind="var" path="M2_REPO/com/fasterxml/jackson/core/jackson-annotations/2.1.1/jackson-annotations-2.1.1.jar"/>
+	<classpathentry kind="var" path="M2_REPO/com/fasterxml/jackson/core/jackson-core/2.1.1/jackson-core-2.1.1.jar"/>
+	<classpathentry kind="var" path="M2_REPO/org/apache/commons/commons-lang3/3.1/commons-lang3-3.1.jar"/>
+	<classpathentry kind="output" path="src/main/webapp/WEB-INF/classes"/>
 </classpath>

+ 3 - 0
servlet/.gitignore

@@ -0,0 +1,3 @@
+lib/
+work/
+*.bat

+ 12 - 15
servlet/.project

@@ -1,17 +1,14 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <projectDescription>
-	<name>ServletHello</name>
-	<comment></comment>
-	<projects>
-	</projects>
-	<buildSpec>
-		<buildCommand>
-			<name>org.eclipse.jdt.core.javabuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-	</buildSpec>
-	<natures>
-		<nature>org.eclipse.jdt.core.javanature</nature>
-	</natures>
-</projectDescription>
+  <name>world</name>
+  <comment>NO_M2ECLIPSE_SUPPORT: Project files created with the maven-eclipse-plugin are not supported in M2Eclipse.</comment>
+  <projects/>
+  <buildSpec>
+    <buildCommand>
+      <name>org.eclipse.jdt.core.javabuilder</name>
+    </buildCommand>
+  </buildSpec>
+  <natures>
+    <nature>org.eclipse.jdt.core.javanature</nature>
+  </natures>
+</projectDescription>

+ 5 - 0
servlet/.settings/org.eclipse.jdt.core.prefs

@@ -0,0 +1,5 @@
+#Thu Apr 11 13:42:30 PDT 2013
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.source=1.7
+org.eclipse.jdt.core.compiler.compliance=1.7

+ 9 - 2
servlet/pom.xml

@@ -22,7 +22,7 @@
             <artifactId>jackson-databind</artifactId>
             <version>2.1.2</version>
         </dependency>
- 
+
         <!-- @Inject -->
         <dependency>
             <groupId>javax.inject</groupId>
@@ -37,7 +37,14 @@
             <version>2.5</version>
             <scope>provided</scope>
         </dependency>
- 
+
+        <!-- Apache Commons Lang -->
+        <dependency>
+          	<groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <version>3.1</version>
+        </dependency>
+
     </dependencies>
 
     <build>

+ 30 - 0
servlet/src/main/java/hello/Common.java

@@ -0,0 +1,30 @@
+package hello;
+
+import org.apache.commons.lang3.*;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Some common functionality and constants used by the Servlet tests.
+ */
+public class Common
+{
+
+  // Constants for setting the content type.
+  protected static final String HEADER_CONTENT_TYPE    = "Content-Type";
+  protected static final String CONTENT_TYPE_JSON      = "application/json";
+  protected static final String CONTENT_TYPE_HTML      = "text/html";
+
+  // Jackson encoder, reused for each response.
+  protected static final ObjectMapper MAPPER = new ObjectMapper();
+
+  /**
+   * Use the OWASP ESAPI HTML encoder to process an untrusted String into a
+   * form suitable for rendering in output HTML.
+   */
+  public static String render(String input)
+  {
+    return StringEscapeUtils.escapeHtml4(input);        
+  }
+  
+}

+ 10 - 19
servlet/src/main/java/hello/DbPoolServlet.java

@@ -10,37 +10,28 @@ import javax.servlet.*;
 import javax.servlet.http.*;
 import javax.sql.*;
 
-import com.fasterxml.jackson.databind.*;
-
 /**
- * Database connectivity (with a Servlet-container managed pool) test
+ * Database connectivity (with a Servlet-container managed pool) test.
  */
 @SuppressWarnings("serial")
 public class DbPoolServlet extends HttpServlet
 {
-  // Constants for setting the content type.
-  private static final String HEADER_CONTENT_TYPE    = "Content-Type";
-  private static final String CONTENT_TYPE_JSON      = "application/json";
-  
+  // Database details.
+  private static final String DB_QUERY = "SELECT * FROM World WHERE id = ?";
+  private static final int    DB_ROWS  = 10000;
+
+  // Database connection pool.
   @Resource(name="jdbc/hello_world")
   private DataSource mysqlDataSource;
-
-  // Jackson encoder, reused for each response.
-  private final ObjectMapper mapper = new ObjectMapper();
-  
-  // Database details.
-  private static final String DB_QUERY               = "SELECT * FROM World WHERE id = ?";
-  private static final int    DB_ROWS                = 10000;
-  
+    
   @Override
   protected void doGet(HttpServletRequest req, HttpServletResponse res)
       throws ServletException, IOException
   {
     // Set content type to JSON
-    res.setHeader(HEADER_CONTENT_TYPE, CONTENT_TYPE_JSON);
+    res.setHeader(Common.HEADER_CONTENT_TYPE, Common.CONTENT_TYPE_JSON);
 
-    // Select the MySQL source by default, but allow the option of selecting
-    // Postgres by providing a parameter named "postgres".
+    // Reference the data source.
     final DataSource source = mysqlDataSource;
     
     // Get the count of queries to run.
@@ -97,7 +88,7 @@ public class DbPoolServlet extends HttpServlet
     // Write JSON encoded message to the response.
     try
     {
-      mapper.writeValue(res.getOutputStream(), worlds);
+      Common.MAPPER.writeValue(res.getOutputStream(), worlds);
     }
     catch (IOException ioe) 
     {

+ 38 - 0
servlet/src/main/java/hello/Fortune.java

@@ -0,0 +1,38 @@
+package hello;
+
+/**
+ * Simple Fortune cookie entity.
+ */
+public class Fortune
+  implements Comparable<Fortune>
+{
+
+  private final int    id;
+  private final String message;
+  
+  public Fortune(int id, String message)
+  {
+    this.id = id;
+    this.message = message;
+  }
+  
+  public int getId()
+  {
+    return this.id;
+  }
+  
+  public String getMessage()
+  {
+    return this.message;
+  }      
+
+  /**
+   * For our purposes, Fortunes sort by their message text. 
+   */
+  @Override
+  public int compareTo(Fortune other)
+  {
+    return getMessage().compareTo(other.getMessage());
+  }
+  
+}

+ 69 - 0
servlet/src/main/java/hello/FortunesServlet.java

@@ -0,0 +1,69 @@
+package hello;
+
+import java.io.*;
+import java.sql.*;
+import java.util.*;
+
+import javax.annotation.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+import javax.sql.*;
+
+/**
+ * Fortunes test, returns a list of fortune cookie messages fetched from
+ * a database table and then composed by server-side templates.
+ */
+@SuppressWarnings("serial")
+public class FortunesServlet extends HttpServlet
+{
+  
+  // Database details.
+  private static final String DB_QUERY = "SELECT * FROM Fortune";
+
+  // Database connection pool.
+  @Resource(name="jdbc/hello_world")
+  private DataSource mysqlDataSource;
+  
+  @Override
+  protected void doGet(HttpServletRequest req, HttpServletResponse res)
+      throws ServletException, IOException
+  {
+    // Set content type to JSON
+    res.setHeader(Common.HEADER_CONTENT_TYPE, Common.CONTENT_TYPE_HTML);
+
+    // Reference the data source.
+    final DataSource source = mysqlDataSource;
+
+    List<Fortune> fortunes = new ArrayList<>();
+    
+    try (Connection conn = source.getConnection())
+    {
+      try (PreparedStatement statement = conn.prepareStatement(DB_QUERY, 
+          ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY))
+      {
+        try (ResultSet results = statement.executeQuery())
+        {
+          while (results.next())
+          {
+            fortunes.add(new Fortune(results.getInt("id"), results.getString("message")));
+          }
+        }
+      }
+    }
+    catch (SQLException sqlex)
+    {
+      System.err.println("SQL Exception: " + sqlex);
+    }
+    
+    fortunes.add(new Fortune(0, "Additional fortune added at request time."));
+    Collections.sort(fortunes);
+    
+    // Set the list of Fortunes as an attribute of the request, making it
+    // available to the JSP.
+    req.setAttribute("fortunes", fortunes);
+    
+    // Dispatch to the JSP.
+    RequestDispatcher disp = req.getRequestDispatcher("/WEB-INF/jsp/fortunes.jsp");
+    disp.forward(req, res);
+  }
+}

+ 3 - 10
servlet/src/main/java/hello/JsonServlet.java

@@ -5,20 +5,12 @@ import java.io.*;
 import javax.servlet.*;
 import javax.servlet.http.*;
 
-import com.fasterxml.jackson.databind.*;
-
 /**
  * JSON Encoding Test
  */
 @SuppressWarnings("serial")
 public class JsonServlet extends HttpServlet
 {
-  // Constants for setting the content type.
-  private static final String HEADER_CONTENT_TYPE    = "Content-Type";
-  private static final String CONTENT_TYPE_JSON      = "application/json";
-
-  // Jackson encoder, reused for each response.
-  private final ObjectMapper mapper = new ObjectMapper();
 
   // Response message class.
   public static class HelloMessage {
@@ -30,16 +22,17 @@ public class JsonServlet extends HttpServlet
       throws ServletException, IOException
   {
     // Set content type to JSON
-    res.setHeader(HEADER_CONTENT_TYPE, CONTENT_TYPE_JSON);
+    res.setHeader(Common.HEADER_CONTENT_TYPE, Common.CONTENT_TYPE_JSON);
 
     // Write JSON encoded message to the response.
     try
     {
-      mapper.writeValue(res.getOutputStream(), new HelloMessage());
+      Common.MAPPER.writeValue(res.getOutputStream(), new HelloMessage());
     }
     catch (IOException ioe) 
     {
       // do nothing
     }
   }
+  
 }

+ 24 - 0
servlet/src/main/webapp/WEB-INF/jsp/fortunes.jsp

@@ -0,0 +1,24 @@
+<%@ page import="hello.*,
+                 java.util.*" %><%@ page session="false" %><%
+
+List<Fortune> fortunes = (List)request.getAttribute("fortunes");
+
+%>
+<!DOCTYPE html>
+<html>
+<head>
+<title>Fortunes</title>
+</head>
+<body>
+<table>
+<tr>
+<th>id</th>
+<th>message</th>
+</tr>
+<% for (Fortune fortune : fortunes) { %><tr>
+<td><%= fortune.getId() %></td>
+<td><%= Common.render(fortune.getMessage()) %></td>
+</tr>
+<% } %>
+</table></body>
+</html>

+ 1 - 1
servlet/src/main/webapp/WEB-INF/resin-web.xml

@@ -3,7 +3,7 @@
 <database jndi-name='jdbc/hello_world'>
   <driver>
     <type>com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource</type>
-    <url>jdbc:mysql://localhost:3306/hello_world?jdbcCompliantTruncation=false&amp;elideSetAutoCommits=true&amp;useLocalSessionState=true&amp;cachePrepStmts=true&amp;cacheCallableStmts=true&amp;alwaysSendSetIsolation=false&amp;prepStmtCacheSize=4096&amp;cacheServerConfiguration=true&amp;prepStmtCacheSqlLimit=2048&amp;zeroDateTimeBehavior=convertToNull&amp;traceProtocol=false&amp;useUnbufferedInput=false&amp;useReadAheadInput=false&amp;maintainTimeStats=false&amp;useServerPrepStmts&amp;cacheRSMetadata=true</url>
+    <url>jdbc:mysql://172.16.98.98:3306/hello_world?jdbcCompliantTruncation=false&amp;elideSetAutoCommits=true&amp;useLocalSessionState=true&amp;cachePrepStmts=true&amp;cacheCallableStmts=true&amp;alwaysSendSetIsolation=false&amp;prepStmtCacheSize=4096&amp;cacheServerConfiguration=true&amp;prepStmtCacheSqlLimit=2048&amp;zeroDateTimeBehavior=convertToNull&amp;traceProtocol=false&amp;useUnbufferedInput=false&amp;useReadAheadInput=false&amp;maintainTimeStats=false&amp;useServerPrepStmts&amp;cacheRSMetadata=true</url>
     <user>benchmarkdbuser</user>
     <password>benchmarkdbpass</password>
     <useUnicode/>

+ 6 - 0
servlet/src/main/webapp/WEB-INF/web.xml

@@ -11,4 +11,10 @@
     <load-on-startup/>
   </servlet>
   <servlet-mapping url-regexp='^/db$' servlet-name='db'/>
+  <servlet>
+    <servlet-name>fortunes</servlet-name>
+    <servlet-class>hello.FortunesServlet</servlet-class>
+    <load-on-startup/>
+  </servlet>
+  <servlet-mapping url-regexp='^/fortunes$' servlet-name='fortunes'/>
 </web-app>