diff --git a/frameworks/Java/helidon/nima/pom.xml b/frameworks/Java/helidon/nima/pom.xml index c859b758cfc..0d312c8eed8 100644 --- a/frameworks/Java/helidon/nima/pom.xml +++ b/frameworks/Java/helidon/nima/pom.xml @@ -21,7 +21,7 @@ io.helidon.applications helidon-se - 4.1.2 + 4.1.5 @@ -38,6 +38,7 @@ 1.3.0 4.5.3 0.9.23 + 3.1.15 @@ -78,9 +79,9 @@ 42.6.1 - com.fizzed - rocker-runtime - ${rocker.version} + gg.jte + jte + ${jte.version} io.helidon.common.testing @@ -98,7 +99,6 @@ test - @@ -125,20 +125,21 @@ + - com.fizzed - rocker-maven-plugin - ${rocker.version} + gg.jte + jte-maven-plugin + ${jte.version} + + ${project.basedir}/src/main/resources/views + Html + - generate-rocker-templates generate-sources generate - - src/main/resources - diff --git a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/JsonSerializer.java b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/JsonSerializer.java index 322a7cf030c..0564fa8a911 100644 --- a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/JsonSerializer.java +++ b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/JsonSerializer.java @@ -2,8 +2,8 @@ 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 @@ private 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 static byte[] serialize(Object obj) { 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 map) { - JsonStream stream = JsonStreamPool.borrowJsonStream(); + public static void serialize(Map map, JsonStream stream) { try { stream.reset(null); stream.writeObjectStart(); @@ -53,22 +65,18 @@ public static byte[] serialize(Map map) { } }); 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 static byte[] serialize(List objs) { } stream.writeArrayEnd(); - return Arrays.copyOfRange(stream.buffer().data(), 0, stream.buffer().tail()); } catch (IOException e) { throw new JsonException(e); - } finally { - JsonStreamPool.returnJsonStream(stream); } } } diff --git a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/Main.java b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/Main.java index df669d8a7a7..800ce927537 100644 --- a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/Main.java +++ b/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 @@ 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 @@ static void routing(HttpRules rules) { 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 @@ static class JsonHandler implements Handler { 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 String getMessage() { return message; } } -} +} \ No newline at end of file diff --git a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/Fortune.java b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/Fortune.java index 96a5e2070be..190c792d345 100644 --- a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/Fortune.java +++ b/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 { public int id; public String message; @@ -17,4 +17,8 @@ public int getId() { public String getMessage() { return message; } + @Override + public int compareTo(Fortune other) { + return message.compareTo(other.message); + } } diff --git a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientConnectionPool.java b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientConnectionPool.java new file mode 100644 index 00000000000..478847702cb --- /dev/null +++ b/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 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> worldQuery; + private PreparedQuery> fortuneQuery; + private PreparedQuery>[] updateQuery; + + private final PgConnection conn; + + PgClientConnection(PgConnection conn) { + this.conn = conn; + } + + public PgConnection pgConnection() { + return conn; + } + + @Override + public void close() { + conn.close(); + } + + public PreparedQuery> worldQuery() { + return worldQuery; + } + + public PreparedQuery> fortuneQuery() { + return fortuneQuery; + } + + public PreparedQuery> 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>[]) 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(); + } + } +} diff --git a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientRepository.java b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientRepository.java index 291131eca17..3f3bd2624eb 100644 --- a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientRepository.java +++ b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/models/PgClientRepository.java @@ -2,7 +2,6 @@ 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.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> getFortuneQuery; - private final PreparedQuery> getWorldQuery; - private final PreparedQuery>[] 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> 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 World getWorld(int id) { @Override public List getWorlds(int count) { try { + PreparedQuery> worldQuery = connectionPool.clientConnection().worldQuery(); List> 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 List getWorlds(int count) { public List updateWorlds(int count) { List worlds = getWorlds(count); try { - return updateWorlds(worlds, count, updatePool); + PreparedQuery> updateQuery = connectionPool.clientConnection().updateQuery(count); + List 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 List updateWorlds(int count) { @Override public List getFortunes() { try { - return getFortuneQuery.execute() + PreparedQuery> fortuneQuery = connectionPool.clientConnection().fortuneQuery(); + return fortuneQuery.execute() .map(rows -> { List fortunes = new ArrayList<>(rows.size() + 1); for (Row r : rows) { @@ -113,37 +113,4 @@ public List getFortunes() { throw new RuntimeException(e); } } - - private List updateWorlds(List worlds, int count, SqlClient pool) - throws ExecutionException, InterruptedException { - int size = worlds.size(); - List 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(); - } } \ No newline at end of file diff --git a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/services/DbService.java b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/services/DbService.java index e3bd1fe39fc..a1e97de44b5 100644 --- a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/services/DbService.java +++ b/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 void routing(HttpRules httpRules) { } 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 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 @@ private int parseQueryCount(Parameters parameters) { } return Math.min(500, Math.max(1, parsedValue)); } -} \ No newline at end of file +} diff --git a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/services/FortuneHandler.java b/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/services/FortuneHandler.java index 8847b7d0bc1..b821f8f9261 100644 --- a/frameworks/Java/helidon/nima/src/main/java/io/helidon/benchmark/nima/services/FortuneHandler.java +++ b/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 void handle(ServerRequest req, ServerResponse res) { List 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); + } + } + }; } \ No newline at end of file diff --git a/frameworks/Java/helidon/nima/src/main/resources/application.yaml b/frameworks/Java/helidon/nima/src/main/resources/application.yaml index d2d8e8943b4..4a26ea0d0e0 100644 --- a/frameworks/Java/helidon/nima/src/main/resources/application.yaml +++ b/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" diff --git a/frameworks/Java/helidon/nima/src/main/resources/views/fortunes.jte b/frameworks/Java/helidon/nima/src/main/resources/views/fortunes.jte new file mode 100644 index 00000000000..52dc350b25f --- /dev/null +++ b/frameworks/Java/helidon/nima/src/main/resources/views/fortunes.jte @@ -0,0 +1,2 @@ +@param java.util.List fortunes +Fortunes@for(io.helidon.benchmark.nima.models.Fortune fortune : fortunes)@endfor
idmessage
${fortune.getId()}${fortune.getMessage()}
\ No newline at end of file diff --git a/frameworks/Java/helidon/nima/src/main/resources/views/fortunes.rocker.html b/frameworks/Java/helidon/nima/src/main/resources/views/fortunes.rocker.html deleted file mode 100644 index 3ebcae44729..00000000000 --- a/frameworks/Java/helidon/nima/src/main/resources/views/fortunes.rocker.html +++ /dev/null @@ -1,24 +0,0 @@ -@import io.helidon.benchmark.nima.models.Fortune -@import java.util.List -@args (List fortunes) - - - - -Fortunes - - - - - - - - @for (f : fortunes) { - - - - - } -
idmessage
@f.getId()@f.getMessage()
- - \ No newline at end of file