Browse Source

Introduced RestExpress Java REST framework

Todd Fredrich 12 years ago
parent
commit
c5208bc2da

+ 78 - 0
restexpress/.classpath

@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+  <classpathentry kind="src" path="src/main/java" including="**/*.java"/>
+  <classpathentry kind="output" path="target/classes"/>
+  <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+  <classpathentry kind="var" path="M2_REPO/com/strategicgains/RestExpress/0.9.2/RestExpress-0.9.2.jar" sourcepath="M2_REPO/com/strategicgains/RestExpress/0.9.2/RestExpress-0.9.2-sources.jar">
+    <attributes>
+      <attribute value="jar:file:/Users/fredta2/.m2/repository/com/strategicgains/RestExpress/0.9.2/RestExpress-0.9.2-javadoc.jar!/" name="javadoc_location"/>
+    </attributes>
+  </classpathentry>
+  <classpathentry kind="var" path="M2_REPO/com/strategicgains/RestExpress-Common/1.0.1/RestExpress-Common-1.0.1.jar" sourcepath="M2_REPO/com/strategicgains/RestExpress-Common/1.0.1/RestExpress-Common-1.0.1-sources.jar">
+    <attributes>
+      <attribute value="jar:file:/Users/fredta2/.m2/repository/com/strategicgains/RestExpress-Common/1.0.1/RestExpress-Common-1.0.1-javadoc.jar!/" name="javadoc_location"/>
+    </attributes>
+  </classpathentry>
+  <classpathentry kind="var" path="M2_REPO/com/strategicgains/DateAdapterJ/1.1.2/DateAdapterJ-1.1.2.jar" sourcepath="M2_REPO/com/strategicgains/DateAdapterJ/1.1.2/DateAdapterJ-1.1.2-sources.jar">
+    <attributes>
+      <attribute value="jar:file:/Users/fredta2/.m2/repository/com/strategicgains/DateAdapterJ/1.1.2/DateAdapterJ-1.1.2-javadoc.jar!/" name="javadoc_location"/>
+    </attributes>
+  </classpathentry>
+  <classpathentry kind="var" path="M2_REPO/com/fasterxml/jackson/core/jackson-databind/2.1.4/jackson-databind-2.1.4.jar" sourcepath="M2_REPO/com/fasterxml/jackson/core/jackson-databind/2.1.4/jackson-databind-2.1.4-sources.jar">
+    <attributes>
+      <attribute value="jar:file:/Users/fredta2/.m2/repository/com/fasterxml/jackson/core/jackson-databind/2.1.4/jackson-databind-2.1.4-javadoc.jar!/" name="javadoc_location"/>
+    </attributes>
+  </classpathentry>
+  <classpathentry kind="var" path="M2_REPO/com/fasterxml/jackson/core/jackson-annotations/2.1.4/jackson-annotations-2.1.4.jar" sourcepath="M2_REPO/com/fasterxml/jackson/core/jackson-annotations/2.1.4/jackson-annotations-2.1.4-sources.jar">
+    <attributes>
+      <attribute value="jar:file:/Users/fredta2/.m2/repository/com/fasterxml/jackson/core/jackson-annotations/2.1.4/jackson-annotations-2.1.4-javadoc.jar!/" name="javadoc_location"/>
+    </attributes>
+  </classpathentry>
+  <classpathentry kind="var" path="M2_REPO/com/fasterxml/jackson/core/jackson-core/2.1.4/jackson-core-2.1.4.jar" sourcepath="M2_REPO/com/fasterxml/jackson/core/jackson-core/2.1.4/jackson-core-2.1.4-sources.jar">
+    <attributes>
+      <attribute value="jar:file:/Users/fredta2/.m2/repository/com/fasterxml/jackson/core/jackson-core/2.1.4/jackson-core-2.1.4-javadoc.jar!/" name="javadoc_location"/>
+    </attributes>
+  </classpathentry>
+  <classpathentry kind="var" path="M2_REPO/com/thoughtworks/xstream/xstream/1.4.4/xstream-1.4.4.jar" sourcepath="M2_REPO/com/thoughtworks/xstream/xstream/1.4.4/xstream-1.4.4-sources.jar">
+    <attributes>
+      <attribute value="jar:file:/Users/fredta2/.m2/repository/com/thoughtworks/xstream/xstream/1.4.4/xstream-1.4.4-javadoc.jar!/" name="javadoc_location"/>
+    </attributes>
+  </classpathentry>
+  <classpathentry kind="var" path="M2_REPO/xmlpull/xmlpull/1.1.3.1/xmlpull-1.1.3.1.jar"/>
+  <classpathentry kind="var" path="M2_REPO/xpp3/xpp3_min/1.1.4c/xpp3_min-1.1.4c.jar" sourcepath="M2_REPO/xpp3/xpp3_min/1.1.4c/xpp3_min-1.1.4c-sources.jar">
+    <attributes>
+      <attribute value="jar:file:/Users/fredta2/.m2/repository/xpp3/xpp3_min/1.1.4c/xpp3_min-1.1.4c-javadoc.jar!/" name="javadoc_location"/>
+    </attributes>
+  </classpathentry>
+  <classpathentry kind="var" path="M2_REPO/io/netty/netty/3.6.2.Final/netty-3.6.2.Final.jar" sourcepath="M2_REPO/io/netty/netty/3.6.2.Final/netty-3.6.2.Final-sources.jar">
+    <attributes>
+      <attribute value="jar:file:/Users/fredta2/.m2/repository/io/netty/netty/3.6.2.Final/netty-3.6.2.Final-javadoc.jar!/" name="javadoc_location"/>
+    </attributes>
+  </classpathentry>
+  <classpathentry kind="var" path="M2_REPO/com/strategicgains/repoexpress/repoexpress-mongodb/0.3.2/repoexpress-mongodb-0.3.2.jar" sourcepath="M2_REPO/com/strategicgains/repoexpress/repoexpress-mongodb/0.3.2/repoexpress-mongodb-0.3.2-sources.jar">
+    <attributes>
+      <attribute value="jar:file:/Users/fredta2/.m2/repository/com/strategicgains/repoexpress/repoexpress-mongodb/0.3.2/repoexpress-mongodb-0.3.2-javadoc.jar!/" name="javadoc_location"/>
+    </attributes>
+  </classpathentry>
+  <classpathentry kind="var" path="M2_REPO/com/strategicgains/repoexpress/repoexpress-common/0.3.2/repoexpress-common-0.3.2.jar" sourcepath="M2_REPO/com/strategicgains/repoexpress/repoexpress-common/0.3.2/repoexpress-common-0.3.2-sources.jar">
+    <attributes>
+      <attribute value="jar:file:/Users/fredta2/.m2/repository/com/strategicgains/repoexpress/repoexpress-common/0.3.2/repoexpress-common-0.3.2-javadoc.jar!/" name="javadoc_location"/>
+    </attributes>
+  </classpathentry>
+  <classpathentry kind="var" path="M2_REPO/org/mongodb/mongo-java-driver/2.10.1/mongo-java-driver-2.10.1.jar" sourcepath="M2_REPO/org/mongodb/mongo-java-driver/2.10.1/mongo-java-driver-2.10.1-sources.jar">
+    <attributes>
+      <attribute value="jar:file:/Users/fredta2/.m2/repository/org/mongodb/mongo-java-driver/2.10.1/mongo-java-driver-2.10.1-javadoc.jar!/" name="javadoc_location"/>
+    </attributes>
+  </classpathentry>
+  <classpathentry kind="var" path="M2_REPO/com/github/jmkgreen/morphia/morphia/1.2.2/morphia-1.2.2.jar" sourcepath="M2_REPO/com/github/jmkgreen/morphia/morphia/1.2.2/morphia-1.2.2-sources.jar">
+    <attributes>
+      <attribute value="jar:file:/Users/fredta2/.m2/repository/com/github/jmkgreen/morphia/morphia/1.2.2/morphia-1.2.2-javadoc.jar!/" name="javadoc_location"/>
+    </attributes>
+  </classpathentry>
+  <classpathentry kind="var" path="M2_REPO/com/thoughtworks/proxytoys/proxytoys/1.0/proxytoys-1.0.jar" sourcepath="M2_REPO/com/thoughtworks/proxytoys/proxytoys/1.0/proxytoys-1.0-sources.jar">
+    <attributes>
+      <attribute value="jar:file:/Users/fredta2/.m2/repository/com/thoughtworks/proxytoys/proxytoys/1.0/proxytoys-1.0-javadoc.jar!/" name="javadoc_location"/>
+    </attributes>
+  </classpathentry>
+  <classpathentry kind="var" path="M2_REPO/mysql/mysql-connector-java/5.1.23/mysql-connector-java-5.1.23.jar"/>
+</classpath>

+ 1 - 0
restexpress/.gitignore

@@ -0,0 +1 @@
+.settings/

+ 14 - 0
restexpress/.project

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+  <name>world</name>
+  <comment>A Minimal RestExpress Server. 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>

+ 35 - 0
restexpress/README.md

@@ -0,0 +1,35 @@
+#RestExpress Benchmarking Test
+
+This is the Java RestExpress portion of a [benchmarking test suite](../) comparing a variety of web development platforms.
+
+### JSON Encoding Test
+For raw Servlets there is no broad consensus on JSON encoding so we have selected the fastest available JSON encoder for Java: [Jackson](http://wiki.fasterxml.com/JacksonHome).
+
+* [JSON test source](src/main/java/hello/JsonServlet.java)
+
+### Data-Store/Database Mapping Test
+* [DB test source](src/main/java/hello/DBServlet.java)
+
+## Infrastructure Software Versions
+The tests were run with:
+
+* [Java OpenJDK 1.7.0_09](http://openjdk.java.net/)
+* [Netty 3.6.2](http://netty.io)
+* [Jackson 2.1.4](http://wiki.fasterxml.com/JacksonHome)
+* [Morphia 1.2.2](https://github.com/jmkgreen/morphia)
+* [RepoExpress 0.3.2](https://github.com/RestExpress/RepoExpress)
+* [MySQL 5.5.29](https://dev.mysql.com/)
+
+## Test URLs
+### JSON Encoding Test
+
+http://localhost:8080/restexpress/json
+
+### MySQL Database Mapping Test
+
+http://localhost:8080/restexpress/mysql?queries=5
+
+### MongoDB Database Mapping Test
+
+http://localhost:8080/restexpress/mongodb?queries=5
+

+ 0 - 0
restexpress/__init__.py


+ 25 - 0
restexpress/benchmark_config

@@ -0,0 +1,25 @@
+{
+  "framework": "restexpress",
+  "tests": [{
+    "default": {
+      "setup_file": "setup",
+      "json_url": "/restexpress/json",
+      "port": 8080,
+      "sort": 31
+    },
+    "mysql-raw": {
+      "setup_file": "setup",
+      "db_url": "/restexpress/mysql",
+      "query_url": "/restexpress/mysql?queries=",
+      "port": 8080,
+      "sort": 32
+    },
+    "mongodb": {
+      "setup_file": "setup",
+      "db_url": "/restexpress/mongodb",
+      "query_url": "/restexpress/mongodb?queries=",
+      "port": 8080,
+      "sort": 33
+    }
+  }]
+}

+ 14 - 0
restexpress/config/dev/environment.properties

@@ -0,0 +1,14 @@
+# The size of the executor thread pool (that can handle blocking back-end processing).
+executor.threadPool.size = 300
+
+# A MongoDB URI/Connection string
+# see: http://docs.mongodb.org/manual/reference/connection-string/
+mongodb.uri = mongodb://localhost:27017/hello_world
+
+# A MySQL URI/Connection string
+mysql.uri = jdbc:mysql://localhost:3306/hello_world
+
+# MySQL useConfigs value, See section "21.3.5.1.1. Properties Files for the useConfigs Option" of:
+# http://dev.mysql.com/doc/refman/5.6/en/connector-j-reference-configuration-properties.html
+# Empty is a valid selection.
+mysql.useConfigs = maxPerformance

+ 94 - 0
restexpress/pom.xml

@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+
+	<name>RestExpress-Minimal-Scaffold</name>
+	<!-- 
+		To run the project: mvn clean package exec:java
+		To create a project deployable assembly:
+			mvn clean package
+			mvn assembly:single
+	 -->
+	<description>A Minimal RestExpress Server</description>
+	<url>https://github.com/RestExpress/RestExpress-Scaffold</url>
+	<groupId>hello.world</groupId>
+	<artifactId>world</artifactId>
+	<version>1.0-SNAPSHOT</version>
+	<packaging>jar</packaging>
+
+	<dependencies>
+		<dependency>
+			<groupId>com.strategicgains</groupId>
+			<artifactId>RestExpress</artifactId>
+			<version>0.9.2</version>
+		</dependency>
+		<dependency>
+			<groupId>com.strategicgains.repoexpress</groupId>
+			<artifactId>repoexpress-mongodb</artifactId>
+			<version>0.3.2</version>
+		</dependency>
+		<dependency>
+			<groupId>mysql</groupId>
+			<artifactId>mysql-connector-java</artifactId>
+			<version>5.1.23</version>
+		</dependency>
+	</dependencies>
+
+	<build>
+		<defaultGoal>package</defaultGoal>
+
+		<plugins>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-compiler-plugin</artifactId>
+				<version>3.0</version>
+				<configuration>
+					<source>1.7</source>
+					<target>1.7</target>
+					<encoding>UTF-8</encoding>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.codehaus.mojo</groupId>
+				<artifactId>exec-maven-plugin</artifactId>
+				<version>1.2.1</version>
+				<configuration>
+					<mainClass>hello.Main</mainClass>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-jar-plugin</artifactId>
+				<version>2.4</version>
+				<configuration>
+					<archive>
+						<addMavenDescriptor>false</addMavenDescriptor>
+						<manifest>
+							<addClasspath>true</addClasspath>
+							<classpathPrefix>./lib/</classpathPrefix>
+							<mainClass>hello.Main</mainClass>
+						</manifest>
+					</archive>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-assembly-plugin</artifactId>
+				<version>2.4</version>
+				<configuration>
+					<descriptor>zip-with-dependencies.xml</descriptor>
+				</configuration>
+			</plugin>
+		</plugins>
+	</build>
+
+	<reporting>
+		<plugins>
+			<plugin>
+				<groupId>org.codehaus.mojo</groupId>
+				<artifactId>versions-maven-plugin</artifactId>
+				<version>2.0</version>
+			</plugin>
+		</plugins>
+	</reporting>
+</project>

+ 24 - 0
restexpress/setup.py

@@ -0,0 +1,24 @@
+
+import subprocess
+import sys
+import setup_util
+
+def start(args):
+  setup_util.replace_text("restexpress/config/dev/environment.properties", "mysql:\/\/.*:3306", "mysql://" + args.database_host + ":3306")
+
+  try:
+    subprocess.check_call("mvn clean package", shell=True, cwd="restexpress")
+    subprocess.check_call("mvn assembly:single", shell=True, cwd="restexpress")
+    subprocess.check_call("unzip world-1.0-SNAPSHOT-zip-with-dependencies.zip", shell=True, cwd="restexpress/target")
+    subprocess.Popen("java -jar netty-example-0.1-jar-with-dependencies.jar".rsplit(" "), cwd="restexpress/target/world-1.0-SNAPSHOT")
+    return 0
+  except subprocess.CalledProcessError:
+    return 1
+def stop():
+  p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
+  out, err = p.communicate()
+  for line in out.splitlines():
+    if 'hello.Main' in line:
+      pid = int(line.split(None, 2)[1])
+      os.kill(pid, 9)
+  return 0

+ 48 - 0
restexpress/src/main/java/hello/Main.java

@@ -0,0 +1,48 @@
+package hello;
+
+import hello.config.Configuration;
+import hello.controller.JsonController.HelloWorld;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+import org.jboss.netty.handler.codec.http.HttpMethod;
+
+import com.strategicgains.restexpress.RestExpress;
+import com.strategicgains.restexpress.util.Environment;
+
+public class Main
+{
+	public static void main(String[] args) throws Exception
+	{
+		Configuration config = loadEnvironment(args);
+		RestExpress server = new RestExpress()
+			.setName("RestExpress Benchmark")
+		    .setExecutorThreadCount(config.getExecutorThreadPoolSize())
+		    .alias("HelloWorld", HelloWorld.class);
+
+		server.uri("/restexpress/json", config.getJsonController())
+			.action("helloWorld", HttpMethod.GET);
+
+		server.uri("/restexpress/mysql", config.getMysqlController())
+			.method(HttpMethod.GET);
+
+		server.uri("/restexpress/mongodb", config.getMongodbController())
+		    .method(HttpMethod.GET);
+
+		server.bind(config.getPort());
+		server.awaitShutdown();
+	}
+
+	private static Configuration loadEnvironment(String[] args)
+	throws FileNotFoundException,
+	    IOException
+	{
+		if (args.length > 0)
+		{
+			return Environment.from(args[0], Configuration.class);
+		}
+
+		return Environment.fromDefault(Configuration.class);
+	}
+}

+ 96 - 0
restexpress/src/main/java/hello/config/Configuration.java

@@ -0,0 +1,96 @@
+package hello.config;
+
+import hello.controller.JsonController;
+import hello.controller.MongodbController;
+import hello.controller.MysqlController;
+import hello.controller.persistence.WorldsMongodbRepository;
+
+import java.util.Properties;
+
+import com.strategicgains.repoexpress.adapter.IdentiferAdapter;
+import com.strategicgains.repoexpress.exception.InvalidObjectIdException;
+import com.strategicgains.restexpress.Format;
+import com.strategicgains.restexpress.util.Environment;
+
+public class Configuration
+extends Environment
+{
+	private static final String DEFAULT_EXECUTOR_THREAD_POOL_SIZE = "20";
+
+	private static final String PORT_PROPERTY = "port";
+	private static final String DEFAULT_FORMAT_PROPERTY = "default.format";
+	private static final String BASE_URL_PROPERTY = "base.url";
+	private static final String EXECUTOR_THREAD_POOL_SIZE = "executor.threadPool.size";
+
+	private int port;
+	private String defaultFormat;
+	private String baseUrl;
+	private int executorThreadPoolSize;
+
+	private JsonController jsonController;
+	private MysqlController mysqlController;
+	private MongodbController mongodbController;
+
+	@Override
+	protected void fillValues(Properties p)
+	{
+		this.port = Integer.parseInt(p.getProperty(PORT_PROPERTY, "8080"));
+		this.defaultFormat = p.getProperty(DEFAULT_FORMAT_PROPERTY, Format.JSON);
+		this.baseUrl = p.getProperty(BASE_URL_PROPERTY, "http://localhost:" + String.valueOf(port));
+		this.executorThreadPoolSize = Integer.parseInt(p.getProperty(EXECUTOR_THREAD_POOL_SIZE, DEFAULT_EXECUTOR_THREAD_POOL_SIZE));
+		MongoConfig mongoSettings = new MongoConfig(p);
+		MysqlConfig mysqlSettings = new MysqlConfig(p);
+		initialize(mysqlSettings, mongoSettings);
+	}
+
+	private void initialize(MysqlConfig mysqlSettings, MongoConfig mongo)
+	{
+		jsonController = new JsonController();
+		mysqlController = new MysqlController(mysqlSettings.getDataSource());
+        WorldsMongodbRepository worldMongodbRepository = new WorldsMongodbRepository(mongo.getClient(), mongo.getDbName());
+        worldMongodbRepository.setIdentifierAdapter(new IdentiferAdapter<Long>()
+		{
+			@Override
+			public Long convert(String id) throws InvalidObjectIdException
+			{
+				return Long.valueOf(id);
+			}
+		});
+		mongodbController = new MongodbController(worldMongodbRepository);
+	}
+
+	public String getDefaultFormat()
+	{
+		return defaultFormat;
+	}
+
+	public int getPort()
+	{
+		return port;
+	}
+	
+	public String getBaseUrl()
+	{
+		return baseUrl;
+	}
+	
+	public int getExecutorThreadPoolSize()
+	{
+		return executorThreadPoolSize;
+	}
+
+	public JsonController getJsonController()
+	{
+		return jsonController;
+	}
+
+	public MysqlController getMysqlController()
+	{
+		return mysqlController;
+	}
+
+	public MongodbController getMongodbController()
+	{
+		return mongodbController;
+	}
+}

+ 47 - 0
restexpress/src/main/java/hello/config/MongoConfig.java

@@ -0,0 +1,47 @@
+package hello.config;
+
+import java.net.UnknownHostException;
+import java.util.Properties;
+
+import com.mongodb.MongoClient;
+import com.mongodb.MongoClientURI;
+import com.strategicgains.restexpress.exception.ConfigurationException;
+
+public class MongoConfig
+{
+	private static final String URI_PROPERTY = "mongodb.uri";
+
+	private String dbName;
+	private MongoClient client;
+
+    public MongoConfig(Properties p)
+    {
+		String uri = p.getProperty(URI_PROPERTY);
+
+		if (uri == null)
+		{
+			throw new ConfigurationException("Please define a MongoDB URI for property: " + URI_PROPERTY);
+		}
+
+		MongoClientURI mongoUri = new MongoClientURI(uri);
+		dbName = mongoUri.getDatabase();
+		try
+        {
+	        client = new MongoClient(mongoUri);
+        }
+        catch (UnknownHostException e)
+        {
+        	throw new ConfigurationException(e);
+        }
+    }
+
+	public String getDbName()
+	{
+		return dbName;
+	}
+
+	public MongoClient getClient()
+	{
+		return client;
+	}
+}

+ 42 - 0
restexpress/src/main/java/hello/config/MysqlConfig.java

@@ -0,0 +1,42 @@
+package hello.config;
+
+import java.util.Properties;
+
+import javax.sql.DataSource;
+
+import com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource;
+import com.strategicgains.restexpress.exception.ConfigurationException;
+
+public class MysqlConfig
+{
+	private static final String URI_PROPERTY = "mysql.uri";
+	private static final String USE_CONFIGS_PROPERTY = "mysql.useConfigs";
+
+	private DataSource dataSource;
+
+	public MysqlConfig(Properties p)
+    {
+		String uri = p.getProperty(URI_PROPERTY);
+		String useConfigs = p.getProperty(USE_CONFIGS_PROPERTY);
+
+		if (uri == null)
+		{
+			throw new ConfigurationException("Please define a MySQL URI for property: " + URI_PROPERTY);
+		}
+		
+		MysqlConnectionPoolDataSource ds = new MysqlConnectionPoolDataSource();
+		ds.setUrl(uri);
+		
+		if (useConfigs != null)
+		{
+			ds.setUseConfigs(useConfigs);
+		}
+
+		this.dataSource = ds;
+    }
+
+	public DataSource getDataSource()
+    {
+	    return dataSource;
+    }
+}

+ 23 - 0
restexpress/src/main/java/hello/controller/JsonController.java

@@ -0,0 +1,23 @@
+package hello.controller;
+
+import com.strategicgains.restexpress.Request;
+import com.strategicgains.restexpress.Response;
+
+public class JsonController
+{
+	private static final HelloWorld MESSAGE_OBJECT = new HelloWorld();
+
+	public HelloWorld helloWorld(Request request, Response response)
+	{
+		return MESSAGE_OBJECT;
+	}
+
+	/**
+	 * Inner class just to illustrate JSON serialization.
+	 */
+	public static class HelloWorld
+	{
+		@SuppressWarnings("unused")
+        private String message = "Hello, World!";
+	}
+}

+ 57 - 0
restexpress/src/main/java/hello/controller/MongodbController.java

@@ -0,0 +1,57 @@
+package hello.controller;
+
+import hello.controller.persistence.WorldsMongodbRepository;
+import hello.domain.World;
+
+import java.util.Random;
+import java.util.concurrent.ThreadLocalRandom;
+
+import com.strategicgains.restexpress.Request;
+import com.strategicgains.restexpress.Response;
+
+public class MongodbController
+{
+	// Database details.
+	private static final int DB_ROWS = 10000;
+
+	private WorldsMongodbRepository worldRepo;
+
+	public MongodbController(WorldsMongodbRepository worldsRepository)
+	{
+		super();
+		this.worldRepo = worldsRepository;
+	}
+
+	public World[] read(Request request, Response response)
+	{
+		// Get the count of queries to run.
+		int count = 1;
+		String value = request.getHeader("queries");
+
+		if (value != null)
+		{
+			count = Integer.parseInt(value);
+		}
+
+		// Bounds check.
+		if (count > 500)
+		{
+			count = 500;
+		}
+		else if (count < 1)
+		{
+			count = 1;
+		}
+
+		// Fetch some rows from the database.
+		final World[] worlds = new World[count];
+		final Random random = ThreadLocalRandom.current();
+
+		for (int i = 0; i < count; i++)
+		{
+			worlds[i] = worldRepo.find(random.nextInt(DB_ROWS) + 1);
+		}
+
+		return worlds;
+	}
+}

+ 74 - 0
restexpress/src/main/java/hello/controller/MysqlController.java

@@ -0,0 +1,74 @@
+package hello.controller;
+
+import hello.domain.World;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Random;
+import java.util.concurrent.ThreadLocalRandom;
+
+import javax.sql.DataSource;
+
+import com.strategicgains.restexpress.Request;
+import com.strategicgains.restexpress.Response;
+
+public class MysqlController
+{
+	// Database details.
+	private static final String DB_QUERY = "SELECT * FROM World WHERE id = ?";
+	private static final int DB_ROWS = 10000;
+
+	private DataSource mysqlDataSource;
+
+	public MysqlController(DataSource dataSource)
+	{
+		super();
+		this.mysqlDataSource = dataSource;
+	}
+
+	public World[] read(Request request, Response response)
+	throws SQLException
+	{
+		final DataSource source = mysqlDataSource;
+
+		// Get the count of queries to run.
+		int count = 1;
+		count = Integer.parseInt(request.getHeader("queries"));
+
+		// Bounds check.
+		if (count > 500)
+		{
+			count = 500;
+		}
+		if (count < 1)
+		{
+			count = 1;
+		}
+
+		// Fetch some rows from the database.
+		final World[] worlds = new World[count];
+		final Random random = ThreadLocalRandom.current();
+
+		Connection conn = source.getConnection();
+		PreparedStatement statement = conn.prepareStatement(DB_QUERY, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
+
+		// Run the query the number of times requested.
+		for (int i = 0; i < count; i++)
+		{
+			final Long id = (long) (random.nextInt(DB_ROWS) + 1);
+			statement.setLong(1, id);
+
+			try (ResultSet results = statement.executeQuery())
+			{
+				if (results.next())
+				{
+					worlds[i] = new World(id, results.getInt("randomNumber"));
+				}
+			}
+		}
+
+		return worlds;
+	}
+}

+ 21 - 0
restexpress/src/main/java/hello/controller/persistence/WorldsMongodbRepository.java

@@ -0,0 +1,21 @@
+package hello.controller.persistence;
+
+import hello.domain.World;
+
+import com.mongodb.Mongo;
+import com.strategicgains.repoexpress.mongodb.MongodbRepository;
+
+public class WorldsMongodbRepository
+extends MongodbRepository<World, Long>
+{
+	@SuppressWarnings("unchecked")
+    public WorldsMongodbRepository(Mongo mongo, String dbName)
+    {
+	    super(mongo, dbName, World.class);
+    }
+
+	public World find(int id)
+	{
+		return getDataStore().find(World.class, "id", (long) id).retrievedFields(false, "_id").get();
+	}
+}

+ 46 - 0
restexpress/src/main/java/hello/domain/World.java

@@ -0,0 +1,46 @@
+package hello.domain;
+
+import org.bson.types.ObjectId;
+
+import com.github.jmkgreen.morphia.annotations.Entity;
+import com.github.jmkgreen.morphia.annotations.Id;
+import com.github.jmkgreen.morphia.annotations.Indexed;
+import com.strategicgains.repoexpress.domain.Identifiable;
+
+@Entity(value="world", noClassnameStored=true)
+public class World
+implements Identifiable
+{
+	@Id
+	private ObjectId oid;
+	
+	@Indexed(unique=true)
+	private Long id;
+	private int randomNumber;
+
+	public World()
+	{
+		super();
+	}
+
+	public World(Long id, int randomNumber)
+	{
+		this.id = id;
+		this.randomNumber = randomNumber;
+	}
+
+	public String getId()
+	{
+		return (id == null ? null : id.toString());
+	}
+	
+	public void setId(String id)
+	{
+		this.id = (id == null ? null : Long.parseLong(id));
+	}
+
+	public int getRandomNumber()
+	{
+		return this.randomNumber;
+	}
+}

+ 34 - 0
restexpress/zip-with-dependencies.xml

@@ -0,0 +1,34 @@
+<assembly
+	xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
+	<id>zip-with-dependencies</id>
+	<formats>
+		<format>zip</format>
+	</formats>
+	<fileSets>
+		<fileSet>
+			<directory>${project.basedir}</directory>
+			<outputDirectory>/</outputDirectory>
+			<includes>
+				<include>README*</include>
+				<include>LICENSE*</include>
+				<include>NOTICE*</include>
+				<include>config/**/*</include>
+			</includes>
+		</fileSet>
+		<fileSet>
+			<directory>${project.build.directory}</directory>
+			<outputDirectory>/</outputDirectory>
+			<includes>
+				<include>*.jar</include>
+			</includes>
+		</fileSet>
+	</fileSets>
+	<dependencySets>
+		<dependencySet>
+			<outputDirectory>lib</outputDirectory>
+			<useProjectArtifact>false</useProjectArtifact>
+		</dependencySet>
+	</dependencySets>
+</assembly>