Kaynağa Gözat

Merge pull request #9556 from spericas/helidon-4.1.5-test2

Upgrades to Helidon 4.1.5
Mike Smith 7 ay önce
ebeveyn
işleme
86bcd44b16

+ 13 - 12
frameworks/Java/helidon/nima/pom.xml

@@ -21,7 +21,7 @@
     <parent>
         <groupId>io.helidon.applications</groupId>
         <artifactId>helidon-se</artifactId>
-        <version>4.1.2</version>
+        <version>4.1.5</version>
         <relativePath/>
     </parent>
 
@@ -38,6 +38,7 @@
         <rocker.version>1.3.0</rocker.version>
         <vertx-pg-client.version>4.5.3</vertx-pg-client.version>
         <jsoniter.version>0.9.23</jsoniter.version>
+        <jte.version>3.1.15</jte.version>
     </properties>
 
     <dependencies>
@@ -78,9 +79,9 @@
             <version>42.6.1</version>
         </dependency>
         <dependency>
-            <groupId>com.fizzed</groupId>
-            <artifactId>rocker-runtime</artifactId>
-            <version>${rocker.version}</version>
+            <groupId>gg.jte</groupId>
+            <artifactId>jte</artifactId>
+            <version>${jte.version}</version>
         </dependency>
         <dependency>
             <groupId>io.helidon.common.testing</groupId>
@@ -98,7 +99,6 @@
             <scope>test</scope>
         </dependency>
     </dependencies>
-
     <build>
         <plugins>
             <plugin>
@@ -125,20 +125,21 @@
                     </execution>
                 </executions>
             </plugin>
+
             <plugin>
-                <groupId>com.fizzed</groupId>
-                <artifactId>rocker-maven-plugin</artifactId>
-                <version>${rocker.version}</version>
+                <groupId>gg.jte</groupId>
+                <artifactId>jte-maven-plugin</artifactId>
+                <version>${jte.version}</version>
+                <configuration>
+                    <sourceDirectory>${project.basedir}/src/main/resources/views</sourceDirectory>
+                    <contentType>Html</contentType>
+                </configuration>
                 <executions>
                     <execution>
-                        <id>generate-rocker-templates</id>
                         <phase>generate-sources</phase>
                         <goals>
                             <goal>generate</goal>
                         </goals>
-                        <configuration>
-                            <templateDirectory>src/main/resources</templateDirectory>
-                        </configuration>
                     </execution>
                 </executions>
             </plugin>

+ 23 - 18
frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/JsonSerializer.java

@@ -2,8 +2,8 @@ package io.helidon.benchmark.nima;
 
 import java.io.IOException;
 import java.util.Arrays;
-import java.util.Map;
 import java.util.List;
+import java.util.Map;
 
 import com.jsoniter.output.JsonStream;
 import com.jsoniter.output.JsonStreamPool;
@@ -15,7 +15,7 @@ public class JsonSerializer {
     }
 
     /**
-     * Serialize an instance into a JSON object and return it as a byte array.
+     * Serialize an instance into a byte array.
      *
      * @param obj the instance
      * @return the byte array
@@ -28,19 +28,31 @@ public class JsonSerializer {
             return Arrays.copyOfRange(stream.buffer().data(), 0, stream.buffer().tail());
         } catch (IOException e) {
             throw new JsonException(e);
-        } finally {
-            JsonStreamPool.returnJsonStream(stream);
         }
     }
 
     /**
-     * Serialize a map of strings into a JSON object and return it as a byte array.
+     * Serialize an instance into a JSON stream.
+     *
+     * @param obj the instance
+     * @param stream the JSON stream
+     */
+    public static void serialize(Object obj, JsonStream stream) {
+        try {
+            stream.reset(null);
+            stream.writeVal(obj.getClass(), obj);
+        } catch (IOException e) {
+            throw new JsonException(e);
+        }
+    }
+
+    /**
+     * Serialize a map of strings into a JSON stream.
      *
      * @param map the map
-     * @return the byte array
+     * @param stream the JSON stream
      */
-    public static byte[] serialize(Map<String, String> map) {
-        JsonStream stream = JsonStreamPool.borrowJsonStream();
+    public static void serialize(Map<String, String> map, JsonStream stream) {
         try {
             stream.reset(null);
             stream.writeObjectStart();
@@ -53,22 +65,18 @@ public class JsonSerializer {
                 }
             });
             stream.writeObjectEnd();
-            return Arrays.copyOfRange(stream.buffer().data(), 0, stream.buffer().tail());
         } catch (IOException e) {
             throw new JsonException(e);
-        } finally {
-            JsonStreamPool.returnJsonStream(stream);
         }
     }
 
     /**
-     * Serialize a list of objects into a JSON array and return it as a byte array.
+     * Serialize a list of objects into a JSON stream.
      *
      * @param objs the list of objects
-     * @return the byte array
+     * @param stream the JSON stream
      */
-    public static byte[] serialize(List<?> objs) {
-        JsonStream stream = JsonStreamPool.borrowJsonStream();
+    public static void serialize(List<?> objs, JsonStream stream) {
         try {
             stream.reset(null);
             stream.writeArrayStart();
@@ -82,11 +90,8 @@ public class JsonSerializer {
 
             }
             stream.writeArrayEnd();
-            return Arrays.copyOfRange(stream.buffer().data(), 0, stream.buffer().tail());
         } catch (IOException e) {
             throw new JsonException(e);
-        } finally {
-            JsonStreamPool.returnJsonStream(stream);
         }
     }
 }

+ 18 - 10
frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/Main.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Oracle and/or its affiliates.
+ * Copyright (c) 2022, 2025 Oracle and/or its affiliates.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,16 +19,18 @@ package io.helidon.benchmark.nima;
 import java.nio.charset.StandardCharsets;
 import java.util.logging.Logger;
 
+import com.jsoniter.output.JsonStream;
+import com.jsoniter.output.JsonStreamPool;
 import io.helidon.benchmark.nima.models.DbRepository;
 import io.helidon.benchmark.nima.models.HikariJdbcRepository;
 import io.helidon.benchmark.nima.models.PgClientRepository;
 import io.helidon.benchmark.nima.services.DbService;
 import io.helidon.benchmark.nima.services.FortuneHandler;
+import io.helidon.config.Config;
+import io.helidon.config.ConfigException;
 import io.helidon.http.Header;
 import io.helidon.http.HeaderNames;
 import io.helidon.http.HeaderValues;
-import io.helidon.config.Config;
-import io.helidon.config.ConfigException;
 import io.helidon.logging.common.LogConfig;
 import io.helidon.webserver.WebServer;
 import io.helidon.webserver.http.Handler;
@@ -93,7 +95,7 @@ public final class Main {
 
     static class PlaintextHandler implements Handler {
         static final Header CONTENT_TYPE = HeaderValues.createCached(HeaderNames.CONTENT_TYPE,
-                                                                     "text/plain; charset=UTF-8");
+                "text/plain; charset=UTF-8");
         static final Header CONTENT_LENGTH = HeaderValues.createCached(HeaderNames.CONTENT_LENGTH, "13");
         private static final byte[] RESPONSE_BYTES = "Hello, World!".getBytes(StandardCharsets.UTF_8);
 
@@ -110,14 +112,20 @@ public final class Main {
         private static final String MESSAGE = "Hello, World!";
         private static final int JSON_LENGTH = serialize(new Message(MESSAGE)).length;
         static final Header CONTENT_LENGTH = HeaderValues.createCached(HeaderNames.CONTENT_LENGTH,
-                                                                       String.valueOf(JSON_LENGTH));
+                String.valueOf(JSON_LENGTH));
 
         @Override
         public void handle(ServerRequest req, ServerResponse res) {
-            res.header(CONTENT_LENGTH);
-            res.header(HeaderValues.CONTENT_TYPE_JSON);
-            res.header(Main.SERVER);
-            res.send(serialize(new Message(MESSAGE)));
+            JsonStream stream = JsonStreamPool.borrowJsonStream();
+            try {
+                res.header(CONTENT_LENGTH);
+                res.header(HeaderValues.CONTENT_TYPE_JSON);
+                res.header(Main.SERVER);
+                serialize(new Message(MESSAGE), stream);
+                res.send(stream.buffer().data(), 0, stream.buffer().tail());
+            } finally {
+                JsonStreamPool.returnJsonStream(stream);
+            }
         }
     }
 
@@ -147,4 +155,4 @@ public final class Main {
             return message;
         }
     }
-}
+}

+ 5 - 1
frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/Fortune.java

@@ -1,7 +1,7 @@
 
 package io.helidon.benchmark.nima.models;
 
-public final class Fortune {
+public final class Fortune implements Comparable<Fortune> {
     public int id;
     public String message;
 
@@ -17,4 +17,8 @@ public final class Fortune {
     public String getMessage() {
         return message;
     }
+    @Override
+    public int compareTo(Fortune other) {
+        return message.compareTo(other.message);
+    }
 }

+ 143 - 0
frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientConnectionPool.java

@@ -0,0 +1,143 @@
+
+package io.helidon.benchmark.nima.models;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.locks.ReentrantLock;
+
+import io.vertx.core.Vertx;
+import io.vertx.pgclient.PgConnectOptions;
+import io.vertx.pgclient.PgConnection;
+import io.vertx.sqlclient.PreparedQuery;
+import io.vertx.sqlclient.Row;
+import io.vertx.sqlclient.RowSet;
+
+class PgClientConnectionPool implements AutoCloseable {
+
+    private final Vertx vertx;
+    private final PgConnectOptions options;
+    private final ReentrantLock lock = new ReentrantLock();
+    private final Map<String, PgClientConnection> connectionMap = new HashMap<>();
+
+    public PgClientConnectionPool(Vertx vertx, PgConnectOptions options) {
+        this.vertx = vertx;
+        this.options = options;
+    }
+
+    public PgClientConnection clientConnection() {
+        String carrierThread = carrierThread();
+        PgClientConnection connection = connectionMap.get(carrierThread);
+        if (connection == null) {
+            try {
+                lock.lock();
+                connection = connectionMap.get(carrierThread);
+                if (connection == null) {
+                    connection = newConnection();
+                    connectionMap.put(carrierThread, connection);
+                }
+            } finally {
+                lock.unlock();
+            }
+        }
+        return connection;
+    }
+
+    @Override
+    public void close() {
+        try {
+            for (PgClientConnection connection : connectionMap.values()) {
+                connection.close();
+            }
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private PgClientConnection newConnection() {
+        try {
+            PgConnection conn = PgConnection.connect(vertx, options)
+                    .toCompletionStage().toCompletableFuture().get();
+            PgClientConnection clientConn = new PgClientConnection(conn);
+            clientConn.prepare();
+            return clientConn;
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    static String carrierThread() {
+        String threadName = Thread.currentThread().toString();
+        return threadName.substring(threadName.indexOf('@') + 1);
+    }
+
+    public static class PgClientConnection implements AutoCloseable {
+        static final int UPDATE_QUERIES = 500;
+        private static String SELECT_WORLD = "SELECT id, randomnumber from WORLD where id=$1";
+        private static String SELECT_FORTUNE = "SELECT * from FORTUNE";
+
+        private PreparedQuery<RowSet<Row>> worldQuery;
+        private PreparedQuery<RowSet<Row>> fortuneQuery;
+        private PreparedQuery<RowSet<Row>>[] updateQuery;
+
+        private final PgConnection conn;
+
+        PgClientConnection(PgConnection conn) {
+            this.conn = conn;
+        }
+
+        public PgConnection pgConnection() {
+            return conn;
+        }
+
+        @Override
+        public void close() {
+            conn.close();
+        }
+
+        public PreparedQuery<RowSet<Row>> worldQuery() {
+            return worldQuery;
+        }
+
+        public PreparedQuery<RowSet<Row>> fortuneQuery() {
+            return fortuneQuery;
+        }
+
+        public PreparedQuery<RowSet<Row>> updateQuery(int queryCount) {
+            return updateQuery[queryCount - 1];
+        }
+
+        @SuppressWarnings("unchecked")
+        void prepare() {
+            try {
+                worldQuery = conn.prepare(SELECT_WORLD)
+                        .toCompletionStage().toCompletableFuture().get().query();
+                fortuneQuery = conn.prepare(SELECT_FORTUNE)
+                        .toCompletionStage().toCompletableFuture().get().query();
+                updateQuery = (PreparedQuery<RowSet<Row>>[]) new PreparedQuery<?>[UPDATE_QUERIES];
+                for (int i = 0; i < UPDATE_QUERIES; i++) {
+                    updateQuery[i] = conn.prepare(singleUpdate(i + 1))
+                            .toCompletionStage().toCompletableFuture().get().query();
+                }
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        private static String singleUpdate(int count) {
+            StringBuilder sql = new StringBuilder();
+            sql.append("UPDATE WORLD SET RANDOMNUMBER = CASE ID");
+            for (int i = 0; i < count; i++) {
+                int k = i * 2 + 1;
+                sql.append(" WHEN $").append(k).append(" THEN $").append(k + 1);
+            }
+            sql.append(" ELSE RANDOMNUMBER");
+            sql.append(" END WHERE ID IN ($1");
+            for (int i = 1; i < count; i++) {
+                int k = i * 2 + 1;
+                sql.append(",$").append(k);
+            }
+            sql.append(")");
+            return sql.toString();
+        }
+    }
+}

+ 35 - 68
frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientRepository.java

@@ -2,7 +2,6 @@ package io.helidon.benchmark.nima.models;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.concurrent.ExecutionException;
 import java.util.logging.Logger;
 
 import io.helidon.config.Config;
@@ -10,58 +9,46 @@ import io.vertx.core.Future;
 import io.vertx.core.Vertx;
 import io.vertx.core.VertxOptions;
 import io.vertx.pgclient.PgConnectOptions;
-import io.vertx.pgclient.PgPool;
-import io.vertx.sqlclient.PoolOptions;
 import io.vertx.sqlclient.PreparedQuery;
 import io.vertx.sqlclient.Row;
 import io.vertx.sqlclient.RowSet;
-import io.vertx.sqlclient.SqlClient;
 import io.vertx.sqlclient.Tuple;
 
 import static io.helidon.benchmark.nima.models.DbRepository.randomWorldNumber;
+import static io.helidon.benchmark.nima.models.PgClientConnectionPool.PgClientConnection.UPDATE_QUERIES;
 
 public class PgClientRepository implements DbRepository {
     private static final Logger LOGGER = Logger.getLogger(PgClientRepository.class.getName());
-    private static final int UPDATE_QUERIES = 500;
 
-    private final SqlClient updatePool;
-
-    private final PreparedQuery<RowSet<Row>> getFortuneQuery;
-    private final PreparedQuery<RowSet<Row>> getWorldQuery;
-    private final PreparedQuery<RowSet<Row>>[] updateWorldSingleQuery;
+    private final PgClientConnectionPool connectionPool;
 
     @SuppressWarnings("unchecked")
     public PgClientRepository(Config config) {
-        Vertx vertx = Vertx.vertx(new VertxOptions().setPreferNativeTransport(true));
+        VertxOptions vertxOptions = new VertxOptions()
+                .setPreferNativeTransport(true)
+                .setBlockedThreadCheckInterval(100000);
+        Vertx vertx = Vertx.vertx(vertxOptions);
         PgConnectOptions connectOptions = new PgConnectOptions()
                 .setPort(config.get("port").asInt().orElse(5432))
-                .setCachePreparedStatements(config.get("cache-prepared-statements").asBoolean().orElse(true))
                 .setHost(config.get("host").asString().orElse("tfb-database"))
                 .setDatabase(config.get("db").asString().orElse("hello_world"))
                 .setUser(config.get("username").asString().orElse("benchmarkdbuser"))
                 .setPassword(config.get("password").asString().orElse("benchmarkdbpass"))
+                .setCachePreparedStatements(true)
+                .setPreparedStatementCacheMaxSize(UPDATE_QUERIES + 2)
+                .setPreparedStatementCacheSqlFilter(s -> true)          // cache all
+                .setTcpNoDelay(true)
+                .setTcpQuickAck(true)
+                .setTcpKeepAlive(true)
                 .setPipeliningLimit(100000);
-
-        int sqlPoolSize = config.get("sql-pool-size").asInt().orElse(64);
-        PoolOptions clientOptions = new PoolOptions().setMaxSize(sqlPoolSize);
-        LOGGER.info("sql-pool-size is " + sqlPoolSize);
-
-        SqlClient queryPool = PgPool.client(vertx, connectOptions, clientOptions);
-        updatePool = PgPool.client(vertx, connectOptions, clientOptions);
-
-        getWorldQuery = queryPool.preparedQuery("SELECT id, randomnumber FROM world WHERE id = $1");
-        getFortuneQuery = queryPool.preparedQuery("SELECT id, message FROM fortune");
-
-        updateWorldSingleQuery = new PreparedQuery[UPDATE_QUERIES];
-        for (int i = 0; i < UPDATE_QUERIES; i++) {
-            updateWorldSingleQuery[i] = queryPool.preparedQuery(singleUpdate(i + 1));
-        }
+        connectionPool = new PgClientConnectionPool(vertx, connectOptions);
     }
 
     @Override
     public World getWorld(int id) {
         try {
-            return getWorldQuery.execute(Tuple.of(id))
+            PreparedQuery<RowSet<Row>> worldQuery = connectionPool.clientConnection().worldQuery();
+            return worldQuery.execute(Tuple.of(id))
                     .map(rows -> {
                         Row r = rows.iterator().next();
                         return new World(r.getInteger(0), r.getInteger(1));
@@ -74,13 +61,14 @@ public class PgClientRepository implements DbRepository {
     @Override
     public List<World> getWorlds(int count) {
         try {
+            PreparedQuery<RowSet<Row>> worldQuery = connectionPool.clientConnection().worldQuery();
             List<Future<?>> futures = new ArrayList<>();
             for (int i = 0; i < count; i++) {
-                futures.add(getWorldQuery.execute(Tuple.of(randomWorldNumber()))
-                                    .map(rows -> {
-                                        Row r = rows.iterator().next();
-                                        return new World(r.getInteger(0), r.getInteger(1));
-                                    }));
+                futures.add(worldQuery.execute(Tuple.of(randomWorldNumber()))
+                        .map(rows -> {
+                            Row r = rows.iterator().next();
+                            return new World(r.getInteger(0), r.getInteger(1));
+                        }));
             }
             return Future.all(futures).toCompletionStage().toCompletableFuture().get().list();
         } catch (Exception e) {
@@ -92,7 +80,18 @@ public class PgClientRepository implements DbRepository {
     public List<World> updateWorlds(int count) {
         List<World> worlds = getWorlds(count);
         try {
-            return updateWorlds(worlds, count, updatePool);
+            PreparedQuery<RowSet<Row>> updateQuery = connectionPool.clientConnection().updateQuery(count);
+            List<Integer> updateParams = new ArrayList<>(count * 2);
+            for (World world : worlds) {
+                updateParams.add(world.id);
+                world.randomNumber = randomWorldNumber();
+                updateParams.add(world.randomNumber);
+            }
+            return updateQuery.execute(Tuple.wrap(updateParams))
+                    .toCompletionStage()
+                    .thenApply(rows -> worlds)
+                    .toCompletableFuture()
+                    .get();
         } catch (Exception e) {
             throw new RuntimeException(e);
         }
@@ -101,7 +100,8 @@ public class PgClientRepository implements DbRepository {
     @Override
     public List<Fortune> getFortunes() {
         try {
-            return getFortuneQuery.execute()
+            PreparedQuery<RowSet<Row>> fortuneQuery = connectionPool.clientConnection().fortuneQuery();
+            return fortuneQuery.execute()
                     .map(rows -> {
                         List<Fortune> fortunes = new ArrayList<>(rows.size() + 1);
                         for (Row r : rows) {
@@ -113,37 +113,4 @@ public class PgClientRepository implements DbRepository {
             throw new RuntimeException(e);
         }
     }
-
-    private List<World> updateWorlds(List<World> worlds, int count, SqlClient pool)
-            throws ExecutionException, InterruptedException {
-        int size = worlds.size();
-        List<Integer> updateParams = new ArrayList<>(size * 2);
-        for (World world : worlds) {
-            updateParams.add(world.id);
-            world.randomNumber = randomWorldNumber();
-            updateParams.add(world.randomNumber);
-        }
-        return updateWorldSingleQuery[count - 1].execute(Tuple.wrap(updateParams))
-                .toCompletionStage()
-                .thenApply(rows -> worlds)
-                .toCompletableFuture()
-                .get();
-    }
-
-    private static String singleUpdate(int count) {
-        StringBuilder sql = new StringBuilder();
-        sql.append("UPDATE WORLD SET RANDOMNUMBER = CASE ID");
-        for (int i = 0; i < count; i++) {
-            int k = i * 2 + 1;
-            sql.append(" WHEN $").append(k).append(" THEN $").append(k + 1);
-        }
-        sql.append(" ELSE RANDOMNUMBER");
-        sql.append(" END WHERE ID IN ($1");
-        for (int i = 1; i < count; i++) {
-            int k = i * 2 + 1;
-            sql.append(",$").append(k);
-        }
-        sql.append(")");
-        return sql.toString();
-    }
 }

+ 34 - 19
frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/services/DbService.java

@@ -1,21 +1,19 @@
-
 package io.helidon.benchmark.nima.services;
 
-import java.util.List;
-
+import com.jsoniter.output.JsonStream;
+import com.jsoniter.output.JsonStreamPool;
 import io.helidon.benchmark.nima.models.DbRepository;
-import io.helidon.benchmark.nima.models.World;
+import io.helidon.common.mapper.OptionalValue;
 import io.helidon.common.parameters.Parameters;
 import io.helidon.http.HeaderValues;
 import io.helidon.webserver.http.HttpRules;
 import io.helidon.webserver.http.HttpService;
 import io.helidon.webserver.http.ServerRequest;
 import io.helidon.webserver.http.ServerResponse;
-import io.helidon.common.mapper.OptionalValue;
 
+import static io.helidon.benchmark.nima.JsonSerializer.serialize;
 import static io.helidon.benchmark.nima.Main.SERVER;
 import static io.helidon.benchmark.nima.models.DbRepository.randomWorldNumber;
-import static io.helidon.benchmark.nima.JsonSerializer.serialize;
 
 public class DbService implements HttpService {
 
@@ -33,24 +31,41 @@ public class DbService implements HttpService {
     }
 
     private void db(ServerRequest req, ServerResponse res) {
-        res.header(SERVER);
-        res.header(HeaderValues.CONTENT_TYPE_JSON);
-        res.send(serialize(repository.getWorld(randomWorldNumber())));
+        JsonStream stream = JsonStreamPool.borrowJsonStream();
+        try {
+            res.header(SERVER);
+            res.header(HeaderValues.CONTENT_TYPE_JSON);
+            serialize(repository.getWorld(randomWorldNumber()), stream);
+            res.send(stream.buffer().data(), 0, stream.buffer().tail());
+        } finally {
+            JsonStreamPool.returnJsonStream(stream);
+        }
     }
 
     private void queries(ServerRequest req, ServerResponse res) {
-        res.header(SERVER);
-        res.header(HeaderValues.CONTENT_TYPE_JSON);
-        int count = parseQueryCount(req.query());
-        res.send(serialize(repository.getWorlds(count)));
+        JsonStream stream = JsonStreamPool.borrowJsonStream();
+        try {
+            res.header(SERVER);
+            res.header(HeaderValues.CONTENT_TYPE_JSON);
+            int count = parseQueryCount(req.query());
+            serialize(repository.getWorlds(count), stream);
+            res.send(stream.buffer().data(), 0, stream.buffer().tail());
+        } finally {
+            JsonStreamPool.returnJsonStream(stream);
+        }
     }
 
     private void updates(ServerRequest req, ServerResponse res) {
-        res.header(SERVER);
-        res.header(HeaderValues.CONTENT_TYPE_JSON);
-        int count = parseQueryCount(req.query());
-        List<World> worlds = repository.updateWorlds(count);
-        res.send(serialize(worlds));
+        JsonStream stream = JsonStreamPool.borrowJsonStream();
+        try {
+            res.header(SERVER);
+            res.header(HeaderValues.CONTENT_TYPE_JSON);
+            int count = parseQueryCount(req.query());
+            serialize(repository.updateWorlds(count), stream);
+            res.send(stream.buffer().data(), 0, stream.buffer().tail());
+        } finally {
+            JsonStreamPool.returnJsonStream(stream);
+        }
     }
 
     private int parseQueryCount(Parameters parameters) {
@@ -66,4 +81,4 @@ public class DbService implements HttpService {
         }
         return Math.min(500, Math.max(1, parsedValue));
     }
-}
+}

+ 40 - 5
frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/services/FortuneHandler.java

@@ -1,16 +1,21 @@
 
 package io.helidon.benchmark.nima.services;
 
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
 import java.util.Comparator;
 import java.util.List;
 
-import com.fizzed.rocker.runtime.ArrayOfByteArraysOutput;
+import gg.jte.TemplateOutput;
 import io.helidon.benchmark.nima.models.DbRepository;
 import io.helidon.benchmark.nima.models.Fortune;
 import io.helidon.webserver.http.Handler;
 import io.helidon.webserver.http.ServerRequest;
 import io.helidon.webserver.http.ServerResponse;
-import views.fortunes;
+
+import gg.jte.html.OwaspHtmlTemplateOutput;
+import gg.jte.generated.precompiled.JtefortunesGenerated;
 
 import static io.helidon.benchmark.nima.Main.CONTENT_TYPE_HTML;
 import static io.helidon.benchmark.nima.Main.SERVER;
@@ -33,8 +38,38 @@ public class FortuneHandler implements Handler {
         List<Fortune> fortuneList = repository.getFortunes();
         fortuneList.add(ADDITIONAL_FORTUNE);
         fortuneList.sort(Comparator.comparing(Fortune::getMessage));
-        res.send(fortunes.template(fortuneList)
-                .render(ArrayOfByteArraysOutput.FACTORY)
-                .toByteArray());
+        try (OutputStream os = res.outputStream()) {
+            JtefortunesGenerated.render(new OwaspHtmlTemplateOutput(new HelidonTemplateOutput(os)),
+                    null, fortuneList);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
     }
+
+    static class HelidonTemplateOutput implements TemplateOutput{
+        private final OutputStream os;
+
+        HelidonTemplateOutput(OutputStream os) {
+            this.os = os;
+        }
+
+        @Override
+        public void writeContent(String value) {
+            writeBinaryContent(value.getBytes(StandardCharsets.UTF_8));
+        }
+
+        @Override
+        public void writeContent(String value, int beginIndex, int endIndex) {
+            writeBinaryContent(value.substring(beginIndex, endIndex).getBytes(StandardCharsets.UTF_8));
+        }
+
+        @Override
+        public void writeBinaryContent(byte[] value) {
+            try {
+                os.write(value);
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    };
 }

+ 0 - 1
frameworks/Java/helidon/nima/src/main/resources/application.yaml

@@ -36,6 +36,5 @@ host: "tfb-database"
 db: "hello_world"
 username: benchmarkdbuser
 password: benchmarkdbpass
-sql-pool-size: 300
 db-repository: "pgclient"     # "pgclient" (default) or "hikari"
 

+ 2 - 0
frameworks/Java/helidon/nima/src/main/resources/views/fortunes.jte

@@ -0,0 +1,2 @@
+@param java.util.List<io.helidon.benchmark.nima.models.Fortune> fortunes
+<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>@for(io.helidon.benchmark.nima.models.Fortune fortune : fortunes)<tr><td>${fortune.getId()}</td><td>${fortune.getMessage()}</td></tr>@endfor</table></body></html>

+ 0 - 24
frameworks/Java/helidon/nima/src/main/resources/views/fortunes.rocker.html

@@ -1,24 +0,0 @@
-@import io.helidon.benchmark.nima.models.Fortune
-@import java.util.List
-@args (List<Fortune> fortunes)
-
-<!DOCTYPE html>
-<html>
-<head>
-<title>Fortunes</title>
-</head>
-<body>
-<table>
-    <tr>
-        <th>id</th>
-        <th>message</th>
-    </tr>
-    @for (f : fortunes) {
-    <tr>
-        <td>@f.getId()</td>
-        <td>@f.getMessage()</td>
-    </tr>
-    }
-</table>
-</body>
-</html>