Browse Source

Merge pull request #1331 from jamming/master

Add Sabina framework benchmark
Mike Smith 10 years ago
parent
commit
c0647a0036

+ 1 - 0
.travis.yml

@@ -69,6 +69,7 @@ env:
     - "TESTDIR=Java/servlet"
     - "TESTDIR=Java/servlet"
     - "TESTDIR=Java/servlet3-cass"
     - "TESTDIR=Java/servlet3-cass"
     - "TESTDIR=Java/spark"
     - "TESTDIR=Java/spark"
+    - "TESTDIR=Java/sabina"
     - "TESTDIR=Java/spring"
     - "TESTDIR=Java/spring"
     - "TESTDIR=Java/tapestry"
     - "TESTDIR=Java/tapestry"
     - "TESTDIR=Java/undertow"
     - "TESTDIR=Java/undertow"

+ 3 - 0
frameworks/Java/sabina/bash_profile.sh

@@ -0,0 +1,3 @@
+#@IgnoreInspection AddShebang
+
+export JAVA_HOME=/opt/java8

+ 26 - 0
frameworks/Java/sabina/benchmark_config

@@ -0,0 +1,26 @@
+{
+  "framework": "sabina",
+  "tests": [{
+    "default": {
+      "setup_file": "setup",
+      "json_url": "/json",
+      "db_url": "/db",
+      "query_url": "/db?queries=",
+      "plaintext_url": "/plaintext",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Micro",
+      "database": "MySQL",
+      "framework": "sabina",
+      "language": "Java",
+      "orm": "Raw",
+      "platform": "Servlet",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "Sabina",
+      "notes": "",
+      "versus": "servlet"
+    }
+  }]
+}

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

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

+ 135 - 0
frameworks/Java/sabina/pom.xml

@@ -0,0 +1,135 @@
+<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>sabina</groupId>
+    <artifactId>sabina</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+
+    <name>Sabina benchmark project</name>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+
+        <maven.compiler.source>${java.specification.version}</maven.compiler.source>
+        <maven.compiler.target>${java.specification.version}</maven.compiler.target>
+        <maven.compiler.optimize>true</maven.compiler.optimize>
+        <maven.compiler.debug>false</maven.compiler.debug>
+
+        <db.host>localhost</db.host>
+        <web.port>8080</web.port>
+
+        <sabina-version>1.0.0</sabina-version>
+        <mysql-connector-version>5.1.28</mysql-connector-version>
+    </properties>
+
+    <repositories>
+        <repository>
+            <snapshots>
+                <enabled>false</enabled>
+            </snapshots>
+            <id>central</id>
+            <name>bintray</name>
+            <url>http://jcenter.bintray.com</url>
+        </repository>
+    </repositories>
+    <pluginRepositories>
+        <pluginRepository>
+            <snapshots>
+                <enabled>false</enabled>
+            </snapshots>
+            <id>central</id>
+            <name>bintray-plugins</name>
+            <url>http://jcenter.bintray.com</url>
+        </pluginRepository>
+    </pluginRepositories>
+
+    <dependencies>
+        <dependency>
+            <groupId>sabina</groupId>
+            <artifactId>http</artifactId>
+            <version>${sabina-version}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>javax.servlet</groupId>
+                    <artifactId>javax.servlet-api</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>sabina</groupId>
+            <artifactId>extra</artifactId>
+            <version>${sabina-version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.spec.javax.servlet</groupId>
+            <artifactId>jboss-servlet-api_3.1_spec</artifactId>
+            <version>1.0.0.Final</version>
+        </dependency>
+        <dependency>
+            <groupId>com.mchange</groupId>
+            <artifactId>c3p0</artifactId>
+            <version>0.9.2.1</version>
+        </dependency>
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <version>${mysql-connector-version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.11</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>fluent-hc</artifactId>
+            <version>4.4</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <resources>
+            <resource>
+                <directory>src/main/resources</directory>
+                <filtering>true</filtering>
+            </resource>
+        </resources>
+
+        <plugins>
+            <plugin>
+                <artifactId>maven-shade-plugin</artifactId>
+                <version>2.3</version>
+                <configuration>
+                    <createDependencyReducedPom>false</createDependencyReducedPom>
+                </configuration>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>shade</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <artifactId>maven-jar-plugin</artifactId>
+                <version>2.5</version>
+                <configuration>
+                    <archive>
+                        <manifest>
+                            <mainClass>sabina.benchmark.Application</mainClass>
+                        </manifest>
+                    </archive>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>

+ 44 - 0
frameworks/Java/sabina/readme.md

@@ -0,0 +1,44 @@
+
+# Sabina Benchmarking Test
+
+This is the Sabina portion of a [benchmarking test suite](../) comparing a variety of web
+development platforms. The test utilizes Sabina routes, Gson for JSON serialization and a custom
+OSIV pattern created with Sabina filters.
+
+
+## Tests
+
+* [Sabina application](/src/main/java/sabina/benchmark/Application.java)
+
+
+## Infrastructure Software Versions
+
+* [Sabina 1.0](http://there4.co/)
+
+
+## Different test setups
+
+* Local environment with Sabina's built in embedded Jetty (port=8080, context=/)
+ * Start application from [Application](/src/main/java/sabina/benchmark/Application.java)'s main method
+* Local environment with Sabina's built in embedded Undertow (port=8080, context=/)
+ * Start application from [Application](/src/main/java/sabina/benchmark/Application.java)'s main method
+
+
+## Test URLs
+
+### JSON Encoding Test
+
+http://localhost:8080/json
+
+### Data-Store/Database Mapping Test
+
+http://localhost:8080/db?queries=5
+
+### Plain Text Test
+
+http://localhost:8080/plaintext
+
+## TODO
+
+* Implement 'update' test
+* Implement 'fortunes' test

+ 4 - 0
frameworks/Java/sabina/setup.sh

@@ -0,0 +1,4 @@
+#!/bin/bash
+
+mvn clean package -Ddb.host=${DBHOST}
+${JAVA_HOME}/bin/java -jar target/sabina-1.0.0-SNAPSHOT.jar &

+ 4 - 0
frameworks/Java/sabina/source_code

@@ -0,0 +1,4 @@
+./sabina/src/main/java/
+./sabina/src/main/java/Application.java
+./sabina/src/main/java/Message.java
+./sabina/src/main/java/World.java

+ 141 - 0
frameworks/Java/sabina/src/main/java/sabina/benchmark/Application.java

@@ -0,0 +1,141 @@
+package sabina.benchmark;
+
+import static java.lang.Integer.parseInt;
+import static sabina.Sabina.*;
+import static sabina.content.JsonContent.toJson;
+
+import com.mchange.v2.c3p0.ComboPooledDataSource;
+import sabina.Exchange;
+import sabina.Request;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Date;
+import java.util.Properties;
+import java.util.Random;
+import java.util.concurrent.ThreadLocalRandom;
+
+import javax.sql.DataSource;
+
+/**
+ * When it is implemented, add this to benchmark_config
+ * "fortune_url": "/fortune",
+ * "update_url": "/update",
+ */
+final class Application {
+    private static final Properties CONFIG = loadConfig ();
+    private static final DataSource DS = createSessionFactory ();
+    private static final String QUERY = "select * from world where id = ?";
+
+    private static final int DB_ROWS = 10000;
+    private static final String MESSAGE = "Hello, World!";
+    private static final String CONTENT_TYPE_TEXT = "text/plain";
+
+    private static Properties loadConfig () {
+        try {
+            Properties config = new Properties ();
+            config.load (Class.class.getResourceAsStream ("/server.properties"));
+            return config;
+        }
+        catch (Exception ex) {
+            throw new RuntimeException (ex);
+        }
+    }
+
+    private static DataSource createSessionFactory () {
+        try {
+            ComboPooledDataSource cpds = new ComboPooledDataSource ();
+            cpds.setJdbcUrl (CONFIG.getProperty ("mysql.uri"));
+            cpds.setMinPoolSize (32);
+            cpds.setMaxPoolSize (256);
+            cpds.setCheckoutTimeout (1800);
+            cpds.setMaxStatements (50);
+            return cpds;
+        }
+        catch (Exception ex) {
+            throw new RuntimeException (ex);
+        }
+    }
+
+    private static int getQueries (final Request request) {
+        try {
+            String param = request.queryParams ("queries");
+            if (param == null)
+                return 1;
+
+            int queries = parseInt (param);
+            if (queries < 1)
+                return 1;
+            if (queries > 500)
+                return 500;
+
+            return queries;
+        }
+        catch (NumberFormatException ex) {
+            return 1;
+        }
+    }
+
+    private static Object getJson (Exchange it) {
+        it.response.type ("application/json");
+        return toJson (new Message ());
+    }
+
+    private static Object getDb (Exchange it) {
+        final int queries = getQueries (it.request);
+        final World[] worlds = new World[queries];
+
+        try (final Connection con = DS.getConnection ()) {
+            final Random random = ThreadLocalRandom.current ();
+            PreparedStatement stmt = con.prepareStatement (QUERY);
+
+            for (int i = 0; i < queries; i++) {
+                stmt.setInt (1, random.nextInt (DB_ROWS) + 1);
+                ResultSet rs = stmt.executeQuery ();
+                while (rs.next ()) {
+                    worlds[i] = new World ();
+                    worlds[i].id = rs.getInt (1);
+                    worlds[i].randomNumber = rs.getInt (2);
+                }
+            }
+        }
+        catch (SQLException e) {
+            e.printStackTrace ();
+        }
+
+        it.response.type ("application/json");
+        return toJson (it.request.queryParams ("queries") == null? worlds[0] : worlds);
+    }
+
+    private static Object getFortune (Exchange aExchange) {
+        throw new UnsupportedOperationException ();
+    }
+
+    private static Object getUpdate (Exchange aExchange) {
+        throw new UnsupportedOperationException ();
+    }
+
+    private static Object getPlaintext (Exchange it) {
+        it.response.type (CONTENT_TYPE_TEXT);
+        return MESSAGE;
+    }
+
+    private static void addCommonHeaders (Exchange it) {
+        it.header ("Server", "Undertow/1.1.2");
+        it.response.raw ().addDateHeader ("Date", new Date ().getTime ());
+    }
+
+    public static void main (String[] args) {
+        get ("/json", Application::getJson);
+        get ("/db", Application::getDb);
+        get ("/fortune", Application::getFortune);
+        get ("/update", Application::getUpdate);
+        get ("/plaintext", Application::getPlaintext);
+        after (Application::addCommonHeaders);
+
+        setIpAddress (CONFIG.getProperty ("web.host"));
+        start (parseInt (CONFIG.getProperty ("web.port")));
+    }
+}

+ 9 - 0
frameworks/Java/sabina/src/main/java/sabina/benchmark/Fortune.java

@@ -0,0 +1,9 @@
+package sabina.benchmark;
+
+/**
+ * TODO .
+ *
+ * @author jam
+ */
+public class Fortune {
+}

+ 5 - 0
frameworks/Java/sabina/src/main/java/sabina/benchmark/Message.java

@@ -0,0 +1,5 @@
+package sabina.benchmark;
+
+final class Message {
+    public final String message = "Hello, World!";
+}

+ 5 - 0
frameworks/Java/sabina/src/main/java/sabina/benchmark/World.java

@@ -0,0 +1,5 @@
+package sabina.benchmark;
+
+final class World {
+    public int id, randomNumber;
+}

+ 20 - 0
frameworks/Java/sabina/src/main/resources/sabina/view/fortunes.ftl

@@ -0,0 +1,20 @@
+<!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>

+ 20 - 0
frameworks/Java/sabina/src/main/resources/sabina/view/fortunes.html

@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Fortunes</title>
+</head>
+<body>
+<table>
+    <tr>
+        <th>id</th>
+        <th>message</th>
+    </tr>
+    <#list fortunes as fortune>
+      <tr>
+          <td>${fortune.id}</td>
+          <td>${fortune.message!null}</td>
+      </tr>
+    </#list>
+</table>
+</body>
+</html>

+ 25 - 0
frameworks/Java/sabina/src/main/resources/server.properties

@@ -0,0 +1,25 @@
+web.port = ${web.port}
+web.host = 0.0.0.0
+
+mongodb.uri = ${db.host}:27017
+mongodb.name = hello_world
+
+mysql.uri = jdbc:mysql://${db.host}:3306/hello_world?\
+user=benchmarkdbuser&\
+password=benchmarkdbpass&\
+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

+ 136 - 0
frameworks/Java/sabina/src/test/java/sabina/benchmark/ApplicationTest.java

@@ -0,0 +1,136 @@
+package sabina.benchmark;
+
+import static org.apache.http.client.fluent.Request.Get;
+import static org.junit.Assert.*;
+import static sabina.benchmark.Application.main;
+import static sabina.Sabina.stop;
+import static sun.misc.IOUtils.readFully;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import com.google.gson.Gson;
+import org.apache.http.HttpResponse;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public final class ApplicationTest {
+    private static final String ENDPOINT = "http://localhost:8080";
+    private static final Gson GSON = new Gson ();
+
+    @BeforeClass public static void setup () {
+        main (null);
+    }
+
+    @AfterClass public static void close () {
+        stop ();
+    }
+
+    @Test public void json () throws IOException {
+        HttpResponse response = get (ENDPOINT + "/json");
+        String content = getContent (response);
+
+        checkResponse (response, content, "application/json");
+        assertEquals ("Hello, World!", GSON.fromJson (content, Map.class).get ("message"));
+    }
+
+    @Test public void plaintext () throws IOException {
+        HttpResponse response = get (ENDPOINT + "/plaintext");
+        String content = getContent (response);
+
+        checkResponse (response, content, "text/plain");
+        assertEquals ("Hello, World!", content);
+    }
+
+    @Test public void no_query_parameter () throws IOException {
+        HttpResponse response = get (ENDPOINT + "/db");
+        String content = getContent (response);
+
+        checkResponse (response, content, "application/json");
+        Map<?, ?> resultsMap = GSON.fromJson (content, Map.class);
+        assertTrue (resultsMap.containsKey ("id") && resultsMap.containsKey ("randomNumber"));
+    }
+
+    @Test public void empty_query_parameter () throws IOException {
+        HttpResponse response = get (ENDPOINT + "/db?queries");
+        String content = getContent (response);
+
+        checkResponse (response, content, "application/json");
+        checkResultItems (content, 1);
+    }
+
+    @Test public void text_query_parameter () throws IOException {
+        HttpResponse response = get (ENDPOINT + "/db?queries=text");
+        String content = getContent (response);
+
+        checkResponse (response, content, "application/json");
+        checkResultItems (content, 1);
+    }
+
+    @Test public void zero_queries () throws IOException {
+        HttpResponse response = get (ENDPOINT + "/db?queries=0");
+        String content = getContent (response);
+
+        checkResponse (response, content, "application/json");
+        checkResultItems (content, 1);
+    }
+
+    @Test public void one_thousand_queries () throws IOException {
+        HttpResponse response = get (ENDPOINT + "/db?queries=1000");
+        String content = getContent (response);
+
+        checkResponse (response, content, "application/json");
+        checkResultItems (content, 500);
+    }
+
+    @Test public void one_query () throws IOException {
+        HttpResponse response = get (ENDPOINT + "/db?queries=1");
+        String content = getContent (response);
+
+        checkResponse (response, content, "application/json");
+        checkResultItems (content, 1);
+    }
+
+    @Test public void ten_query () throws IOException {
+        HttpResponse response = get (ENDPOINT + "/db?queries=10");
+        String content = getContent (response);
+
+        checkResponse (response, content, "application/json");
+        checkResultItems (content, 10);
+    }
+
+    @Test public void five_hundred_queries () throws IOException {
+        HttpResponse response = get (ENDPOINT + "/db?queries=500");
+        String content = getContent (response);
+
+        checkResponse (response, content, "application/json");
+        checkResultItems (content, 500);
+    }
+
+    private HttpResponse get (String uri) throws IOException {
+        return Get (uri).execute ().returnResponse ();
+    }
+
+    private String getContent (HttpResponse aResponse) throws IOException {
+        return new String (readFully (aResponse.getEntity ().getContent (), -1, true));
+    }
+
+    private void checkResponse (HttpResponse aRes, String aContent, String contentType) {
+        assertTrue (aRes.getFirstHeader ("Server") != null);
+        assertTrue (aRes.getFirstHeader ("Date") != null);
+        assertEquals (aContent.length (), aRes.getEntity ().getContentLength ());
+        assertEquals (contentType, aRes.getEntity ().getContentType ().getValue ());
+    }
+
+    private void checkResultItems (String result, int size) {
+        List<?> resultsList = GSON.fromJson (result, List.class);
+        assertEquals (size, resultsList.size ());
+
+        for (int ii = 0; ii < size; ii++) {
+            Map<?, ?> r = (Map)resultsList.get (ii);
+            assertTrue (r.containsKey ("id") && r.containsKey ("randomNumber"));
+        }
+    }
+}