Browse Source

new(framework): added the jawn framework to the benchmark

MTD 10 years ago
parent
commit
fbfbdaca58
27 changed files with 786 additions and 0 deletions
  1. 3 0
      frameworks/Java/jawn/.gitignore
  2. 34 0
      frameworks/Java/jawn/README.md
  3. 28 0
      frameworks/Java/jawn/benchmark_config.json
  4. 54 0
      frameworks/Java/jawn/build.gradle
  5. BIN
      frameworks/Java/jawn/gradle/wrapper/gradle-wrapper.jar
  6. 6 0
      frameworks/Java/jawn/gradle/wrapper/gradle-wrapper.properties
  7. 164 0
      frameworks/Java/jawn/gradlew
  8. 90 0
      frameworks/Java/jawn/gradlew.bat
  9. 3 0
      frameworks/Java/jawn/install.sh
  10. 8 0
      frameworks/Java/jawn/setup.sh
  11. 22 0
      frameworks/Java/jawn/source_code
  12. 23 0
      frameworks/Java/jawn/src/main/java/app/UndertowMain.java
  13. 17 0
      frameworks/Java/jawn/src/main/java/app/config/Bootstrap.java
  14. 49 0
      frameworks/Java/jawn/src/main/java/app/config/Database.java
  15. 19 0
      frameworks/Java/jawn/src/main/java/app/config/Routing.java
  16. 58 0
      frameworks/Java/jawn/src/main/java/app/controllers/DbController.java
  17. 23 0
      frameworks/Java/jawn/src/main/java/app/controllers/FortunesController.java
  18. 15 0
      frameworks/Java/jawn/src/main/java/app/controllers/IndexController.java
  19. 97 0
      frameworks/Java/jawn/src/main/java/app/db/DbManager.java
  20. 12 0
      frameworks/Java/jawn/src/main/java/app/db/DbModule.java
  21. 12 0
      frameworks/Java/jawn/src/main/java/app/models/Fortune.java
  22. 9 0
      frameworks/Java/jawn/src/main/java/app/models/Message.java
  23. 12 0
      frameworks/Java/jawn/src/main/java/app/models/World.java
  24. 15 0
      frameworks/Java/jawn/src/main/resources/logback.xml
  25. 4 0
      frameworks/Java/jawn/webapp/WEB-INF/views/fortunes/index.st
  26. 9 0
      frameworks/Java/jawn/webapp/WEB-INF/views/index.html.st
  27. BIN
      frameworks/Java/jawn/webapp/favicon.ico

+ 3 - 0
frameworks/Java/jawn/.gitignore

@@ -0,0 +1,3 @@
+.README.md.html
+.gradle
+build/

+ 34 - 0
frameworks/Java/jawn/README.md

@@ -0,0 +1,34 @@
+# Jawn framework benchmarking test
+This is an implementation of [jawn](http://javapla.net)
+as a portion of a [benchmarking test suite](../) comparing a variety 
+of web development platforms.
+
+
+## Running the application
+If you have [gradle](http://gradle.org) installed:
+```
+gradle run
+```
+Or use the included wrapper:
+```
+./gradlew run
+```
+Point your browser at `localhost:8081/json`
+
+The framework use an embedded server, that runs on port `8081` in order to
+not interfere with potentially running webcontainers.
+
+### A couple of commands to get going with the benchmark framework
+
+toolset/run-tests.py --install server --test jawn --verbose --install-only
+
+toolset/run-tests.py --test jawn --mode verify
+
+toolset/run-tests.py --test jawn
+
+toolset/run-tests.py --test jawn --type fortune --sleep 5
+
+
+### Problems with the mounting of FrameworkBenchmarks?
+* **On host**:     vagrant gem install vagrant-vbguest
+* **In guest OS**: sudo mount.vboxsf -o uid=`id -u vagrant`,gid=`getent group vagrant | cut -d: -f3`,dmode=777,fmode=777 FrameworkBenchmarks /FrameworkBenchmarks

+ 28 - 0
frameworks/Java/jawn/benchmark_config.json

@@ -0,0 +1,28 @@
+{
+  "framework": "jawn",
+  "tests": [{
+    "default": {
+      "setup_file": "setup",
+      "json_url": "/json",
+      "db_url": "/db",
+      "query_url": "/queries?queries=",
+      "fortune_url": "/fortunes",
+      "update_url": "/updates?queries=",
+      "plaintext_url": "/plaintext",
+      "port": 8081,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "MySQL",
+      "framework": "java-web-planet",
+      "language": "Java",
+      "orm": "Raw",
+      "platform": "Servlet",
+      "webserver": "Jetty",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "jawn",
+      "notes": "",
+      "versus": "servlet"
+    }
+  }]
+}

+ 54 - 0
frameworks/Java/jawn/build.gradle

@@ -0,0 +1,54 @@
+apply plugin: 'java'
+apply plugin: 'eclipse'
+apply plugin: 'application'
+
+// compiler options
+sourceCompatibility = JavaVersion.VERSION_1_8
+targetCompatibility = JavaVersion.VERSION_1_8
+
+mainClassName = 'app.UndertowMain'
+
+repositories {
+    mavenCentral()
+    
+    //snapshot repository - not actually needed to run this example
+    maven {
+        url 'http://oss.sonatype.org/content/repositories/snapshots'
+    }
+}
+
+dependencies {
+	// Framework
+	compile 'net.javapla.jawn:jawn-server-undertow:0.8.7-SNAPSHOT'
+	//compile project(':jawn-server'), project(':jawn-server-undertow'), project(':jawn-server-jetty')
+
+	
+	// Database
+	compile 'mysql:mysql-connector-java:5.1.35'
+	
+	//Logging 
+	runtime group: 'ch.qos.logback', name: 'logback-classic', version: '1.1.3' exclude group: 'org.slf4j'
+}
+
+/* ****************** */
+/*    Application     */
+/* ****************** */
+run {
+    if(project.hasProperty('args')){
+        args project.args.split('\\s')
+    }
+}
+applicationDistribution.from("webapp") {
+    into "webapp"
+}
+
+/* ****************** */
+/*    Eclipse         */
+/* ****************** */
+eclipse {
+	classpath {
+		// we need the output dir to be the same as the one gradle uses when running Jetty
+		// or else the dynamic loading does not apply
+		defaultOutputDir = file('build/classes/main')
+	}
+}

BIN
frameworks/Java/jawn/gradle/wrapper/gradle-wrapper.jar


+ 6 - 0
frameworks/Java/jawn/gradle/wrapper/gradle-wrapper.properties

@@ -0,0 +1,6 @@
+#Thu May 28 12:11:23 CEST 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-bin.zip

+ 164 - 0
frameworks/Java/jawn/gradlew

@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+    echo "$*"
+}
+
+die ( ) {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+    [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+    JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

+ 90 - 0
frameworks/Java/jawn/gradlew.bat

@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega

+ 3 - 0
frameworks/Java/jawn/install.sh

@@ -0,0 +1,3 @@
+#!/bin/bash
+
+fw_depends java8

+ 8 - 0
frameworks/Java/jawn/setup.sh

@@ -0,0 +1,8 @@
+#!/bin/bash
+
+# load java environment variables
+source $IROOT/java8.installed
+
+./gradlew clean --daemon
+
+./gradlew run

+ 22 - 0
frameworks/Java/jawn/source_code

@@ -0,0 +1,22 @@
+./jawn/src/
+./jawn/src/main/
+./jawn/src/main/java/
+./jawn/src/main/java/app
+./jawn/src/main/java/app/config
+./jawn/src/main/java/app/config/Bootstrap.java
+./jawn/src/main/java/app/config/Database.java
+./jawn/src/main/java/app/config/Routing.java
+./jawn/src/main/java/app/controllers
+./jawn/src/main/java/app/controllers/DbController.java
+./jawn/src/main/java/app/controllers/FortunesController.java
+./jawn/src/main/java/app/controllers/IndexController.java
+./jawn/src/main/java/app/db
+./jawn/src/main/java/app/db/DbManager.java
+./jawn/src/main/java/app/db/DbModule.java
+./jawn/src/main/java/app/models
+./jawn/src/main/java/app/models/Fortune.java
+./jawn/src/main/java/app/models/Message.java
+./jawn/src/main/java/app/models/World.java
+./jawn/src/main/java/app/UndertowMain.java
+./jawn/src/main/resources/
+./jawn/src/main/resources/logback.xml

+ 23 - 0
frameworks/Java/jawn/src/main/java/app/UndertowMain.java

@@ -0,0 +1,23 @@
+package app;
+
+import net.javapla.jawn.server.UndertowServer;
+import net.javapla.jawn.server.spi.ServerConfig;
+
+public class UndertowMain {
+
+    public static void main(String[] args) throws Exception {
+        // Automatically set environment to production if nothing is specified
+        // Framework defaults to development
+        String environment = "production";
+        if (args.length > 0) environment = args[0];
+        System.setProperty("JAWN_ENV", environment);
+        
+        ServerConfig config = new ServerConfig();
+        config.setContextPath("/");
+        config.setPort(8081);
+        config.setWebappPath("webapp");
+        
+        UndertowServer server = new UndertowServer();
+        server.setupAndStartServer(config);
+    }
+}

+ 17 - 0
frameworks/Java/jawn/src/main/java/app/config/Bootstrap.java

@@ -0,0 +1,17 @@
+package app.config;
+
+import net.javapla.jawn.core.ApplicationConfig;
+import net.javapla.jawn.core.spi.ApplicationBootstrap;
+import app.db.DbModule;
+
+public class Bootstrap implements ApplicationBootstrap {
+
+    @Override
+    public void bootstrap(ApplicationConfig config) {
+        config.registerModules(new DbModule());
+    }
+
+    @Override
+    public void destroy() {}
+
+}

+ 49 - 0
frameworks/Java/jawn/src/main/java/app/config/Database.java

@@ -0,0 +1,49 @@
+package app.config;
+
+import net.javapla.jawn.core.database.DatabaseConnections;
+import net.javapla.jawn.core.spi.ApplicationDatabaseBootstrap;
+import net.javapla.jawn.core.util.Modes;
+
+public class Database implements ApplicationDatabaseBootstrap {
+
+    @Override
+    public void dbConnections(DatabaseConnections connections) {
+        
+        String jdbcParams = "jdbcCompliantTruncation=false&elideSetAutoCommits=true" +
+                "&useLocalSessionState=true" +
+                "&cachePrepStmts=true" +
+                "&cacheCallableStmts=true" +
+                "&alwaysSendSetIsolation=false" +
+                "&prepStmtCacheSize=4096" +
+                "&cacheServerConfiguration=true" +
+                "&prepStmtCacheSqlLimit=2048" +
+                "&zeroDateTimeBehavior=convertToNull" +
+                "&traceProtocol=false" +
+                "&useUnbufferedInput=false" +
+                "&useReadAheadInput=false" +
+                "&maintainTimeStats=false" +
+                "&useServerPrepStmts" +
+                "&cacheRSMetadata=true";
+        
+        connections
+            .environment(Modes.prod)
+            .jdbc()
+            .driver("com.mysql.jdbc.Driver")
+            .url("jdbc:mysql://localhost/hello_world?" + jdbcParams)
+//            .url("jdbc:mysql://192.168.100.11/hello_world?" + jdbcParams)
+            .user("benchmarkdbuser")
+            .password("benchmarkdbpass")
+            .maxPoolSize(256)
+            .minPoolSize(256);
+        
+        connections
+            .environment(Modes.dev)
+            .jdbc()
+            .driver("com.mysql.jdbc.Driver")
+//            .url("jdbc:mysql://localhost/hello_world?" + jdbcParams)
+            .url("jdbc:mysql://192.168.100.11/hello_world?" + jdbcParams)
+            .user("benchmarkdbuser")
+            .password("benchmarkdbpass");
+    }
+
+}

+ 19 - 0
frameworks/Java/jawn/src/main/java/app/config/Routing.java

@@ -0,0 +1,19 @@
+package app.config;
+
+import net.javapla.jawn.core.spi.ApplicationRoutes;
+import net.javapla.jawn.core.spi.Routes;
+import app.controllers.DbController;
+import app.controllers.IndexController;
+
+
+public class Routing implements ApplicationRoutes {
+    
+    @Override
+    public void router(Routes routes) {
+        routes.GET().route("/json").to(IndexController.class, "json");
+        routes.GET().route("/plaintext").to(IndexController.class, "plaintext");
+        routes.GET().route("/queries").to(DbController.class, "queries");
+        routes.GET().route("/updates").to(DbController.class, "updates");
+    }
+
+}

+ 58 - 0
frameworks/Java/jawn/src/main/java/app/controllers/DbController.java

@@ -0,0 +1,58 @@
+package app.controllers;
+
+import java.util.concurrent.ThreadLocalRandom;
+
+import com.google.inject.Inject;
+
+import app.db.DbManager;
+import app.models.World;
+import net.javapla.jawn.core.ApplicationController;
+import net.javapla.jawn.core.Param;
+
+public class DbController extends ApplicationController {
+
+    private static final int NUMBER_OF_ROWS = 10_000;
+    
+    @Inject
+    private DbManager db;
+    
+    public void index() {
+        respond().json(db.getWorld(getRandomNumber()));
+    }
+    
+    public void getQueries() {
+        int param = parseQueryParam();
+        
+        World[] worlds = new World[param];
+        for (int i = 0; i < param; i++) {
+            worlds[i] = db.getWorld(getRandomNumber());
+        }
+        respond().json(worlds);
+    }
+    
+    public void getUpdates() {
+        int param = parseQueryParam();
+        
+        World[] worlds = new World[param];
+        for (int i = 0; i < param; i++) {
+            World world = db.getWorld(getRandomNumber());
+            world.randomNumber = getRandomNumber();
+            worlds[i] = world;
+        }
+        db.updateWorlds(worlds);
+        respond().json(worlds);
+    }
+    
+    private int getRandomNumber() {
+        return ThreadLocalRandom.current().nextInt(NUMBER_OF_ROWS) + 1;
+    }
+    
+    private int parseQueryParam() {
+        Param queries = param("queries");
+        Integer param = queries.asInt(1);
+        if (param > 500) param = 500;
+        else if (param < 1) param = 1;
+        
+        return param;
+    }
+}

+ 23 - 0
frameworks/Java/jawn/src/main/java/app/controllers/FortunesController.java

@@ -0,0 +1,23 @@
+package app.controllers;
+
+import java.util.Collections;
+import java.util.List;
+
+import net.javapla.jawn.core.ApplicationController;
+import app.db.DbManager;
+import app.models.Fortune;
+
+import com.google.inject.Inject;
+
+public class FortunesController extends ApplicationController {
+
+    @Inject
+    private DbManager db;
+    
+    public void index() {
+        List<Fortune> fortunes = db.fetchAllFortunes();
+        fortunes.add(new Fortune(0, "Additional fortune added at request time."));
+        Collections.sort(fortunes, (f1, f2) -> f1.message.compareTo(f2.message));
+        view("fortunes", fortunes);
+    }
+}

+ 15 - 0
frameworks/Java/jawn/src/main/java/app/controllers/IndexController.java

@@ -0,0 +1,15 @@
+package app.controllers;
+
+import net.javapla.jawn.core.ApplicationController;
+import app.models.Message;
+
+public class IndexController extends ApplicationController {
+
+    public void getJson() {
+        respond().json(new Message("Hello, World!"));
+    }
+    
+    public void getPlaintext() {
+        respond().text("Hello, World!");
+    }
+}

+ 97 - 0
frameworks/Java/jawn/src/main/java/app/db/DbManager.java

@@ -0,0 +1,97 @@
+package app.db;
+
+import java.beans.PropertyVetoException;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.sql.DataSource;
+
+import net.javapla.jawn.core.database.DatabaseConnection;
+import net.javapla.jawn.core.exceptions.InitException;
+import app.models.Fortune;
+import app.models.World;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+@Singleton
+public class DbManager {
+
+    private DataSource source;
+
+    @Inject
+    public DbManager(DatabaseConnection spec) throws ClassNotFoundException, SQLException, PropertyVetoException {
+        if (spec == null) throw new InitException("DatabaseConnection is null");
+        
+        source = spec.createDataSource();
+    }
+    
+    public World getWorld(int id) {
+        try (Connection connection = source.getConnection()) {
+            PreparedStatement statement = connection.prepareStatement("SELECT id, randomNumber FROM World WHERE id = ?");
+            statement.setInt(1, id);
+            try (ResultSet set = statement.executeQuery()) {
+                if (!set.next()) return null;
+                
+                return new World(set.getInt(1), set.getInt(2));
+            }
+        } catch (SQLException e) {
+            return null;
+        }
+    }
+    
+    public boolean updateWorlds(World[] worlds) {
+        try (Connection connection = source.getConnection()) {
+            PreparedStatement update = connection.prepareStatement("UPDATE World SET randomNumber = ? WHERE id= ?");
+            for (World world : worlds) {
+                update.setInt(1, world.randomNumber);
+                update.setInt(2, world.id);
+                update.addBatch();
+            }
+            update.executeBatch();
+            return true;
+        } catch (SQLException e) {
+            return false;
+        }
+    }
+    
+    public List<Fortune> fetchAllFortunes() {
+        List<Fortune> list = new ArrayList<>();
+        try (Connection connection = source.getConnection()) {
+            PreparedStatement fetch = connection.prepareStatement("SELECT id, message FROM Fortune");
+            ResultSet set = fetch.executeQuery();
+            while (set.next()) {
+                list.add(new Fortune(set.getInt(1), escape(set.getString(2))));
+            }
+        } catch (SQLException ignore) { }
+        return list;
+    }
+    
+    private static final String escape(String html) {
+        StringBuilder bob = new StringBuilder();
+        char[] arr = html.toCharArray();
+        for (char c : arr) {
+            switch(c) {
+            case '<':
+                bob.append("&lt;");
+                break;
+            case '>':
+                bob.append("&gt;");
+                break;
+            case '&':
+                bob.append("&amp;");
+                break;
+            case '"':
+                bob.append("&quot;");
+                break;
+            default:
+                bob.append(c);
+            }
+        }
+        return bob.toString();
+    }
+}

+ 12 - 0
frameworks/Java/jawn/src/main/java/app/db/DbModule.java

@@ -0,0 +1,12 @@
+package app.db;
+
+import com.google.inject.AbstractModule;
+
+public class DbModule extends AbstractModule {
+
+    @Override
+    protected void configure() {
+        bind(DbManager.class);
+    }
+
+}

+ 12 - 0
frameworks/Java/jawn/src/main/java/app/models/Fortune.java

@@ -0,0 +1,12 @@
+package app.models;
+
+public class Fortune {
+
+    public int id;
+    public String message;
+    
+    public Fortune(int i , String m) {
+        id = i;
+        message = m;
+    }
+}

+ 9 - 0
frameworks/Java/jawn/src/main/java/app/models/Message.java

@@ -0,0 +1,9 @@
+package app.models;
+
+public class Message {
+
+    public String message;
+    public Message(String m) {
+        message = m;
+    }
+}

+ 12 - 0
frameworks/Java/jawn/src/main/java/app/models/World.java

@@ -0,0 +1,12 @@
+package app.models;
+
+public class World {
+
+    public int id;
+    public int randomNumber;
+    
+    public World(int i , int r) {
+        id = i;
+        randomNumber = r;
+    }
+}

+ 15 - 0
frameworks/Java/jawn/src/main/resources/logback.xml

@@ -0,0 +1,15 @@
+<configuration>
+    
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder>
+            <pattern>%d{HH:mm:ss.SSS} [%-8.8thread] %-5level %-20logger{30} - %msg%n</pattern>
+        </encoder>
+    </appender>
+
+    
+    <!-- Disables all logging -->
+    <root level="OFF">
+        <appender-ref ref="STDOUT" />
+    </root>
+    
+</configuration>

+ 4 - 0
frameworks/Java/jawn/webapp/WEB-INF/views/fortunes/index.st

@@ -0,0 +1,4 @@
+<table>
+	<tr><th>id</th><th>message</th></tr>
+	$fortunes:{ fortune | <tr><td>$fortune.id$</td><td>$fortune.message$</td></tr>}$
+</table>

+ 9 - 0
frameworks/Java/jawn/webapp/WEB-INF/views/index.html.st

@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+	<title>Fortunes</title>
+</head>
+<body>
+	$site.content$
+</body>
+</html>

BIN
frameworks/Java/jawn/webapp/favicon.ico