[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
This commit is contained in:
Thiago dos Santos Hora 2021-09-23 11:07:26 +02:00 committed by GitHub
parent 785319af37
commit 47249aa1ff
34 changed files with 1602 additions and 0 deletions

View File

@ -1356,6 +1356,7 @@
<module>core-java-modules/core-java-time-measurements</module>
<module>core-java-modules/multimodulemavenproject</module>
<module>core-java-modules/core-java-strings</module>
<module>quarkus-vs-springboot</module>
</modules>
</profile>
</profiles>

View File

@ -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.

View File

@ -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
1 Holtsville
2 Adjuntas
3 Aguada
4 Aguadilla
5 Maricao
6 Anasco
7 Angeles
8 Arecibo
9 Bajadero
10 Barceloneta
11 Boqueron
12 Cabo Rojo
13 Penuelas
14 Camuy
15 Castaner
16 Rosario
17 Sabana Grande
18 Ciales
19 Utuado
20 Dorado
21 Ensenada
22 Florida
23 Garrochales
24 Guanica
25 Guayanilla
26 Hatillo
27 Hormigueros
28 Isabela
29 Jayuya
30 Lajas
31 Lares
32 Las Marias
33 Manati
34 Moca
35 Rincon
36 Quebradillas
37 Mayaguez
38 San German
39 San Sebastian
40 Morovis
41 Sabana Hoyos
42 San Antonio
43 Vega Alta
44 Vega Baja
45 Yauco
46 Aguas Buenas
47 Aguirre
48 Aibonito
49 Maunabo
50 Arroyo
51 Mercedita
52 Ponce
53 Naguabo
54 Naranjito
55 Orocovis
56 Palmer
57 Patillas
58 Caguas
59 Canovanas
60 Ceiba
61 Cayey
62 Fajardo
63 Cidra
64 Puerto Real
65 Punta Santiago
66 Roosevelt Roads
67 Rio Blanco
68 Rio Grande
69 Salinas
70 San Lorenzo
71 Santa Isabel
72 Vieques
73 Villalba
74 Yabucoa
75 Coamo
76 Las Piedras
77 Loiza
78 Luquillo
79 Culebra
80 Juncos
81 Gurabo
82 Coto Laurel
83 Comerio
84 Corozal
85 Guayama
86 La Plata
87 Humacao
88 Barranquitas
89 Juana Diaz
90 St Thomas
91 Christiansted
92 St John
93 Frederiksted
94 Kingshill
95 San Juan
96 Fort Buchanan
97 Toa Baja
98 Sabana Seca
99 Toa Alta
100 Bayamon
101 Catano
102 Guaynabo
103 Trujillo Alto
104 Saint Just
105 Carolina
106 Agawam
107 Amherst
108 Barre
109 Belchertown
110 Blandford
111 Bondsville
112 Brimfield
113 Chester
114 Chesterfield
115 Chicopee
116 Cummington
117 Easthampton
118 East Longmeadow
119 East Otis
120 Feeding Hills
121 Gilbertville
122 Goshen
123 Granby
124 Granville
125 Hadley
126 Hampden
127 Hardwick
128 Hatfield
129 Haydenville
130 Holyoke
131 Huntington
132 Leeds
133 Leverett
134 Ludlow
135 Monson
136 North Amherst

View File

@ -0,0 +1,384 @@
<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="5.0" jmeter="5.4.1">
<hashTree>
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Test Plan" enabled="true">
<stringProp name="TestPlan.comments"></stringProp>
<boolProp name="TestPlan.functional_mode">false</boolProp>
<boolProp name="TestPlan.tearDown_on_shutdown">true</boolProp>
<boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
<elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="TestPlan.user_define_classpath"></stringProp>
</TestPlan>
<hashTree>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Create zipcode" enabled="true">
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
<boolProp name="LoopController.continue_forever">false</boolProp>
<intProp name="LoopController.loops">-1</intProp>
</elementProp>
<stringProp name="ThreadGroup.num_threads">500</stringProp>
<stringProp name="ThreadGroup.ramp_time">150</stringProp>
<boolProp name="ThreadGroup.scheduler">true</boolProp>
<stringProp name="ThreadGroup.duration">300</stringProp>
<stringProp name="ThreadGroup.delay"></stringProp>
<boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp>
</ThreadGroup>
<hashTree>
<CSVDataSet guiclass="TestBeanGUI" testclass="CSVDataSet" testname="CSV Data Set Config" enabled="true">
<stringProp name="delimiter">,</stringProp>
<stringProp name="fileEncoding"></stringProp>
<stringProp name="filename">zip_code_database.csv</stringProp>
<boolProp name="ignoreFirstLine">true</boolProp>
<boolProp name="quotedData">true</boolProp>
<boolProp name="recycle">false</boolProp>
<stringProp name="shareMode">shareMode.group</stringProp>
<boolProp name="stopThread">true</boolProp>
<stringProp name="variableNames">zip,type,decommissioned,primary_city,acceptable_cities,unacceptable_cities,state,county,timezone,area_codes,world_region,country,latitude,longitude,irs_estimated_population</stringProp>
</CSVDataSet>
<hashTree/>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="HTTP Request" enabled="true">
<boolProp name="HTTPSampler.postBodyRaw">true</boolProp>
<elementProp name="HTTPsampler.Arguments" elementType="Arguments">
<collectionProp name="Arguments.arguments">
<elementProp name="" elementType="HTTPArgument">
<boolProp name="HTTPArgument.always_encode">false</boolProp>
<stringProp name="Argument.value">{&#xd;
&quot;zip&quot;:&quot;${zip}&quot;,&#xd;
&quot;type&quot;:&quot;${type}&quot;,&#xd;
&quot;city&quot;:&quot;${primary_city}&quot;,&#xd;
&quot;state&quot;:&quot;${state}&quot;,&#xd;
&quot;county&quot;:&quot;${country}&quot;,&#xd;
&quot;timezone&quot;:&quot;${timezone}&quot;&#xd;
}</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
</collectionProp>
</elementProp>
<stringProp name="HTTPSampler.domain">localhost</stringProp>
<stringProp name="HTTPSampler.port">8080</stringProp>
<stringProp name="HTTPSampler.protocol"></stringProp>
<stringProp name="HTTPSampler.contentEncoding">UTF-8</stringProp>
<stringProp name="HTTPSampler.path">zipcode</stringProp>
<stringProp name="HTTPSampler.method">POST</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
</HTTPSamplerProxy>
<hashTree>
<HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true">
<collectionProp name="HeaderManager.headers">
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Content-Type</stringProp>
<stringProp name="Header.value">application/json</stringProp>
</elementProp>
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Accept</stringProp>
<stringProp name="Header.value">*/*</stringProp>
</elementProp>
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Cache-Control</stringProp>
<stringProp name="Header.value">no-cache</stringProp>
</elementProp>
</collectionProp>
</HeaderManager>
<hashTree/>
</hashTree>
<ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="View Results Tree" enabled="true">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>false</xml>
<fieldNames>true</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
<sentBytes>true</sentBytes>
<url>true</url>
<threadCounts>true</threadCounts>
<idleTime>true</idleTime>
<connectTime>true</connectTime>
</value>
</objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
</hashTree>
<kg.apc.jmeter.threads.UltimateThreadGroup guiclass="kg.apc.jmeter.threads.UltimateThreadGroupGui" testclass="kg.apc.jmeter.threads.UltimateThreadGroup" testname="GET zipcode" enabled="true">
<collectionProp name="ultimatethreadgroupdata">
<collectionProp name="2144744627">
<stringProp name="1603">25</stringProp>
<stringProp name="1629">30</stringProp>
<stringProp name="1572">15</stringProp>
<stringProp name="49746">255</stringProp>
<stringProp name="0"></stringProp>
</collectionProp>
<collectionProp name="1094620787">
<stringProp name="48692">125</stringProp>
<stringProp name="1629">30</stringProp>
<stringProp name="1572">15</stringProp>
<stringProp name="49746">255</stringProp>
<stringProp name="0"></stringProp>
</collectionProp>
<collectionProp name="-2147086262">
<stringProp name="48692">125</stringProp>
<stringProp name="1722">60</stringProp>
<stringProp name="1572">15</stringProp>
<stringProp name="49653">225</stringProp>
<stringProp name="0"></stringProp>
</collectionProp>
<collectionProp name="-690133601">
<stringProp name="49653">225</stringProp>
<stringProp name="48780">150</stringProp>
<stringProp name="30">30</stringProp>
<stringProp name="48687">120</stringProp>
<stringProp name="0"></stringProp>
</collectionProp>
</collectionProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
<boolProp name="LoopController.continue_forever">false</boolProp>
<intProp name="LoopController.loops">-1</intProp>
</elementProp>
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
</kg.apc.jmeter.threads.UltimateThreadGroup>
<hashTree>
<CSVDataSet guiclass="TestBeanGUI" testclass="CSVDataSet" testname="CSV Data Set Config" enabled="true">
<stringProp name="delimiter">,</stringProp>
<stringProp name="fileEncoding"></stringProp>
<stringProp name="filename">zip_code_database.csv</stringProp>
<boolProp name="ignoreFirstLine">true</boolProp>
<boolProp name="quotedData">true</boolProp>
<boolProp name="recycle">false</boolProp>
<stringProp name="shareMode">shareMode.group</stringProp>
<boolProp name="stopThread">true</boolProp>
<stringProp name="variableNames">zip,type,decommissioned,primary_city,acceptable_cities,unacceptable_cities,state,county,timezone,area_codes,world_region,country,latitude,longitude,irs_estimated_population</stringProp>
</CSVDataSet>
<hashTree/>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="HTTP Request" enabled="true">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="HTTPSampler.domain">localhost</stringProp>
<stringProp name="HTTPSampler.port">8080</stringProp>
<stringProp name="HTTPSampler.protocol"></stringProp>
<stringProp name="HTTPSampler.contentEncoding">UTF-8</stringProp>
<stringProp name="HTTPSampler.path">zipcode/${zip}</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
</HTTPSamplerProxy>
<hashTree>
<HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true">
<collectionProp name="HeaderManager.headers">
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Content-Type</stringProp>
<stringProp name="Header.value">application/json</stringProp>
</elementProp>
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Accept</stringProp>
<stringProp name="Header.value">*/*</stringProp>
</elementProp>
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Cache-Control</stringProp>
<stringProp name="Header.value">no-cache</stringProp>
</elementProp>
</collectionProp>
</HeaderManager>
<hashTree/>
</hashTree>
<ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="View Results Tree" enabled="true">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>false</xml>
<fieldNames>true</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
<sentBytes>true</sentBytes>
<url>true</url>
<threadCounts>true</threadCounts>
<idleTime>true</idleTime>
<connectTime>true</connectTime>
</value>
</objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
</hashTree>
<kg.apc.jmeter.threads.UltimateThreadGroup guiclass="kg.apc.jmeter.threads.UltimateThreadGroupGui" testclass="kg.apc.jmeter.threads.UltimateThreadGroup" testname="GET By city" enabled="true">
<collectionProp name="ultimatethreadgroupdata">
<collectionProp name="2144744627">
<stringProp name="1603">25</stringProp>
<stringProp name="1629">30</stringProp>
<stringProp name="1572">15</stringProp>
<stringProp name="49746">255</stringProp>
<stringProp name="0"></stringProp>
</collectionProp>
<collectionProp name="1094620787">
<stringProp name="48692">125</stringProp>
<stringProp name="1629">30</stringProp>
<stringProp name="1572">15</stringProp>
<stringProp name="49746">255</stringProp>
<stringProp name="0"></stringProp>
</collectionProp>
<collectionProp name="-2147086262">
<stringProp name="48692">125</stringProp>
<stringProp name="1722">60</stringProp>
<stringProp name="1572">15</stringProp>
<stringProp name="49653">225</stringProp>
<stringProp name="0"></stringProp>
</collectionProp>
<collectionProp name="-690133601">
<stringProp name="49653">225</stringProp>
<stringProp name="48780">150</stringProp>
<stringProp name="30">30</stringProp>
<stringProp name="48687">120</stringProp>
<stringProp name="0"></stringProp>
</collectionProp>
</collectionProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
<boolProp name="LoopController.continue_forever">false</boolProp>
<intProp name="LoopController.loops">-1</intProp>
</elementProp>
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
</kg.apc.jmeter.threads.UltimateThreadGroup>
<hashTree>
<CSVDataSet guiclass="TestBeanGUI" testclass="CSVDataSet" testname="CSV Data Set Config" enabled="true">
<stringProp name="delimiter">,</stringProp>
<stringProp name="fileEncoding"></stringProp>
<stringProp name="filename">cities.csv</stringProp>
<boolProp name="ignoreFirstLine">false</boolProp>
<boolProp name="quotedData">false</boolProp>
<boolProp name="recycle">true</boolProp>
<stringProp name="shareMode">shareMode.group</stringProp>
<boolProp name="stopThread">false</boolProp>
<stringProp name="variableNames">city</stringProp>
</CSVDataSet>
<hashTree/>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="HTTP Request" enabled="true">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="HTTPSampler.domain">localhost</stringProp>
<stringProp name="HTTPSampler.port">8080</stringProp>
<stringProp name="HTTPSampler.protocol"></stringProp>
<stringProp name="HTTPSampler.contentEncoding">UTF-8</stringProp>
<stringProp name="HTTPSampler.path">zipcode/by_city?city=${city}</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
</HTTPSamplerProxy>
<hashTree>
<HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true">
<collectionProp name="HeaderManager.headers">
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Content-Type</stringProp>
<stringProp name="Header.value">application/json</stringProp>
</elementProp>
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Accept</stringProp>
<stringProp name="Header.value">*/*</stringProp>
</elementProp>
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Cache-Control</stringProp>
<stringProp name="Header.value">no-cache</stringProp>
</elementProp>
</collectionProp>
</HeaderManager>
<hashTree/>
</hashTree>
<ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="View Results Tree" enabled="true">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>false</xml>
<fieldNames>true</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
<sentBytes>true</sentBytes>
<url>true</url>
<threadCounts>true</threadCounts>
<idleTime>true</idleTime>
<connectTime>true</connectTime>
</value>
</objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
</hashTree>
<Summariser guiclass="SummariserGui" testclass="Summariser" testname="Generate Summary Results" enabled="true"/>
<hashTree/>
</hashTree>
</hashTree>
</jmeterTestPlan>

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>parent-modules</artifactId>
<groupId>com.baeldung</groupId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<version>1.0-SNAPSHOT</version>
<name>quarkus-vs-springboot</name>
<artifactId>quarkus-vs-springboot</artifactId>
<packaging>pom</packaging>
<modelVersion>4.0.0</modelVersion>
<modules>
<module>quarkus-project</module>
<module>spring-project</module>
</modules>
</project>

View File

@ -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

View File

@ -0,0 +1,149 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>quarkus-vs-springboot</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>quarkus-project</artifactId>
<version>0.1-SNAPSHOT</version>
<properties>
<compiler-plugin.version>3.8.1</compiler-plugin.version>
<maven.compiler.parameters>true</maven.compiler.parameters>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
<quarkus.platform.version>2.2.2.Final</quarkus.platform.version>
<surefire-plugin.version>3.0.0-M4</surefire-plugin.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>${quarkus.platform.group-id}</groupId>
<artifactId>${quarkus.platform.artifact-id}</artifactId>
<version>${quarkus.platform.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-reactive-panache</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-reactive-pg-client</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-container-image-docker</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>${quarkus.platform.group-id}</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<version>${quarkus.platform.version}</version>
<extensions>true</extensions>
<executions>
<execution>
<goals>
<goal>build</goal>
<goal>generate-code</goal>
<goal>generate-code-tests</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${compiler-plugin.version}</version>
<configuration>
<parameters>${maven.compiler.parameters}</parameters>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire-plugin.version}</version>
<configuration>
<useSystemClassLoader>false</useSystemClassLoader>
<systemPropertyVariables>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>native</id>
<activation>
<property>
<name>native</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>${surefire-plugin.version}</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
<configuration>
<systemPropertyVariables>
<native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
</systemPropertyVariables>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<properties>
<quarkus.native.additional-build-args>-H:+AllowVMInspection</quarkus.native.additional-build-args>
<quarkus.package.type>native</quarkus.package.type>
</properties>
</profile>
</profiles>
</project>

View File

@ -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" ]

View File

@ -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" ]

View File

@ -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"]

View File

@ -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"]

View File

@ -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

View File

@ -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";
}
}

View File

@ -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;
}
}

View File

@ -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<ZipCode, String> {
public Multi<ZipCode> findByCity(String city) {
return find("city = ?1", city).stream();
}
public Uni<ZipCode> save(ZipCode zipCode) {
return zipCode.persistAndFlush();
}
}

View File

@ -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<ZipCode> findById(@PathParam("zipcode") String zipcode) {
return zipRepo.findById(zipcode);
}
@GET
@Path("/by_city")
public Multi<ZipCode> postZipCode(@QueryParam("city") String city) {
return zipRepo.findByCity(city);
}
@POST
@Transactional
public Uni<ZipCode> create(ZipCode zipCode) {
return zipRepo.findById(zipCode.getZip())
.onItem()
.ifNull()
.switchTo(createZipCode(zipCode));
}
private Uni<ZipCode> createZipCode(ZipCode zipCode) {
return Uni.createFrom().deferred(() -> zipRepo.save(zipCode));
}
}

View File

@ -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

View File

@ -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);
}
}

View File

@ -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"

View File

@ -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

View File

@ -0,0 +1,3 @@
#!/bin/bash
$Jmeter_home/bin/jmeter -n -t load_test.jmx -l log.csv -e -o ./report

View File

@ -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/.

View File

@ -0,0 +1,171 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.4</version>
<relativePath/>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-project</artifactId>
<groupId>com.baeldung</groupId>
<version>0.1-SNAPSHOT</version>
<properties>
<java.version>11</java.version>
<repackage.classifier/>
<spring-native.version>0.10.3</spring-native.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-native</artifactId>
<version>${spring-native.version}</version>
</dependency>
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<classifier>${repackage.classifier}</classifier>
<image>
<builder>paketobuildpacks/builder:tiny</builder>
<env>
<BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE>
</env>
</image>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-aot-maven-plugin</artifactId>
<version>${spring-native.version}</version>
<executions>
<execution>
<id>test-generate</id>
<goals>
<goal>test-generate</goal>
</goals>
</execution>
<execution>
<id>generate</id>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>https://repo.spring.io/release</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>https://repo.spring.io/release</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
<profiles>
<profile>
<id>native</id>
<properties>
<repackage.classifier>exec</repackage.classifier>
<native-buildtools.version>0.9.3</native-buildtools.version>
</properties>
<dependencies>
<dependency>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>junit-platform-native</artifactId>
<version>${native-buildtools.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<version>${native-buildtools.version}</version>
<configuration>
<buildArgs combine.children="append">
<buildArgs>-H:+AllowVMInspection</buildArgs>
</buildArgs>
</configuration>
<executions>
<execution>
<id>test-native</id>
<phase>test</phase>
<goals>
<goal>test</goal>
</goals>
</execution>
<execution>
<id>build-native</id>
<phase>package</phase>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>-DspringAot=true -agentlib:native-image-agent=access-filter-file=src/test/resources/access-filter.json,config-merge-dir=target/classes/META-INF/native-image</argLine>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@ -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" ]

View File

@ -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

View File

@ -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);
}
}

View File

@ -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<ZipCode> findById(@PathVariable String zipcode) {
return zipRepo.findById(zipcode);
}
@GetMapping("/by_city")
public Flux<ZipCode> postZipCode(@RequestParam String city) {
return zipRepo.findByCity(city);
}
@Transactional
@PostMapping
public Mono<ZipCode> create(@RequestBody ZipCode zipCode) {
return zipRepo.findById(zipCode.getZip()).switchIfEmpty(Mono.defer(createZipCode(zipCode)));
}
private Supplier<Mono<? extends ZipCode>> createZipCode(ZipCode zipCode) {
return () -> {
zipCode.setId(zipCode.getZip());
return zipRepo.save(zipCode);
};
}
}

View File

@ -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<ZipCode, String> {
@Query("SELECT * FROM zipcode WHERE city = :city")
Flux<ZipCode> findByCity(String city);
}

View File

@ -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<String> {
@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;
}
}

View File

@ -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

View File

@ -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() {
}
}

View File

@ -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.**"}
]
}

View File

@ -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"

View File

@ -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