From 47249aa1ffe155b8a481b854b6e4361e8ea11388 Mon Sep 17 00:00:00 2001 From: Thiago dos Santos Hora Date: Thu, 23 Sep 2021 11:07:26 +0200 Subject: [PATCH] [BAEL-4747] Add read me and utils scripts (#11242) * [BAEL-4747] Create quarkus and spring boot projects * [BAEL-4747] Add quarkus implementation and fixing native image plugins setup * Fixing build config * [BAEL-4747] Add read me and utils scripts --- pom.xml | 1 + quarkus-vs-springboot/README.md | 95 +++++ quarkus-vs-springboot/cities.csv | 136 +++++++ quarkus-vs-springboot/load_test.jmx | 384 ++++++++++++++++++ quarkus-vs-springboot/pom.xml | 22 + .../quarkus-project/build.sh | 13 + quarkus-vs-springboot/quarkus-project/pom.xml | 149 +++++++ .../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/docker/quarkus.yml | 23 ++ .../ReactiveGreetingResource.java | 17 + .../com/baeldung/quarkus_project/ZipCode.java | 68 ++++ .../baeldung/quarkus_project/ZipCodeRepo.java | 19 + .../quarkus_project/ZipCodeResource.java | 46 +++ .../src/main/resources/application.properties | 9 + .../NativeGreetingResourceIT.java | 21 + .../quarkus-project/start_app.sh | 5 + .../quarkus-project/start_jvm.sh | 5 + quarkus-vs-springboot/run_test.sh | 3 + .../spring-project/build_jvm_docker.sh | 6 + quarkus-vs-springboot/spring-project/pom.xml | 171 ++++++++ .../src/main/docker/Dockerfile.jvm | 12 + .../spring-project/src/main/docker/spring.yml | 22 + .../com/baeldung/spring_project/Startup.java | 37 ++ .../baeldung/spring_project/ZipCodeApi.java | 44 ++ .../spring_project/domain/ZIPRepo.java | 11 + .../spring_project/domain/ZipCode.java | 86 ++++ .../src/main/resources/application.properties | 5 + .../baeldung/spring_project/StartupIT.java | 13 + .../src/test/resources/access-filter.json | 11 + .../spring-project/start_app.sh | 6 + .../spring-project/start_jvm.sh | 6 + 34 files changed, 1602 insertions(+) create mode 100644 quarkus-vs-springboot/README.md create mode 100644 quarkus-vs-springboot/cities.csv create mode 100644 quarkus-vs-springboot/load_test.jmx create mode 100644 quarkus-vs-springboot/pom.xml create mode 100755 quarkus-vs-springboot/quarkus-project/build.sh create mode 100644 quarkus-vs-springboot/quarkus-project/pom.xml create mode 100644 quarkus-vs-springboot/quarkus-project/src/main/docker/Dockerfile.jvm create mode 100644 quarkus-vs-springboot/quarkus-project/src/main/docker/Dockerfile.legacy-jar create mode 100644 quarkus-vs-springboot/quarkus-project/src/main/docker/Dockerfile.native create mode 100644 quarkus-vs-springboot/quarkus-project/src/main/docker/Dockerfile.native-distroless create mode 100644 quarkus-vs-springboot/quarkus-project/src/main/docker/quarkus.yml create mode 100644 quarkus-vs-springboot/quarkus-project/src/main/java/com/baeldung/quarkus_project/ReactiveGreetingResource.java create mode 100644 quarkus-vs-springboot/quarkus-project/src/main/java/com/baeldung/quarkus_project/ZipCode.java create mode 100644 quarkus-vs-springboot/quarkus-project/src/main/java/com/baeldung/quarkus_project/ZipCodeRepo.java create mode 100644 quarkus-vs-springboot/quarkus-project/src/main/java/com/baeldung/quarkus_project/ZipCodeResource.java create mode 100644 quarkus-vs-springboot/quarkus-project/src/main/resources/application.properties create mode 100644 quarkus-vs-springboot/quarkus-project/src/test/java/com/baeldung/quarkus_project/NativeGreetingResourceIT.java create mode 100755 quarkus-vs-springboot/quarkus-project/start_app.sh create mode 100755 quarkus-vs-springboot/quarkus-project/start_jvm.sh create mode 100755 quarkus-vs-springboot/run_test.sh create mode 100755 quarkus-vs-springboot/spring-project/build_jvm_docker.sh create mode 100644 quarkus-vs-springboot/spring-project/pom.xml create mode 100644 quarkus-vs-springboot/spring-project/src/main/docker/Dockerfile.jvm create mode 100644 quarkus-vs-springboot/spring-project/src/main/docker/spring.yml create mode 100644 quarkus-vs-springboot/spring-project/src/main/java/com/baeldung/spring_project/Startup.java create mode 100644 quarkus-vs-springboot/spring-project/src/main/java/com/baeldung/spring_project/ZipCodeApi.java create mode 100644 quarkus-vs-springboot/spring-project/src/main/java/com/baeldung/spring_project/domain/ZIPRepo.java create mode 100644 quarkus-vs-springboot/spring-project/src/main/java/com/baeldung/spring_project/domain/ZipCode.java create mode 100644 quarkus-vs-springboot/spring-project/src/main/resources/application.properties create mode 100644 quarkus-vs-springboot/spring-project/src/test/java/com/baeldung/spring_project/StartupIT.java create mode 100644 quarkus-vs-springboot/spring-project/src/test/resources/access-filter.json create mode 100755 quarkus-vs-springboot/spring-project/start_app.sh create mode 100755 quarkus-vs-springboot/spring-project/start_jvm.sh diff --git a/pom.xml b/pom.xml index b94283277b..f5ac14a009 100644 --- a/pom.xml +++ b/pom.xml @@ -1356,6 +1356,7 @@ core-java-modules/core-java-time-measurements core-java-modules/multimodulemavenproject core-java-modules/core-java-strings + quarkus-vs-springboot diff --git a/quarkus-vs-springboot/README.md b/quarkus-vs-springboot/README.md new file mode 100644 index 0000000000..35fc8eb5eb --- /dev/null +++ b/quarkus-vs-springboot/README.md @@ -0,0 +1,95 @@ +# Spring Boot vs Quarkus + +To follow this tutorial, you will need the following things: +- GRAALVM (https://www.graalvm.org/) +- VisualVM (https://visualvm.github.io/) +- Maven (Embedded, IDE, or local installation) +- Docker (https://www.docker.com/) +- Jmeter (https://jmeter.apache.org/) + +To create this test, I used some custom features from Jmeter. You can install the Jmeter plugin manager here: +https://loadium.com/blog/how-to-install-use-jmeter-plugin. After that, please install the following plugins: +- https://jmeter-plugins.org/?search=jpgc-casutg + +The test file is `load_test.jmx` in case of any change need. You can open it with the Jmeter GUI. For example, to run the start, you can execute the file `run_test.sh` or run the comment bellow: + +``` +$jmeter_home/bin/jmeter -n -t load_test.jmx -l log.csv -e -o ./report +``` + +Just remember to change the variable `jmeter_home` with the path to the JMeter folder. The path to the data files is relative, so either keep them in the same folder as the test or use Jmeter GUI to change it. + +Open the VisualVM application and select your application to start monitoring before running the test, and of course, start the sample application first. + +## Spring Boot +To build the application, you only need to run the following command in the Spring project root: +``` +./mvnw package -f pom.xml +``` +Or this one in case you want to build the native one: +``` +./mvnw -DskipTests package -Pnative -f pom.xml +``` +In this case, you will need to have the `GRAALVM_HOME` env variable defined. You only need this if you want to build the image locally. Otherwise, you can build it using docker by leveraging the Spring Boot maven plugin. It will pull a docker image of the GraalVM, and with that, it will create the native image of the app. To do that, run: +``` +./mvnw spring-boot:build-image +``` +You can also create a docker image with the JVM version of the app running the script `build_jvm_docker.sh` or: +``` +docker build -f src/main/docker/Dockerfile.jvm -t spring-project:0.1-SNAPSHOT . +``` + +You can execute the script `start_app.sh` or `start_jvm.sh` to run the application locally. In this case, you will need the Postgres DB. You can run it in docker with the command: +``` +docker run -e POSTGRES_PASSWORD=example -p 5432:5432 postgres +``` +You can also run both application and DB from docker, using: +``` +docker-compose -f src/main/docker/spring.yml up +``` +But remember to rebuild the image to switch between native and JVM versions. + +## Quarkus +The process to build and run the Quarkus application is very similar to the Spring Boot one. First, to create the native image, you also need either the GRAALVM installed and the `GRAALVM_HOME` env variable set, or we can use docker to build the native image. + +To build the native version locally, run the command: +``` +./mvnw package -Pnative -f pom.xml +``` +Or this one to build using docker: +``` +./mvnw package -Pnative -Dquarkus.native.container-build=true -f pom.xml +``` +And to the JVM version: +``` +./mvnw package -f pom.xml +``` + +To start the application locally, use either the scripts `start_app.sh` and `start_jvm.sh` with the docker DB: +``` +docker run -e POSTGRES_PASSWORD=example -p 5432:5432 postgres +``` +Or use the script to build the docker image of the application, running: +```bash +./build.sh + +## script content +## ./mvnw quarkus:add-extension -Dextensions=container-image-docker +## ./mvnw package -Dquarkus.container-build=true -f pom.xml && +## docker build -f src/main/docker/Dockerfile.jvm -t quarkus-project:0.1-SNAPSHOT . +``` +To build the docker image of the JVM version, and running the following command to the native version: +```bash +./build.sh native + +## script content +## ./mvnw quarkus:add-extension -Dextensions=container-image-docker +## ./mvnw package -Pnative -Dquarkus.native.container-build=true -f pom.xml && +## docker build -f src/main/docker/Dockerfile.native -t quarkus-project:0.1-SNAPSHOT . +``` +Then, once again, you can also run both application and DB from docker, using: +``` +docker-compose -f src/main/docker/quarkus.yml up +``` + +Now you have all you need to reproduce the tests with your machine. \ No newline at end of file diff --git a/quarkus-vs-springboot/cities.csv b/quarkus-vs-springboot/cities.csv new file mode 100644 index 0000000000..3b7016f3b5 --- /dev/null +++ b/quarkus-vs-springboot/cities.csv @@ -0,0 +1,136 @@ +Holtsville +Adjuntas +Aguada +Aguadilla +Maricao +Anasco +Angeles +Arecibo +Bajadero +Barceloneta +Boqueron +Cabo Rojo +Penuelas +Camuy +Castaner +Rosario +Sabana Grande +Ciales +Utuado +Dorado +Ensenada +Florida +Garrochales +Guanica +Guayanilla +Hatillo +Hormigueros +Isabela +Jayuya +Lajas +Lares +Las Marias +Manati +Moca +Rincon +Quebradillas +Mayaguez +San German +San Sebastian +Morovis +Sabana Hoyos +San Antonio +Vega Alta +Vega Baja +Yauco +Aguas Buenas +Aguirre +Aibonito +Maunabo +Arroyo +Mercedita +Ponce +Naguabo +Naranjito +Orocovis +Palmer +Patillas +Caguas +Canovanas +Ceiba +Cayey +Fajardo +Cidra +Puerto Real +Punta Santiago +Roosevelt Roads +Rio Blanco +Rio Grande +Salinas +San Lorenzo +Santa Isabel +Vieques +Villalba +Yabucoa +Coamo +Las Piedras +Loiza +Luquillo +Culebra +Juncos +Gurabo +Coto Laurel +Comerio +Corozal +Guayama +La Plata +Humacao +Barranquitas +Juana Diaz +St Thomas +Christiansted +St John +Frederiksted +Kingshill +San Juan +Fort Buchanan +Toa Baja +Sabana Seca +Toa Alta +Bayamon +Catano +Guaynabo +Trujillo Alto +Saint Just +Carolina +Agawam +Amherst +Barre +Belchertown +Blandford +Bondsville +Brimfield +Chester +Chesterfield +Chicopee +Cummington +Easthampton +East Longmeadow +East Otis +Feeding Hills +Gilbertville +Goshen +Granby +Granville +Hadley +Hampden +Hardwick +Hatfield +Haydenville +Holyoke +Huntington +Leeds +Leverett +Ludlow +Monson +North Amherst \ No newline at end of file diff --git a/quarkus-vs-springboot/load_test.jmx b/quarkus-vs-springboot/load_test.jmx new file mode 100644 index 0000000000..7da984343b --- /dev/null +++ b/quarkus-vs-springboot/load_test.jmx @@ -0,0 +1,384 @@ + + + + + + false + true + false + + + + + + + + continue + + false + -1 + + 500 + 150 + true + 300 + + true + + + + , + + zip_code_database.csv + true + true + false + shareMode.group + true + zip,type,decommissioned,primary_city,acceptable_cities,unacceptable_cities,state,county,timezone,area_codes,world_region,country,latitude,longitude,irs_estimated_population + + + + true + + + + false + { + "zip":"${zip}", + "type":"${type}", + "city":"${primary_city}", + "state":"${state}", + "county":"${country}", + "timezone":"${timezone}" +} + = + + + + localhost + 8080 + + UTF-8 + zipcode + POST + true + false + true + false + + + + + + + + + Content-Type + application/json + + + Accept + */* + + + Cache-Control + no-cache + + + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + true + + + + + + + + + + 25 + 30 + 15 + 255 + + + + 125 + 30 + 15 + 255 + + + + 125 + 60 + 15 + 225 + + + + 225 + 150 + 30 + 120 + + + + + false + -1 + + continue + + + + , + + zip_code_database.csv + true + true + false + shareMode.group + true + zip,type,decommissioned,primary_city,acceptable_cities,unacceptable_cities,state,county,timezone,area_codes,world_region,country,latitude,longitude,irs_estimated_population + + + + + + + localhost + 8080 + + UTF-8 + zipcode/${zip} + GET + true + false + true + false + + + + + + + + + Content-Type + application/json + + + Accept + */* + + + Cache-Control + no-cache + + + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + true + + + + + + + + + + 25 + 30 + 15 + 255 + + + + 125 + 30 + 15 + 255 + + + + 125 + 60 + 15 + 225 + + + + 225 + 150 + 30 + 120 + + + + + false + -1 + + continue + + + + , + + cities.csv + false + false + true + shareMode.group + false + city + + + + + + + localhost + 8080 + + UTF-8 + zipcode/by_city?city=${city} + GET + true + false + true + false + + + + + + + + + Content-Type + application/json + + + Accept + */* + + + Cache-Control + no-cache + + + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + true + + + + + + + + + + + diff --git a/quarkus-vs-springboot/pom.xml b/quarkus-vs-springboot/pom.xml new file mode 100644 index 0000000000..cf1cbb5d85 --- /dev/null +++ b/quarkus-vs-springboot/pom.xml @@ -0,0 +1,22 @@ + + + + parent-modules + com.baeldung + 1.0.0-SNAPSHOT + + + 1.0-SNAPSHOT + quarkus-vs-springboot + quarkus-vs-springboot + pom + 4.0.0 + + + quarkus-project + spring-project + + + \ No newline at end of file diff --git a/quarkus-vs-springboot/quarkus-project/build.sh b/quarkus-vs-springboot/quarkus-project/build.sh new file mode 100755 index 0000000000..85761adab0 --- /dev/null +++ b/quarkus-vs-springboot/quarkus-project/build.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +SCRIPTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" + +./mvnw quarkus:add-extension -Dextensions=container-image-docker + +if [ "$1" = "native" ]; then + ./mvnw package -Pnative -Dquarkus.native.container-build=true -f $SCRIPTPATH/pom.xml && + docker build -f $SCRIPTPATH/src/main/docker/Dockerfile.native -t quarkus-project:0.1-SNAPSHOT $SCRIPTPATH/. +else + ./mvnw package -Dquarkus.container-build=true -f $SCRIPTPATH/pom.xml && + docker build -f $SCRIPTPATH/src/main/docker/Dockerfile.jvm -t quarkus-project:0.1-SNAPSHOT $SCRIPTPATH/. +fi \ No newline at end of file diff --git a/quarkus-vs-springboot/quarkus-project/pom.xml b/quarkus-vs-springboot/quarkus-project/pom.xml new file mode 100644 index 0000000000..c9eae79a88 --- /dev/null +++ b/quarkus-vs-springboot/quarkus-project/pom.xml @@ -0,0 +1,149 @@ + + + 4.0.0 + + com.baeldung + quarkus-vs-springboot + 1.0-SNAPSHOT + + quarkus-project + 0.1-SNAPSHOT + + 3.8.1 + true + 11 + 11 + UTF-8 + UTF-8 + quarkus-bom + io.quarkus.platform + 2.2.2.Final + 3.0.0-M4 + + + + + ${quarkus.platform.group-id} + ${quarkus.platform.artifact-id} + ${quarkus.platform.version} + pom + import + + + + + + io.quarkus + quarkus-hibernate-reactive-panache + + + io.quarkus + quarkus-resteasy-reactive + + + io.quarkus + quarkus-resteasy-reactive-jackson + + + io.quarkus + quarkus-reactive-pg-client + + + io.quarkus + quarkus-arc + + + io.quarkus + quarkus-container-image-docker + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + junit + junit + 4.8.2 + test + + + + + + ${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} + + false + + org.jboss.logmanager.LogManager + + + + + + + + native + + + native + + + + + + maven-failsafe-plugin + ${surefire-plugin.version} + + + + integration-test + verify + + + + ${project.build.directory}/${project.build.finalName}-runner + org.jboss.logmanager.LogManager + + + + + + + + + -H:+AllowVMInspection + native + + + + diff --git a/quarkus-vs-springboot/quarkus-project/src/main/docker/Dockerfile.jvm b/quarkus-vs-springboot/quarkus-project/src/main/docker/Dockerfile.jvm new file mode 100644 index 0000000000..e5d6d4d851 --- /dev/null +++ b/quarkus-vs-springboot/quarkus-project/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/code-with-quarkus-jvm . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/code-with-quarkus-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/code-with-quarkus-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/quarkus-vs-springboot/quarkus-project/src/main/docker/Dockerfile.legacy-jar b/quarkus-vs-springboot/quarkus-project/src/main/docker/Dockerfile.legacy-jar new file mode 100644 index 0000000000..da0fe018f6 --- /dev/null +++ b/quarkus-vs-springboot/quarkus-project/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/code-with-quarkus-legacy-jar . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/code-with-quarkus-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/code-with-quarkus-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/quarkus-vs-springboot/quarkus-project/src/main/docker/Dockerfile.native b/quarkus-vs-springboot/quarkus-project/src/main/docker/Dockerfile.native new file mode 100644 index 0000000000..33698500f6 --- /dev/null +++ b/quarkus-vs-springboot/quarkus-project/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/code-with-quarkus . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/code-with-quarkus +# +### +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/quarkus-vs-springboot/quarkus-project/src/main/docker/Dockerfile.native-distroless b/quarkus-vs-springboot/quarkus-project/src/main/docker/Dockerfile.native-distroless new file mode 100644 index 0000000000..5fda989696 --- /dev/null +++ b/quarkus-vs-springboot/quarkus-project/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/code-with-quarkus . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/code-with-quarkus +# +### +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/quarkus-vs-springboot/quarkus-project/src/main/docker/quarkus.yml b/quarkus-vs-springboot/quarkus-project/src/main/docker/quarkus.yml new file mode 100644 index 0000000000..00bdcf9292 --- /dev/null +++ b/quarkus-vs-springboot/quarkus-project/src/main/docker/quarkus.yml @@ -0,0 +1,23 @@ +version: '3.1' + +services: + db: + image: postgres + ports: + - '5432:5432' + environment: + POSTGRES_PASSWORD: example + app: + image: quarkus-project:0.1-SNAPSHOT + ports: + - '8080:8080' + environment: + DB_URL: postgresql://db:5432/postgres + links: + - "db" + depends_on: + - "db" +networks: + default: + driver: bridge + diff --git a/quarkus-vs-springboot/quarkus-project/src/main/java/com/baeldung/quarkus_project/ReactiveGreetingResource.java b/quarkus-vs-springboot/quarkus-project/src/main/java/com/baeldung/quarkus_project/ReactiveGreetingResource.java new file mode 100644 index 0000000000..b885e828c7 --- /dev/null +++ b/quarkus-vs-springboot/quarkus-project/src/main/java/com/baeldung/quarkus_project/ReactiveGreetingResource.java @@ -0,0 +1,17 @@ +package com.baeldung.quarkus_project; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +@Path("/hello") +public class ReactiveGreetingResource { + + @GET + @Produces(MediaType.TEXT_PLAIN) + public String hello() { + return "Hello RESTEasy Reactive"; + } + +} \ No newline at end of file diff --git a/quarkus-vs-springboot/quarkus-project/src/main/java/com/baeldung/quarkus_project/ZipCode.java b/quarkus-vs-springboot/quarkus-project/src/main/java/com/baeldung/quarkus_project/ZipCode.java new file mode 100644 index 0000000000..6764c510bf --- /dev/null +++ b/quarkus-vs-springboot/quarkus-project/src/main/java/com/baeldung/quarkus_project/ZipCode.java @@ -0,0 +1,68 @@ +package com.baeldung.quarkus_project; + +import io.quarkus.hibernate.reactive.panache.PanacheEntityBase; +import io.quarkus.runtime.annotations.RegisterForReflection; + +import javax.persistence.Entity; +import javax.persistence.Id; + +@Entity +@RegisterForReflection +public class ZipCode extends PanacheEntityBase { + + @Id + private String zip; + private String type; + private String city; + private String state; + private String county; + private String timezone; + + public String getZip() { + return zip; + } + + public void setZip(String zip) { + this.zip = zip; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + + public String getCounty() { + return county; + } + + public void setCounty(String county) { + this.county = county; + } + + public String getTimezone() { + return timezone; + } + + public void setTimezone(String timezone) { + this.timezone = timezone; + } +} diff --git a/quarkus-vs-springboot/quarkus-project/src/main/java/com/baeldung/quarkus_project/ZipCodeRepo.java b/quarkus-vs-springboot/quarkus-project/src/main/java/com/baeldung/quarkus_project/ZipCodeRepo.java new file mode 100644 index 0000000000..74f46c33ea --- /dev/null +++ b/quarkus-vs-springboot/quarkus-project/src/main/java/com/baeldung/quarkus_project/ZipCodeRepo.java @@ -0,0 +1,19 @@ +package com.baeldung.quarkus_project; + +import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase; +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.Uni; + +import javax.enterprise.context.ApplicationScoped; + +@ApplicationScoped +public class ZipCodeRepo implements PanacheRepositoryBase { + + public Multi findByCity(String city) { + return find("city = ?1", city).stream(); + } + + public Uni save(ZipCode zipCode) { + return zipCode.persistAndFlush(); + } +} diff --git a/quarkus-vs-springboot/quarkus-project/src/main/java/com/baeldung/quarkus_project/ZipCodeResource.java b/quarkus-vs-springboot/quarkus-project/src/main/java/com/baeldung/quarkus_project/ZipCodeResource.java new file mode 100644 index 0000000000..b4d41fd855 --- /dev/null +++ b/quarkus-vs-springboot/quarkus-project/src/main/java/com/baeldung/quarkus_project/ZipCodeResource.java @@ -0,0 +1,46 @@ +package com.baeldung.quarkus_project; + +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.Uni; +import org.jboss.logging.Logger; + +import javax.transaction.Transactional; +import javax.ws.rs.*; +import javax.ws.rs.core.MediaType; + +@Path("/zipcode") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class ZipCodeResource { + + private ZipCodeRepo zipRepo; + + public ZipCodeResource(ZipCodeRepo zipRepo) { + this.zipRepo = zipRepo; + } + + @GET + @Path("/{zipcode}") + public Uni findById(@PathParam("zipcode") String zipcode) { + return zipRepo.findById(zipcode); + } + + @GET + @Path("/by_city") + public Multi postZipCode(@QueryParam("city") String city) { + return zipRepo.findByCity(city); + } + + @POST + @Transactional + public Uni create(ZipCode zipCode) { + return zipRepo.findById(zipCode.getZip()) + .onItem() + .ifNull() + .switchTo(createZipCode(zipCode)); + } + + private Uni createZipCode(ZipCode zipCode) { + return Uni.createFrom().deferred(() -> zipRepo.save(zipCode)); + } +} \ No newline at end of file diff --git a/quarkus-vs-springboot/quarkus-project/src/main/resources/application.properties b/quarkus-vs-springboot/quarkus-project/src/main/resources/application.properties new file mode 100644 index 0000000000..918a129500 --- /dev/null +++ b/quarkus-vs-springboot/quarkus-project/src/main/resources/application.properties @@ -0,0 +1,9 @@ +quarkus.datasource.db-kind=postgresql +quarkus.datasource.username=postgres +quarkus.datasource.password=example + +quarkus.datasource.reactive.url=${DB_URL:postgresql://localhost:5432/postgres} +quarkus.datasource.reactive.max-size=20 + +#quarkus.hibernate-orm.log.sql=true +quarkus.hibernate-orm.database.generation=drop-and-create diff --git a/quarkus-vs-springboot/quarkus-project/src/test/java/com/baeldung/quarkus_project/NativeGreetingResourceIT.java b/quarkus-vs-springboot/quarkus-project/src/test/java/com/baeldung/quarkus_project/NativeGreetingResourceIT.java new file mode 100644 index 0000000000..99b91ea5a8 --- /dev/null +++ b/quarkus-vs-springboot/quarkus-project/src/test/java/com/baeldung/quarkus_project/NativeGreetingResourceIT.java @@ -0,0 +1,21 @@ +package com.baeldung.quarkus_project; + +import io.quarkus.test.junit.NativeImageTest; +import io.quarkus.test.junit.QuarkusTest; +import org.junit.jupiter.api.Test; + +import static io.restassured.RestAssured.given; + +@NativeImageTest +@QuarkusTest +public class NativeGreetingResourceIT { + + @Test + void testEndpoint() { + given() + .when().get("/hello") + .then() + .statusCode(200); + } + +} \ No newline at end of file diff --git a/quarkus-vs-springboot/quarkus-project/start_app.sh b/quarkus-vs-springboot/quarkus-project/start_app.sh new file mode 100755 index 0000000000..ddffe29736 --- /dev/null +++ b/quarkus-vs-springboot/quarkus-project/start_app.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +SCRIPTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" + +$SCRIPTPATH/target/quarkus-project-0.1-SNAPSHOT-runner -XX:+FlightRecorder -XX:StartFlightRecording="filename=$SCRIPTPATH/recording.jfr,name=Profiling quarkus" \ No newline at end of file diff --git a/quarkus-vs-springboot/quarkus-project/start_jvm.sh b/quarkus-vs-springboot/quarkus-project/start_jvm.sh new file mode 100755 index 0000000000..e7a7039154 --- /dev/null +++ b/quarkus-vs-springboot/quarkus-project/start_jvm.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +SCRIPTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" + +java -jar $SCRIPTPATH/target/quarkus-app/quarkus-run.jar \ No newline at end of file diff --git a/quarkus-vs-springboot/run_test.sh b/quarkus-vs-springboot/run_test.sh new file mode 100755 index 0000000000..a2d31c8587 --- /dev/null +++ b/quarkus-vs-springboot/run_test.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +$Jmeter_home/bin/jmeter -n -t load_test.jmx -l log.csv -e -o ./report \ No newline at end of file diff --git a/quarkus-vs-springboot/spring-project/build_jvm_docker.sh b/quarkus-vs-springboot/spring-project/build_jvm_docker.sh new file mode 100755 index 0000000000..c7ee730ec7 --- /dev/null +++ b/quarkus-vs-springboot/spring-project/build_jvm_docker.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +SCRIPTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" + +docker build -f $SCRIPTPATH/src/main/docker/Dockerfile.jvm -t spring-project:0.1-SNAPSHOT $SCRIPTPATH/. + diff --git a/quarkus-vs-springboot/spring-project/pom.xml b/quarkus-vs-springboot/spring-project/pom.xml new file mode 100644 index 0000000000..be5cc57765 --- /dev/null +++ b/quarkus-vs-springboot/spring-project/pom.xml @@ -0,0 +1,171 @@ + + + + + org.springframework.boot + spring-boot-starter-parent + 2.5.4 + + + + 4.0.0 + spring-project + com.baeldung + 0.1-SNAPSHOT + + + 11 + + 0.10.3 + + + + + org.springframework.boot + spring-boot-starter-data-r2dbc + + + org.springframework.boot + spring-boot-starter-webflux + + + org.springframework.experimental + spring-native + ${spring-native.version} + + + io.r2dbc + r2dbc-postgresql + runtime + + + org.postgresql + postgresql + runtime + + + org.springframework.boot + spring-boot-starter-test + test + + + io.projectreactor + reactor-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + ${repackage.classifier} + + paketobuildpacks/builder:tiny + + true + + + + + + org.springframework.experimental + spring-aot-maven-plugin + ${spring-native.version} + + + test-generate + + test-generate + + + + generate + + generate + + + + + + + + + + spring-releases + Spring Releases + https://repo.spring.io/release + + false + + + + + + spring-releases + Spring Releases + https://repo.spring.io/release + + false + + + + + + + native + + exec + 0.9.3 + + + + org.graalvm.buildtools + junit-platform-native + ${native-buildtools.version} + + + + + + org.graalvm.buildtools + native-maven-plugin + ${native-buildtools.version} + + + -H:+AllowVMInspection + + + + + test-native + test + + test + + + + build-native + package + + build + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + -DspringAot=true -agentlib:native-image-agent=access-filter-file=src/test/resources/access-filter.json,config-merge-dir=target/classes/META-INF/native-image + + + + + + + \ No newline at end of file diff --git a/quarkus-vs-springboot/spring-project/src/main/docker/Dockerfile.jvm b/quarkus-vs-springboot/spring-project/src/main/docker/Dockerfile.jvm new file mode 100644 index 0000000000..ca3f3cca76 --- /dev/null +++ b/quarkus-vs-springboot/spring-project/src/main/docker/Dockerfile.jvm @@ -0,0 +1,12 @@ +FROM openjdk:11 + +ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' + +COPY --chown=1001 target/spring-project-0.1-SNAPSHOT-exec.jar /spring-app/ + +WORKDIR /spring-app + +EXPOSE 8080 +USER 1001 + +ENTRYPOINT ["java", "-jar", "spring-project-0.1-SNAPSHOT-exec.jar" ] \ No newline at end of file diff --git a/quarkus-vs-springboot/spring-project/src/main/docker/spring.yml b/quarkus-vs-springboot/spring-project/src/main/docker/spring.yml new file mode 100644 index 0000000000..2214e0a898 --- /dev/null +++ b/quarkus-vs-springboot/spring-project/src/main/docker/spring.yml @@ -0,0 +1,22 @@ +version: '3.1' + +services: + db: + image: postgres + ports: + - '5432:5432' + environment: + POSTGRES_PASSWORD: example + app: + image: spring-project:0.1-SNAPSHOT + ports: + - '8080:8080' + environment: + DB_URL: r2dbc:postgresql://db:5432/postgres + links: + - "db" + depends_on: + - "db" +networks: + default: + driver: bridge \ No newline at end of file diff --git a/quarkus-vs-springboot/spring-project/src/main/java/com/baeldung/spring_project/Startup.java b/quarkus-vs-springboot/spring-project/src/main/java/com/baeldung/spring_project/Startup.java new file mode 100644 index 0000000000..48cf7e8ed1 --- /dev/null +++ b/quarkus-vs-springboot/spring-project/src/main/java/com/baeldung/spring_project/Startup.java @@ -0,0 +1,37 @@ +package com.baeldung.spring_project; + +import com.baeldung.spring_project.domain.ZIPRepo; +import io.r2dbc.spi.ConnectionFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.r2dbc.connection.R2dbcTransactionManager; +import org.springframework.r2dbc.connection.init.ConnectionFactoryInitializer; +import org.springframework.r2dbc.connection.init.ResourceDatabasePopulator; +import org.springframework.transaction.ReactiveTransactionManager; + +@SpringBootApplication +public class Startup { + + public static void main(String[] args) { + SpringApplication.run(Startup.class, args).getBean(ZIPRepo.class).findById(""); + } + + @Bean + ConnectionFactoryInitializer initializer(ConnectionFactory connectionFactory) { + + var initializer = new ConnectionFactoryInitializer(); + initializer.setConnectionFactory(connectionFactory); + initializer.setDatabasePopulator(new ResourceDatabasePopulator(new ByteArrayResource(("" + + "DROP TABLE IF EXISTS zipcode;" + + "CREATE TABLE zipcode (zip VARCHAR(100) PRIMARY KEY, type VARCHAR(255) NULL, city VARCHAR(255) NULL, state VARCHAR(255) NULL, county VARCHAR(255) NULL, timezone VARCHAR(255) NULL);") + .getBytes()))); + + return initializer; + } + + @Bean ReactiveTransactionManager transactionManager(ConnectionFactory connectionFactory) { + return new R2dbcTransactionManager(connectionFactory); + } +} diff --git a/quarkus-vs-springboot/spring-project/src/main/java/com/baeldung/spring_project/ZipCodeApi.java b/quarkus-vs-springboot/spring-project/src/main/java/com/baeldung/spring_project/ZipCodeApi.java new file mode 100644 index 0000000000..263ce67e21 --- /dev/null +++ b/quarkus-vs-springboot/spring-project/src/main/java/com/baeldung/spring_project/ZipCodeApi.java @@ -0,0 +1,44 @@ +package com.baeldung.spring_project; + +import com.baeldung.spring_project.domain.ZIPRepo; +import com.baeldung.spring_project.domain.ZipCode; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.util.function.Supplier; + +@RestController +@RequestMapping(value = "/zipcode") +public class ZipCodeApi { + + private ZIPRepo zipRepo; + + public ZipCodeApi(ZIPRepo zipRepo) { + this.zipRepo = zipRepo; + } + + @GetMapping("/{zipcode}") + public Mono findById(@PathVariable String zipcode) { + return zipRepo.findById(zipcode); + } + + @GetMapping("/by_city") + public Flux postZipCode(@RequestParam String city) { + return zipRepo.findByCity(city); + } + + @Transactional + @PostMapping + public Mono create(@RequestBody ZipCode zipCode) { + return zipRepo.findById(zipCode.getZip()).switchIfEmpty(Mono.defer(createZipCode(zipCode))); + } + + private Supplier> createZipCode(ZipCode zipCode) { + return () -> { + zipCode.setId(zipCode.getZip()); + return zipRepo.save(zipCode); + }; + } +} diff --git a/quarkus-vs-springboot/spring-project/src/main/java/com/baeldung/spring_project/domain/ZIPRepo.java b/quarkus-vs-springboot/spring-project/src/main/java/com/baeldung/spring_project/domain/ZIPRepo.java new file mode 100644 index 0000000000..59d3fb4293 --- /dev/null +++ b/quarkus-vs-springboot/spring-project/src/main/java/com/baeldung/spring_project/domain/ZIPRepo.java @@ -0,0 +1,11 @@ +package com.baeldung.spring_project.domain; + +import org.springframework.data.r2dbc.repository.Query; +import org.springframework.data.repository.reactive.ReactiveCrudRepository; +import reactor.core.publisher.Flux; + +public interface ZIPRepo extends ReactiveCrudRepository { + + @Query("SELECT * FROM zipcode WHERE city = :city") + Flux findByCity(String city); +} diff --git a/quarkus-vs-springboot/spring-project/src/main/java/com/baeldung/spring_project/domain/ZipCode.java b/quarkus-vs-springboot/spring-project/src/main/java/com/baeldung/spring_project/domain/ZipCode.java new file mode 100644 index 0000000000..b2ef7b17a8 --- /dev/null +++ b/quarkus-vs-springboot/spring-project/src/main/java/com/baeldung/spring_project/domain/ZipCode.java @@ -0,0 +1,86 @@ +package com.baeldung.spring_project.domain; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.springframework.data.annotation.Id; +import org.springframework.data.annotation.Transient; +import org.springframework.data.domain.Persistable; +import org.springframework.data.relational.core.mapping.Table; + +@Table(value = "zipcode") +public class ZipCode implements Persistable { + + @Id + private String zip; + private String type; + private String city; + private String state; + private String county; + private String timezone; + + @Transient + private boolean persisted; + + public String getZip() { + return zip; + } + + void setZip(String zip) { + this.zip = zip; + this.persisted = true; + } + + public void setId(String zip) { + this.zip = zip; + this.persisted = false; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + + public String getCounty() { + return county; + } + + public void setCounty(String county) { + this.county = county; + } + + public String getTimezone() { + return timezone; + } + + public void setTimezone(String timezone) { + this.timezone = timezone; + } + + @JsonIgnore + @Override public String getId() { + return zip; + } + + @JsonIgnore + @Override public boolean isNew() { + return !persisted; + } +} diff --git a/quarkus-vs-springboot/spring-project/src/main/resources/application.properties b/quarkus-vs-springboot/spring-project/src/main/resources/application.properties new file mode 100644 index 0000000000..adc2f8b0b4 --- /dev/null +++ b/quarkus-vs-springboot/spring-project/src/main/resources/application.properties @@ -0,0 +1,5 @@ +spring.r2dbc.url=${DB_URL:'r2dbc:postgresql://localhost:5432/postgres'} +spring.r2dbc.username=postgres +spring.r2dbc.password=example +spring.r2dbc.pool.enabled=true +spring.r2dbc.pool.maxSize=20 \ No newline at end of file diff --git a/quarkus-vs-springboot/spring-project/src/test/java/com/baeldung/spring_project/StartupIT.java b/quarkus-vs-springboot/spring-project/src/test/java/com/baeldung/spring_project/StartupIT.java new file mode 100644 index 0000000000..7487e5aa7f --- /dev/null +++ b/quarkus-vs-springboot/spring-project/src/test/java/com/baeldung/spring_project/StartupIT.java @@ -0,0 +1,13 @@ +package com.baeldung.spring_project; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class StartupIT { + + @Test + void contextLoads() { + } + +} diff --git a/quarkus-vs-springboot/spring-project/src/test/resources/access-filter.json b/quarkus-vs-springboot/spring-project/src/test/resources/access-filter.json new file mode 100644 index 0000000000..9718fed1c6 --- /dev/null +++ b/quarkus-vs-springboot/spring-project/src/test/resources/access-filter.json @@ -0,0 +1,11 @@ +{ "rules": [ + {"excludeClasses": "org.apache.maven.surefire.**"}, + {"excludeClasses": "net.bytebuddy.**"}, + {"excludeClasses": "org.apiguardian.**"}, + {"excludeClasses": "org.junit.**"}, + {"excludeClasses": "org.mockito.**"}, + {"excludeClasses": "org.springframework.test.**"}, + {"excludeClasses": "org.springframework.boot.test.**"}, + {"excludeClasses": "com.example.demo.test.**"} +] +} \ No newline at end of file diff --git a/quarkus-vs-springboot/spring-project/start_app.sh b/quarkus-vs-springboot/spring-project/start_app.sh new file mode 100755 index 0000000000..f596213c4c --- /dev/null +++ b/quarkus-vs-springboot/spring-project/start_app.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +SCRIPTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" + +$SCRIPTPATH/target/spring-project -XX:+FlightRecorder -XX:StartFlightRecording="filename=$SCRIPTPATH/recording.jfr,name=Profiling spring" + diff --git a/quarkus-vs-springboot/spring-project/start_jvm.sh b/quarkus-vs-springboot/spring-project/start_jvm.sh new file mode 100755 index 0000000000..f62762fcca --- /dev/null +++ b/quarkus-vs-springboot/spring-project/start_jvm.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +SCRIPTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" + +java -jar $SCRIPTPATH/target/spring-project-0.1-SNAPSHOT-exec.jar +