Browse Source

Proteus: Added Framework (#2759)

* Proteus: Added Framework

* Proteus: Added link to project in the README.
noboomu 8 years ago
parent
commit
32a0dc4583

+ 1 - 0
.travis.yml

@@ -90,6 +90,7 @@ env:
     - "TESTDIR=Java/permeagility"
     - "TESTDIR=Java/play1"
     - "TESTDIR=Java/play2-java"
+    - "TESTDIR=Java/proteus"
     - "TESTDIR=Java/rapidoid"
     - "TESTDIR=Java/restexpress"
     - "TESTDIR=Java/revenj-jvm"

+ 15 - 0
frameworks/Java/proteus/README.md

@@ -0,0 +1,15 @@
+# Proteus Benchmarking Test
+
+This is an implementation of [proteus](https://github.com/noboomu/proteus)
+as a portion of a [benchmarking test suite](../) comparing a variety 
+of web development platforms.
+
+This project consists of several tests:
+
+* MySql / Postgres DB and Fortunes  
+
+* Plaintext and JSON 
+
+* All are defined in : `Benchmarks.java` 
+
+All configuration is done thorugh the `application.conf` configuration file

+ 47 - 0
frameworks/Java/proteus/benchmark_config.json

@@ -0,0 +1,47 @@
+{
+  "framework": "proteus",
+  "tests": [{
+    "default": {
+      "setup_file": "setup",
+      "json_url": "/json",
+      "db_url": "/db/postgres",
+      "fortune_url": "/fortunes/postgres",
+      "plaintext_url": "/plaintext",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Micro",
+      "database": "Postgres",
+      "framework": "Proteus",
+      "language": "Java",
+      "flavor": "None",
+      "orm": "Raw",
+      "platform": "Undertow",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "proteus",
+      "notes": "",
+      "versus": ""
+    },
+    "mysql" : {
+      "setup_file": "setup",
+      "db_url": "/db/mysql",
+      "fortune_url": "/fortunes/mysql",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Micro",
+      "database": "MySQL",
+      "framework": "Proteus",
+      "language": "Java",
+      "flavor": "None",
+      "orm": "Raw",
+      "platform": "Undertow",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "proteus-mysql",
+      "notes": "",
+      "versus": ""
+    }
+  }]
+}

+ 86 - 0
frameworks/Java/proteus/conf/application.conf

@@ -0,0 +1,86 @@
+
+application {
+ 
+  env = dev
+
+  version = "1.0"
+
+  name="Proteus" 
+ 
+  path = ""
+
+  # localhost
+  host = "localhost"
+
+  # HTTP ports
+  port = 8080
+ 
+}
+
+application.tmpdir=${java.io.tmpdir}
+
+api.version="v1"
+
+globalHeaders 
+{
+#  Access-Control-Allow-Origin: "*"
+#  Access-Control-Allow-Methods: "*"
+#  Access-Control-Allow-Headers: "*"
+  Server: ${application.name}
+}
+
+assets {
+  path = "/public"
+  dir = "./assets"
+  cache {
+    time = 500
+  }
+}
+
+
+
+mysql
+{
+   hikaricp 
+   {
+        
+        dataSourceClassName = "com.mysql.jdbc.jdbc2.optional.MysqlDataSource" 
+        jdbcUrl= "jdbc:mysql://127.0.0.1:3306/hello_world",
+        maxLifetime=900000
+        idleTimeout=300000
+        maximumPoolSize=128
+        minimumIdle=128
+        username = benchmarkdbuser
+        password = benchmarkdbpass
+        ds  
+        {
+            databaseName = "hello_world"
+            serverName = "192.168.1.121"
+            portNumber = 3306
+            interactiveClient=true
+            characterEncoding=utf8
+            useUnicode=true
+            autoReconnect=true
+            prepStmtCacheSqlLimit=2048 
+            cachePrepStmts=true
+            cacheCallableStmts=true
+            prepStmtCacheSize=4096
+            useServerPrepStmts=true
+        }
+  }
+}
+
+postgres
+{
+   hikaricp 
+   {
+        description="postgresql server"
+        jdbcUrl= "jdbc:postgresql://127.0.0.1:5432/hello_world",
+        maximumPoolSize=256
+        minimumIdle=128
+        username = benchmarkdbuser
+        password = benchmarkdbpass
+  }
+}
+
+ 

+ 113 - 0
frameworks/Java/proteus/conf/logback.xml

@@ -0,0 +1,113 @@
+<configuration packagingData="true">
+
+	<turboFilter class="ch.qos.logback.classic.turbo.DuplicateMessageFilter" />
+
+	<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+		<withJansi>true</withJansi>
+		<encoder>
+			<pattern>%date{ISO8601} %highlight(%-5level) [%boldCyan(%logger)] [%boldYellow(%method %F) ] - %boldWhite(%message) %n %red(%ex)
+			</pattern>
+		</encoder>
+	</appender>
+
+	<logger name="org.apache.http" level="ERROR" />
+
+ 	
+	<logger name="javax.mail" level="ERROR" />
+	<logger name="com.squareup.okhttp" level="ERROR" />
+	<logger name="javax.activation" level="ERROR" /> 
+ 
+	<logger name="io.netty" level="ERROR" />
+	<logger name="io.netty.handler" level="ERROR" />
+	 
+	<logger name="io.sinistral.proteus.server.swagger" level="ERROR" />
+	<logger name="io.sinistral.proteus.server.handlers" level="ERROR" />
+	<logger name="io.sinistral.proteus.services" level="ERROR" />
+	<logger name="io.sinistral.proteus.modules" level="ERROR" />
+	 
+	<logger name="com.relayrides" level="ERROR" />
+	<logger name="org.apache.activemq" level="ERROR" />
+	<logger name="org.apache.camel" level="ERROR" />
+	<logger name="org.apache.activemq" level="ERROR" />
+	<logger name="sun.net" level="ERROR" />
+	<logger name="org.springsource" level="ERROR" />
+	<logger name="com.sun.jersey" level="ERROR" />
+   
+ 
+	<logger name="org.xnio.nio" level="ERROR" />
+	<logger name="io.undertow" level="ERROR" />
+
+ 
+ 	<logger name="org.reflections" level="OFF" />
+	<logger name="org.elasticsearch" level="ERROR" />
+	<logger name="org.ajave.ebean" level="ERROR" />
+
+	<logger name="org.avaje.ebean.SQL" level="ERROR" />
+	<logger name="org.avaje.ebean.SUM" level="ERROR" />
+	<logger name="org.avaje.ebean.TXN" level="ERROR" />
+	<logger name="com.avaje.ebeaninternal" level="ERROR" />
+	<logger name="org.avaje.ebean.cache.BEAN" level="ERROR" />
+	
+	<logger name="io.swagger.converter" level="ERROR" />
+   	<logger name="io.swagger.jackson" level="ERROR" />
+ 
+ 
+	<logger name="com.avaje.ebean.config.PropertyMapLoader" level="ERROR" />
+	<logger name="com.avaje.ebeaninternal.server.core.XmlConfigLoader" level="ERROR" />
+	<logger name="com.avaje.ebeaninternal.server.lib.BackgroundThread" level="ERROR" />
+	 
+	<logger name="com.gargoylesoftware.htmlunit.javascript" level="OFF" />
+	<logger name="swagger" level="ERROR" />
+	<logger name="com.zaxxer.hikari" level="ERROR" />
+	<logger name="com.zaxxer.hikari.pool" level="ERROR" />
+	<logger name="javax.management" level="ERROR" />
+	<logger name="javax.management.mbeanserver" level="OFF" />
+  	<logger name="java.jmx" level="ERROR" />
+
+	<logger name="net.sf.ehcache" level="ERROR" />
+
+	<logger name="org.apache.http" level="ERROR" />
+	<logger name="com.amazonaws" level="ERROR" />
+	<logger name="org.apache.http.wire" level="ERROR" />
+	
+	<logger name="com.wordnik" level="OFF" />
+
+	<logger name="javax.mail" level="ERROR" />
+	<logger name="com.squareup.okhttp" level="WARN" />
+
+	<logger name="org.hibernate.validator" level="ERROR" />
+	<logger name="org.xnio.nio" level="ERROR" />
+
+   
+	<logger name="org.apache.camel" level="ERROR" />
+	<logger name="org.apache.activemq" level="ERROR" />
+	<logger name="sun.net" level="ERROR" />
+	<logger name="org.springsource" level="ERROR" />
+	<logger name="com.sun.jersey" level="ERROR" />
+	<logger name="akka.event.EventStream" level="ERROR" />
+	<logger name="akka.event.slf4j" level="ERROR" />
+	<logger name="com.googlecode.mp4parser" level="ERROR" />
+	<logger name="com.googlecode.mp4parser.util" level="ERROR" />
+
+ 	<logger name="org.elasticsearch.plugins" level="ERROR" />
+ 
+ 	<logger name="org.apache.commons.logging.impl.Jdk14Logger" level="ERROR" />
+ 	<logger name="org.zeroturnaround.exec.ProcessExecutor" level="ERROR" />
+	<logger name="org.zeroturnaround.exec" level="ERROR" />
+	 
+<!-- 	 	<logger name="com.datastax.driver.core.QueryLogger.NORMAL" level="trace" />
+ -->	 
+	  
+	<logger name="org.apache.kafka.clients.NetworkClient" level="ERROR" />
+	<logger name="org.apache.kafka.clients.producer" level="ERROR" />
+	 
+      
+       	<logger name="java.util.logging.Logger" level="ERROR" />
+     	<logger name="com.google.inject.internal" level="ERROR" />
+	<logger name="com.google.inject" level="ERROR" />  
+      	<logger name="com.datastax" level="ERROR" />
+     
+  <root level="OFF">
+    <appender-ref ref="STDOUT" />
+  </root>
+</configuration>

+ 266 - 0
frameworks/Java/proteus/pom.xml

@@ -0,0 +1,266 @@
+<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/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<groupId>io.sinistral</groupId>
+	<artifactId>proteus-techempower</artifactId>
+	<packaging>jar</packaging>
+	<version>1.0.0</version>
+	<name>proteus-techempower</name>
+	<url>http://maven.apache.org</url>
+	<build>
+	 <resources>
+            <resource>
+                <directory>src/main/resources</directory>
+                <filtering>true</filtering>
+            </resource>
+        </resources>
+        <testResources>
+            <testResource>
+                <directory>src/test/resources</directory>
+            </testResource>
+            <testResource>
+                <directory>src/test/java</directory>
+                <excludes>
+                    <exclude>**/*.java</exclude>
+                </excludes>
+            </testResource>
+        </testResources>
+		<pluginManagement>
+			<plugins>
+				<plugin>
+					<groupId>org.eclipse.m2e</groupId>
+					<artifactId>lifecycle-mapping</artifactId>
+					<version>1.0.0</version>
+					<configuration>
+						<lifecycleMappingMetadata>
+							<pluginExecutions>
+								<pluginExecution>
+									<pluginExecutionFilter>
+										<groupId>org.apache.maven.plugins</groupId>
+										<artifactId>maven-compiler-plugin</artifactId>
+										<versionRange>[3.3,)</versionRange>
+										<goals>
+											<goal>compile</goal>
+											<goal>testCompile</goal>
+										</goals>
+									</pluginExecutionFilter>
+
+									<action>
+										<execute />
+									</action>
+								</pluginExecution>
+								<!-- <pluginExecution>
+									<pluginExecutionFilter>
+										<groupId>com.fizzed</groupId>
+										<artifactId>rocker-maven-plugin</artifactId>
+										<versionRange>[${rocker.version},)</versionRange>
+										<goals>
+											<goal>generate</goal>
+										</goals>
+									</pluginExecutionFilter>
+									<action>
+										<execute>
+											<runOnIncremental>true</runOnIncremental>
+										</execute>
+									</action>
+								</pluginExecution> -->
+							</pluginExecutions>
+						</lifecycleMappingMetadata>
+					</configuration>
+				</plugin>
+			</plugins>
+		</pluginManagement>
+		<plugins>
+			<plugin>
+				<artifactId>maven-compiler-plugin</artifactId>
+				<version>3.5.1</version>
+				<configuration>
+					<source>1.8</source>
+					<target>1.8</target>
+					<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+
+					<compilerArgument>-parameters</compilerArgument>
+
+				</configuration>
+			</plugin>
+			<plugin>
+				<artifactId>maven-resources-plugin</artifactId>
+				<version>3.0.2</version>
+				<executions>
+					<execution>
+						<id>copy-configuration-resources</id>
+						<phase>validate</phase>
+						<goals>
+							<goal>copy-resources</goal>
+						</goals>
+						<configuration>
+							<outputDirectory>${project.build.directory}/conf</outputDirectory>
+							<resources>
+								<resource>
+									<directory>conf</directory>
+									<filtering>true</filtering>
+								</resource>
+							</resources>
+						</configuration>
+					</execution>
+					<execution>
+						<id>copy-asset-resources</id>
+						<phase>validate</phase>
+						<goals>
+							<goal>copy-resources</goal>
+						</goals>
+						<configuration>
+							<outputDirectory>${project.build.directory}/assets</outputDirectory>
+							<resources>
+								<resource>
+									<directory>assets</directory>
+									<filtering>true</filtering>
+								</resource>
+							</resources>
+						</configuration>
+					</execution>
+				</executions>
+			</plugin>
+
+			<plugin>
+				<groupId>org.codehaus.mojo</groupId>
+				<artifactId>exec-maven-plugin</artifactId>
+				<version>1.6.0</version>
+				<executions>
+					<execution>
+						<goals>
+							<goal>exec</goal>
+						</goals>
+					</execution>
+				</executions>
+				<configuration>
+					<executable>java</executable>
+					<arguments>
+						<argument>-Dlogback.configurationFile=${project.build.directory}/conf/logback.xml</argument>
+						<argument>-Xbootclasspath/p:lib/alpn-boot-8.1.11.v20170118.jar</argument>
+						<argument>-Xms1g</argument>
+						<argument>-Xmx2g</argument>
+						<argument>-XX:+AggressiveOpts</argument>
+						<argument>-XX:-UseBiasedLocking</argument>
+						<argument>-XX:+UseStringDeduplication</argument>
+						<argument>-classpath</argument>
+						<!-- automatically creates the classpath using all project dependencies, 
+							also adding the project build directory -->
+						<classpath />
+						<argument>io.sinistral.ExampleApplication</argument>
+
+					</arguments>
+
+				</configuration>
+			</plugin>
+			<plugin>
+				<artifactId>maven-dependency-plugin</artifactId>
+				<executions>
+					<execution>
+						<phase>package</phase>
+						<goals>
+							<goal>copy-dependencies</goal>
+						</goals>
+						<configuration>
+							<outputDirectory>${project.build.directory}/lib</outputDirectory>
+						</configuration>
+					</execution>
+				</executions>
+			</plugin>
+			<plugin>
+				<!-- Build an executable JAR -->
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-jar-plugin</artifactId>
+				<version>3.0.2</version>
+				<configuration>
+					<archive>
+						<manifest>
+							<addClasspath>true</addClasspath>
+							<classpathPrefix>lib/</classpathPrefix>
+							<mainClass>io.sinistral.ExampleApplication</mainClass>
+						</manifest>
+					</archive>
+				</configuration>
+			</plugin>
+		  <!-- 	<plugin>
+				<groupId>com.fizzed</groupId>
+				<artifactId>rocker-maven-plugin</artifactId>
+				<version>0.18.0</version>
+				<executions>
+					<execution>
+						<id>generate-rocker-templates</id>
+						<phase>generate-sources</phase>
+						<goals>
+							<goal>generate</goal>
+						</goals>
+						<configuration>
+					<outputDirectory>${project.basedir}/src</outputDirectory>
+					</configuration>
+					</execution>
+					 
+				</executions>
+			</plugin>   -->
+		</plugins>
+	</build>
+	<dependencies>
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<version>4.12</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.mortbay.jetty.alpn</groupId>
+			<artifactId>alpn-boot</artifactId>
+			<version>8.1.11.v20170118</version>
+			<scope>runtime</scope>
+		</dependency>
+		<dependency>
+			<groupId>io.sinistral</groupId>
+			<artifactId>proteus-core</artifactId>
+			<version>0.1.2-SNAPSHOT</version>
+			
+		</dependency>
+			<dependency>
+			<groupId>com.fizzed</groupId>
+			<artifactId>rocker-runtime</artifactId>
+			<version>0.18.0</version>
+		</dependency>
+		<dependency>
+			<groupId>com.fizzed</groupId>
+			<artifactId>rocker-compiler</artifactId>
+			<version>0.18.0</version>
+			<scope>provided</scope>
+		</dependency>
+		 
+		<dependency>
+			<groupId>com.zaxxer</groupId>
+			<artifactId>HikariCP</artifactId>
+			<version>2.6.1</version>
+		</dependency>
+		<dependency>
+			<groupId>mysql</groupId>
+			<artifactId>mysql-connector-java</artifactId>
+			<version>5.1.41</version>
+		</dependency>
+		<dependency>
+			<groupId>org.postgresql</groupId>
+			<artifactId>postgresql</artifactId>
+			<version>9.4.1212</version>
+		</dependency>
+	</dependencies>
+	<properties>
+		<rocker.version>0.18.0</rocker.version>
+		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+	</properties>
+	<repositories>
+    <repository>
+        <id>oss-sonatype</id>
+        <name>oss-sonatype</name>
+        <url>https://oss.sonatype.org/content/groups/public/</url>
+        <snapshots>
+            <enabled>true</enabled>
+        </snapshots>
+    </repository>
+</repositories>
+</project>

+ 10 - 0
frameworks/Java/proteus/setup.sh

@@ -0,0 +1,10 @@
+#!/bin/bash
+
+fw_depends postgresql mysql java maven
+
+sed -i 's|mysql://.*:3306|mysql://'"${DBHOST}"':3306|g' conf/application.conf
+sed -i 's|postgresql://.*:5432|postgresql://'"${DBHOST}"':5432|g' conf/application.conf
+
+mvn -U clean package
+cd target
+java -Dlogback.configurationFile="conf/logback.xml" -server  -Xms1g -Xmx2g -XX:+UseG1GC -XX:+AggressiveOpts -XX:-UseBiasedLocking -XX:+UseStringDeduplication -classpath "./proteus-techempower-1.0.0.jar:lib/*" io.sinistral.ExampleApplication &

+ 38 - 0
frameworks/Java/proteus/src/main/java/io/sinistral/ExampleApplication.java

@@ -0,0 +1,38 @@
+package io.sinistral;
+
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import io.sinistral.controllers.Benchmarks;
+import io.sinistral.proteus.ProteusApplication;
+import io.sinistral.proteus.services.AssetsService;
+import io.sinistral.proteus.services.SwaggerService;
+import io.sinistral.services.MySqlService;
+import io.sinistral.services.PostgresService;
+
+/**
+ * Hello world!
+ *
+ */
+public class ExampleApplication extends ProteusApplication
+{
+    public static void main( String[] args )
+    {
+        
+        ExampleApplication app = new ExampleApplication();
+        
+        app.addService(SwaggerService.class);
+		 
+		app.addService(AssetsService.class);
+
+		app.addService(MySqlService.class);
+
+		app.addService(PostgresService.class);
+ 
+		app.addController(Benchmarks.class);  
+		
+		app.start();
+        
+    }
+}

+ 207 - 0
frameworks/Java/proteus/src/main/java/io/sinistral/controllers/Benchmarks.java

@@ -0,0 +1,207 @@
+/**
+ * 
+ */
+package io.sinistral.controllers;
+
+import static io.sinistral.proteus.server.ServerResponse.response;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ThreadLocalRandom;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import com.jsoniter.output.JsonStream;
+
+import io.sinistral.models.Fortune;
+import io.sinistral.models.World;
+import io.sinistral.services.MySqlService;
+import io.sinistral.services.PostgresService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.undertow.server.HttpHandler;
+import io.undertow.server.HttpServerExchange;
+
+/**
+ * Much of this borrowed with reverence from Light-Java
+ * 
+ * @author jbauer
+ *
+ */
+
+@Api(tags="benchmark")
+@Path("")
+@Produces((MediaType.APPLICATION_JSON)) 
+@Consumes((MediaType.MEDIA_TYPE_WILDCARD)) 
+@Singleton
+public class Benchmarks
+{
+	private static final String HTML_UTF8_TYPE = io.sinistral.proteus.server.MediaType.TEXT_HTML_UTF8.toString();
+	private static final String PLAINTEXT_UTF8_TYPE = io.sinistral.proteus.server.MediaType.TEXT_PLAIN_UTF8.toString();
+	private static final String PLAINTEXT_TYPE = io.sinistral.proteus.server.MediaType.TEXT_PLAIN.toString();
+	
+	@Inject 
+	protected MySqlService sqlService;
+	
+	@Inject 
+	protected PostgresService postgresService;
+	
+   
+    public static int randomWorld() {
+        return 1 + ThreadLocalRandom.current().nextInt(10000);
+    }
+
+ 
+	
+	
+	@GET
+	@Path("/db/postgres")
+	@ApiOperation(value = "World postgres db endpoint",   httpMethod = "GET" , response = World.class)
+	public void dbPostgres(HttpServerExchange exchange)
+	{ 		
+		final World world;
+		
+		try (final Connection connection = postgresService.getConnection()) {
+            try (PreparedStatement statement = connection.prepareStatement(
+                    "SELECT id,randomNumber FROM world WHERE id = ?",
+                    ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY)) {
+                statement.setInt(1, randomWorld());
+                try (ResultSet resultSet = statement.executeQuery()) {
+                    resultSet.next();
+                    world =  new World(
+                            resultSet.getInt("id"),
+                            resultSet.getInt("randomNumber"));
+                }
+            }
+            
+    		response( JsonStream.serializeToBytes(world) ).applicationJson().send(exchange);
+
+        } catch (Exception e) {
+            throw new IllegalArgumentException();
+        }
+		  
+ 		 
+	}
+	
+	@GET
+	@Path("/db/mysql")
+	@ApiOperation(value = "World mysql db endpoint",   httpMethod = "GET" , response = World.class)
+	public void dbMySql(HttpServerExchange exchange)
+	{ 		
+		final World world;
+		
+		try (final Connection connection = sqlService.getConnection()) {
+            try (PreparedStatement statement = connection.prepareStatement(
+                    "SELECT id,randomNumber FROM world WHERE id = ?",
+                    ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY)) {
+                statement.setInt(1, randomWorld());
+                try (ResultSet resultSet = statement.executeQuery()) {
+                    resultSet.next();
+                    world =  new World(
+                            resultSet.getInt("id"),
+                            resultSet.getInt("randomNumber"));
+                }
+            }
+            
+    		response( JsonStream.serializeToBytes(world) ).applicationJson().send(exchange);
+
+        } catch (Exception e) {
+            throw new IllegalArgumentException();
+        }
+		  
+ 		 
+	}
+	
+	
+
+	@GET
+	@Path("/fortunes/mysql")
+	@Produces(MediaType.TEXT_HTML)
+	@ApiOperation(value = "Fortunes mysql endpoint",   httpMethod = "GET"  )
+	public void fortunesMysql(HttpServerExchange exchange, HttpHandler handler ) throws Exception
+	{ 
+ 
+ 		final List<Fortune> fortunes = new ArrayList<>();
+ 		 
+ 		 try (Connection connection = sqlService.getConnection();
+ 	             PreparedStatement statement = connection.prepareStatement(
+ 	                     "SELECT * FROM fortune",
+ 	                     ResultSet.TYPE_FORWARD_ONLY,
+ 	                     ResultSet.CONCUR_READ_ONLY);
+ 	             ResultSet resultSet = statement.executeQuery()) {
+ 	                while (resultSet.next()) {
+ 	                fortunes.add(new Fortune(
+ 	                        resultSet.getInt("id"),
+ 	                        resultSet.getString("message")));
+ 	            }
+ 	        }
+ 
+ 		 fortunes.add(new Fortune(0, "Additional fortune added at request time."));
+         Collections.sort(fortunes); 
+		 
+		 response( views.Fortunes.template(fortunes).render().toString() ).contentType(HTML_UTF8_TYPE).send(exchange);
+		  
+	}
+	
+
+	@GET
+	@Path("/fortunes/postgres")
+	@Produces(MediaType.TEXT_HTML)
+	@ApiOperation(value = "Fortunes postgres endpoint",   httpMethod = "GET"  )
+	public void fortunesPostgres(HttpServerExchange exchange) throws Exception
+	{ 
+		
+
+ 		final List<Fortune> fortunes = new ArrayList<>();
+ 		 
+ 		  try (Connection connection = postgresService.getConnection();
+ 	             PreparedStatement statement = connection.prepareStatement(
+ 	                     "SELECT * FROM Fortune",
+ 	                     ResultSet.TYPE_FORWARD_ONLY,
+ 	                     ResultSet.CONCUR_READ_ONLY);
+ 	             ResultSet resultSet = statement.executeQuery()) {
+ 	            while (resultSet.next()) {
+ 	                fortunes.add(new Fortune(
+ 	                        resultSet.getInt("id"),
+ 	                        resultSet.getString("message")));
+ 	            }
+ 	        }
+ 
+ 		 fortunes.add(new Fortune(0, "Additional fortune added at request time."));
+         Collections.sort(fortunes); 
+		 
+		 response( views.Fortunes.template(fortunes).render().toString()).contentType(HTML_UTF8_TYPE).send(exchange);
+		  
+	}
+
+	
+	@GET
+	@Path("/plaintext")
+	@Produces((MediaType.TEXT_PLAIN)) 
+	@ApiOperation(value = "Plaintext endpoint",   httpMethod = "GET" )
+	public void plaintext(HttpServerExchange exchange)
+	{ 
+		response("Hello, World!").contentType(PLAINTEXT_TYPE).send(exchange);
+
+	}
+	
+	
+	@GET
+	@Path("/json")
+	@ApiOperation(value = "Json serialization endpoint",   httpMethod = "GET" )
+	public void json(HttpServerExchange exchange)
+	{ 
+		response( JsonStream.serializeToBytes(ImmutableMap.of("message", "Hello, World!")) ).applicationJson().send(exchange);
+	}
+}

+ 75 - 0
frameworks/Java/proteus/src/main/java/io/sinistral/models/Fortune.java

@@ -0,0 +1,75 @@
+/**
+ * 
+ */
+package io.sinistral.models;
+
+import java.util.Random;
+
+/**
+ * @author jbauer
+ *
+ */
+public class Fortune implements Comparable<Fortune> {
+
+	public static final String[] TMP_MESSAGES = new String[]{
+			"A bad random number generator: 1, 1, 1, 1, 1, 4.33e+67, 1, 1, 1",
+			"A computer program does what you tell it to do, not what you want it to do.",
+			"A list is only as strong as its weakest link. — Donald Knuth",
+			"A computer scientist is someone who fixes things that aren&apos;t broken.",
+			"&lt;script&gt;alert(&quot;This should not be displayed in a browser alert box.&quot;);&lt;/script&gt;",
+			"Additional fortune added at request time.",
+			"Emacs is a nice operating system, but I prefer UNIX. — Tom Christaensen",
+			"フレームワークのベンチマーク",
+			"Computers make very fast, very accurate mistakes."
+	};
+	
+	public static final Random random = new Random();
+	
+	public static Fortune generate()
+	{
+		return new Fortune( random.nextInt(10000)+1, TMP_MESSAGES[random.nextInt(TMP_MESSAGES.length-1)] );
+	}
+	
+	
+	public int id;
+	public String message;
+
+	public Fortune() {
+	}
+
+	public Fortune(int id, String message) {
+		this.id = id;
+		this.message = message;
+	}
+
+	public int getId() {
+		return id;
+	}
+
+	public Fortune setId(int id) {
+		this.id = id;
+		return this;
+	}
+
+	public String getMessage() {
+		return message;
+	}
+
+	public Fortune setMessage(String message) {
+		this.message = message;
+		return this;
+	}
+
+	@Override
+	public String toString() {
+		return "Fortune{" +
+			"id=" + id +
+			", message='" + message + '\'' +
+			'}';
+	}
+
+	public int compareTo(Fortune o) {
+		return this.message.compareTo(o.message);
+	}
+
+}

+ 40 - 0
frameworks/Java/proteus/src/main/java/io/sinistral/models/World.java

@@ -0,0 +1,40 @@
+/**
+ * 
+ */
+package io.sinistral.models;
+
+import com.jsoniter.output.JsonStream;
+import com.jsoniter.output.StreamImplNumber;
+import com.jsoniter.output.StreamImplString;
+
+/**
+ * @author jbauer
+ *
+ */
+public final class World {
+    public int id;
+    public int randomNumber;
+
+    /**
+     * Constructs a new world object with the given parameters.
+     *
+     * @param id the ID of the world
+     * @param randomNumber the random number of the world
+     */
+    public World(int id, int randomNumber) {
+        this.id = id;
+        this.randomNumber = randomNumber;
+    }
+
+ 
+    public void serialize(JsonStream stream) throws Exception {
+        stream.writeByte(JsonStream.OBJECT_START);
+        StreamImplString.writeStringWithoutQuote(stream,"\"id\":");
+        StreamImplNumber.writeInt(stream,this.id);
+        stream.writeByte(JsonStream.COMMA);
+
+        StreamImplString.writeStringWithoutQuote(stream,",\"randomNumber\":");
+        StreamImplNumber.writeInt(stream,this.randomNumber);
+        stream.writeByte(JsonStream.OBJECT_END);
+    }
+}

+ 142 - 0
frameworks/Java/proteus/src/main/java/io/sinistral/services/MySqlService.java

@@ -0,0 +1,142 @@
+/**
+ * 
+ */
+package io.sinistral.services;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.inject.Binder;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import com.google.inject.name.Named;
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+
+import io.sinistral.proteus.services.BaseService;
+
+/**
+ * @author jbauer
+ *
+ */
+@Singleton
+public class MySqlService   extends BaseService 
+{
+
+	private static Logger log = LoggerFactory.getLogger(MySqlService.class.getCanonicalName());
+
+	@Inject
+	@Named("mysql.hikaricp.jdbcUrl")
+	protected String jdbcUrl;
+
+	@Inject
+	@Named("mysql.hikaricp.username")
+	protected String username;
+	
+	@Inject
+	@Named("mysql.hikaricp.password")
+	protected String password;
+	
+	@Inject
+	@Named("mysql.hikaricp.dataSourceClassName")
+	protected String dataSourceClassName;
+	
+	@Inject
+	@Named("mysql.hikaricp.ds.databaseName")
+	protected String databaseName;
+	
+	@Inject
+	@Named("mysql.hikaricp.ds.serverName")
+	protected String serverName;
+	
+	@Inject
+	@Named("mysql.hikaricp.ds.portNumber")
+	protected Integer portNumber;
+	
+	@Inject
+	@Named("mysql.hikaricp.maximumPoolSize")
+	protected Integer maximumPoolSize;
+	
+	@Inject
+	@Named("mysql.hikaricp.minimumIdle")
+	protected Integer minimumIdle;
+	
+	@Inject
+	@Named("mysql.hikaricp.ds.useServerPrepStmts")
+	protected Boolean useServerPrepStmts;
+	
+	@Inject
+	@Named("mysql.hikaricp.ds.cachePrepStmts")
+	protected Boolean cachePrepStmts;
+	
+	@Inject
+	@Named("mysql.hikaricp.ds.cacheCallableStmts")
+	protected Boolean useCacheCallableStmts;
+	
+	@Inject
+	@Named("mysql.hikaricp.ds.prepStmtCacheSize")
+	protected Integer prepStmtCacheSize;
+	
+	@Inject
+	@Named("mysql.hikaricp.ds.prepStmtCacheSqlLimit")
+	protected Integer prepStmtCacheSqlLimit;
+	
+	
+	protected HikariDataSource ds;
+	
+	
+	
+	/* (non-Javadoc)
+	 * @see io.sinistral.proteus.services.BaseService#configure(com.google.inject.Binder)
+	 */
+
+	public void configure(Binder binder)
+	{
+		log.debug("Binding " + this.getClass().getSimpleName());
+		binder.bind(MySqlService.class).toInstance(this);
+	}
+
+	@Override
+	protected void startUp() throws Exception
+	{
+		super.startUp();
+ 		
+		HikariConfig config = new HikariConfig();
+		 
+		config.setJdbcUrl(jdbcUrl);
+		config.setUsername(username);
+		config.setPassword(password); 
+		config.setMinimumIdle(minimumIdle);
+		config.setMaximumPoolSize(maximumPoolSize);
+		config.addDataSourceProperty("databaseName", databaseName);
+		config.addDataSourceProperty("useSSL",false);
+		config.addDataSourceProperty("UseServerPrepStmts", useServerPrepStmts);
+		config.addDataSourceProperty("CachePrepStmts", cachePrepStmts);
+		config.addDataSourceProperty("CacheCallableStmts", useCacheCallableStmts);
+		config.addDataSourceProperty("PrepStmtCacheSize", prepStmtCacheSize);
+		config.addDataSourceProperty("PrepStmtCacheSqlLimit", prepStmtCacheSqlLimit);
+
+		this.ds = new HikariDataSource(config);
+		
+		log.info( this.getClass().getSimpleName() + " started with ds: " + ds);  
+	}
+	
+	public Connection getConnection() throws SQLException
+	{
+		return this.ds.getConnection();
+	}
+
+	
+	@Override
+	protected void shutDown() throws Exception
+	{
+		super.shutDown();
+
+		this.ds.close();
+ 	}
+
+}

+ 103 - 0
frameworks/Java/proteus/src/main/java/io/sinistral/services/PostgresService.java

@@ -0,0 +1,103 @@
+/**
+ * 
+ */
+package io.sinistral.services;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.inject.Binder;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import com.google.inject.name.Named;
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+
+import io.sinistral.proteus.services.BaseService;
+
+/**
+ * @author jbauer
+ *
+ */
+@Singleton
+public class PostgresService   extends BaseService 
+{
+
+	private static Logger log = LoggerFactory.getLogger(PostgresService.class.getCanonicalName());
+
+	@Inject
+	@Named("postgres.hikaricp.jdbcUrl")
+	protected String jdbcUrl;
+
+	@Inject
+	@Named("postgres.hikaricp.username")
+	protected String username;
+	
+	@Inject
+	@Named("postgres.hikaricp.password")
+	protected String password;
+	
+	@Inject
+	@Named("postgres.hikaricp.maximumPoolSize")
+	protected Integer maximumPoolSize;
+	
+	@Inject
+	@Named("postgres.hikaricp.minimumIdle")
+	protected Integer minimumIdle;
+	
+	@Inject
+	@Named("postgres.hikaricp.description")
+	protected String description;
+
+	protected HikariDataSource ds;
+	
+	
+	
+	/* (non-Javadoc)
+	 * @see io.sinistral.proteus.services.BaseService#configure(com.google.inject.Binder)
+	 */
+
+	public void configure(Binder binder)
+	{
+		log.debug("Binding " + this.getClass().getSimpleName());
+		binder.bind(PostgresService.class).toInstance(this);
+	}
+
+	@Override
+	protected void startUp() throws Exception
+	{
+		super.startUp();
+ 		
+		HikariConfig config = new HikariConfig();
+		
+		
+		config.setJdbcUrl(jdbcUrl);
+		config.setUsername(username);
+		config.setPassword(password); 
+		config.setMinimumIdle(minimumIdle);
+		config.setMaximumPoolSize(maximumPoolSize);
+
+		this.ds = new HikariDataSource(config);
+		
+		log.info( this.getClass().getSimpleName() + " started with ds: " + ds); 
+	}
+	
+	public Connection getConnection() throws SQLException
+	{
+		return this.ds.getConnection();
+	}
+
+	
+	@Override
+	protected void shutDown() throws Exception
+	{
+		super.shutDown();
+
+		this.ds.close();
+ 	}
+
+}

+ 138 - 0
frameworks/Java/proteus/src/main/java/views/Fortunes.java

@@ -0,0 +1,138 @@
+package views;
+
+import java.io.IOException;
+import com.fizzed.rocker.ForIterator;
+import com.fizzed.rocker.RenderingException;
+import com.fizzed.rocker.RockerContent;
+import com.fizzed.rocker.RockerOutput;
+import com.fizzed.rocker.runtime.DefaultRockerTemplate;
+import com.fizzed.rocker.runtime.PlainTextUnloadedClassLoader;
+// import @ [1:1]
+import io.sinistral.models.*;
+// import @ [2:1]
+import java.util.List;
+
+/*
+ * Auto generated code to render template main/java/views/Fortunes.rocker.html
+ * Do not edit this file. Changes will eventually be overwritten by Rocker parser!
+ */
+public class Fortunes extends com.fizzed.rocker.runtime.DefaultRockerModel {
+
+    static public final com.fizzed.rocker.ContentType CONTENT_TYPE = com.fizzed.rocker.ContentType.HTML;
+    static public final String TEMPLATE_NAME = "Fortunes.rocker.html";
+    static public final String TEMPLATE_PACKAGE_NAME = "views";
+    static public final String HEADER_HASH = "331039920";
+    static public final long MODIFIED_AT = 1493423796000L;
+    static public final String[] ARGUMENT_NAMES = { "items" };
+
+    // argument @ [3:2]
+    private List<Fortune> items;
+
+    public Fortunes items(List<Fortune> items) {
+        this.items = items;
+        return this;
+    }
+
+    public List<Fortune> items() {
+        return this.items;
+    }
+
+    static public Fortunes template(List<Fortune> items) {
+        return new Fortunes()
+            .items(items);
+    }
+
+    @Override
+    protected DefaultRockerTemplate buildTemplate() throws RenderingException {
+        // optimized for convenience (runtime auto reloading enabled if rocker.reloading=true)
+        return com.fizzed.rocker.runtime.RockerRuntime.getInstance().getBootstrap().template(this.getClass(), this);
+    }
+
+    static public class Template extends com.fizzed.rocker.runtime.DefaultRockerTemplate {
+
+        // <!DOCTYPE html>\n<html>\n<head><title>Fortunes</title></head>\n<body><table>\n<tr><th>id</th><th>message</th></tr>\n
+        static private final byte[] PLAIN_TEXT_0_0;
+        // \t<tr><td>
+        static private final byte[] PLAIN_TEXT_1_0;
+        // </td><td>
+        static private final byte[] PLAIN_TEXT_2_0;
+        // </td></tr>\n
+        static private final byte[] PLAIN_TEXT_3_0;
+        // </table></body>\n</html>
+        static private final byte[] PLAIN_TEXT_4_0;
+
+        static {
+            PlainTextUnloadedClassLoader loader = PlainTextUnloadedClassLoader.tryLoad(Fortunes.class.getClassLoader(), Fortunes.class.getName() + "$PlainText", "UTF-8");
+            PLAIN_TEXT_0_0 = loader.tryGet("PLAIN_TEXT_0_0");
+            PLAIN_TEXT_1_0 = loader.tryGet("PLAIN_TEXT_1_0");
+            PLAIN_TEXT_2_0 = loader.tryGet("PLAIN_TEXT_2_0");
+            PLAIN_TEXT_3_0 = loader.tryGet("PLAIN_TEXT_3_0");
+            PLAIN_TEXT_4_0 = loader.tryGet("PLAIN_TEXT_4_0");
+        }
+
+        // argument @ [3:2]
+        protected final List<Fortune> items;
+
+        public Template(Fortunes model) {
+            super(model);
+            __internal.setCharset("UTF-8");
+            __internal.setContentType(CONTENT_TYPE);
+            __internal.setTemplateName(TEMPLATE_NAME);
+            __internal.setTemplatePackageName(TEMPLATE_PACKAGE_NAME);
+            this.items = model.items();
+        }
+
+        @Override
+        protected void __doRender() throws IOException, RenderingException {
+            // PlainText @ [3:28]
+            __internal.aboutToExecutePosInTemplate(3, 28);
+            __internal.writeValue(PLAIN_TEXT_0_0);
+            // ForBlockBegin @ [9:1]
+            __internal.aboutToExecutePosInTemplate(9, 1);
+            try {
+                final com.fizzed.rocker.runtime.CollectionForIterator<Fortune> __forIterator0 = new com.fizzed.rocker.runtime.CollectionForIterator<Fortune>(items);
+                while (__forIterator0.hasNext()) {
+                    final com.fizzed.rocker.ForIterator i = __forIterator0;
+                    final Fortune item = __forIterator0.next();
+                    try {
+                        // PlainText @ [9:47]
+                        __internal.aboutToExecutePosInTemplate(9, 47);
+                        __internal.writeValue(PLAIN_TEXT_1_0);
+                        // ValueExpression @ [10:10]
+                        __internal.aboutToExecutePosInTemplate(10, 10);
+                        __internal.renderValue(item.id, false);
+                        // PlainText @ [10:18]
+                        __internal.aboutToExecutePosInTemplate(10, 18);
+                        __internal.writeValue(PLAIN_TEXT_2_0);
+                        // ValueExpression @ [10:27]
+                        __internal.aboutToExecutePosInTemplate(10, 27);
+                        __internal.renderValue(item.message, false);
+                        // PlainText @ [10:40]
+                        __internal.aboutToExecutePosInTemplate(10, 40);
+                        __internal.writeValue(PLAIN_TEXT_3_0);
+                        // ForBlockEnd @ [9:1]
+                        __internal.aboutToExecutePosInTemplate(9, 1);
+                    } catch (com.fizzed.rocker.runtime.ContinueException e) {
+                        // support for continuing for loops
+                    }
+                } // for end @ [9:1]
+            } catch (com.fizzed.rocker.runtime.BreakException e) {
+                // support for breaking for loops
+            }
+            // PlainText @ [11:2]
+            __internal.aboutToExecutePosInTemplate(11, 2);
+            __internal.writeValue(PLAIN_TEXT_4_0);
+        }
+    }
+
+    private static class PlainText {
+
+        static private final String PLAIN_TEXT_0_0 = "<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>";
+        static private final String PLAIN_TEXT_1_0 = "<tr><td>";
+        static private final String PLAIN_TEXT_2_0 = "</td><td>";
+        static private final String PLAIN_TEXT_3_0 = "</td></tr>";
+        static private final String PLAIN_TEXT_4_0 = "</table></body></html>";
+
+    }
+
+}

+ 13 - 0
frameworks/Java/proteus/src/main/java/views/Fortunes.rocker.html

@@ -0,0 +1,13 @@
+@import io.sinistral.models.*
+@import java.util.List
+@args (List<Fortune> items)
+<!DOCTYPE html>
+<html>
+<head><title>Fortunes</title></head>
+<body><table>
+<tr><th>id</th><th>message</th></tr>
+@for ((ForIterator i, Fortune item) : items) {
+	<tr><td>@item.id</td><td>@item.message</td></tr>
+}
+</table></body>
+</html>

+ 6 - 0
frameworks/Java/proteus/src/main/resources/templates/Fortunes.html

@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html> <head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>
+{% for item in items %}
+<tr><td>{{ item.id }}</td><td>{{ item.message }}</td></tr> 
+{% endfor %}
+</table></body></html>

+ 38 - 0
frameworks/Java/proteus/src/test/java/io/sinistral/AppTest.java

@@ -0,0 +1,38 @@
+package io.sinistral;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Unit test for simple ExampleApplication.
+ */
+public class AppTest 
+    extends TestCase
+{
+    /**
+     * Create the test case
+     *
+     * @param testName name of the test case
+     */
+    public AppTest( String testName )
+    {
+        super( testName );
+    }
+
+    /**
+     * @return the suite of tests being tested
+     */
+    public static Test suite()
+    {
+        return new TestSuite( AppTest.class );
+    }
+
+    /**
+     * Rigourous Test :-)
+     */
+    public void testApp()
+    {
+        assertTrue( true );
+    }
+}