diff --git a/frameworks/Java/solon/pom.xml b/frameworks/Java/solon/pom.xml index 428d95b35b7..b3485e353e9 100644 --- a/frameworks/Java/solon/pom.xml +++ b/frameworks/Java/solon/pom.xml @@ -5,7 +5,7 @@ org.noear solon-parent - 3.0.2 + 3.0.3 hello @@ -15,24 +15,70 @@ 21 + 1.3.6 org.noear - solon-boot-smarthttp + solon-web org.noear solon-serialization-jackson + + + org.noear + solon-data-sqlutils + + + + io.jstach + jstachio + ${jstachio.version} + + + + io.jstach + jstachio-apt + ${jstachio.version} + provided + true + + + + com.zaxxer + HikariCP + 6.0.0 + + + + org.postgresql + postgresql + 42.7.4 + ${project.artifactId} + + org.apache.maven.plugins + maven-compiler-plugin + + + + io.jstach + jstachio-apt + ${jstachio.version} + + + + + org.apache.maven.plugins maven-assembly-plugin diff --git a/frameworks/Java/solon/src/main/java/hello/Main.java b/frameworks/Java/solon/src/main/java/hello/Main.java index 0746f86be45..75fd11bfc36 100644 --- a/frameworks/Java/solon/src/main/java/hello/Main.java +++ b/frameworks/Java/solon/src/main/java/hello/Main.java @@ -2,10 +2,6 @@ import org.noear.solon.Solon; -/** - * @author pmg1991 - * @version V1.0 - */ public class Main { public static void main(String[] args) { Solon.start(Main.class, args); diff --git a/frameworks/Java/solon/src/main/java/hello/Utils.java b/frameworks/Java/solon/src/main/java/hello/Utils.java new file mode 100644 index 00000000000..162f89cc79c --- /dev/null +++ b/frameworks/Java/solon/src/main/java/hello/Utils.java @@ -0,0 +1,17 @@ +package hello; + +import java.util.concurrent.ThreadLocalRandom; +import java.util.stream.IntStream; + +abstract public class Utils { + private static final int MIN_WORLD_NUMBER = 1; + private static final int MAX_WORLD_NUMBER_PLUS_ONE = 10_001; + + public static int randomWorldNumber() { + return ThreadLocalRandom.current().nextInt(MIN_WORLD_NUMBER, MAX_WORLD_NUMBER_PLUS_ONE); + } + + public static IntStream randomWorldNumbers() { + return ThreadLocalRandom.current().ints(MIN_WORLD_NUMBER, MAX_WORLD_NUMBER_PLUS_ONE).distinct(); + } +} diff --git a/frameworks/Java/solon/src/main/java/hello/controller/HelloController.java b/frameworks/Java/solon/src/main/java/hello/controller/HelloController.java deleted file mode 100644 index 77049043e97..00000000000 --- a/frameworks/Java/solon/src/main/java/hello/controller/HelloController.java +++ /dev/null @@ -1,25 +0,0 @@ -package hello.controller; - -import org.noear.solon.annotation.Controller; -import org.noear.solon.annotation.Get; -import org.noear.solon.annotation.Mapping; -import hello.model.Message; - -/** - * @author noear - * @version V1.0 - */ -@Controller -public class HelloController { - @Get - @Mapping("plaintext") - public String plaintext() { - return "Hello, World!"; - } - - @Get - @Mapping("json") - public Message json() { - return new Message("Hello, World!"); - } -} diff --git a/frameworks/Java/solon/src/main/java/hello/model/Fortune.java b/frameworks/Java/solon/src/main/java/hello/model/Fortune.java new file mode 100644 index 00000000000..f7b5769ef28 --- /dev/null +++ b/frameworks/Java/solon/src/main/java/hello/model/Fortune.java @@ -0,0 +1,16 @@ +package hello.model; + +public final class Fortune implements Comparable{ + public final int id; + public final String message; + + public Fortune(int id, String message) { + this.id = id; + this.message = message; + } + + @Override + public int compareTo(final Fortune other) { + return message.compareTo(other.message); + } +} diff --git a/frameworks/Java/solon/src/main/java/hello/model/Message.java b/frameworks/Java/solon/src/main/java/hello/model/Message.java deleted file mode 100644 index 1df5276ca8d..00000000000 --- a/frameworks/Java/solon/src/main/java/hello/model/Message.java +++ /dev/null @@ -1,21 +0,0 @@ -package hello.model; - -/** - * @author pmg1991 - * @version V1.0 - */ -public class Message { - private String message; - - public Message(String message) { - this.message = message; - } - - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } -} diff --git a/frameworks/Java/solon/src/main/java/hello/model/World.java b/frameworks/Java/solon/src/main/java/hello/model/World.java new file mode 100644 index 00000000000..ec6fc5b3abf --- /dev/null +++ b/frameworks/Java/solon/src/main/java/hello/model/World.java @@ -0,0 +1,12 @@ +package hello.model; + + +public final class World { + public int id; + public int randomNumber; + public World(int id, int randomNumber) { + this.id = id; + this.randomNumber = randomNumber; + } + +} \ No newline at end of file diff --git a/frameworks/Java/solon/src/main/java/hello/repository/DbRepository.java b/frameworks/Java/solon/src/main/java/hello/repository/DbRepository.java new file mode 100644 index 00000000000..e5445184077 --- /dev/null +++ b/frameworks/Java/solon/src/main/java/hello/repository/DbRepository.java @@ -0,0 +1,15 @@ +package hello.repository; + +import hello.model.Fortune; +import hello.model.World; + +import java.util.List; + +public interface DbRepository { + + World getWorld(int id) throws Exception; + + void updateWorlds(List worlds) throws Exception; + + List fortunes() throws Exception; +} diff --git a/frameworks/Java/solon/src/main/java/hello/repository/JdbcDbRepository.java b/frameworks/Java/solon/src/main/java/hello/repository/JdbcDbRepository.java new file mode 100644 index 00000000000..6e58090c32a --- /dev/null +++ b/frameworks/Java/solon/src/main/java/hello/repository/JdbcDbRepository.java @@ -0,0 +1,46 @@ +package hello.repository; + +import hello.model.Fortune; +import hello.model.World; +import org.noear.solon.annotation.Component; +import org.noear.solon.annotation.Inject; +import org.noear.solon.data.sql.SqlUtils; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +@Component +public class JdbcDbRepository implements DbRepository { + @Inject + SqlUtils sqlUtils; + + @Override + public World getWorld(int id) { + try { + return sqlUtils.sql("SELECT id, randomnumber FROM world WHERE id = ?", id) + .queryRow() + .toBean(World.class, (r, t) -> new World((int) r.getObject(1), (int) r.getObject(2))); + } catch (Exception e) { + return null; + } + } + + @Override + public void updateWorlds(List worlds) throws SQLException { + List values = new ArrayList<>(); + for (World w : worlds) { + values.add(new Object[]{w.randomNumber, w.id}); + } + + sqlUtils.sql("UPDATE world SET randomnumber = ? WHERE id = ?") + .updateBatch(values); + } + + @Override + public List fortunes() throws SQLException { + return sqlUtils.sql("SELECT id, message FROM fortune") + .queryRowList() + .toBeanList(Fortune.class, (r, t) -> new Fortune((int) r.getObject(1), (String) r.getObject(2))); + } +} diff --git a/frameworks/Java/solon/src/main/java/hello/web/DbHandler.java b/frameworks/Java/solon/src/main/java/hello/web/DbHandler.java new file mode 100644 index 00000000000..9163c94e3c9 --- /dev/null +++ b/frameworks/Java/solon/src/main/java/hello/web/DbHandler.java @@ -0,0 +1,61 @@ +package hello.web; + +import hello.Utils; +import hello.model.Fortune; +import hello.model.World; +import hello.repository.JdbcDbRepository; +import org.noear.solon.annotation.Component; +import org.noear.solon.annotation.Inject; +import org.noear.solon.core.handle.Context; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +@Component +public class DbHandler { + @Inject + JdbcDbRepository dbRepository; + + public void db(Context ctx) throws Throwable { + ctx.render(dbRepository.getWorld(Utils.randomWorldNumber())); + } + + public void queries(Context ctx) throws Throwable { + int queries = ctx.paramAsInt("queries", 0); + + World[] worlds = Utils.randomWorldNumbers() + .mapToObj(dbRepository::getWorld).limit(queries) + .toArray(World[]::new); + + ctx.render(worlds); + } + + public void updates(Context ctx) throws Throwable { + int queries = ctx.paramAsInt("queries", 0); + + List worlds = Utils.randomWorldNumbers() + .mapToObj(id -> { + World world = dbRepository.getWorld(id); + int randomNumber; + do { + randomNumber = Utils.randomWorldNumber(); + } while (randomNumber == world.randomNumber); + world.randomNumber = randomNumber; + return world; + }).limit(queries) + .sorted(Comparator.comparingInt(w -> w.id)) + .toList(); + dbRepository.updateWorlds(worlds); + + ctx.render(worlds); + } + + public void fortunes(Context ctx) throws Throwable { + List fortunes = dbRepository.fortunes(); + fortunes.add(new Fortune(0, "Additional fortune added at request time.")); + Collections.sort(fortunes); + + ctx.render(new Fortunes(fortunes)); + } +} diff --git a/frameworks/Java/solon/src/main/java/hello/web/Fortunes.java b/frameworks/Java/solon/src/main/java/hello/web/Fortunes.java new file mode 100644 index 00000000000..a5416ec07bc --- /dev/null +++ b/frameworks/Java/solon/src/main/java/hello/web/Fortunes.java @@ -0,0 +1,10 @@ +package hello.web; + +import hello.model.Fortune; +import io.jstach.jstache.JStache; + +import java.util.List; + +@JStache(path = "fortunes.mustache") +public record Fortunes(List fortunes) { +} diff --git a/frameworks/Java/solon/src/main/java/hello/web/JsonHandler.java b/frameworks/Java/solon/src/main/java/hello/web/JsonHandler.java new file mode 100644 index 00000000000..c65bb73ab74 --- /dev/null +++ b/frameworks/Java/solon/src/main/java/hello/web/JsonHandler.java @@ -0,0 +1,27 @@ +package hello.web; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; +import org.noear.solon.annotation.Component; +import org.noear.solon.boot.web.MimeType; +import org.noear.solon.core.handle.Context; +import org.noear.solon.core.handle.Handler; + +import java.util.Map; + +@Component +public class JsonHandler implements Handler { + private final ObjectWriter writer; + + public JsonHandler() { + this.writer = new ObjectMapper().writerFor(Map.class); + } + + @Override + public void handle(Context ctx) throws Throwable { + byte[] body = this.writer.writeValueAsBytes(Map.of("message", "Hello, world!")); + ctx.contentLength(body.length); + ctx.contentType(MimeType.APPLICATION_JSON_VALUE); + ctx.output(body); + } +} diff --git a/frameworks/Java/solon/src/main/java/hello/web/TextHandler.java b/frameworks/Java/solon/src/main/java/hello/web/TextHandler.java new file mode 100644 index 00000000000..96d68e6e7df --- /dev/null +++ b/frameworks/Java/solon/src/main/java/hello/web/TextHandler.java @@ -0,0 +1,25 @@ +package hello.web; + +import org.noear.solon.annotation.Component; +import org.noear.solon.boot.web.MimeType; +import org.noear.solon.core.handle.Context; +import org.noear.solon.core.handle.Handler; + +import java.nio.charset.StandardCharsets; + +@Component +public class TextHandler implements Handler { + private static final byte[] TEXT_BODY = "Hello, World!".getBytes(StandardCharsets.UTF_8); + + private static final String TEXT_BODY_LENGTH = String.valueOf(TEXT_BODY.length); + private static final String CONTENT_LENGTH = "Content-Length"; + private static final String CONTENT_TYPE = "Content-Type"; + + @Override + public void handle(Context ctx) throws Throwable { + ctx.headerSet(CONTENT_LENGTH, TEXT_BODY_LENGTH); + ctx.headerSet(CONTENT_TYPE, MimeType.TEXT_PLAIN_VALUE); + ctx.output("Hello, World!".getBytes()); + } +} + diff --git a/frameworks/Java/solon/src/main/java/hello/web/WebmvcRouter.java b/frameworks/Java/solon/src/main/java/hello/web/WebmvcRouter.java new file mode 100644 index 00000000000..6024ddcc548 --- /dev/null +++ b/frameworks/Java/solon/src/main/java/hello/web/WebmvcRouter.java @@ -0,0 +1,29 @@ +package hello.web; + +import org.noear.solon.SolonApp; +import org.noear.solon.annotation.Bean; +import org.noear.solon.annotation.Configuration; + +@Configuration +public class WebmvcRouter { + @Bean + public void initRouter(SolonApp app, + DbHandler dbHandler, + JsonHandler jsonHandler, + TextHandler textHandler) { + app.handler().prev(ctx -> { + ctx.setHandled(true); + ctx.headerSet("Server", "Solon"); + + switch (ctx.path()) { + case "/plaintext" -> textHandler.handle(ctx); + case "/json" -> jsonHandler.handle(ctx); + case "/db" -> dbHandler.db(ctx); + case "/queries" -> dbHandler.queries(ctx); + case "/updates" -> dbHandler.updates(ctx); + case "/fortunes" -> dbHandler.fortunes(ctx); + default -> ctx.status(404); + } + }); + } +} diff --git a/frameworks/Java/solon/src/main/resources/app.properties b/frameworks/Java/solon/src/main/resources/app.properties deleted file mode 100644 index 26a5df70351..00000000000 --- a/frameworks/Java/solon/src/main/resources/app.properties +++ /dev/null @@ -1 +0,0 @@ -server.http.ioBound=false \ No newline at end of file diff --git a/frameworks/Java/solon/src/main/resources/app.yml b/frameworks/Java/solon/src/main/resources/app.yml new file mode 100644 index 00000000000..cbd9e850625 --- /dev/null +++ b/frameworks/Java/solon/src/main/resources/app.yml @@ -0,0 +1,23 @@ +database: + name: hello_world + host: tfb-database + port: 5432 + username: benchmarkdbuser + password: benchmarkdbpass + +server.http: + ioBound: false + +solon.threads: + virtual: + enabled: true + +solon.dataSources: + test!: + class: "com.zaxxer.hikari.HikariDataSource" + driverClassName: "org.postgresql.Driver" + url: jdbc:postgresql://${database.host}:${database.port}/${database.name}?loggerLevel=OFF&disableColumnSanitiser=true&assumeMinServerVersion=16&sslmode=disable + username: ${database.username} + password: ${database.password} + hikari: + maximum-pool-size: 256 \ No newline at end of file diff --git a/frameworks/Java/solon/src/main/resources/fortunes.mustache b/frameworks/Java/solon/src/main/resources/fortunes.mustache new file mode 100644 index 00000000000..3043546205b --- /dev/null +++ b/frameworks/Java/solon/src/main/resources/fortunes.mustache @@ -0,0 +1,20 @@ + + + + Fortunes + + + + + + + + {{#fortunes}} + + + + + {{/fortunes}} +
idmessage
{{id}}{{message}}
+ +