Prechádzať zdrojové kódy

[Javalin] Complete rewrite (#6377)

* Update to Javalin 3

* Add database tests: PostgreSQL & MongoDB
miguelmp-dev 4 rokov pred
rodič
commit
9fd7a0895d
22 zmenil súbory, kde vykonal 815 pridanie a 98 odobranie
  1. 41 13
      frameworks/Java/javalin/README.md
  2. 48 5
      frameworks/Java/javalin/benchmark_config.json
  3. 0 32
      frameworks/Java/javalin/build.gradle
  4. 0 15
      frameworks/Java/javalin/config.toml
  5. 17 0
      frameworks/Java/javalin/javalin-mongodb.dockerfile
  6. 17 0
      frameworks/Java/javalin/javalin-postgres.dockerfile
  7. 12 12
      frameworks/Java/javalin/javalin.dockerfile
  8. 98 0
      frameworks/Java/javalin/pom.xml
  9. 0 2
      frameworks/Java/javalin/settings.gradle
  10. 119 0
      frameworks/Java/javalin/src/main/java/benchmark/Main.java
  11. 38 0
      frameworks/Java/javalin/src/main/java/benchmark/model/Fortune.java
  12. 38 0
      frameworks/Java/javalin/src/main/java/benchmark/model/World.java
  13. 68 0
      frameworks/Java/javalin/src/main/java/benchmark/repository/DbFactory.java
  14. 33 0
      frameworks/Java/javalin/src/main/java/benchmark/repository/DbService.java
  15. 28 0
      frameworks/Java/javalin/src/main/java/benchmark/repository/JDBCConnectionFactory.java
  16. 97 0
      frameworks/Java/javalin/src/main/java/benchmark/repository/JDBCDbService.java
  17. 53 0
      frameworks/Java/javalin/src/main/java/benchmark/repository/MongoDBFactory.java
  18. 80 0
      frameworks/Java/javalin/src/main/java/benchmark/repository/MongoDbService.java
  19. 0 19
      frameworks/Java/javalin/src/main/java/com/lostsys/test/javalin/Bench.java
  20. 18 0
      frameworks/Java/javalin/src/main/resources/fortune.html
  21. 7 0
      frameworks/Java/javalin/src/main/resources/hikari.properties
  22. 3 0
      frameworks/Java/javalin/src/main/resources/mongodb.properties

+ 41 - 13
frameworks/Java/javalin/README.md

@@ -1,17 +1,45 @@
-# Javalin framework benchmarking test
-This is an implementation of [javalin](https://javalin.io/)
-as a portion of a [benchmarking test suite](../) comparing a variety 
-of web development platforms.
+# Javalin Benchmarking Test
 
+### Test Type Implementation Source Code
 
-## Running the application
-Have [gradle](http://gradle.org) installed, then:
-```
-gradle run
-```
+* [All Tests](src/main/java/benchmark/Main.java)
 
-## json test
-Point your browser at `localhost:8080/json`
+## Important Libraries
+The tests were run with:
+* [Java 11](https://openjdk.java.net)
+* [Javalin 3.13.3](https://github.com/tipsy/javalin)
+* [HikariCP 4.0.2](https://github.com/brettwooldridge/HikariCP)
+* [Pebble 3.1.5](https://github.com/PebbleTemplates/pebble)
 
-## plaintext test
-Point your browser at `localhost:8080/plaintext`
+## Test URLs
+### JSON
+
+http://localhost:8080/json
+
+### PLAINTEXT
+
+http://localhost:8080/plaintext
+
+### DB
+
+http://localhost:8080/db
+
+http://localhost:8080/mongodb/db
+
+### QUERY
+
+http://localhost:8080/queries?queries=
+
+http://localhost:8080/mongodb/queries?queries=
+
+### UPDATE
+
+http://localhost:8080/updates?queries=
+
+http://localhost:8080/mongodb/updates?queries=
+
+### FORTUNES
+
+http://localhost:8080/fortunes
+
+http://localhost:8080/mongodb/fortunes

+ 48 - 5
frameworks/Java/javalin/benchmark_config.json

@@ -6,19 +6,62 @@
         "json_url": "/json",
         "plaintext_url": "/plaintext",
         "port": 8080,
-        "approach": "Stripped",
-        "classification": "Platform",
+        "approach": "Realistic",
+        "classification": "Micro",
         "database": "None",
-        "framework": "None",
+        "framework": "javalin",
         "language": "Java",
         "flavor": "None",
         "orm": "Raw",
-        "platform": "Servlet",
+        "platform": "Jetty",
         "webserver": "None",
         "os": "Linux",
         "database_os": "Linux",
         "display_name": "javalin",
-        "versus": ""
+        "notes": "",
+        "versus": "None"
+      },
+      "postgres": {
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "update_url": "/updates?queries=",
+        "fortune_url": "/fortunes",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "Postgres",
+        "framework": "javalin",
+        "language": "Java",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "Jetty",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "javalin-postgres",
+        "notes": "",
+        "versus": "None"
+      },
+      "mongodb": {
+        "db_url": "/mongo/db",
+        "query_url": "/mongo/queries?queries=",
+        "update_url": "/mongo/updates?queries=",
+        "fortune_url": "/mongo/fortunes",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "MongoDB",
+        "framework": "javalin",
+        "language": "Java",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "Jetty",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "javalin-mongodb",
+        "notes": "",
+        "versus": "None"
       }
     }
   ]

+ 0 - 32
frameworks/Java/javalin/build.gradle

@@ -1,32 +0,0 @@
-apply plugin: 'java'
-apply plugin: 'application'
-
-group 'com.lostsys.test.javalin'
-version '1.0-SNAPSHOT'
-
-mainClassName = 'com.lostsys.test.javalin.Bench'
-
-sourceCompatibility = JavaVersion.VERSION_11
-targetCompatibility = JavaVersion.VERSION_11
-
-repositories {
-    mavenCentral()
-}
-
-dependencies {
-    compile 'io.javalin:javalin:2.8.0'
-    compile "com.fasterxml.jackson.core:jackson-databind:2.9.9"
-    compile "org.slf4j:slf4j-simple:1.8.0-beta4"
-}
-
-//create a single Jar with all dependencies
-task fatJar(type: Jar) {
-    manifest {
-        attributes(
-                "Main-Class": mainClassName
-        )
-    }
-    baseName = project.name + '-all'
-    from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
-    with jar
-}

+ 0 - 15
frameworks/Java/javalin/config.toml

@@ -1,15 +0,0 @@
-[framework]
-name = "javalin"
-
-[main]
-urls.plaintext = "/plaintext"
-urls.json = "/json"
-approach = "Stripped"
-classification = "Platform"
-database = "None"
-database_os = "Linux"
-os = "Linux"
-orm = "Raw"
-platform = "Servlet"
-webserver = "None"
-versus = ""

+ 17 - 0
frameworks/Java/javalin/javalin-mongodb.dockerfile

@@ -0,0 +1,17 @@
+FROM maven:3.6.3-jdk-11-slim as maven
+WORKDIR /javalin
+COPY src src
+COPY pom.xml pom.xml
+RUN mvn clean package -q
+
+FROM openjdk:11.0.10-jdk-slim
+WORKDIR /javalin
+COPY --from=maven /javalin/target/javalin-1.0-shaded.jar app.jar
+
+ARG BENCHMARK_ENV
+
+ENV BENCHMARK_ENV=$BENCHMARK_ENV
+
+EXPOSE 8080
+
+CMD ["java", "-server", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-Dlogging.level.root=OFF", "-jar", "app.jar"]

+ 17 - 0
frameworks/Java/javalin/javalin-postgres.dockerfile

@@ -0,0 +1,17 @@
+FROM maven:3.6.3-jdk-11-slim as maven
+WORKDIR /javalin
+COPY src src
+COPY pom.xml pom.xml
+RUN mvn clean package -q
+
+FROM openjdk:11.0.10-jdk-slim
+WORKDIR /javalin
+COPY --from=maven /javalin/target/javalin-1.0-shaded.jar app.jar
+
+ARG BENCHMARK_ENV
+
+ENV BENCHMARK_ENV=$BENCHMARK_ENV
+
+EXPOSE 8080
+
+CMD ["java", "-server", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-Dlogging.level.root=OFF", "-jar", "app.jar"]

+ 12 - 12
frameworks/Java/javalin/javalin.dockerfile

@@ -1,17 +1,17 @@
-FROM gradle:5.4.1-jdk11 as gradle
-USER root
+FROM maven:3.6.3-jdk-11-slim as maven
 WORKDIR /javalin
-COPY build.gradle build.gradle
 COPY src src
-RUN gradle --refresh-dependencies clean fatJar
+COPY pom.xml pom.xml
+RUN mvn clean package -q
+
+FROM openjdk:11.0.10-jdk-slim
+WORKDIR /javalin
+COPY --from=maven /javalin/target/javalin-1.0-shaded.jar app.jar
+
+ARG BENCHMARK_ENV
+
+ENV BENCHMARK_ENV=$BENCHMARK_ENV
 
 EXPOSE 8080
 
-CMD java \
-    -Xmx2G \
-    -Xms2G \
-    -server \
-    -XX:+UseNUMA \
-    -XX:+UseParallelGC \
-    -XX:+AggressiveOpts \
-    -jar build/libs/javalin-all-1.0-SNAPSHOT.jar env=prod
+CMD ["java", "-server", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-Dlogging.level.root=OFF", "-jar", "app.jar"]

+ 98 - 0
frameworks/Java/javalin/pom.xml

@@ -0,0 +1,98 @@
+<?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/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>benchmark</groupId>
+    <artifactId>javalin</artifactId>
+    <version>1.0</version>
+
+    <properties>
+        <maven.compiler.source>11</maven.compiler.source>
+        <maven.compiler.target>11</maven.compiler.target>
+        <javalin.version>3.13.3</javalin.version>
+        <slf4j.version>1.7.30</slf4j.version>
+        <jackson.version>2.12.0</jackson.version>
+        <hikaricp.version>4.0.2</hikaricp.version>
+        <postgresql.version>42.2.16</postgresql.version>
+        <mongodb.version>4.2.0</mongodb.version>
+        <pebble.version>3.1.5</pebble.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>io.javalin</groupId>
+            <artifactId>javalin</artifactId>
+            <version>${javalin.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <version>${slf4j.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <version>${jackson.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.zaxxer</groupId>
+            <artifactId>HikariCP</artifactId>
+            <version>${hikaricp.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.postgresql</groupId>
+            <artifactId>postgresql</artifactId>
+            <version>${postgresql.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.mongodb</groupId>
+            <artifactId>mongodb-driver-sync</artifactId>
+            <version>${mongodb.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.pebbletemplates</groupId>
+            <artifactId>pebble</artifactId>
+            <version>${pebble.version}</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.8.1</version>
+                <configuration>
+                    <source>${maven.compiler.source}</source>
+                    <target>${maven.compiler.target}</target>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-shade-plugin</artifactId>
+                <version>3.2.4</version>
+                <executions>
+                    <!-- Run shade goal on package phase -->
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>shade</goal>
+                        </goals>
+                        <configuration>
+                            <shadedArtifactAttached>true</shadedArtifactAttached>
+                            <transformers>
+                                <!-- add Main-Class to manifest file -->
+                                <transformer
+                                        implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+                                    <mainClass>benchmark.Main</mainClass>
+                                </transformer>
+                            </transformers>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>

+ 0 - 2
frameworks/Java/javalin/settings.gradle

@@ -1,2 +0,0 @@
-rootProject.name = 'javalin'
-

+ 119 - 0
frameworks/Java/javalin/src/main/java/benchmark/Main.java

@@ -0,0 +1,119 @@
+package benchmark;
+
+import benchmark.model.Fortune;
+import benchmark.repository.DbFactory;
+import benchmark.repository.DbService;
+import io.javalin.Javalin;
+import io.javalin.core.compression.CompressionStrategy;
+import io.javalin.http.Context;
+import io.javalin.plugin.rendering.template.JavalinPebble;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+public class Main {
+
+    public static final int MIN_QUERIES = 1;
+    public static final int MAX_QUERIES = 500;
+    public static final int SERVICE_UNAVAILABLE_CODE = 503;
+    public static final String SERVICE_UNAVAILABLE_TEXT = "503 Service Unavailable";
+
+    public static void main(String[] args) {
+
+        Javalin app = Javalin
+                .create(config -> config.compressionStrategy(CompressionStrategy.NONE))
+                .start(8080);
+
+        app.get("/plaintext", Main::handlePlainText);
+        app.get("/json", Main::handleJson);
+
+        // PostgreSQL
+        app.get("/db", Main::handleSingleDbQuery);
+        app.get("/queries", Main::handleMultipleDbQueries);
+        app.get("/fortunes", Main::handleFortunes);
+        app.get("/updates", Main::handleUpdates);
+
+        // MongoDb
+        app.get("/mongo/db", Main::handleSingleDbQuery);
+        app.get("/mongo/queries", Main::handleMultipleDbQueries);
+        app.get("/mongo/fortunes", Main::handleFortunes);
+        app.get("/mongo/updates", Main::handleUpdates);
+    }
+
+
+    private static void handlePlainText(Context ctx) {
+        ctx.result("Hello, World!");
+    }
+
+    private static void handleJson(Context ctx) {
+        ctx.json(Collections.singletonMap("message", "Hello, World!"));
+    }
+
+    private static void handleSingleDbQuery(Context ctx) {
+
+        DbService dbService = getDbServiceFromPath(ctx.path());
+
+        try {
+            ctx.json(dbService.getWorld(1).get(0));
+        } catch (Throwable t) {
+            ctx.status(SERVICE_UNAVAILABLE_CODE).result(SERVICE_UNAVAILABLE_TEXT);
+        }
+    }
+
+    private static void handleMultipleDbQueries(Context ctx) {
+
+        int num = getBoundedRowNumber(ctx.queryParam("queries", String.class).getOrNull());
+        DbService dbService = getDbServiceFromPath(ctx.path());
+
+        try {
+            ctx.json(dbService.getWorld(num));
+        } catch (Throwable t) {
+            ctx.status(SERVICE_UNAVAILABLE_CODE).result(SERVICE_UNAVAILABLE_TEXT);
+        }
+    }
+
+    private static void handleFortunes(Context ctx) {
+
+        DbService dbService = getDbServiceFromPath(ctx.path());
+
+        try {
+            List<Fortune> fortuneList = dbService.getFortune();
+            Map<String, List<Fortune>> map = Collections.singletonMap("list", fortuneList);
+            ctx.html(JavalinPebble.INSTANCE.render("fortune.html", map, ctx))
+                    .header("Content-Type", "text/html; charset=utf-8");
+        } catch (Throwable t) {
+            ctx.status(SERVICE_UNAVAILABLE_CODE).result(SERVICE_UNAVAILABLE_TEXT);
+        }
+    }
+
+    private static void handleUpdates(Context ctx) {
+
+        int num = getBoundedRowNumber(ctx.queryParam("queries", String.class).getOrNull());
+        DbService dbService = getDbServiceFromPath(ctx.path());
+
+        try {
+            ctx.json(dbService.updateWorld(num));
+        } catch (Throwable t) {
+            ctx.status(SERVICE_UNAVAILABLE_CODE).result(SERVICE_UNAVAILABLE_TEXT);
+        }
+    }
+
+    private static DbService getDbServiceFromPath(String path) {
+
+        return (path.contains("mongo")) ?
+                DbFactory.INSTANCE.getDbService(DbFactory.DbType.MONGODB) :
+                DbFactory.INSTANCE.getDbService(DbFactory.DbType.POSTGRES);
+    }
+
+    private static int getBoundedRowNumber(String number) {
+
+        int num;
+        try {
+            num = Integer.parseInt(number);
+        } catch (NumberFormatException e) {
+            num = MIN_QUERIES;
+        }
+        return Math.max(MIN_QUERIES, Math.min(num, MAX_QUERIES));
+    }
+}

+ 38 - 0
frameworks/Java/javalin/src/main/java/benchmark/model/Fortune.java

@@ -0,0 +1,38 @@
+package benchmark.model;
+
+public class Fortune {
+
+    private int id;
+    private String message;
+
+    public Fortune() {}
+
+    public Fortune(int id, String message) {
+        this.id = id;
+        this.message = message;
+    }
+
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+
+    @Override
+    public String toString() {
+        return "Fortune{" +
+                "id=" + id +
+                ", message='" + message + '\'' +
+                '}';
+    }
+}

+ 38 - 0
frameworks/Java/javalin/src/main/java/benchmark/model/World.java

@@ -0,0 +1,38 @@
+package benchmark.model;
+
+public class World {
+
+    private int id;
+    private int randomNumber;
+
+    public World() {}
+
+    public World(int id, int randomNumber) {
+        this.id = id;
+        this.randomNumber = randomNumber;
+    }
+
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    public int getRandomNumber() {
+        return randomNumber;
+    }
+
+    public void setRandomNumber(int randomNumber) {
+        this.randomNumber = randomNumber;
+    }
+
+    @Override
+    public String toString() {
+        return "World{" +
+                "id=" + id +
+                ", randomNumber=" + randomNumber +
+                '}';
+    }
+}

+ 68 - 0
frameworks/Java/javalin/src/main/java/benchmark/repository/DbFactory.java

@@ -0,0 +1,68 @@
+package benchmark.repository;
+
+public enum DbFactory {
+
+    INSTANCE;
+
+    static final String PHYSICAL_TAG = "Citrine";
+    static final String CLOUD_TAG = "Azure";
+
+    private static final int POSTGRES_PHYSICAL_POOL_SIZE = 112;
+    private static final int POSTGRES_CLOUD_POOL_SIZE = 16;
+    private static final int POSTGRES_DEFAULT_POOL_SIZE = 10;
+
+    private static final int MONGODB_PHYSICAL_POOL_SIZE = 200;
+    private static final int MONGODB_CLOUD_POOL_SIZE = 100;
+    private static final int MONGODB_DEFAULT_POOL_SIZE = 50;
+
+    public enum DbType {POSTGRES, MONGODB}
+
+    public DbService getDbService(DbType type) {
+
+        DbService dbService;
+
+        switch(type) {
+            case POSTGRES:
+                dbService=  new JDBCDbService();
+                break;
+            case MONGODB:
+                dbService = new MongoDbService();
+                break;
+            default:
+                dbService = null;
+        }
+
+        return dbService;
+    }
+
+    public int getMaxPoolSize(DbType dbType) {
+
+        int maxPoolSize;
+        String env = System.getenv("BENCHMARK_ENV");
+
+        switch (dbType) {
+            case POSTGRES:
+                if (PHYSICAL_TAG.equals(env)) {
+                    maxPoolSize = POSTGRES_PHYSICAL_POOL_SIZE;
+                } else if (CLOUD_TAG.equals(env)) {
+                    maxPoolSize = POSTGRES_CLOUD_POOL_SIZE;
+                } else {
+                    maxPoolSize = POSTGRES_DEFAULT_POOL_SIZE;
+                }
+                break;
+            case MONGODB:
+                if (PHYSICAL_TAG.equals(env)) {
+                    maxPoolSize = MONGODB_PHYSICAL_POOL_SIZE;
+                } else if (CLOUD_TAG.equals(env)) {
+                    maxPoolSize = MONGODB_CLOUD_POOL_SIZE;
+                } else {
+                    maxPoolSize = MONGODB_DEFAULT_POOL_SIZE;
+                }
+                break;
+            default:
+                maxPoolSize = 100;
+        }
+
+        return maxPoolSize;
+    }
+}

+ 33 - 0
frameworks/Java/javalin/src/main/java/benchmark/repository/DbService.java

@@ -0,0 +1,33 @@
+package benchmark.repository;
+
+import benchmark.model.Fortune;
+import benchmark.model.World;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ThreadLocalRandom;
+
+public interface DbService {
+
+    int MIN_RANDOM_NUMBER = 1;
+    int MAX_RANDOM_NUMBER_PLUS_ONE = 10001;
+    int defaultFortuneId = 0;
+    String defaultFortuneMessage = "Additional fortune added at request time.";
+
+
+    List<World> getWorld(int num);
+    List<Fortune> getFortune();
+    List<World> updateWorld(int num);
+
+    default int getRandomNumber() {
+        return ThreadLocalRandom.current().nextInt(MIN_RANDOM_NUMBER, MAX_RANDOM_NUMBER_PLUS_ONE);
+    }
+
+    default Set<Integer> getRandomNumberSet(int num) {
+        Set<Integer> set = new HashSet<>();
+        while (set.size() < num)
+            set.add(getRandomNumber());
+        return set;
+    }
+}

+ 28 - 0
frameworks/Java/javalin/src/main/java/benchmark/repository/JDBCConnectionFactory.java

@@ -0,0 +1,28 @@
+package benchmark.repository;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+
+public enum JDBCConnectionFactory {
+
+    INSTANCE;
+
+    private final HikariDataSource ds;
+
+    JDBCConnectionFactory() {
+        String propertiesFileName = "/hikari.properties";
+        HikariConfig config = new HikariConfig(propertiesFileName);
+        int maxPoolSize = DbFactory.INSTANCE.getMaxPoolSize(DbFactory.DbType.POSTGRES);
+
+        ds = new HikariDataSource(config);
+        ds.setMaximumPoolSize(maxPoolSize);
+    }
+
+    public Connection getConnection() throws SQLException {
+        return ds.getConnection();
+    }
+}

+ 97 - 0
frameworks/Java/javalin/src/main/java/benchmark/repository/JDBCDbService.java

@@ -0,0 +1,97 @@
+package benchmark.repository;
+
+import benchmark.model.Fortune;
+import benchmark.model.World;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+
+public class JDBCDbService implements DbService {
+
+    @Override
+    public List<World> getWorld(int num) {
+
+        String select = "select id, randomNumber from World where id = ?";
+        List<World> worldList = new ArrayList<>();
+
+        try (Connection conn = JDBCConnectionFactory.INSTANCE.getConnection();
+             PreparedStatement pstm = conn.prepareStatement(select)) {
+
+            for (int randomId : getRandomNumberSet(num)) {
+                pstm.setInt(1, randomId);
+                try (ResultSet rs = pstm.executeQuery()) {
+                    rs.next();
+                    World world = new World();
+                    world.setId(rs.getInt("id"));
+                    world.setRandomNumber(rs.getInt("randomNumber"));
+                    worldList.add(world);
+                }
+            }
+        } catch (SQLException e) {
+            throw new RuntimeException(e);
+        }
+
+        return worldList;
+    }
+
+    @Override
+    public List<Fortune> getFortune() {
+
+        String select = "select id, message from Fortune";
+        List<Fortune> fortuneList = new ArrayList<>();
+
+        try (Connection conn = JDBCConnectionFactory.INSTANCE.getConnection();
+             PreparedStatement pstm = conn.prepareStatement(select);
+             ResultSet rs = pstm.executeQuery()) {
+
+            while (rs.next()) {
+                Fortune fortune = new Fortune();
+                fortune.setId(rs.getInt("id"));
+                fortune.setMessage(rs.getString("message"));
+                fortuneList.add(fortune);
+            }
+            fortuneList.add(new Fortune(defaultFortuneId, defaultFortuneMessage));
+        } catch (SQLException e) {
+            throw new RuntimeException(e);
+        }
+
+        fortuneList.sort(Comparator.comparing(Fortune::getMessage));
+        return fortuneList;
+    }
+
+    @Override
+    public List<World> updateWorld(int num) {
+
+        String update = "update World set randomNumber = ? where id = ?";
+        List<World> worldList = getWorld(num);
+
+        try (Connection conn = JDBCConnectionFactory.INSTANCE.getConnection();
+             PreparedStatement pstm = conn.prepareStatement(update)) {
+
+            conn.setAutoCommit(false);
+            for (World world : worldList) {
+                int newRandomNumber;
+                do {
+                    newRandomNumber = getRandomNumber();
+                } while (newRandomNumber == world.getRandomNumber());
+
+                pstm.setInt(1, newRandomNumber);
+                pstm.setInt(2, world.getId());
+                pstm.addBatch();
+
+                world.setRandomNumber(newRandomNumber);
+            }
+            pstm.executeBatch();
+            conn.commit();
+        } catch (SQLException e) {
+            throw new RuntimeException(e);
+        }
+
+        return worldList;
+    }
+}

+ 53 - 0
frameworks/Java/javalin/src/main/java/benchmark/repository/MongoDBFactory.java

@@ -0,0 +1,53 @@
+package benchmark.repository;
+
+import com.mongodb.MongoClientSettings;
+import com.mongodb.ServerAddress;
+import com.mongodb.client.MongoClient;
+import com.mongodb.client.MongoClients;
+import com.mongodb.client.MongoDatabase;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.Properties;
+
+public enum MongoDBFactory {
+
+    INSTANCE;
+
+    private final MongoDatabase db;
+
+    MongoDBFactory() {
+
+        String propertiesFileName = "/mongodb.properties";
+        File propFile = new File(propertiesFileName);
+
+        try (InputStream is = propFile.isFile() ?
+                new FileInputStream(propFile) :
+                this.getClass().getResourceAsStream(propertiesFileName)) {
+            Properties prop = new Properties();
+            prop.load(is);
+
+            String host = prop.getProperty("host");
+            int port = Integer.parseInt(prop.getProperty("port"));
+            ServerAddress server = new ServerAddress(host, port);
+            int maxPoolSize = DbFactory.INSTANCE.getMaxPoolSize(DbFactory.DbType.MONGODB);
+
+            MongoClientSettings settings = MongoClientSettings.builder()
+                    .applyToClusterSettings(builder -> builder.hosts(Collections.singletonList(server)))
+                    .applyToConnectionPoolSettings(builder -> builder.maxSize(maxPoolSize))
+                    .build();
+
+            MongoClient client = MongoClients.create(settings);
+            db = client.getDatabase(prop.getProperty("database"));
+        } catch (IOException e) {
+            throw new RuntimeException("Failed to read property file " + propertiesFileName);
+        }
+    }
+
+    public MongoDatabase getDatabase() {
+        return db;
+    }
+}

+ 80 - 0
frameworks/Java/javalin/src/main/java/benchmark/repository/MongoDbService.java

@@ -0,0 +1,80 @@
+package benchmark.repository;
+
+import benchmark.model.Fortune;
+import benchmark.model.World;
+import com.mongodb.client.MongoCollection;
+import com.mongodb.client.MongoDatabase;
+import com.mongodb.client.model.UpdateOneModel;
+import org.bson.Document;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+
+import static com.mongodb.client.model.Filters.eq;
+
+public class MongoDbService implements DbService {
+
+    @Override
+    public List<World> getWorld(int num) {
+
+        List<World> worldList = new ArrayList<>();
+        MongoDatabase db = MongoDBFactory.INSTANCE.getDatabase();
+        MongoCollection<Document> collection = db.getCollection("world");
+
+        for (int randomId : getRandomNumberSet(num)) {
+            Document doc = collection.find(eq("_id", randomId)).first();
+            if (doc != null) {
+                World world = new World();
+                world.setId(((Double) doc.get("_id")).intValue());
+                world.setRandomNumber(((Double) doc.get("randomNumber")).intValue());
+                worldList.add(world);
+            }
+        }
+
+        return worldList;
+    }
+
+    @Override
+    public List<Fortune> getFortune() {
+
+        List<Fortune> fortuneList = new ArrayList<>();
+        MongoDatabase db = MongoDBFactory.INSTANCE.getDatabase();
+        MongoCollection<Document> collection = db.getCollection("fortune");
+
+        collection.find().forEach(doc -> {
+                Fortune fortune = new Fortune();
+                fortune.setId(((Double)doc.get("_id")).intValue());
+                fortune.setMessage((String)doc.get("message"));
+                fortuneList.add(fortune);
+            });
+
+        fortuneList.add(new Fortune(defaultFortuneId, defaultFortuneMessage));
+
+        fortuneList.sort(Comparator.comparing(Fortune::getMessage));
+        return fortuneList;
+    }
+
+    @Override
+    public List<World> updateWorld(int num) {
+
+        MongoDatabase db = MongoDBFactory.INSTANCE.getDatabase();
+        MongoCollection<Document> collection = db.getCollection("world");
+        List<UpdateOneModel<Document>> bulkList = new ArrayList<>();
+        List<World> worldList = getWorld(num);
+
+        for (World world : worldList) {
+            int newRandomNum;
+            do {
+                newRandomNum = getRandomNumber();
+            } while (newRandomNum == world.getRandomNumber());
+            world.setRandomNumber(newRandomNum);
+            bulkList.add(new UpdateOneModel<>(new Document("_id", world.getId()),
+                    new Document("$set", new Document("randomNumber",
+                            ((Integer)world.getRandomNumber()).doubleValue()))));
+        }
+        collection.bulkWrite(bulkList);
+
+        return worldList;
+    }
+}

+ 0 - 19
frameworks/Java/javalin/src/main/java/com/lostsys/test/javalin/Bench.java

@@ -1,19 +0,0 @@
-package com.lostsys.test.javalin;
-
-import io.javalin.Javalin;
-
-import java.util.Collections;
-
-public class Bench {
-
-    public static void main(String[] args) {
-
-        Javalin app = Javalin.create().start(8080);
-        app.get("/plaintext", ctx -> ctx.result("Hello, World!"));
-
-        app.get("/json", ctx -> {
-            ctx.json(Collections.singletonMap("message", "Hello, World!"));
-        });
-
-    }
-}

+ 18 - 0
frameworks/Java/javalin/src/main/resources/fortune.html

@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<head><title>Fortunes</title></head>
+<body>
+<table>
+    <tr>
+        <th>id</th>
+        <th>message</th>
+    </tr>
+    {% for fortune in list %}
+    <tr>
+        <td>{{ fortune.getId() }}</td>
+        <td>{{ fortune.getMessage() }}</td>
+    </tr>
+    {% endfor %}
+</table>
+</body>
+</html>

+ 7 - 0
frameworks/Java/javalin/src/main/resources/hikari.properties

@@ -0,0 +1,7 @@
+dataSourceClassName=org.postgresql.ds.PGSimpleDataSource
+dataSource.serverName=tfb-database
+dataSource.portNumber=5432
+dataSource.user=benchmarkdbuser
+dataSource.password=benchmarkdbpass
+dataSource.databaseName=hello_world
+

+ 3 - 0
frameworks/Java/javalin/src/main/resources/mongodb.properties

@@ -0,0 +1,3 @@
+host=tfb-database
+port=27017
+database=hello_world