Browse Source

Jooby 2.0.0.M1 (#4470)

Edgar Espina 6 years ago
parent
commit
fc87c77a56

+ 1 - 0
.travis.yml

@@ -59,6 +59,7 @@ env:
      - "TESTDIR=Java/jetty"
      - "TESTDIR=Java/jlhttp"
      - "TESTDIR=Java/jooby"
+     - "TESTDIR=Java/jooby2"
      - "TESTDIR=Java/light-java"
      - "TESTDIR=Java/micronaut"
      - "TESTDIR=Java/minijax"

+ 77 - 0
frameworks/Java/jooby2/README.md

@@ -0,0 +1,77 @@
+# Jooby Benchmarking Test
+
+[Jooby](https://jooby.io) the modular micro web framework for Java and Kotlin.
+
+```java
+public class App extends Jooby {
+
+  {
+    get("/", ctx -> "Hello, World!");
+  }
+
+}
+```
+
+This is the [Jooby](https://jooby.io) portion of a [benchmarking test suite](../) comparing a variety of web development platforms.
+
+### Plain Text Test
+* [Plain test source](src/main/java/com/techempower/App.java)
+
+### JSON Encoding Test
+* [JSON test source](src/main/java/com/techempower/App.java)
+
+### Single Query Test
+* [Single query test source](src/main/java/com/techempower/App.java)
+
+### Multiple Queries Test
+* [Multiple queries test source](src/main/java/com/techempower/App.java)
+
+### Database Update Test
+* [Database update test source](src/main/java/com/techempower/App.java)
+
+### Fortunes Test
+* [Fortunes test source](src/main/java/com/techempower/App.java)
+
+## Test URLs
+
+### Plain Text Test
+
+    http://localhost:8080/plaintext
+
+### JSON Encoding Test
+
+    http://localhost:8080/json
+
+### Single Query Test
+
+    http://localhost:8080/db
+
+### Multiple Queries Test
+
+    http://localhost:8080/queries
+
+### Database updates Test
+
+    http://localhost:8080/updates
+
+### Fortunes Test
+
+    http://localhost:8080/fortunes
+
+## build
+
+### netty
+
+    mvn clean package -P netty
+
+### undertow
+
+    mvn clean package -P undertow
+
+### jetty
+
+    mvn clean package -P jetty
+    
+## run
+
+    java -jar target/jooby-2x.jar

+ 74 - 0
frameworks/Java/jooby2/benchmark_config.json

@@ -0,0 +1,74 @@
+{
+  "framework": "jooby2",
+  "tests": [{
+    "default": {
+      "json_url": "/json",
+      "plaintext_url": "/plaintext",
+      "db_url": "/db",
+      "query_url": "/queries?queries=",
+      "fortune_url": "/fortunes",
+      "update_url": "/updates?queries=",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "framework": "jooby 2.x",
+      "language": "Java",
+      "flavor": "None",
+      "platform": "Netty",
+      "database": "Postgres",
+      "database_os": "Linux",
+      "orm": "Raw",
+      "webserver": "None",
+      "os": "Linux",
+      "notes": "Jooby 2 using Netty",
+      "display_name": "jooby2-netty",
+      "versus": "netty"
+    },
+    "undertow": {
+      "json_url": "/json",
+      "plaintext_url": "/plaintext",
+      "db_url": "/db",
+      "query_url": "/queries?queries=",
+      "fortune_url": "/fortunes",
+      "update_url": "/updates?queries=",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "framework": "jooby 2.x",
+      "language": "Java",
+      "flavor": "None",
+      "platform": "Undertow",
+      "database": "Postgres",
+      "database_os": "Linux",
+      "orm": "Raw",
+      "webserver": "None",
+      "os": "Linux",
+      "notes": "Jooby 2 using Undertow",
+      "display_name": "jooby2-undertow",
+      "versus": "undertow"
+    },
+    "jetty": {
+      "json_url": "/json",
+      "plaintext_url": "/plaintext",
+      "db_url": "/db",
+      "query_url": "/queries?queries=",
+      "fortune_url": "/fortunes",
+      "update_url": "/updates?queries=",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "framework": "jooby 2.x",
+      "language": "Java",
+      "flavor": "None",
+      "platform": "Jetty",
+      "database": "Postgres",
+      "database_os": "Linux",
+      "orm": "Raw",
+      "webserver": "None",
+      "os": "Linux",
+      "notes": "Jooby 2 using Jetty",
+      "display_name": "jooby2-jetty",
+      "versus": "jetty"
+    }
+  }]
+}

+ 4 - 0
frameworks/Java/jooby2/conf/application.properties

@@ -0,0 +1,4 @@
+
+db.url = jdbc:postgresql://tfb-database:5432/hello_world
+db.user = benchmarkdbuser
+db.password = benchmarkdbpass

+ 12 - 0
frameworks/Java/jooby2/conf/logback.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+    <encoder>
+      <pattern>[%d{ISO8601}]-[%thread] %-5level %logger - %msg%n</pattern>
+    </encoder>
+  </appender>
+
+  <root level="INFO">
+    <appender-ref ref="STDOUT" />
+  </root>
+</configuration>

+ 12 - 0
frameworks/Java/jooby2/jooby2-jetty.dockerfile

@@ -0,0 +1,12 @@
+FROM maven:3.5.3-jdk-8-slim as maven
+WORKDIR /jooby2
+COPY pom.xml pom.xml
+COPY src src
+COPY public public
+RUN mvn package -q -P jetty
+
+FROM openjdk:8-jre-slim
+WORKDIR /jooby2
+COPY --from=maven /jooby2/target/jooby-2x.jar app.jar
+COPY conf conf
+CMD ["java", "-server", "-Xms512m", "-Xmx2g", "-jar", "app.jar"]

+ 12 - 0
frameworks/Java/jooby2/jooby2-undertow.dockerfile

@@ -0,0 +1,12 @@
+FROM maven:3.5.3-jdk-8-slim as maven
+WORKDIR /jooby2
+COPY pom.xml pom.xml
+COPY src src
+COPY public public
+RUN mvn package -q -P undertow
+
+FROM openjdk:8-jre-slim
+WORKDIR /jooby2
+COPY --from=maven /jooby2/target/jooby-2x.jar app.jar
+COPY conf conf
+CMD ["java", "-server", "-Xms512m", "-Xmx2g", "-jar", "app.jar"]

+ 12 - 0
frameworks/Java/jooby2/jooby2.dockerfile

@@ -0,0 +1,12 @@
+FROM maven:3.5.3-jdk-8-slim as maven
+WORKDIR /jooby2
+COPY pom.xml pom.xml
+COPY src src
+COPY public public
+RUN mvn package -q -P netty
+
+FROM openjdk:8-jre-slim
+WORKDIR /jooby2
+COPY --from=maven /jooby2/target/jooby-2x.jar app.jar
+COPY conf conf
+CMD ["java", "-server", "-Xms512m", "-Xmx2g", "-jar", "app.jar"]

+ 133 - 0
frameworks/Java/jooby2/pom.xml

@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>io.jooby</groupId>
+    <artifactId>jooby-project</artifactId>
+    <version>2.0.0.M1</version>
+  </parent>
+
+  <artifactId>jooby2</artifactId>
+  <groupId>com.techempower</groupId>
+  <version>2.0</version>
+
+  <name>jooby 2.x</name>
+
+  <properties>
+    <jooby.version>2.0.0.M1</jooby.version>
+    <postgresql.version>42.2.2</postgresql.version>
+
+    <!-- Startup class -->
+    <application.class>com.techempower.App</application.class>
+  </properties>
+
+  <dependencies>
+    <!-- jackson -->
+    <dependency>
+      <groupId>io.jooby</groupId>
+      <artifactId>jooby-jackson</artifactId>
+    </dependency>
+
+    <!-- jdbc -->
+    <dependency>
+      <groupId>io.jooby</groupId>
+      <artifactId>jooby-hikari</artifactId>
+    </dependency>
+
+    <!-- mySQL -->
+    <dependency>
+      <groupId>mysql</groupId>
+      <artifactId>mysql-connector-java</artifactId>
+    </dependency>
+
+    <!-- postgresql -->
+    <dependency>
+      <groupId>org.postgresql</groupId>
+      <artifactId>postgresql</artifactId>
+      <version>${postgresql.version}</version>
+    </dependency>
+
+    <!-- rocker -->
+    <dependency>
+      <groupId>com.fizzed</groupId>
+      <artifactId>rocker-runtime</artifactId>
+      <version>1.2.1</version>
+    </dependency>
+
+    <!-- logging -->
+    <dependency>
+      <groupId>ch.qos.logback</groupId>
+      <artifactId>logback-classic</artifactId>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <finalName>jooby-2x</finalName>
+    <plugins>
+      <plugin>
+        <groupId>com.fizzed</groupId>
+        <artifactId>rocker-maven-plugin</artifactId>
+        <version>1.2.1</version>
+        <executions>
+          <execution>
+            <id>generate-rocker-templates</id>
+            <phase>generate-sources</phase>
+            <goals>
+              <goal>generate</goal>
+            </goals>
+            <configuration>
+              <templateDirectory>public</templateDirectory>
+              <touchFile>/dev/null</touchFile>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+
+      <!-- Build fat jar -->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-shade-plugin</artifactId>
+      </plugin>
+
+    </plugins>
+  </build>
+
+  <profiles>
+    <profile>
+      <id>netty</id>
+      <dependencies>
+        <dependency>
+          <groupId>io.jooby</groupId>
+          <artifactId>jooby-netty</artifactId>
+          <version>${jooby.version}</version>
+        </dependency>
+      </dependencies>
+    </profile>
+
+    <profile>
+      <id>undertow</id>
+      <dependencies>
+        <dependency>
+          <groupId>io.jooby</groupId>
+          <artifactId>jooby-utow</artifactId>
+          <version>${jooby.version}</version>
+        </dependency>
+      </dependencies>
+    </profile>
+
+    <profile>
+      <id>jetty</id>
+      <dependencies>
+        <dependency>
+          <groupId>io.jooby</groupId>
+          <artifactId>jooby-jetty</artifactId>
+          <version>${jooby.version}</version>
+        </dependency>
+      </dependencies>
+    </profile>
+  </profiles>
+
+</project>

+ 15 - 0
frameworks/Java/jooby2/public/views/fortunes.rocker.html

@@ -0,0 +1,15 @@
+@import java.util.List
+@import com.techempower.Fortune
+@args (List<Fortune> fortunes)
+<!DOCTYPE html>
+<html>
+<head><title>Fortunes</title></head>
+<body>
+<table>
+    <tr><th>id</th><th>message</th></tr>
+    @for (it: fortunes) {
+    <tr><td>@it.id</td><td>@it.message</td></tr>
+    }
+</table>
+</body>
+</html>

+ 190 - 0
frameworks/Java/jooby2/src/main/java/com/techempower/App.java

@@ -0,0 +1,190 @@
+package com.techempower;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fizzed.rocker.runtime.ArrayOfByteArraysOutput;
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+import io.jooby.Context;
+import io.jooby.Env;
+import io.jooby.Jooby;
+import io.jooby.hikari.Hikari;
+import io.jooby.json.Jackson;
+
+import java.io.OutputStream;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.ThreadLocalRandom;
+
+import javax.sql.DataSource;
+
+import static io.jooby.ExecutionMode.EVENT_LOOP;
+import static io.jooby.MediaType.html;
+import static io.jooby.MediaType.json;
+import static java.sql.ResultSet.CONCUR_READ_ONLY;
+import static java.sql.ResultSet.TYPE_FORWARD_ONLY;
+
+public class App extends Jooby {
+
+  private static final String UPDATE_WORLD = "update world set randomNumber=? where id=?";
+
+  private static final String SELECT_WORLD = "select * from world where id=?";
+
+  private static final int DB_ROWS = 10000;
+
+  private static final String HELLO_WORLD = "Hello, World!";
+
+  public static class Message {
+    public final String message = HELLO_WORLD;
+  }
+
+  {
+    Env env = Env.defaultEnvironment(getClass().getClassLoader());
+
+    /** JSON: */
+    ObjectMapper mapper = Jackson.defaultObjectMapper();
+
+    /** Database: */
+    HikariConfig conf = new Hikari.Builder().build(env);
+    DataSource ds = new HikariDataSource(conf);
+
+    get("/plaintext", ctx ->
+        ctx.sendString(HELLO_WORLD)
+    );
+
+    get("/json", ctx -> ctx
+        .setContentType(json)
+        .sendBytes(mapper.writeValueAsBytes(new Message()))
+    );
+
+    /** Go blocking: */
+    dispatch(() -> {
+
+      /** Single query: */
+      get("/db", ctx -> ctx
+          .setContentType(json)
+          .sendBytes(mapper.writeValueAsBytes(findOne(ds, rndId(ThreadLocalRandom.current()))))
+      );
+
+      /** Multiple queries: */
+      get("/queries", ctx -> {
+        // bound count
+        int count = queries(ctx);
+        List<World> result = new ArrayList<>();
+        Random rnd = ThreadLocalRandom.current();
+        try (Connection conn = ds.getConnection()) {
+          for (int i = 0; i < count; i++) {
+            int id = rndId(rnd);
+            try (final PreparedStatement query = conn
+                .prepareStatement(SELECT_WORLD, TYPE_FORWARD_ONLY,
+                    CONCUR_READ_ONLY)) {
+              result.add(findOne(query, id));
+            }
+          }
+        }
+        ctx.setContentType(json);
+        return ctx.sendBytes(mapper.writeValueAsBytes(result));
+      });
+
+      /** Updates: */
+      get("/updates", ctx -> {
+        // bound count
+        int count = queries(ctx);
+        List<World> result = new ArrayList<>();
+        Random rnd = ThreadLocalRandom.current();
+        try (Connection conn = ds.getConnection()) {
+          for (int i = 0; i < count; i++) {
+            int id = rndId(rnd);
+            int newRandomNumber = rndId(rnd);
+            try (final PreparedStatement query = conn
+                .prepareStatement(SELECT_WORLD, TYPE_FORWARD_ONLY, CONCUR_READ_ONLY);
+                final PreparedStatement update = conn.prepareStatement(UPDATE_WORLD)) {
+              // find
+              World world = findOne(query, id);
+
+              // update
+              update.setInt(1, newRandomNumber);
+              update.setInt(2, id);
+              update.execute();
+
+              result.add(new World(world.id, newRandomNumber));
+            }
+          }
+        }
+        ctx.setContentType(json);
+        return ctx.sendBytes(mapper.writeValueAsBytes(result));
+      });
+
+      /** Fortunes: */
+      get("/fortunes", ctx -> {
+        List<Fortune> fortunes = new ArrayList<>();
+        try (Connection connection = ds.getConnection()) {
+          try (PreparedStatement stt = connection
+              .prepareStatement("select * from fortune", TYPE_FORWARD_ONLY, CONCUR_READ_ONLY)) {
+            try (ResultSet rs = stt.executeQuery()) {
+              while (rs.next()) {
+                fortunes.add(new Fortune(rs.getInt("id"), rs.getString("message")));
+              }
+            }
+          }
+        }
+        fortunes.add(new Fortune(0, "Additional fortune added at request time."));
+        Collections.sort(fortunes);
+
+        /** render view: */
+        views.fortunes template = views.fortunes.template(fortunes);
+        ArrayOfByteArraysOutput buff = template.render(ArrayOfByteArraysOutput.FACTORY);
+        ctx.setContentLength(buff.getByteLength());
+        try (OutputStream output = ctx.responseStream(html)) {
+          for (byte[] chunk : buff.getArrays()) {
+            output.write(chunk);
+          }
+        }
+        return ctx;
+      });
+    });
+  }
+
+  public static void main(final String[] args) {
+    run(App::new, EVENT_LOOP, args);
+  }
+
+  private World findOne(DataSource ds, int id) throws SQLException {
+    try (Connection conn = ds.getConnection()) {
+      try (PreparedStatement query = conn
+          .prepareStatement(SELECT_WORLD, TYPE_FORWARD_ONLY,
+              CONCUR_READ_ONLY)) {
+        return findOne(query, id);
+      }
+    }
+  }
+
+  private World findOne(PreparedStatement query, int id) throws SQLException {
+    query.setInt(1, id);
+    try (ResultSet resultSet = query.executeQuery()) {
+      resultSet.next();
+      return new World(resultSet.getInt("id"), resultSet.getInt("randomNumber"));
+    }
+  }
+
+  private final int rndId(Random rnd) {
+    return rnd.nextInt(DB_ROWS) + 1;
+  }
+
+  private int   queries(Context ctx) {
+    String value = ctx.query("queries").value("");
+    if (value.length() == 0) {
+      return 1;
+    }
+    try {
+      return Math.min(500, Math.max(1, Integer.parseInt(value)));
+    } catch (NumberFormatException x) {
+      return 1;
+    }
+  }
+}

+ 17 - 0
frameworks/Java/jooby2/src/main/java/com/techempower/Fortune.java

@@ -0,0 +1,17 @@
+package com.techempower;
+
+public final class Fortune implements Comparable<Fortune> {
+  public final int id;
+
+  public final String message;
+
+  public Fortune(int id, String message) {
+    this.id = id;
+    this.message = message;
+  }
+
+  @Override
+  public int compareTo(Fortune other) {
+    return message.compareTo(other.message);
+  }
+}

+ 13 - 0
frameworks/Java/jooby2/src/main/java/com/techempower/World.java

@@ -0,0 +1,13 @@
+package com.techempower;
+
+public class World {
+
+  public final int id;
+  public final int randomNumber;
+
+  public World(int id, int randomNumber) {
+    this.id = id;
+    this.randomNumber = randomNumber;
+  }
+
+}