Browse Source

Updated per review. (#3050)

noboomu 7 years ago
parent
commit
c40fa801ed

+ 1 - 1
frameworks/Java/proteus/README.md

@@ -12,4 +12,4 @@ This project consists of several tests:
 
 
 * All are defined in : `Benchmarks.java` 
 * All are defined in : `Benchmarks.java` 
 
 
-All configuration is done thorugh the `application.conf` configuration file
+All configuration is done thorugh the `application.conf` configuration file

+ 1 - 1
frameworks/Java/proteus/benchmark_config.json

@@ -44,4 +44,4 @@
       "versus": ""
       "versus": ""
     }
     }
   }]
   }]
-}
+}

+ 13 - 28
frameworks/Java/proteus/conf/application.conf

@@ -13,8 +13,11 @@ application {
   host = "0.0.0.0"
   host = "0.0.0.0"
 
 
   # HTTP ports
   # HTTP ports
-  port = 8080
- 
+
+  ports
+  { 
+    http = 8080
+  }
 }
 }
 
 
 application.tmpdir=${java.io.tmpdir}
 application.tmpdir=${java.io.tmpdir}
@@ -26,7 +29,7 @@ globalHeaders
 #  Access-Control-Allow-Origin: "*"
 #  Access-Control-Allow-Origin: "*"
 #  Access-Control-Allow-Methods: "*"
 #  Access-Control-Allow-Methods: "*"
 #  Access-Control-Allow-Headers: "*"
 #  Access-Control-Allow-Headers: "*"
-  Server: ${application.name}
+  Server: "P"
 }
 }
 
 
 assets {
 assets {
@@ -43,30 +46,13 @@ mysql
 {
 {
    hikaricp 
    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
+        jdbcUrl= "jdbc:mysql://TFB-database:3306/hello_world?useServerPrepStmts=true&cachePrepStmts=true",
+
+        #jdbcUrl= "jdbc:mysql://TFB-database:3306/hello_world?useServerPrepStmts=true&cachePrepStmts=true&useUnicode=true",
+        maximumPoolSize=48
         username = benchmarkdbuser
         username = benchmarkdbuser
         password = benchmarkdbpass
         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
-        }
+     
   }
   }
 }
 }
 
 
@@ -75,9 +61,8 @@ postgres
    hikaricp 
    hikaricp 
    {
    {
         description="postgresql server"
         description="postgresql server"
-        jdbcUrl= "jdbc:postgresql://127.0.0.1:5432/hello_world",
-        maximumPoolSize=256
-        minimumIdle=128
+        jdbcUrl= "jdbc:postgresql://TFB-database:5432/hello_world",
+        maximumPoolSize=48
         username = benchmarkdbuser
         username = benchmarkdbuser
         password = benchmarkdbpass
         password = benchmarkdbpass
   }
   }

+ 15 - 13
frameworks/Java/proteus/pom.xml

@@ -49,7 +49,7 @@
 										<execute />
 										<execute />
 									</action>
 									</action>
 								</pluginExecution>
 								</pluginExecution>
-								<!-- <pluginExecution>
+								 <pluginExecution>
 									<pluginExecutionFilter>
 									<pluginExecutionFilter>
 										<groupId>com.fizzed</groupId>
 										<groupId>com.fizzed</groupId>
 										<artifactId>rocker-maven-plugin</artifactId>
 										<artifactId>rocker-maven-plugin</artifactId>
@@ -63,7 +63,7 @@
 											<runOnIncremental>true</runOnIncremental>
 											<runOnIncremental>true</runOnIncremental>
 										</execute>
 										</execute>
 									</action>
 									</action>
-								</pluginExecution> -->
+								</pluginExecution> 
 							</pluginExecutions>
 							</pluginExecutions>
 						</lifecycleMappingMetadata>
 						</lifecycleMappingMetadata>
 					</configuration>
 					</configuration>
@@ -137,7 +137,7 @@
 					<executable>java</executable>
 					<executable>java</executable>
 					<arguments>
 					<arguments>
 						<argument>-Dlogback.configurationFile=${project.build.directory}/conf/logback.xml</argument>
 						<argument>-Dlogback.configurationFile=${project.build.directory}/conf/logback.xml</argument>
-						<argument>-Xbootclasspath/p:lib/alpn-boot-8.1.11.v20170118.jar</argument>
+						<argument>-Dconfig.file=${project.build.directory}/conf/application.conf</argument> 
 						<argument>-Xms1g</argument>
 						<argument>-Xms1g</argument>
 						<argument>-Xmx2g</argument>
 						<argument>-Xmx2g</argument>
 						<argument>-XX:+AggressiveOpts</argument>
 						<argument>-XX:+AggressiveOpts</argument>
@@ -153,6 +153,7 @@
 
 
 				</configuration>
 				</configuration>
 			</plugin>
 			</plugin>
+			
 			<plugin>
 			<plugin>
 				<artifactId>maven-dependency-plugin</artifactId>
 				<artifactId>maven-dependency-plugin</artifactId>
 				<executions>
 				<executions>
@@ -182,6 +183,7 @@
 					</archive>
 					</archive>
 				</configuration>
 				</configuration>
 			</plugin>
 			</plugin>
+			 
 		  <!-- 	<plugin>
 		  <!-- 	<plugin>
 				<groupId>com.fizzed</groupId>
 				<groupId>com.fizzed</groupId>
 				<artifactId>rocker-maven-plugin</artifactId>
 				<artifactId>rocker-maven-plugin</artifactId>
@@ -209,16 +211,11 @@
 			<version>4.12</version>
 			<version>4.12</version>
 			<scope>test</scope>
 			<scope>test</scope>
 		</dependency>
 		</dependency>
-		<dependency>
-			<groupId>org.mortbay.jetty.alpn</groupId>
-			<artifactId>alpn-boot</artifactId>
-			<version>8.1.11.v20170118</version>
-			<scope>runtime</scope>
-		</dependency>
+
 		<dependency>
 		<dependency>
 			<groupId>io.sinistral</groupId>
 			<groupId>io.sinistral</groupId>
 			<artifactId>proteus-core</artifactId>
 			<artifactId>proteus-core</artifactId>
-			<version>0.1.2-SNAPSHOT</version>
+			<version>0.1.7.2-SNAPSHOT</version>
 			
 			
 		</dependency>
 		</dependency>
 			<dependency>
 			<dependency>
@@ -232,7 +229,12 @@
 			<version>0.18.0</version>
 			<version>0.18.0</version>
 			<scope>provided</scope>
 			<scope>provided</scope>
 		</dependency>
 		</dependency>
-		 
+		 <dependency>
+      <groupId>com.github.spullara.mustache.java</groupId>
+      <artifactId>compiler</artifactId>
+      <version>0.9.4</version>
+    </dependency>
+
 		<dependency>
 		<dependency>
 			<groupId>com.zaxxer</groupId>
 			<groupId>com.zaxxer</groupId>
 			<artifactId>HikariCP</artifactId>
 			<artifactId>HikariCP</artifactId>
@@ -243,11 +245,11 @@
 			<artifactId>mysql-connector-java</artifactId>
 			<artifactId>mysql-connector-java</artifactId>
 			<version>5.1.41</version>
 			<version>5.1.41</version>
 		</dependency>
 		</dependency>
-		<dependency>
+	  	<dependency>
 			<groupId>org.postgresql</groupId>
 			<groupId>org.postgresql</groupId>
 			<artifactId>postgresql</artifactId>
 			<artifactId>postgresql</artifactId>
 			<version>9.4.1212</version>
 			<version>9.4.1212</version>
-		</dependency>
+		</dependency>  
 	</dependencies>
 	</dependencies>
 	<properties>
 	<properties>
 		<rocker.version>0.18.0</rocker.version>
 		<rocker.version>0.18.0</rocker.version>

+ 2 - 4
frameworks/Java/proteus/setup.sh

@@ -1,10 +1,8 @@
 #!/bin/bash
 #!/bin/bash
 
 
 fw_depends postgresql mysql java maven
 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
 mvn -U clean package
 cd target
 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 &
+java -Dlogback.configurationFile="conf/logback.xml" -Dconfig.file="conf/application.conf" -server  -Xms1g -Xmx2g -classpath "./proteus-techempower-1.0.0.jar:lib/*" io.sinistral.ExampleApplication 

+ 31 - 8
frameworks/Java/proteus/src/main/java/io/sinistral/ExampleApplication.java

@@ -1,22 +1,41 @@
 package io.sinistral;
 package io.sinistral;
 
 
-import java.io.File;
-import java.nio.file.Files;
-import java.nio.file.Path;
+import static io.undertow.util.Headers.CONTENT_TYPE;
+
+import java.nio.ByteBuffer;
+
+import com.jsoniter.output.EncodingMode;
+import com.jsoniter.output.JsonStream;
 
 
 import io.sinistral.controllers.Benchmarks;
 import io.sinistral.controllers.Benchmarks;
+import io.sinistral.models.Message;
 import io.sinistral.proteus.ProteusApplication;
 import io.sinistral.proteus.ProteusApplication;
 import io.sinistral.proteus.services.AssetsService;
 import io.sinistral.proteus.services.AssetsService;
 import io.sinistral.proteus.services.SwaggerService;
 import io.sinistral.proteus.services.SwaggerService;
 import io.sinistral.services.MySqlService;
 import io.sinistral.services.MySqlService;
 import io.sinistral.services.PostgresService;
 import io.sinistral.services.PostgresService;
+import io.undertow.Handlers;
+import io.undertow.Undertow;
+import io.undertow.UndertowOptions;
+import io.undertow.server.HttpHandler;
+import io.undertow.server.HttpServerExchange;
+import io.undertow.server.handlers.PathHandler;
+import io.undertow.server.handlers.SetHeaderHandler;
+import io.undertow.util.Headers;
+
 
 
-/**
- * Hello world!
- *
- */
 public class ExampleApplication extends ProteusApplication
 public class ExampleApplication extends ProteusApplication
 {
 {
+ 
+    static {
+   
+    	
+    	JsonStream.setMode(EncodingMode.STATIC_MODE);
+    	 
+    	 
+    }
+    
+    
     public static void main( String[] args )
     public static void main( String[] args )
     {
     {
         
         
@@ -33,6 +52,10 @@ public class ExampleApplication extends ProteusApplication
 		app.addController(Benchmarks.class);  
 		app.addController(Benchmarks.class);  
 		
 		
 		app.start();
 		app.start();
-        
+		
+		 
+		
+	 
     }
     }
+    
 }
 }

+ 177 - 95
frameworks/Java/proteus/src/main/java/io/sinistral/controllers/Benchmarks.java

@@ -3,13 +3,14 @@
  */
  */
 package io.sinistral.controllers;
 package io.sinistral.controllers;
 
 
-import static io.sinistral.proteus.server.ServerResponse.response;
+import static io.undertow.util.Headers.CONTENT_TYPE;
 
 
+import java.io.ByteArrayOutputStream;
+import java.nio.ByteBuffer;
 import java.sql.Connection;
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.ResultSet;
 import java.util.ArrayList;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 import java.util.List;
 import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.ThreadLocalRandom;
 
 
@@ -19,20 +20,29 @@ import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MediaType;
 
 
-import com.google.common.collect.ImmutableMap;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.module.afterburner.AfterburnerModule;
+import com.fizzed.rocker.runtime.StringBuilderOutput;
+import com.github.mustachejava.DefaultMustacheFactory;
+import com.github.mustachejava.MustacheFactory;
 import com.google.inject.Inject;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import com.google.inject.Singleton;
+import com.jsoniter.output.EncodingMode;
 import com.jsoniter.output.JsonStream;
 import com.jsoniter.output.JsonStream;
 
 
 import io.sinistral.models.Fortune;
 import io.sinistral.models.Fortune;
 import io.sinistral.models.Message;
 import io.sinistral.models.Message;
+import io.sinistral.models.MessageEncoder;
 import io.sinistral.models.World;
 import io.sinistral.models.World;
+import io.sinistral.models.WorldEncoder;
+import io.sinistral.proteus.annotations.Blocking;
 import io.sinistral.services.MySqlService;
 import io.sinistral.services.MySqlService;
 import io.sinistral.services.PostgresService;
 import io.sinistral.services.PostgresService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiOperation;
-import io.undertow.server.HttpHandler;
 import io.undertow.server.HttpServerExchange;
 import io.undertow.server.HttpServerExchange;
+import io.undertow.util.Headers;
 
 
 /**
 /**
  * Much of this borrowed with reverence from Light-Java
  * Much of this borrowed with reverence from Light-Java
@@ -48,77 +58,125 @@ import io.undertow.server.HttpServerExchange;
 @Singleton
 @Singleton
 public class Benchmarks
 public class Benchmarks
 {
 {
-	private static final String HTML_UTF8_TYPE = io.sinistral.proteus.server.MediaType.TEXT_HTML_UTF8.toString();
-	private static final String PLAINTEXT_TYPE = io.sinistral.proteus.server.MediaType.TEXT_PLAIN.toString();
-	
-	@Inject 
-	protected MySqlService sqlService;
+	private static final String HTML_UTF8_TYPE = io.sinistral.proteus.server.MediaType.TEXT_HTML_UTF8.toString(); 
+	private static final ByteBuffer MESSAGE_BUFFER;
+    private static final String MESSAGE = "Hello, World!";
+    
+	private static final ObjectMapper DEFAULT_MAPPER = new ObjectMapper();
+ 
+     
+
+ 
+    static {
+    	MESSAGE_BUFFER = ByteBuffer.allocateDirect(MESSAGE.length());
+    	try {
+    		MESSAGE_BUFFER.put(MESSAGE.getBytes("US-ASCII"));
+     } catch (Exception e) {
+    	 throw new RuntimeException(e);
+     	}
+    	MESSAGE_BUFFER.flip();
+    	
+    	JsonStream.setMode(EncodingMode.STATIC_MODE);
+    	 
+    	
+    	JsonStream.registerNativeEncoder(Message.class, new MessageEncoder());
+    	JsonStream.registerNativeEncoder(World.class, new WorldEncoder());
+    	
+    	DEFAULT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+    	DEFAULT_MAPPER.configure(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT, true);
+    	DEFAULT_MAPPER.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
+    	DEFAULT_MAPPER.configure(DeserializationFeature.EAGER_DESERIALIZER_FETCH,true); 
+    	DEFAULT_MAPPER.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
+    	
+    	DEFAULT_MAPPER.registerModule(new AfterburnerModule()); 
+
+    }
+    
+    
+ 
+	protected final MySqlService sqlService;
 	
 	
-	@Inject 
-	protected PostgresService postgresService;
+	 
+	protected final PostgresService postgresService;
 	
 	
    
    
     public static int randomWorld() {
     public static int randomWorld() {
         return 1 + ThreadLocalRandom.current().nextInt(10000);
         return 1 + ThreadLocalRandom.current().nextInt(10000);
     }
     }
 
 
- 
+    @Inject
+    public Benchmarks(PostgresService postgresService, MySqlService sqlService)
+    {
+    	this.sqlService = sqlService;
+    	this.postgresService = postgresService;
+    }
 	
 	
 	
 	
 	@GET
 	@GET
 	@Path("/db/postgres")
 	@Path("/db/postgres")
+	@Blocking
 	@ApiOperation(value = "World postgres db endpoint",   httpMethod = "GET" , response = World.class)
 	@ApiOperation(value = "World postgres db endpoint",   httpMethod = "GET" , response = World.class)
 	public void dbPostgres(HttpServerExchange exchange)
 	public void dbPostgres(HttpServerExchange exchange)
 	{ 		
 	{ 		
 		final World world;
 		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();
-        }
+		try (final Connection connection = postgresService.getConnection())
+		{
+			try (PreparedStatement statement = connection.prepareStatement("SELECT * FROM world WHERE id = ?"))
+			{
+				statement.setInt(1, randomWorld());
+				try (ResultSet resultSet = statement.executeQuery())
+				{
+					resultSet.next();
+					int id = resultSet.getInt("id");
+					int randomNumber = resultSet.getInt("randomNumber");
+					world = new World(id, randomNumber);
+				}
+			}
+
+			
+			exchange.getResponseHeaders().put(io.undertow.util.Headers.CONTENT_TYPE, "application/json");
+			exchange.getResponseSender().send(JsonStream.serializeToBytes(world));
+
+		} catch (Exception e)
+		{
+			throw new IllegalArgumentException();
+		}
 		  
 		  
  		 
  		 
 	}
 	}
 	
 	
+	
 	@GET
 	@GET
 	@Path("/db/mysql")
 	@Path("/db/mysql")
+	@Blocking
 	@ApiOperation(value = "World mysql db endpoint",   httpMethod = "GET" , response = World.class)
 	@ApiOperation(value = "World mysql db endpoint",   httpMethod = "GET" , response = World.class)
 	public void dbMySql(HttpServerExchange exchange)
 	public void dbMySql(HttpServerExchange exchange)
 	{ 		
 	{ 		
 		final World world;
 		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();
-        }
+		try (final Connection connection = sqlService.getConnection())
+		{
+			try (PreparedStatement statement = connection.prepareStatement("SELECT id,randomNumber FROM world WHERE id = ?"))
+			{
+				statement.setInt(1, randomWorld());
+				try (ResultSet resultSet = statement.executeQuery())
+				{
+					resultSet.next();
+					world = new World(resultSet.getInt("id"), resultSet.getInt("randomNumber"));
+				}
+			}
+
+			exchange.getResponseHeaders().put(io.undertow.util.Headers.CONTENT_TYPE, "application/json");
+			ByteArrayOutputStream os = new  ByteArrayOutputStream(128);
+			WorldEncoder.encodeRaw(world, os); 
+			 
+			exchange.getResponseSender().send(ByteBuffer.wrap(os.toByteArray()));
+
+		} catch (Exception e)
+		{
+			throw new IllegalArgumentException();
+		}
 		  
 		  
  		 
  		 
 	}
 	}
@@ -128,80 +186,104 @@ public class Benchmarks
 	@GET
 	@GET
 	@Path("/fortunes/mysql")
 	@Path("/fortunes/mysql")
 	@Produces(MediaType.TEXT_HTML)
 	@Produces(MediaType.TEXT_HTML)
+	@Blocking
 	@ApiOperation(value = "Fortunes mysql endpoint",   httpMethod = "GET"  )
 	@ApiOperation(value = "Fortunes mysql endpoint",   httpMethod = "GET"  )
-	public void fortunesMysql(HttpServerExchange exchange, HttpHandler handler ) throws Exception
+	public void fortunesMysql(HttpServerExchange exchange  ) 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);
+		List<Fortune> fortunes = new ArrayList<>();
+	        
+			try (final Connection connection = postgresService.getConnection())
+			{
+				try (PreparedStatement statement = connection.prepareStatement("SELECT * FROM Fortune"))
+				{
+	
+					try (ResultSet resultSet = statement.executeQuery())
+					{
+						while (resultSet.next())
+						{
+							int id = resultSet.getInt("id");
+							String msg = resultSet.getString("message");
+	
+							fortunes.add(new Fortune(id, msg));
+						}
+					}
+				}
+			}
+			
+	        fortunes.add(new Fortune(0, "Additional fortune added at request time."));
+	        
+	        fortunes.sort(null);
+         
+	        final String render = views.Fortunes.template(fortunes).render(StringBuilderOutput.FACTORY).toString(); 
+	         
+	        exchange.getResponseHeaders().put(
+	                Headers.CONTENT_TYPE, HTML_UTF8_TYPE);
+	        exchange.getResponseSender().send(render);  
 		  
 		  
 	}
 	}
-	
 
 
+	
 	@GET
 	@GET
 	@Path("/fortunes/postgres")
 	@Path("/fortunes/postgres")
+	@Blocking
 	@Produces(MediaType.TEXT_HTML)
 	@Produces(MediaType.TEXT_HTML)
 	@ApiOperation(value = "Fortunes postgres endpoint",   httpMethod = "GET"  )
 	@ApiOperation(value = "Fortunes postgres endpoint",   httpMethod = "GET"  )
 	public void fortunesPostgres(HttpServerExchange exchange) throws Exception
 	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);
+			List<Fortune> fortunes = new ArrayList<>();
 		  
 		  
+			try (final Connection connection = postgresService.getConnection())
+			{
+				try (PreparedStatement statement = connection.prepareStatement("SELECT * FROM Fortune"))
+				{
+	
+					try (ResultSet resultSet = statement.executeQuery())
+					{
+						while (resultSet.next())
+						{
+							int id = resultSet.getInt("id");
+							String msg = resultSet.getString("message");
+	
+							fortunes.add(new Fortune(id, msg));
+						}
+					}
+				}
+			}
+			
+	        fortunes.add(new Fortune(0, "Additional fortune added at request time."));
+	        
+	        fortunes.sort(null);
+	        
+	        final String render = views.Fortunes.template(fortunes).render(StringBuilderOutput.FACTORY).toString(); 
+	         
+	        exchange.getResponseHeaders().put(
+	                Headers.CONTENT_TYPE, HTML_UTF8_TYPE);
+	        exchange.getResponseSender().send(render);  
 	}
 	}
-
 	
 	
+
 	@GET
 	@GET
 	@Path("/plaintext")
 	@Path("/plaintext")
-	@Produces((MediaType.TEXT_PLAIN)) 
 	@ApiOperation(value = "Plaintext endpoint",   httpMethod = "GET" )
 	@ApiOperation(value = "Plaintext endpoint",   httpMethod = "GET" )
 	public void plaintext(HttpServerExchange exchange)
 	public void plaintext(HttpServerExchange exchange)
 	{ 
 	{ 
-		response("Hello, World!").contentType(PLAINTEXT_TYPE).send(exchange);
-
+		   exchange.getResponseHeaders().put(CONTENT_TYPE, "text/plain");
+		    exchange.getResponseSender().send(MESSAGE_BUFFER.duplicate());
 	}
 	}
 	
 	
+  
 	
 	
 	@GET
 	@GET
 	@Path("/json")
 	@Path("/json")
 	@ApiOperation(value = "Json serialization endpoint",   httpMethod = "GET" )
 	@ApiOperation(value = "Json serialization endpoint",   httpMethod = "GET" )
 	public void json(HttpServerExchange exchange)
 	public void json(HttpServerExchange exchange)
 	{ 
 	{ 
-		response( JsonStream.serializeToBytes( new Message("hello, world!") ) ).applicationJson().send(exchange);
+		 exchange.getResponseHeaders().put(CONTENT_TYPE, "application/json");
+		 
+		 ByteArrayOutputStream os = new  ByteArrayOutputStream(128);
+		 MessageEncoder.encodeRaw(  new Message("Hello, World!"), os); 
+		 exchange.getResponseSender().send( ByteBuffer.wrap(os.toByteArray())   );
+		
 	}
 	}
 }
 }

+ 7 - 51
frameworks/Java/proteus/src/main/java/io/sinistral/models/Fortune.java

@@ -9,64 +9,20 @@ import java.util.Random;
  * @author jbauer
  * @author jbauer
  *
  *
  */
  */
-public class Fortune implements Comparable<Fortune> {
+public final 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 final int id;
+	  public final String message;
 
 
-	public Fortune() {
-	}
+ 
 
 
 	public Fortune(int id, String message) {
 	public Fortune(int id, String message) {
 		this.id = id;
 		this.id = id;
 		this.message = message;
 		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) {
 	public int compareTo(Fortune o) {
 		return this.message.compareTo(o.message);
 		return this.message.compareTo(o.message);

+ 1 - 0
frameworks/Java/proteus/src/main/java/io/sinistral/models/Message.java

@@ -18,3 +18,4 @@ public final class Message
 	
 	
 	
 	
 }
 }
+

+ 67 - 0
frameworks/Java/proteus/src/main/java/io/sinistral/models/MessageEncoder.java

@@ -0,0 +1,67 @@
+package io.sinistral.models;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MessageEncoder extends com.jsoniter.spi.EmptyEncoder
+{
+	private static Logger Logger = LoggerFactory.getLogger(MessageEncoder.class.getCanonicalName());
+
+	public void encode(Object obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException
+	{
+		if (obj == null)
+		{
+			stream.writeNull();
+			return;
+		}
+		stream.writeByte(com.jsoniter.output.JsonStream.OBJECT_START);
+		encode_((io.sinistral.models.Message) obj, stream);
+		stream.writeByte(com.jsoniter.output.JsonStream.OBJECT_END);
+	}
+
+	public static void encode_(io.sinistral.models.Message obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException
+	{
+		boolean notFirst = false;
+		if (obj.message != null)
+		{
+			if (notFirst)
+			{
+				stream.write(com.jsoniter.output.JsonStream.COMMA);
+			}
+			else
+			{
+				notFirst = true;
+			}
+			stream.writeRaw("\"message\":", 10);
+			stream.writeString((java.lang.String) obj.message);
+		}
+	}
+	
+	public static void encodeRaw(io.sinistral.models.Message obj, java.io.OutputStream stream)  
+	{
+		try
+		{
+			boolean notFirst = false;
+			if (obj.message != null)
+			{
+				if (notFirst)
+				{
+					stream.write(com.jsoniter.output.JsonStream.COMMA);
+				}
+				else
+				{
+					notFirst = true;
+				}
+				stream.write(com.jsoniter.output.JsonStream.OBJECT_START);
+				stream.write("\"message\":".getBytes());
+				stream.write(com.jsoniter.output.JsonStream.QUOTE); 
+				stream.write(obj.message.getBytes());
+				stream.write(com.jsoniter.output.JsonStream.QUOTE);
+				stream.write(com.jsoniter.output.JsonStream.OBJECT_END);
+			}
+		} catch (Exception e)
+		{
+			Logger.error(e.getMessage());
+		}
+	}
+}

+ 39 - 0
frameworks/Java/proteus/src/main/java/io/sinistral/models/ModelCodegenConfig.java

@@ -0,0 +1,39 @@
+/**
+ * 
+ */
+package io.sinistral.models;
+
+import java.util.List;
+import java.util.Map;
+
+import com.jsoniter.spi.CodegenConfig;
+import com.jsoniter.spi.TypeLiteral;
+
+/**
+ * @author jbauer
+ *
+ */
+public class ModelCodegenConfig implements CodegenConfig {
+
+    @Override
+    public void setup() {
+       
+    }
+
+    @Override
+    public TypeLiteral[] whatToCodegen() {
+        return new TypeLiteral[]{
+                // generic types, need to use this syntax
+                new TypeLiteral<List<Integer>>() {
+                },
+                new TypeLiteral<Map<String, Object>>() {
+                },
+                // array
+                TypeLiteral.create(World.class),
+                // object
+                TypeLiteral.create(Message.class)
+        };
+    }
+}
+
+ 

+ 3 - 2
frameworks/Java/proteus/src/main/java/io/sinistral/models/World.java

@@ -12,8 +12,9 @@ import com.jsoniter.output.StreamImplString;
  *
  *
  */
  */
 public final class World {
 public final class World {
-    public int id;
-    public int randomNumber;
+	
+    public final int id;
+    public final int randomNumber;
 
 
     /**
     /**
      * Constructs a new world object with the given parameters.
      * Constructs a new world object with the given parameters.

+ 46 - 0
frameworks/Java/proteus/src/main/java/io/sinistral/models/WorldEncoder.java

@@ -0,0 +1,46 @@
+package io.sinistral.models;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class WorldEncoder extends com.jsoniter.spi.EmptyEncoder
+{
+	private static Logger Logger = LoggerFactory.getLogger(WorldEncoder.class.getCanonicalName());
+ 
+	public void encode(Object obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException
+	{
+		if (obj == null)
+		{
+			stream.writeNull();
+			return;
+		}
+		stream.writeRaw("{\"id\":", 6);
+		encode_((io.sinistral.models.World) obj, stream);
+		stream.writeByte(com.jsoniter.output.JsonStream.OBJECT_END);
+	}
+
+	public static void encode_(io.sinistral.models.World obj, com.jsoniter.output.JsonStream stream) throws java.io.IOException
+	{
+		stream.writeVal((int) obj.id);
+		stream.writeRaw(",\"randomNumber\":", 16);
+		stream.writeVal((int) obj.randomNumber);
+	}
+	
+	public static void encodeRaw(io.sinistral.models.World obj, java.io.OutputStream stream)  
+	{
+		try
+		{ 
+				stream.write(com.jsoniter.output.JsonStream.OBJECT_START);
+				stream.write("\"id\":".getBytes());
+				stream.write(Integer.toString(obj.id).getBytes());
+				stream.write(com.jsoniter.output.JsonStream.COMMA);
+				stream.write("\"randomNumber\":".getBytes());
+				stream.write(Integer.toString(obj.randomNumber).getBytes());
+				stream.write(com.jsoniter.output.JsonStream.OBJECT_END);
+		 
+		} catch (Exception e)
+		{
+			Logger.error(e.getMessage());
+		}
+	}
+}

+ 7 - 55
frameworks/Java/proteus/src/main/java/io/sinistral/services/MySqlService.java

@@ -41,50 +41,11 @@ public class MySqlService   extends BaseService
 	@Named("mysql.hikaricp.password")
 	@Named("mysql.hikaricp.password")
 	protected String 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
 	@Inject
 	@Named("mysql.hikaricp.maximumPoolSize")
 	@Named("mysql.hikaricp.maximumPoolSize")
 	protected Integer 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;
 	protected HikariDataSource ds;
 	
 	
@@ -105,22 +66,13 @@ public class MySqlService   extends BaseService
 	{
 	{
 		super.startUp();
 		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();
 
 
-		this.ds = new HikariDataSource(config);
+		this.ds.setJdbcUrl(jdbcUrl);
+		this.ds.setUsername(username);
+		this.ds.setPassword(password);  
+		this.ds.setMaximumPoolSize(maximumPoolSize);
+ 
 		
 		
 		log.info( this.getClass().getSimpleName() + " started with ds: " + ds);  
 		log.info( this.getClass().getSimpleName() + " started with ds: " + ds);  
 	}
 	}

+ 8 - 15
frameworks/Java/proteus/src/main/java/io/sinistral/services/PostgresService.java

@@ -44,10 +44,7 @@ public class PostgresService   extends BaseService
 	@Inject
 	@Inject
 	@Named("postgres.hikaricp.maximumPoolSize")
 	@Named("postgres.hikaricp.maximumPoolSize")
 	protected Integer maximumPoolSize;
 	protected Integer maximumPoolSize;
-	
-	@Inject
-	@Named("postgres.hikaricp.minimumIdle")
-	protected Integer minimumIdle;
+	 
 	
 	
 	@Inject
 	@Inject
 	@Named("postgres.hikaricp.description")
 	@Named("postgres.hikaricp.description")
@@ -71,18 +68,14 @@ public class PostgresService   extends BaseService
 	protected void startUp() throws Exception
 	protected void startUp() throws Exception
 	{
 	{
 		super.startUp();
 		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);
+ 		 
 		
 		
+		this.ds = new HikariDataSource();
+ 		this.ds.setJdbcUrl(jdbcUrl);
+		this.ds.setUsername(username);
+		this.ds.setPassword(password);  
+		this.ds.setMaximumPoolSize(maximumPoolSize); 
+ 
 		log.info( this.getClass().getSimpleName() + " started with ds: " + ds); 
 		log.info( this.getClass().getSimpleName() + " started with ds: " + ds); 
 	}
 	}
 	
 	

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

@@ -13,7 +13,7 @@ import io.sinistral.models.*;
 import java.util.List;
 import java.util.List;
 
 
 /*
 /*
- * Auto generated code to render template main/java/views/Fortunes.rocker.html
+ * Auto generated code to render template views/Fortunes.rocker.html
  * Do not edit this file. Changes will eventually be overwritten by Rocker parser!
  * Do not edit this file. Changes will eventually be overwritten by Rocker parser!
  */
  */
 public class Fortunes extends com.fizzed.rocker.runtime.DefaultRockerModel {
 public class Fortunes extends com.fizzed.rocker.runtime.DefaultRockerModel {
@@ -22,7 +22,7 @@ public class Fortunes extends com.fizzed.rocker.runtime.DefaultRockerModel {
     static public final String TEMPLATE_NAME = "Fortunes.rocker.html";
     static public final String TEMPLATE_NAME = "Fortunes.rocker.html";
     static public final String TEMPLATE_PACKAGE_NAME = "views";
     static public final String TEMPLATE_PACKAGE_NAME = "views";
     static public final String HEADER_HASH = "331039920";
     static public final String HEADER_HASH = "331039920";
-    static public final long MODIFIED_AT = 1493423796000L;
+    static public final long MODIFIED_AT = 1506096936000L;
     static public final String[] ARGUMENT_NAMES = { "items" };
     static public final String[] ARGUMENT_NAMES = { "items" };
 
 
     // argument @ [3:2]
     // argument @ [3:2]
@@ -50,15 +50,15 @@ public class Fortunes extends com.fizzed.rocker.runtime.DefaultRockerModel {
 
 
     static public class Template extends com.fizzed.rocker.runtime.DefaultRockerTemplate {
     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
+        // <!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>
         static private final byte[] PLAIN_TEXT_0_0;
         static private final byte[] PLAIN_TEXT_0_0;
-        // \t<tr><td>
+        // <tr><td>
         static private final byte[] PLAIN_TEXT_1_0;
         static private final byte[] PLAIN_TEXT_1_0;
         // </td><td>
         // </td><td>
         static private final byte[] PLAIN_TEXT_2_0;
         static private final byte[] PLAIN_TEXT_2_0;
-        // </td></tr>\n
+        // </td></tr>
         static private final byte[] PLAIN_TEXT_3_0;
         static private final byte[] PLAIN_TEXT_3_0;
-        // </table></body>\n</html>
+        // </table></body></html>
         static private final byte[] PLAIN_TEXT_4_0;
         static private final byte[] PLAIN_TEXT_4_0;
 
 
         static {
         static {
@@ -87,40 +87,40 @@ public class Fortunes extends com.fizzed.rocker.runtime.DefaultRockerModel {
             // PlainText @ [3:28]
             // PlainText @ [3:28]
             __internal.aboutToExecutePosInTemplate(3, 28);
             __internal.aboutToExecutePosInTemplate(3, 28);
             __internal.writeValue(PLAIN_TEXT_0_0);
             __internal.writeValue(PLAIN_TEXT_0_0);
-            // ForBlockBegin @ [9:1]
-            __internal.aboutToExecutePosInTemplate(9, 1);
+            // ForBlockBegin @ [4:107]
+            __internal.aboutToExecutePosInTemplate(4, 107);
             try {
             try {
                 final com.fizzed.rocker.runtime.CollectionForIterator<Fortune> __forIterator0 = new com.fizzed.rocker.runtime.CollectionForIterator<Fortune>(items);
                 final com.fizzed.rocker.runtime.CollectionForIterator<Fortune> __forIterator0 = new com.fizzed.rocker.runtime.CollectionForIterator<Fortune>(items);
                 while (__forIterator0.hasNext()) {
                 while (__forIterator0.hasNext()) {
                     final com.fizzed.rocker.ForIterator i = __forIterator0;
                     final com.fizzed.rocker.ForIterator i = __forIterator0;
                     final Fortune item = __forIterator0.next();
                     final Fortune item = __forIterator0.next();
                     try {
                     try {
-                        // PlainText @ [9:47]
-                        __internal.aboutToExecutePosInTemplate(9, 47);
+                        // PlainText @ [4:153]
+                        __internal.aboutToExecutePosInTemplate(4, 153);
                         __internal.writeValue(PLAIN_TEXT_1_0);
                         __internal.writeValue(PLAIN_TEXT_1_0);
-                        // ValueExpression @ [10:10]
-                        __internal.aboutToExecutePosInTemplate(10, 10);
+                        // ValueExpression @ [4:161]
+                        __internal.aboutToExecutePosInTemplate(4, 161);
                         __internal.renderValue(item.id, false);
                         __internal.renderValue(item.id, false);
-                        // PlainText @ [10:18]
-                        __internal.aboutToExecutePosInTemplate(10, 18);
+                        // PlainText @ [4:169]
+                        __internal.aboutToExecutePosInTemplate(4, 169);
                         __internal.writeValue(PLAIN_TEXT_2_0);
                         __internal.writeValue(PLAIN_TEXT_2_0);
-                        // ValueExpression @ [10:27]
-                        __internal.aboutToExecutePosInTemplate(10, 27);
+                        // ValueExpression @ [4:178]
+                        __internal.aboutToExecutePosInTemplate(4, 178);
                         __internal.renderValue(item.message, false);
                         __internal.renderValue(item.message, false);
-                        // PlainText @ [10:40]
-                        __internal.aboutToExecutePosInTemplate(10, 40);
+                        // PlainText @ [4:191]
+                        __internal.aboutToExecutePosInTemplate(4, 191);
                         __internal.writeValue(PLAIN_TEXT_3_0);
                         __internal.writeValue(PLAIN_TEXT_3_0);
-                        // ForBlockEnd @ [9:1]
-                        __internal.aboutToExecutePosInTemplate(9, 1);
+                        // ForBlockEnd @ [4:107]
+                        __internal.aboutToExecutePosInTemplate(4, 107);
                     } catch (com.fizzed.rocker.runtime.ContinueException e) {
                     } catch (com.fizzed.rocker.runtime.ContinueException e) {
                         // support for continuing for loops
                         // support for continuing for loops
                     }
                     }
-                } // for end @ [9:1]
+                } // for end @ [4:107]
             } catch (com.fizzed.rocker.runtime.BreakException e) {
             } catch (com.fizzed.rocker.runtime.BreakException e) {
                 // support for breaking for loops
                 // support for breaking for loops
             }
             }
-            // PlainText @ [11:2]
-            __internal.aboutToExecutePosInTemplate(11, 2);
+            // PlainText @ [4:202]
+            __internal.aboutToExecutePosInTemplate(4, 202);
             __internal.writeValue(PLAIN_TEXT_4_0);
             __internal.writeValue(PLAIN_TEXT_4_0);
         }
         }
     }
     }

+ 1 - 10
frameworks/Java/proteus/src/main/java/views/Fortunes.rocker.html

@@ -1,13 +1,4 @@
 @import io.sinistral.models.*
 @import io.sinistral.models.*
 @import java.util.List
 @import java.util.List
 @args (List<Fortune> items)
 @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>
+<!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>

+ 1 - 0
frameworks/Java/proteus/src/main/resources/templates/Fortunes.mustache

@@ -0,0 +1 @@
+<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>{{#.}}<tr><td>{{id}}</td><td>{{message}}</td></tr>{{/.}}</table></body></html>