From 6a73fa3a8f7e1ab3499d1f0eff72ef7a9b80db96 Mon Sep 17 00:00:00 2001 From: Andrea Cavalli Date: Fri, 26 Nov 2021 01:01:34 +0100 Subject: [PATCH] Add chats service --- service-chats/.dockerignore | 5 + service-chats/.gitignore | 39 ++++ service-chats/README.md | 59 ++++++ service-chats/pom.xml | 176 ++++++++++++++++++ service-chats/src/main/docker/Dockerfile.jvm | 55 ++++++ .../src/main/docker/Dockerfile.legacy-jar | 51 +++++ .../src/main/docker/Dockerfile.native | 27 +++ .../main/docker/Dockerfile.native-distroless | 23 +++ .../src/main/java/io/volvox/chats/Chat.java | 23 +++ .../src/main/java/io/volvox/chats/ChatId.java | 35 ++++ .../java/io/volvox/chats/ChatRepository.java | 22 +++ .../java/io/volvox/chats/ChatResource.java | 87 +++++++++ .../java/io/volvox/chats/ChatsService.java | 32 ++++ .../src/main/java/io/volvox/chats/Status.java | 7 + .../src/main/resources/application.properties | 0 15 files changed, 641 insertions(+) create mode 100644 service-chats/.dockerignore create mode 100644 service-chats/.gitignore create mode 100644 service-chats/README.md create mode 100644 service-chats/pom.xml create mode 100644 service-chats/src/main/docker/Dockerfile.jvm create mode 100644 service-chats/src/main/docker/Dockerfile.legacy-jar create mode 100644 service-chats/src/main/docker/Dockerfile.native create mode 100644 service-chats/src/main/docker/Dockerfile.native-distroless create mode 100644 service-chats/src/main/java/io/volvox/chats/Chat.java create mode 100644 service-chats/src/main/java/io/volvox/chats/ChatId.java create mode 100644 service-chats/src/main/java/io/volvox/chats/ChatRepository.java create mode 100644 service-chats/src/main/java/io/volvox/chats/ChatResource.java create mode 100644 service-chats/src/main/java/io/volvox/chats/ChatsService.java create mode 100644 service-chats/src/main/java/io/volvox/chats/Status.java create mode 100644 service-chats/src/main/resources/application.properties diff --git a/service-chats/.dockerignore b/service-chats/.dockerignore new file mode 100644 index 0000000..94810d0 --- /dev/null +++ b/service-chats/.dockerignore @@ -0,0 +1,5 @@ +* +!target/*-runner +!target/*-runner.jar +!target/lib/* +!target/quarkus-app/* \ No newline at end of file diff --git a/service-chats/.gitignore b/service-chats/.gitignore new file mode 100644 index 0000000..bdf57ce --- /dev/null +++ b/service-chats/.gitignore @@ -0,0 +1,39 @@ +#Maven +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +release.properties + +# Eclipse +.project +.classpath +.settings/ +bin/ + +# IntelliJ +.idea +*.ipr +*.iml +*.iws + +# NetBeans +nb-configuration.xml + +# Visual Studio Code +.vscode +.factorypath + +# OSX +.DS_Store + +# Vim +*.swp +*.swo + +# patch +*.orig +*.rej + +# Local environment +.env diff --git a/service-chats/README.md b/service-chats/README.md new file mode 100644 index 0000000..2a23a0f --- /dev/null +++ b/service-chats/README.md @@ -0,0 +1,59 @@ +# service-chats Project + +This project uses Quarkus, the Supersonic Subatomic Java Framework. + +If you want to learn more about Quarkus, please visit its website: https://quarkus.io/ . + +## Running the application in dev mode + +You can run your application in dev mode that enables live coding using: +```shell script +./mvnw compile quarkus:dev +``` + +> **_NOTE:_** Quarkus now ships with a Dev UI, which is available in dev mode only at http://localhost:8080/q/dev/. + +## Packaging and running the application + +The application can be packaged using: +```shell script +./mvnw package +``` +It produces the `quarkus-run.jar` file in the `target/quarkus-app/` directory. +Be aware that it’s not an _über-jar_ as the dependencies are copied into the `target/quarkus-app/lib/` directory. + +The application is now runnable using `java -jar target/quarkus-app/quarkus-run.jar`. + +If you want to build an _über-jar_, execute the following command: +```shell script +./mvnw package -Dquarkus.package.type=uber-jar +``` + +The application, packaged as an _über-jar_, is now runnable using `java -jar target/*-runner.jar`. + +## Creating a native executable + +You can create a native executable using: +```shell script +./mvnw package -Pnative +``` + +Or, if you don't have GraalVM installed, you can run the native executable build in a container using: +```shell script +./mvnw package -Pnative -Dquarkus.native.container-build=true +``` + +You can then execute your native executable with: `./target/service-chats-1.0.0-SNAPSHOT-runner` + +If you want to learn more about building native executables, please consult https://quarkus.io/guides/maven-tooling.html. + +## Related Guides + + +## Provided Code + +### RESTEasy Reactive + +Easily start your Reactive RESTful Web Services + +[Related guide section...](https://quarkus.io/guides/getting-started-reactive#reactive-jax-rs-resources) diff --git a/service-chats/pom.xml b/service-chats/pom.xml new file mode 100644 index 0000000..9ecd039 --- /dev/null +++ b/service-chats/pom.xml @@ -0,0 +1,176 @@ + + + 4.0.0 + it.volvox.service-chats + service-chats + 1.0.0-SNAPSHOT + + 3.8.1 + true + 17 + 17 + UTF-8 + UTF-8 + quarkus-bom + io.quarkus.platform + 2.4.1.Final + 3.0.0-M5 + 2.7.9.2 + 4.0.183 + + + + mchv + MCHV Apache Maven Packages + https://mvn.mchv.eu/repository/mchv/ + + + + + + ${quarkus.platform.group-id} + ${quarkus.platform.artifact-id} + ${quarkus.platform.version} + pom + import + + + it.tdlight + tdlight-java + ${volvox.tdlight.version} + + + it.tdlight + tdlight-natives-linux-amd64 + ${volvox.tdlight.natives.version} + + + + + + io.quarkus + quarkus-reactive-pg-client + + + io.quarkus + quarkus-smallrye-openapi + + + io.quarkus + quarkus-resteasy-reactive-jackson + + + io.quarkus + quarkus-hibernate-reactive-panache + + + io.quarkus + quarkus-hibernate-validator + + + io.quarkus + quarkus-arc + + + io.quarkus + quarkus-resteasy-reactive + + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + + it.tdlight + tdlight-java + + + it.tdlight + tdlight-natives-linux-amd64 + + + org.apache.commons + commons-lang3 + + + + + + ${quarkus.platform.group-id} + quarkus-maven-plugin + ${quarkus.platform.version} + true + + + + build + generate-code + generate-code-tests + + + + + + maven-compiler-plugin + ${compiler-plugin.version} + + ${maven.compiler.parameters} + + + + maven-surefire-plugin + ${surefire-plugin.version} + + + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + native + + + native + + + + + + maven-failsafe-plugin + ${surefire-plugin.version} + + + + integration-test + verify + + + + ${project.build.directory}/${project.build.finalName}-runner + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + + native + + + + diff --git a/service-chats/src/main/docker/Dockerfile.jvm b/service-chats/src/main/docker/Dockerfile.jvm new file mode 100644 index 0000000..404ec8e --- /dev/null +++ b/service-chats/src/main/docker/Dockerfile.jvm @@ -0,0 +1,55 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode +# +# Before building the container image run: +# +# ./mvnw package +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.jvm -t quarkus/service-chats-jvm . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/service-chats-jvm +# +# If you want to include the debug port into your docker image +# you will have to expose the debug port (default 5005) like this : EXPOSE 8080 5005 +# +# Then run the container using : +# +# docker run -i --rm -p 8080:8080 -p 5005:5005 -e JAVA_ENABLE_DEBUG="true" quarkus/service-chats-jvm +# +### +FROM registry.access.redhat.com/ubi8/ubi-minimal:8.4 + +ARG JAVA_PACKAGE=java-11-openjdk-headless +ARG RUN_JAVA_VERSION=1.3.8 +ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' +# Install java and the run-java script +# Also set up permissions for user `1001` +RUN microdnf install curl ca-certificates ${JAVA_PACKAGE} \ + && microdnf update \ + && microdnf clean all \ + && mkdir /deployments \ + && chown 1001 /deployments \ + && chmod "g+rwX" /deployments \ + && chown 1001:root /deployments \ + && curl https://repo1.maven.org/maven2/io/fabric8/run-java-sh/${RUN_JAVA_VERSION}/run-java-sh-${RUN_JAVA_VERSION}-sh.sh -o /deployments/run-java.sh \ + && chown 1001 /deployments/run-java.sh \ + && chmod 540 /deployments/run-java.sh \ + && echo "securerandom.source=file:/dev/urandom" >> /etc/alternatives/jre/conf/security/java.security + +# Configure the JAVA_OPTIONS, you can add -XshowSettings:vm to also display the heap size. +ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" +# We make four distinct layers so if there are application changes the library layers can be re-used +COPY --chown=1001 target/quarkus-app/lib/ /deployments/lib/ +COPY --chown=1001 target/quarkus-app/*.jar /deployments/ +COPY --chown=1001 target/quarkus-app/app/ /deployments/app/ +COPY --chown=1001 target/quarkus-app/quarkus/ /deployments/quarkus/ + +EXPOSE 8080 +USER 1001 + +ENTRYPOINT [ "/deployments/run-java.sh" ] + diff --git a/service-chats/src/main/docker/Dockerfile.legacy-jar b/service-chats/src/main/docker/Dockerfile.legacy-jar new file mode 100644 index 0000000..f8da1f8 --- /dev/null +++ b/service-chats/src/main/docker/Dockerfile.legacy-jar @@ -0,0 +1,51 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode +# +# Before building the container image run: +# +# ./mvnw package -Dquarkus.package.type=legacy-jar +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.legacy-jar -t quarkus/service-chats-legacy-jar . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/service-chats-legacy-jar +# +# If you want to include the debug port into your docker image +# you will have to expose the debug port (default 5005) like this : EXPOSE 8080 5005 +# +# Then run the container using : +# +# docker run -i --rm -p 8080:8080 -p 5005:5005 -e JAVA_ENABLE_DEBUG="true" quarkus/service-chats-legacy-jar +# +### +FROM registry.access.redhat.com/ubi8/ubi-minimal:8.4 + +ARG JAVA_PACKAGE=java-11-openjdk-headless +ARG RUN_JAVA_VERSION=1.3.8 +ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' +# Install java and the run-java script +# Also set up permissions for user `1001` +RUN microdnf install curl ca-certificates ${JAVA_PACKAGE} \ + && microdnf update \ + && microdnf clean all \ + && mkdir /deployments \ + && chown 1001 /deployments \ + && chmod "g+rwX" /deployments \ + && chown 1001:root /deployments \ + && curl https://repo1.maven.org/maven2/io/fabric8/run-java-sh/${RUN_JAVA_VERSION}/run-java-sh-${RUN_JAVA_VERSION}-sh.sh -o /deployments/run-java.sh \ + && chown 1001 /deployments/run-java.sh \ + && chmod 540 /deployments/run-java.sh \ + && echo "securerandom.source=file:/dev/urandom" >> /etc/alternatives/jre/conf/security/java.security + +# Configure the JAVA_OPTIONS, you can add -XshowSettings:vm to also display the heap size. +ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" +COPY target/lib/* /deployments/lib/ +COPY target/*-runner.jar /deployments/app.jar + +EXPOSE 8080 +USER 1001 + +ENTRYPOINT [ "/deployments/run-java.sh" ] diff --git a/service-chats/src/main/docker/Dockerfile.native b/service-chats/src/main/docker/Dockerfile.native new file mode 100644 index 0000000..e17ee54 --- /dev/null +++ b/service-chats/src/main/docker/Dockerfile.native @@ -0,0 +1,27 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode +# +# Before building the container image run: +# +# ./mvnw package -Pnative +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.native -t quarkus/service-chats . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/service-chats +# +### +FROM registry.access.redhat.com/ubi8/ubi-minimal:8.4 +WORKDIR /work/ +RUN chown 1001 /work \ + && chmod "g+rwX" /work \ + && chown 1001:root /work +COPY --chown=1001:root target/*-runner /work/application + +EXPOSE 8080 +USER 1001 + +CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] diff --git a/service-chats/src/main/docker/Dockerfile.native-distroless b/service-chats/src/main/docker/Dockerfile.native-distroless new file mode 100644 index 0000000..b4b246c --- /dev/null +++ b/service-chats/src/main/docker/Dockerfile.native-distroless @@ -0,0 +1,23 @@ +#### +# This Dockerfile is used in order to build a distroless container that runs the Quarkus application in native (no JVM) mode +# +# Before building the container image run: +# +# ./mvnw package -Pnative +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.native-distroless -t quarkus/service-chats . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/service-chats +# +### +FROM quay.io/quarkus/quarkus-distroless-image:1.0 +COPY target/*-runner /application + +EXPOSE 8080 +USER nonroot + +CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] diff --git a/service-chats/src/main/java/io/volvox/chats/Chat.java b/service-chats/src/main/java/io/volvox/chats/Chat.java new file mode 100644 index 0000000..650fe51 --- /dev/null +++ b/service-chats/src/main/java/io/volvox/chats/Chat.java @@ -0,0 +1,23 @@ +package io.volvox.chats; + +import io.quarkus.hibernate.reactive.panache.PanacheEntityBase; +import javax.persistence.Entity; +import javax.persistence.Id; + +@Entity +public class Chat extends PanacheEntityBase { + @Id + public String id; + + public String name; + public String username; + public Status status; + + public ChatId getChatId() { + return new ChatId(id); + } + + public void setChatId(ChatId id) { + this.id = id.toString(); + } +} diff --git a/service-chats/src/main/java/io/volvox/chats/ChatId.java b/service-chats/src/main/java/io/volvox/chats/ChatId.java new file mode 100644 index 0000000..9141d4d --- /dev/null +++ b/service-chats/src/main/java/io/volvox/chats/ChatId.java @@ -0,0 +1,35 @@ +package io.volvox.chats; + +public record ChatId(Type type, long id) { + ChatId(String id) { + this(getType(id), getIdLong(id)); + } + + private static Type getType(String id) { + return switch (id.charAt(0)) { + case 's' -> Type.SUPER; + case 'b' -> Type.BASIC; + case 'u' -> Type.PRIVATE; + default -> throw new IllegalArgumentException(); + }; + } + + private static long getIdLong(String id) { + return Long.parseUnsignedLong(id.substring(1)); + } + + public enum Type { + BASIC, + SUPER, + PRIVATE + } + + @Override + public String toString() { + return switch (type) { + case SUPER -> 's'; + case BASIC -> 'b'; + case PRIVATE -> 'u'; + } + Long.toUnsignedString(id); + } +} diff --git a/service-chats/src/main/java/io/volvox/chats/ChatRepository.java b/service-chats/src/main/java/io/volvox/chats/ChatRepository.java new file mode 100644 index 0000000..69d6f33 --- /dev/null +++ b/service-chats/src/main/java/io/volvox/chats/ChatRepository.java @@ -0,0 +1,22 @@ +package io.volvox.chats; + +import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase; +import io.smallrye.mutiny.Uni; +import javax.enterprise.context.ApplicationScoped; +import org.hibernate.annotations.NamedQueries; +import org.hibernate.annotations.NamedQuery; + +@ApplicationScoped +@NamedQueries({ + @NamedQuery(name = "Chat.getByName", query = "from Chat where name = ?1"), + @NamedQuery(name = "Chat.getByUsername", query = "from Chat where username = ?1"), + @NamedQuery(name = "Chat.countByStatus", query = "select count(*) from Chat p where p.status = :status"), + @NamedQuery(name = "Chat.updateStatusById", query = "update Chat p set p.status = :status where p.id = :id"), + @NamedQuery(name = "Chat.deleteById", query = "delete from Chat p where p.id = ?1") +}) +public class ChatRepository implements PanacheRepositoryBase { + + public Uni findByUsername(String username) { + return find("#Chat.getByUsername", username).firstResult(); + } +} diff --git a/service-chats/src/main/java/io/volvox/chats/ChatResource.java b/service-chats/src/main/java/io/volvox/chats/ChatResource.java new file mode 100644 index 0000000..6871cfe --- /dev/null +++ b/service-chats/src/main/java/io/volvox/chats/ChatResource.java @@ -0,0 +1,87 @@ +package io.volvox.chats; + +import io.quarkus.hibernate.reactive.panache.PanacheEntityBase; +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.Uni; +import java.net.URI; +import javax.inject.Inject; +import javax.transaction.Transactional; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.NotFoundException; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +@Path("/api/chats") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class ChatResource { + + @Inject + ChatRepository chatRepository; + + @GET + public Multi listSessions() { + return Chat.streamAll(); + } + + @GET + @Path("/{id}") + public Uni get(@PathParam("id") String id) { + return Chat.findById(id); + } + + @POST + @Transactional + public Uni create(Chat chat) { + return chat.persist() + // Return success + .replaceWith(() -> Response.created(URI.create("/api/" + chat.id)).build()); + } + + @PUT + @Path("/{id}") + @Transactional + public Uni update(@PathParam("id") String id, Chat chat) { + // Find chat by id + return Chat.findById(id) + .flatMap(entity -> { + if (entity == null) { + // Persist the chat if not found + return chat.persist(); + } else { + // Update all fields + entity.name = chat.name; + // Return the updated item + return Uni.createFrom().item(entity); + } + }); + } + + @DELETE + @Path("/{id}") + @Transactional + public Uni delete(@PathParam("id") String id) { + return Chat.findById(id) + .onItem().ifNull().failWith(NotFoundException::new) + .flatMap(PanacheEntityBase::delete); + } + + @GET + @Path("/search/{username}") + public Uni search(@PathParam("username") String username) { + return chatRepository.findByUsername(username); + } + + @GET + @Path("/count") + public Uni count() { + return Chat.count(); + } +} diff --git a/service-chats/src/main/java/io/volvox/chats/ChatsService.java b/service-chats/src/main/java/io/volvox/chats/ChatsService.java new file mode 100644 index 0000000..a00875d --- /dev/null +++ b/service-chats/src/main/java/io/volvox/chats/ChatsService.java @@ -0,0 +1,32 @@ +package io.volvox.chats; + +import io.quarkus.vertx.ConsumeEvent; +import io.vertx.mutiny.core.eventbus.EventBus; +import io.vertx.mutiny.core.eventbus.Message; +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; + +@ApplicationScoped +public class ChatsService { + + @Inject + EventBus bus; + + @Inject + ChatResource chatResource; + + @ConsumeEvent(value = "chats.list") + public void listChats(Message msg) { + chatResource.listSessions().collect().asList().subscribe().with(msg::reply); + } + + @ConsumeEvent(value = "chats.get") + public void get(Message msg) { + chatResource.get(msg.body()).subscribe().with(msg::reply); + } + + @ConsumeEvent(value = "chats.update") + public void update(Message msg) { + chatResource.update(msg.body().id, msg.body()).subscribe().with(msg::reply); + } +} \ No newline at end of file diff --git a/service-chats/src/main/java/io/volvox/chats/Status.java b/service-chats/src/main/java/io/volvox/chats/Status.java new file mode 100644 index 0000000..b95809e --- /dev/null +++ b/service-chats/src/main/java/io/volvox/chats/Status.java @@ -0,0 +1,7 @@ +package io.volvox.chats; + +public enum Status { + ALIVE, + DELETED, + UNKNOWN +} diff --git a/service-chats/src/main/resources/application.properties b/service-chats/src/main/resources/application.properties new file mode 100644 index 0000000..e69de29