forked from DevFW-CICD/spring-petclinic
Compare commits
78 commits
Author | SHA1 | Date | |
---|---|---|---|
2c6b644330 | |||
34ef850730 | |||
![]() |
22caee3d03 | ||
![]() |
42e2c74b0b | ||
![]() |
3b90fac983 | ||
![]() |
300597fc6c | ||
![]() |
40a41375e6 | ||
![]() |
214a8fb87f | ||
![]() |
a0ba075bd8 | ||
![]() |
91f55a4f71 | ||
![]() |
9f1cda1c08 | ||
![]() |
317562a170 | ||
![]() |
1cad4124b7 | ||
![]() |
668629d5bd | ||
![]() |
a3026bddbb | ||
![]() |
50866def72 | ||
![]() |
14af47d4e5 | ||
![]() |
fdc40a7048 | ||
![]() |
a50bfb65bb | ||
![]() |
dff45cf70c | ||
![]() |
90bbb98ea6 | ||
![]() |
bbb237928f | ||
![]() |
912de1648e | ||
![]() |
62dbfa8e9a | ||
![]() |
ae1bb8228c | ||
![]() |
fc442120ce | ||
![]() |
f8001e0add | ||
![]() |
608e2b6142 | ||
![]() |
6fffe61b93 | ||
![]() |
2daa3993ee | ||
![]() |
d90e284621 | ||
![]() |
f6f923bd39 | ||
![]() |
cabb74ed53 | ||
![]() |
91328af12d | ||
![]() |
12eb8b1ff3 | ||
![]() |
383edc1656 | ||
![]() |
d8fcd11e67 | ||
![]() |
308f7ec783 | ||
![]() |
fa8f6836bd | ||
![]() |
39cc7e160e | ||
![]() |
7034d1746a | ||
![]() |
52e528b2ad | ||
![]() |
aa9b9e7f43 | ||
![]() |
055ef65dfb | ||
![]() |
85ae6fbc80 | ||
![]() |
6fa3fa8196 | ||
![]() |
527dad0c17 | ||
![]() |
284d07942e | ||
![]() |
405cdc635b | ||
![]() |
c7bc8b1ae6 | ||
![]() |
c0bc917426 | ||
![]() |
bf3bc29e56 | ||
![]() |
395356afc6 | ||
![]() |
8f799263e0 | ||
![]() |
19f5268ac1 | ||
![]() |
f3a67551c2 | ||
![]() |
7055f0c6a6 | ||
![]() |
c5b3b3597e | ||
![]() |
ca5785465f | ||
![]() |
d604a01984 | ||
![]() |
57eb7443a6 | ||
![]() |
dea3432a6d | ||
![]() |
9ed20bf999 | ||
![]() |
4f78b07490 | ||
![]() |
5941227ab3 | ||
![]() |
516722647a | ||
![]() |
3efd6e0f03 | ||
![]() |
6508cfb05f | ||
![]() |
4148fc9eb3 | ||
![]() |
a2f1ad5503 | ||
![]() |
da1c93d367 | ||
![]() |
ee6573cee0 | ||
![]() |
836d111e99 | ||
![]() |
2fe96130c0 | ||
![]() |
6fe21e5d0b | ||
![]() |
4ad3b4edb5 | ||
![]() |
a35189a9c5 | ||
![]() |
172112492e |
59 changed files with 1342 additions and 1026 deletions
|
@ -1,13 +1,11 @@
|
||||||
|
# Not actually used by the devcontainer, but it is used by gitpod
|
||||||
ARG VARIANT=17-bullseye
|
ARG VARIANT=17-bullseye
|
||||||
FROM mcr.microsoft.com/vscode/devcontainers/java:0-${VARIANT}
|
FROM mcr.microsoft.com/vscode/devcontainers/java:0-${VARIANT}
|
||||||
|
|
||||||
ARG NODE_VERSION="none"
|
ARG NODE_VERSION="none"
|
||||||
RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi
|
RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi
|
||||||
|
|
||||||
ARG USER=vscode
|
ARG USER=vscode
|
||||||
VOLUME /home/$USER/.m2
|
VOLUME /home/$USER/.m2
|
||||||
VOLUME /home/$USER/.gradle
|
VOLUME /home/$USER/.gradle
|
||||||
|
|
||||||
ARG JAVA_VERSION=17.0.7-ms
|
ARG JAVA_VERSION=17.0.7-ms
|
||||||
RUN sudo mkdir /home/$USER/.m2 /home/$USER/.gradle && sudo chown $USER:$USER /home/$USER/.m2 /home/$USER/.gradle
|
RUN sudo mkdir /home/$USER/.m2 /home/$USER/.gradle && sudo chown $USER:$USER /home/$USER/.m2 /home/$USER/.gradle
|
||||||
RUN bash -lc '. /usr/local/sdkman/bin/sdkman-init.sh && sdk install java $JAVA_VERSION && sdk use java $JAVA_VERSION'
|
RUN bash -lc '. /usr/local/sdkman/bin/sdkman-init.sh && sdk install java $JAVA_VERSION && sdk use java $JAVA_VERSION'
|
|
@ -1,32 +1,25 @@
|
||||||
{
|
{
|
||||||
"name": "Petclinic",
|
"name": "Java",
|
||||||
"dockerFile": "Dockerfile",
|
"image": "mcr.microsoft.com/devcontainers/base:ubuntu",
|
||||||
"runArgs": [
|
|
||||||
"--cap-add=SYS_PTRACE",
|
|
||||||
"--security-opt",
|
|
||||||
"seccomp=unconfined",
|
|
||||||
"--mount",
|
|
||||||
"type=bind,source=${env:HOME}/.m2,target=/home/vscode/.m2",
|
|
||||||
"--mount",
|
|
||||||
"type=bind,source=${env:HOME}/.gradle,target=/home/vscode/.gradle",
|
|
||||||
"--env",
|
|
||||||
"GRADLE_USER_HOME=/home/vscode/.gradle"
|
|
||||||
],
|
|
||||||
"initializeCommand": "mkdir -p ${env:HOME}/.m2 ${env:HOME}/.gradle",
|
|
||||||
"postCreateCommand": "sudo chown vscode:vscode /home/vscode/.m2 /home/vscode/.gradle",
|
|
||||||
"remoteUser": "vscode",
|
|
||||||
"features": {
|
"features": {
|
||||||
"docker-in-docker": "latest"
|
"ghcr.io/devcontainers/features/java:1": {
|
||||||
|
"version": "21-oracle",
|
||||||
|
"jdkDistro": "oracle"
|
||||||
},
|
},
|
||||||
|
"ghcr.io/devcontainers/features/azure-cli:1": {},
|
||||||
|
"ghcr.io/devcontainers/features/docker-in-docker:2": {},
|
||||||
|
"ghcr.io/devcontainers/features/github-cli:1": {}
|
||||||
|
},
|
||||||
|
|
||||||
|
"customizations": {
|
||||||
|
"vscode": {
|
||||||
|
"settings": {},
|
||||||
"extensions": [
|
"extensions": [
|
||||||
"vscjava.vscode-java-pack",
|
|
||||||
"redhat.vscode-xml",
|
"redhat.vscode-xml",
|
||||||
"vmware.vscode-boot-dev-pack",
|
"visualstudioexptteam.vscodeintellicode",
|
||||||
"mhutchie.git-graph"
|
"vscjava.vscode-java-pack"
|
||||||
],
|
]
|
||||||
"forwardPorts": [8080],
|
|
||||||
"settings": {
|
|
||||||
"java.import.gradle.enabled": false,
|
|
||||||
"java.server.launchMode": "Standard"
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"remoteUser": "vscode"
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,3 +19,6 @@ indent_style = space
|
||||||
|
|
||||||
[*.{html,sql,less}]
|
[*.{html,sql,less}]
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.gradle]
|
||||||
|
indent_size = 2
|
||||||
|
|
3
.gitattributes
vendored
3
.gitattributes
vendored
|
@ -1,2 +1,5 @@
|
||||||
mvnw text eol=lf
|
mvnw text eol=lf
|
||||||
*.java text eol=lf
|
*.java text eol=lf
|
||||||
|
|
||||||
|
/gradlew text eol=lf
|
||||||
|
*.bat text eol=crlf
|
||||||
|
|
8
.github/workflows/maven-build.yml
vendored
8
.github/workflows/maven-build.yml
vendored
|
@ -1,5 +1,5 @@
|
||||||
# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
|
# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
|
||||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
|
# For more information see: https://docs.github.com/en/actions/use-cases-and-examples/building-and-testing/building-and-testing-java-with-maven
|
||||||
|
|
||||||
name: Java CI with Maven
|
name: Java CI with Maven
|
||||||
|
|
||||||
|
@ -18,12 +18,12 @@ jobs:
|
||||||
java: [ '17' ]
|
java: [ '17' ]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Set up JDK ${{matrix.java}}
|
- name: Set up JDK ${{matrix.java}}
|
||||||
uses: actions/setup-java@v2
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
java-version: ${{matrix.java}}
|
java-version: ${{matrix.java}}
|
||||||
distribution: 'adopt'
|
distribution: 'adopt'
|
||||||
cache: maven
|
cache: maven
|
||||||
- name: Build with Maven Wrapper
|
- name: Build with Maven Wrapper
|
||||||
run: ./mvnw -B package
|
run: ./mvnw -B verify
|
||||||
|
|
56
.gitignore
vendored
56
.gitignore
vendored
|
@ -1,17 +1,51 @@
|
||||||
target/*
|
HELP.md
|
||||||
bin/*
|
pom.xml.bak
|
||||||
build/*
|
target/
|
||||||
.gradle/*
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
.settings/*
|
!**/src/main/**/target/
|
||||||
.classpath
|
!**/src/test/**/target/
|
||||||
.project
|
.gradle
|
||||||
.factorypath
|
build/
|
||||||
|
!gradle/wrapper/gradle-wrapper.jar
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### STS ###
|
||||||
.attach_pid*
|
.attach_pid*
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
bin/
|
||||||
|
!**/src/main/**/bin/
|
||||||
|
!**/src/test/**/bin/
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
.idea
|
.idea
|
||||||
|
*.iws
|
||||||
*.iml
|
*.iml
|
||||||
/target
|
*.ipr
|
||||||
.sts4-cache/
|
out/
|
||||||
.vscode
|
!**/src/main/**/out/
|
||||||
|
!**/src/test/**/out/
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
### SDK Man ###
|
||||||
|
.sdkmanrc
|
||||||
|
|
||||||
|
### CSS ###
|
||||||
_site/
|
_site/
|
||||||
*.css
|
*.css
|
||||||
!petclinic.css
|
!petclinic.css
|
||||||
|
|
117
.mvn/wrapper/MavenWrapperDownloader.java
vendored
117
.mvn/wrapper/MavenWrapperDownloader.java
vendored
|
@ -1,117 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2007-present the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0'
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
import java.net.*;
|
|
||||||
import java.io.*;
|
|
||||||
import java.nio.channels.*;
|
|
||||||
import java.util.Properties;
|
|
||||||
|
|
||||||
public class MavenWrapperDownloader {
|
|
||||||
|
|
||||||
private static final String WRAPPER_VERSION = "0.5.6";
|
|
||||||
/**
|
|
||||||
* Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
|
|
||||||
*/
|
|
||||||
private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
|
|
||||||
+ WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
|
|
||||||
* use instead of the default one.
|
|
||||||
*/
|
|
||||||
private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
|
|
||||||
".mvn/wrapper/maven-wrapper.properties";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Path where the maven-wrapper.jar will be saved to.
|
|
||||||
*/
|
|
||||||
private static final String MAVEN_WRAPPER_JAR_PATH =
|
|
||||||
".mvn/wrapper/maven-wrapper.jar";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Name of the property which should be used to override the default download url for the wrapper.
|
|
||||||
*/
|
|
||||||
private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
|
|
||||||
|
|
||||||
public static void main(String args[]) {
|
|
||||||
System.out.println("- Downloader started");
|
|
||||||
File baseDirectory = new File(args[0]);
|
|
||||||
System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
|
|
||||||
|
|
||||||
// If the maven-wrapper.properties exists, read it and check if it contains a custom
|
|
||||||
// wrapperUrl parameter.
|
|
||||||
File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
|
|
||||||
String url = DEFAULT_DOWNLOAD_URL;
|
|
||||||
if(mavenWrapperPropertyFile.exists()) {
|
|
||||||
FileInputStream mavenWrapperPropertyFileInputStream = null;
|
|
||||||
try {
|
|
||||||
mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
|
|
||||||
Properties mavenWrapperProperties = new Properties();
|
|
||||||
mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
|
|
||||||
url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
|
|
||||||
} catch (IOException e) {
|
|
||||||
System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
|
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
if(mavenWrapperPropertyFileInputStream != null) {
|
|
||||||
mavenWrapperPropertyFileInputStream.close();
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
// Ignore ...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
System.out.println("- Downloading from: " + url);
|
|
||||||
|
|
||||||
File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
|
|
||||||
if(!outputFile.getParentFile().exists()) {
|
|
||||||
if(!outputFile.getParentFile().mkdirs()) {
|
|
||||||
System.out.println(
|
|
||||||
"- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
|
|
||||||
try {
|
|
||||||
downloadFileFromURL(url, outputFile);
|
|
||||||
System.out.println("Done");
|
|
||||||
System.exit(0);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
System.out.println("- Error downloading");
|
|
||||||
e.printStackTrace();
|
|
||||||
System.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void downloadFileFromURL(String urlString, File destination) throws Exception {
|
|
||||||
if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
|
|
||||||
String username = System.getenv("MVNW_USERNAME");
|
|
||||||
char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
|
|
||||||
Authenticator.setDefault(new Authenticator() {
|
|
||||||
@Override
|
|
||||||
protected PasswordAuthentication getPasswordAuthentication() {
|
|
||||||
return new PasswordAuthentication(username, password);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
URL website = new URL(urlString);
|
|
||||||
ReadableByteChannel rbc;
|
|
||||||
rbc = Channels.newChannel(website.openStream());
|
|
||||||
FileOutputStream fos = new FileOutputStream(destination);
|
|
||||||
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
|
|
||||||
fos.close();
|
|
||||||
rbc.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
BIN
.mvn/wrapper/maven-wrapper.jar
vendored
BIN
.mvn/wrapper/maven-wrapper.jar
vendored
Binary file not shown.
21
.mvn/wrapper/maven-wrapper.properties
vendored
21
.mvn/wrapper/maven-wrapper.properties
vendored
|
@ -1,2 +1,19 @@
|
||||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.5/apache-maven-3.9.5-bin.zip
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing,
|
||||||
|
# software distributed under the License is distributed on an
|
||||||
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
# KIND, either express or implied. See the License for the
|
||||||
|
# specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
wrapperVersion=3.3.2
|
||||||
|
distributionType=only-script
|
||||||
|
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Spring PetClinic Sample Application [](https://github.com/spring-projects/spring-petclinic/actions/workflows/maven-build.yml)
|
# Spring PetClinic Sample Application [](https://github.com/spring-projects/spring-petclinic/actions/workflows/maven-build.yml)[](https://github.com/spring-projects/spring-petclinic/actions/workflows/gradle-build.yml)
|
||||||
|
|
||||||
[](https://gitpod.io/#https://github.com/spring-projects/spring-petclinic) [](https://github.com/codespaces/new?hide_repo_select=true&ref=main&repo=7517918)
|
[](https://gitpod.io/#https://github.com/spring-projects/spring-petclinic) [](https://github.com/codespaces/new?hide_repo_select=true&ref=main&repo=7517918)
|
||||||
|
|
||||||
|
@ -47,33 +47,33 @@ In its default configuration, Petclinic uses an in-memory database (H2) which
|
||||||
gets populated at startup with data. The h2 console is exposed at `http://localhost:8080/h2-console`,
|
gets populated at startup with data. The h2 console is exposed at `http://localhost:8080/h2-console`,
|
||||||
and it is possible to inspect the content of the database using the `jdbc:h2:mem:<uuid>` URL. The UUID is printed at startup to the console.
|
and it is possible to inspect the content of the database using the `jdbc:h2:mem:<uuid>` URL. The UUID is printed at startup to the console.
|
||||||
|
|
||||||
A similar setup is provided for MySQL and PostgreSQL if a persistent database configuration is needed. Note that whenever the database type changes, the app needs to run with a different profile: `spring.profiles.active=mysql` for MySQL or `spring.profiles.active=postgres` for PostgreSQL.
|
A similar setup is provided for MySQL and PostgreSQL if a persistent database configuration is needed. Note that whenever the database type changes, the app needs to run with a different profile: `spring.profiles.active=mysql` for MySQL or `spring.profiles.active=postgres` for PostgreSQL. See the [Spring Boot documentation](https://docs.spring.io/spring-boot/how-to/properties-and-configuration.html#howto.properties-and-configuration.set-active-spring-profiles) for more detail on how to set the active profile.
|
||||||
|
|
||||||
You can start MySQL or PostgreSQL locally with whatever installer works for your OS or use docker:
|
You can start MySQL or PostgreSQL locally with whatever installer works for your OS or use docker:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -e MYSQL_USER=petclinic -e MYSQL_PASSWORD=petclinic -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=petclinic -p 3306:3306 mysql:8.2
|
docker run -e MYSQL_USER=petclinic -e MYSQL_PASSWORD=petclinic -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=petclinic -p 3306:3306 mysql:9.1
|
||||||
```
|
```
|
||||||
|
|
||||||
or
|
or
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -e POSTGRES_USER=petclinic -e POSTGRES_PASSWORD=petclinic -e POSTGRES_DB=petclinic -p 5432:5432 postgres:16.1
|
docker run -e POSTGRES_USER=petclinic -e POSTGRES_PASSWORD=petclinic -e POSTGRES_DB=petclinic -p 5432:5432 postgres:17.0
|
||||||
```
|
```
|
||||||
|
|
||||||
Further documentation is provided for [MySQL](https://github.com/spring-projects/spring-petclinic/blob/main/src/main/resources/db/mysql/petclinic_db_setup_mysql.txt)
|
Further documentation is provided for [MySQL](https://github.com/spring-projects/spring-petclinic/blob/main/src/main/resources/db/mysql/petclinic_db_setup_mysql.txt)
|
||||||
and [PostgreSQL](https://github.com/spring-projects/spring-petclinic/blob/main/src/main/resources/db/postgres/petclinic_db_setup_postgres.txt).
|
and [PostgreSQL](https://github.com/spring-projects/spring-petclinic/blob/main/src/main/resources/db/postgres/petclinic_db_setup_postgres.txt).
|
||||||
|
|
||||||
Instead of vanilla `docker` you can also use the provided `docker-compose.yml` file to start the database containers. Each one has a profile just like the Spring profile:
|
Instead of vanilla `docker` you can also use the provided `docker-compose.yml` file to start the database containers. Each one has a service named after the Spring profile:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker-compose --profile mysql up
|
docker compose up mysql
|
||||||
```
|
```
|
||||||
|
|
||||||
or
|
or
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker-compose --profile postgres up
|
docker compose up postgres
|
||||||
```
|
```
|
||||||
|
|
||||||
## Test Applications
|
## Test Applications
|
57
build.gradle
57
build.gradle
|
@ -1,32 +1,50 @@
|
||||||
plugins {
|
plugins {
|
||||||
id 'java'
|
id 'java'
|
||||||
id 'org.springframework.boot' version '3.2.0'
|
id 'org.springframework.boot' version '3.4.0'
|
||||||
id 'io.spring.dependency-management' version '1.1.4'
|
id 'io.spring.dependency-management' version '1.1.6'
|
||||||
id 'org.graalvm.buildtools.native' version '0.9.28'
|
id 'org.graalvm.buildtools.native' version '0.10.3'
|
||||||
|
id 'org.cyclonedx.bom' version '1.10.0'
|
||||||
|
id 'io.spring.javaformat' version '0.0.43'
|
||||||
|
id "io.spring.nohttp" version "0.0.11"
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'java'
|
apply plugin: 'java'
|
||||||
|
apply plugin: 'checkstyle'
|
||||||
|
apply plugin: 'io.spring.javaformat'
|
||||||
|
|
||||||
|
gradle.startParameter.excludedTaskNames += [ "checkFormatAot", "checkFormatAotTest" ]
|
||||||
|
|
||||||
group = 'org.springframework.samples'
|
group = 'org.springframework.samples'
|
||||||
version = '3.2.0'
|
version = '3.4.0'
|
||||||
sourceCompatibility = '17'
|
|
||||||
|
java {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ext.checkstyleVersion = "10.20.1"
|
||||||
|
ext.springJavaformatCheckstyleVersion = "0.0.43"
|
||||||
|
ext.webjarsLocatorLiteVersion = "1.0.1"
|
||||||
ext.webjarsFontawesomeVersion = "4.7.0"
|
ext.webjarsFontawesomeVersion = "4.7.0"
|
||||||
ext.webjarsBootstrapVersion = "5.3.2"
|
ext.webjarsBootstrapVersion = "5.3.3"
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
// Workaround for AOT issue (https://github.com/spring-projects/spring-framework/pull/33949) -->
|
||||||
|
implementation 'io.projectreactor:reactor-core'
|
||||||
|
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-cache'
|
implementation 'org.springframework.boot:spring-boot-starter-cache'
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
|
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-web'
|
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-validation'
|
implementation 'org.springframework.boot:spring-boot-starter-validation'
|
||||||
|
implementation 'io.micrometer:micrometer-registry-prometheus'
|
||||||
implementation 'javax.cache:cache-api'
|
implementation 'javax.cache:cache-api'
|
||||||
implementation 'jakarta.xml.bind:jakarta.xml.bind-api'
|
implementation 'jakarta.xml.bind:jakarta.xml.bind-api'
|
||||||
runtimeOnly 'org.springframework.boot:spring-boot-starter-actuator'
|
runtimeOnly 'org.springframework.boot:spring-boot-starter-actuator'
|
||||||
|
runtimeOnly "org.webjars:webjars-locator-lite:${webjarsLocatorLiteVersion}"
|
||||||
runtimeOnly "org.webjars.npm:bootstrap:${webjarsBootstrapVersion}"
|
runtimeOnly "org.webjars.npm:bootstrap:${webjarsBootstrapVersion}"
|
||||||
runtimeOnly "org.webjars.npm:font-awesome:${webjarsFontawesomeVersion}"
|
runtimeOnly "org.webjars.npm:font-awesome:${webjarsFontawesomeVersion}"
|
||||||
runtimeOnly 'com.github.ben-manes.caffeine:caffeine'
|
runtimeOnly 'com.github.ben-manes.caffeine:caffeine'
|
||||||
|
@ -39,8 +57,35 @@ dependencies {
|
||||||
testImplementation 'org.springframework.boot:spring-boot-docker-compose'
|
testImplementation 'org.springframework.boot:spring-boot-docker-compose'
|
||||||
testImplementation 'org.testcontainers:junit-jupiter'
|
testImplementation 'org.testcontainers:junit-jupiter'
|
||||||
testImplementation 'org.testcontainers:mysql'
|
testImplementation 'org.testcontainers:mysql'
|
||||||
|
checkstyle "io.spring.javaformat:spring-javaformat-checkstyle:${springJavaformatCheckstyleVersion}"
|
||||||
|
checkstyle "com.puppycrawl.tools:checkstyle:${checkstyleVersion}"
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.named('test') {
|
tasks.named('test') {
|
||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkstyle {
|
||||||
|
configDirectory = project.file('src/checkstyle')
|
||||||
|
configFile = file('src/checkstyle/nohttp-checkstyle.xml')
|
||||||
|
}
|
||||||
|
|
||||||
|
checkstyleNohttp {
|
||||||
|
configDirectory = project.file('src/checkstyle')
|
||||||
|
configFile = file('src/checkstyle/nohttp-checkstyle.xml')
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.named("formatMain").configure { dependsOn("checkstyleMain") }
|
||||||
|
tasks.named("formatMain").configure { dependsOn("checkstyleNohttp") }
|
||||||
|
|
||||||
|
tasks.named("formatTest").configure { dependsOn("checkstyleTest") }
|
||||||
|
tasks.named("formatTest").configure { dependsOn("checkstyleNohttp") }
|
||||||
|
|
||||||
|
checkstyleAot.enabled = false
|
||||||
|
checkstyleAotTest.enabled = false
|
||||||
|
|
||||||
|
checkFormatAot.enabled = false
|
||||||
|
checkFormatAotTest.enabled = false
|
||||||
|
|
||||||
|
formatAot.enabled = false
|
||||||
|
formatAotTest.enabled = false
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
version: "2.2"
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
mysql:
|
mysql:
|
||||||
image: mysql:8.2
|
image: mysql:9.1
|
||||||
ports:
|
ports:
|
||||||
- "3306:3306"
|
- "3306:3306"
|
||||||
environment:
|
environment:
|
||||||
|
@ -13,15 +11,11 @@ services:
|
||||||
- MYSQL_DATABASE=petclinic
|
- MYSQL_DATABASE=petclinic
|
||||||
volumes:
|
volumes:
|
||||||
- "./conf.d:/etc/mysql/conf.d:ro"
|
- "./conf.d:/etc/mysql/conf.d:ro"
|
||||||
profiles:
|
|
||||||
- mysql
|
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:16.1
|
image: postgres:17.0
|
||||||
ports:
|
ports:
|
||||||
- "5432:5432"
|
- "5432:5432"
|
||||||
environment:
|
environment:
|
||||||
- POSTGRES_PASSWORD=petclinic
|
- POSTGRES_PASSWORD=petclinic
|
||||||
- POSTGRES_USER=petclinic
|
- POSTGRES_USER=petclinic
|
||||||
- POSTGRES_DB=petclinic
|
- POSTGRES_DB=petclinic
|
||||||
profiles:
|
|
||||||
- postgres
|
|
||||||
|
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,6 +1,6 @@
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|
7
gradlew
vendored
7
gradlew
vendored
|
@ -15,6 +15,8 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
#
|
#
|
||||||
|
@ -55,7 +57,7 @@
|
||||||
# Darwin, MinGW, and NonStop.
|
# Darwin, MinGW, and NonStop.
|
||||||
#
|
#
|
||||||
# (3) This script is generated from the Groovy template
|
# (3) This script is generated from the Groovy template
|
||||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
# within the Gradle project.
|
# within the Gradle project.
|
||||||
#
|
#
|
||||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
@ -84,7 +86,8 @@ done
|
||||||
# shellcheck disable=SC2034
|
# shellcheck disable=SC2034
|
||||||
APP_BASE_NAME=${0##*/}
|
APP_BASE_NAME=${0##*/}
|
||||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||||
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
||||||
|
' "$PWD" ) || exit
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD=maximum
|
MAX_FD=maximum
|
||||||
|
|
22
gradlew.bat
vendored
22
gradlew.bat
vendored
|
@ -13,6 +13,8 @@
|
||||||
@rem See the License for the specific language governing permissions and
|
@rem See the License for the specific language governing permissions and
|
||||||
@rem limitations under the License.
|
@rem limitations under the License.
|
||||||
@rem
|
@rem
|
||||||
|
@rem SPDX-License-Identifier: Apache-2.0
|
||||||
|
@rem
|
||||||
|
|
||||||
@if "%DEBUG%"=="" @echo off
|
@if "%DEBUG%"=="" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
|
@ -43,11 +45,11 @@ set JAVA_EXE=java.exe
|
||||||
%JAVA_EXE% -version >NUL 2>&1
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
if %ERRORLEVEL% equ 0 goto execute
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation.
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
|
@ -57,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
if exist "%JAVA_EXE%" goto execute
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation.
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
|
|
73
k8s/db.yml
Normal file
73
k8s/db.yml
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: demo-db
|
||||||
|
type: servicebinding.io/postgresql
|
||||||
|
stringData:
|
||||||
|
type: "postgresql"
|
||||||
|
provider: "postgresql"
|
||||||
|
host: "demo-db"
|
||||||
|
port: "5432"
|
||||||
|
database: "petclinic"
|
||||||
|
username: "user"
|
||||||
|
password: "pass"
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: demo-db
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 5432
|
||||||
|
selector:
|
||||||
|
app: demo-db
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: demo-db
|
||||||
|
labels:
|
||||||
|
app: demo-db
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: demo-db
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: demo-db
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: postgres:17
|
||||||
|
name: postgresql
|
||||||
|
env:
|
||||||
|
- name: POSTGRES_USER
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: demo-db
|
||||||
|
key: username
|
||||||
|
- name: POSTGRES_PASSWORD
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: demo-db
|
||||||
|
key: password
|
||||||
|
- name: POSTGRES_DB
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: demo-db
|
||||||
|
key: database
|
||||||
|
ports:
|
||||||
|
- containerPort: 5432
|
||||||
|
name: postgresql
|
||||||
|
livenessProbe:
|
||||||
|
tcpSocket:
|
||||||
|
port: postgresql
|
||||||
|
readinessProbe:
|
||||||
|
tcpSocket:
|
||||||
|
port: postgresql
|
||||||
|
startupProbe:
|
||||||
|
tcpSocket:
|
||||||
|
port: postgresql
|
64
k8s/petclinic.yml
Normal file
64
k8s/petclinic.yml
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: petclinic
|
||||||
|
spec:
|
||||||
|
type: NodePort
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
targetPort: 8080
|
||||||
|
selector:
|
||||||
|
app: petclinic
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: petclinic
|
||||||
|
labels:
|
||||||
|
app: petclinic
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: petclinic
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: petclinic
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: workload
|
||||||
|
image: dsyer/petclinic
|
||||||
|
env:
|
||||||
|
- name: SPRING_PROFILES_ACTIVE
|
||||||
|
value: postgres
|
||||||
|
- name: SERVICE_BINDING_ROOT
|
||||||
|
value: /bindings
|
||||||
|
- name: SPRING_APPLICATION_JSON
|
||||||
|
value: |
|
||||||
|
{
|
||||||
|
"management.endpoint.health.probes.add-additional-paths": true
|
||||||
|
}
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
containerPort: 8080
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /livez
|
||||||
|
port: http
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /readyz
|
||||||
|
port: http
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /bindings/secret
|
||||||
|
name: binding
|
||||||
|
readOnly: true
|
||||||
|
volumes:
|
||||||
|
- name: binding
|
||||||
|
projected:
|
||||||
|
sources:
|
||||||
|
- secret:
|
||||||
|
name: demo-db
|
445
mvnw
vendored
445
mvnw
vendored
|
@ -19,290 +19,241 @@
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
# Apache Maven Wrapper startup batch script, version 3.2.0
|
# Apache Maven Wrapper startup batch script, version 3.3.2
|
||||||
#
|
|
||||||
# Required ENV vars:
|
|
||||||
# ------------------
|
|
||||||
# JAVA_HOME - location of a JDK home dir
|
|
||||||
#
|
#
|
||||||
# Optional ENV vars
|
# Optional ENV vars
|
||||||
# -----------------
|
# -----------------
|
||||||
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
# JAVA_HOME - location of a JDK home dir, required when download maven via java source
|
||||||
# e.g. to debug Maven itself, use
|
# MVNW_REPOURL - repo url base for downloading maven distribution
|
||||||
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
|
||||||
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
if [ -z "$MAVEN_SKIP_RC" ] ; then
|
set -euf
|
||||||
|
[ "${MVNW_VERBOSE-}" != debug ] || set -x
|
||||||
|
|
||||||
if [ -f /usr/local/etc/mavenrc ] ; then
|
# OS specific support.
|
||||||
. /usr/local/etc/mavenrc
|
native_path() { printf %s\\n "$1"; }
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -f /etc/mavenrc ] ; then
|
|
||||||
. /etc/mavenrc
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -f "$HOME/.mavenrc" ] ; then
|
|
||||||
. "$HOME/.mavenrc"
|
|
||||||
fi
|
|
||||||
|
|
||||||
fi
|
|
||||||
|
|
||||||
# OS specific support. $var _must_ be set to either true or false.
|
|
||||||
cygwin=false;
|
|
||||||
darwin=false;
|
|
||||||
mingw=false
|
|
||||||
case "$(uname)" in
|
case "$(uname)" in
|
||||||
CYGWIN*) cygwin=true ;;
|
CYGWIN* | MINGW*)
|
||||||
MINGW*) mingw=true;;
|
[ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
|
||||||
Darwin*) darwin=true
|
native_path() { cygpath --path --windows "$1"; }
|
||||||
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
|
|
||||||
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
|
|
||||||
if [ -z "$JAVA_HOME" ]; then
|
|
||||||
if [ -x "/usr/libexec/java_home" ]; then
|
|
||||||
JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME
|
|
||||||
else
|
|
||||||
JAVA_HOME="/Library/Java/Home"; export JAVA_HOME
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
if [ -z "$JAVA_HOME" ] ; then
|
# set JAVACMD and JAVACCMD
|
||||||
if [ -r /etc/gentoo-release ] ; then
|
set_java_home() {
|
||||||
JAVA_HOME=$(java-config --jre-home)
|
# For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
|
||||||
fi
|
if [ -n "${JAVA_HOME-}" ]; then
|
||||||
fi
|
if [ -x "$JAVA_HOME/jre/sh/java" ]; then
|
||||||
|
|
||||||
# For Cygwin, ensure paths are in UNIX format before anything is touched
|
|
||||||
if $cygwin ; then
|
|
||||||
[ -n "$JAVA_HOME" ] &&
|
|
||||||
JAVA_HOME=$(cygpath --unix "$JAVA_HOME")
|
|
||||||
[ -n "$CLASSPATH" ] &&
|
|
||||||
CLASSPATH=$(cygpath --path --unix "$CLASSPATH")
|
|
||||||
fi
|
|
||||||
|
|
||||||
# For Mingw, ensure paths are in UNIX format before anything is touched
|
|
||||||
if $mingw ; then
|
|
||||||
[ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] &&
|
|
||||||
JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$JAVA_HOME" ]; then
|
|
||||||
javaExecutable="$(which javac)"
|
|
||||||
if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then
|
|
||||||
# readlink(1) is not available as standard on Solaris 10.
|
|
||||||
readLink=$(which readlink)
|
|
||||||
if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then
|
|
||||||
if $darwin ; then
|
|
||||||
javaHome="$(dirname "\"$javaExecutable\"")"
|
|
||||||
javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac"
|
|
||||||
else
|
|
||||||
javaExecutable="$(readlink -f "\"$javaExecutable\"")"
|
|
||||||
fi
|
|
||||||
javaHome="$(dirname "\"$javaExecutable\"")"
|
|
||||||
javaHome=$(expr "$javaHome" : '\(.*\)/bin')
|
|
||||||
JAVA_HOME="$javaHome"
|
|
||||||
export JAVA_HOME
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$JAVACMD" ] ; then
|
|
||||||
if [ -n "$JAVA_HOME" ] ; then
|
|
||||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
|
||||||
# IBM's JDK on AIX uses strange locations for the executables
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
JAVACCMD="$JAVA_HOME/jre/sh/javac"
|
||||||
else
|
else
|
||||||
JAVACMD="$JAVA_HOME/bin/java"
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
fi
|
JAVACCMD="$JAVA_HOME/bin/javac"
|
||||||
else
|
|
||||||
JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -x "$JAVACMD" ] ; then
|
if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
|
||||||
echo "Error: JAVA_HOME is not defined correctly." >&2
|
echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
|
||||||
echo " We cannot execute $JAVACMD" >&2
|
echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$JAVA_HOME" ] ; then
|
|
||||||
echo "Warning: JAVA_HOME environment variable is not set."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# traverses directory structure from process work directory to filesystem root
|
|
||||||
# first directory with .mvn subdirectory is considered project base directory
|
|
||||||
find_maven_basedir() {
|
|
||||||
if [ -z "$1" ]
|
|
||||||
then
|
|
||||||
echo "Path not specified to find_maven_basedir"
|
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="$(
|
||||||
|
'set' +e
|
||||||
|
'unset' -f command 2>/dev/null
|
||||||
|
'command' -v java
|
||||||
|
)" || :
|
||||||
|
JAVACCMD="$(
|
||||||
|
'set' +e
|
||||||
|
'unset' -f command 2>/dev/null
|
||||||
|
'command' -v javac
|
||||||
|
)" || :
|
||||||
|
|
||||||
basedir="$1"
|
if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
|
||||||
wdir="$1"
|
echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
|
||||||
while [ "$wdir" != '/' ] ; do
|
return 1
|
||||||
if [ -d "$wdir"/.mvn ] ; then
|
|
||||||
basedir=$wdir
|
|
||||||
break
|
|
||||||
fi
|
fi
|
||||||
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
|
|
||||||
if [ -d "${wdir}" ]; then
|
|
||||||
wdir=$(cd "$wdir/.." || exit 1; pwd)
|
|
||||||
fi
|
fi
|
||||||
# end of workaround
|
}
|
||||||
|
|
||||||
|
# hash string like Java String::hashCode
|
||||||
|
hash_string() {
|
||||||
|
str="${1:-}" h=0
|
||||||
|
while [ -n "$str" ]; do
|
||||||
|
char="${str%"${str#?}"}"
|
||||||
|
h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
|
||||||
|
str="${str#?}"
|
||||||
done
|
done
|
||||||
printf '%s' "$(cd "$basedir" || exit 1; pwd)"
|
printf %x\\n $h
|
||||||
}
|
}
|
||||||
|
|
||||||
# concatenates all lines of a file
|
verbose() { :; }
|
||||||
concat_lines() {
|
[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
|
||||||
if [ -f "$1" ]; then
|
|
||||||
# Remove \r in case we run on Windows within Git Bash
|
die() {
|
||||||
# and check out the repository with auto CRLF management
|
printf %s\\n "$1" >&2
|
||||||
# enabled. Otherwise, we may read lines that are delimited with
|
exit 1
|
||||||
# \r\n and produce $'-Xarg\r' rather than -Xarg due to word
|
|
||||||
# splitting rules.
|
|
||||||
tr -s '\r\n' ' ' < "$1"
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log() {
|
trim() {
|
||||||
if [ "$MVNW_VERBOSE" = true ]; then
|
# MWRAPPER-139:
|
||||||
printf '%s\n' "$1"
|
# Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
|
||||||
fi
|
# Needed for removing poorly interpreted newline sequences when running in more
|
||||||
|
# exotic environments such as mingw bash on Windows.
|
||||||
|
printf "%s" "${1}" | tr -d '[:space:]'
|
||||||
}
|
}
|
||||||
|
|
||||||
BASE_DIR=$(find_maven_basedir "$(dirname "$0")")
|
# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
|
||||||
if [ -z "$BASE_DIR" ]; then
|
|
||||||
exit 1;
|
|
||||||
fi
|
|
||||||
|
|
||||||
MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR
|
|
||||||
log "$MAVEN_PROJECTBASEDIR"
|
|
||||||
|
|
||||||
##########################################################################################
|
|
||||||
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
|
||||||
# This allows using the maven wrapper in projects that prohibit checking in binary data.
|
|
||||||
##########################################################################################
|
|
||||||
wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar"
|
|
||||||
if [ -r "$wrapperJarPath" ]; then
|
|
||||||
log "Found $wrapperJarPath"
|
|
||||||
else
|
|
||||||
log "Couldn't find $wrapperJarPath, downloading it ..."
|
|
||||||
|
|
||||||
if [ -n "$MVNW_REPOURL" ]; then
|
|
||||||
wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
|
|
||||||
else
|
|
||||||
wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
|
|
||||||
fi
|
|
||||||
while IFS="=" read -r key value; do
|
|
||||||
# Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' )
|
|
||||||
safeValue=$(echo "$value" | tr -d '\r')
|
|
||||||
case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;;
|
|
||||||
esac
|
|
||||||
done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
|
|
||||||
log "Downloading from: $wrapperUrl"
|
|
||||||
|
|
||||||
if $cygwin; then
|
|
||||||
wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath")
|
|
||||||
fi
|
|
||||||
|
|
||||||
if command -v wget > /dev/null; then
|
|
||||||
log "Found wget ... using wget"
|
|
||||||
[ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet"
|
|
||||||
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
|
||||||
wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
|
|
||||||
else
|
|
||||||
wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
|
|
||||||
fi
|
|
||||||
elif command -v curl > /dev/null; then
|
|
||||||
log "Found curl ... using curl"
|
|
||||||
[ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent"
|
|
||||||
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
|
||||||
curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
|
|
||||||
else
|
|
||||||
curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
log "Falling back to using Java to download"
|
|
||||||
javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java"
|
|
||||||
javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class"
|
|
||||||
# For Cygwin, switch paths to Windows format before running javac
|
|
||||||
if $cygwin; then
|
|
||||||
javaSource=$(cygpath --path --windows "$javaSource")
|
|
||||||
javaClass=$(cygpath --path --windows "$javaClass")
|
|
||||||
fi
|
|
||||||
if [ -e "$javaSource" ]; then
|
|
||||||
if [ ! -e "$javaClass" ]; then
|
|
||||||
log " - Compiling MavenWrapperDownloader.java ..."
|
|
||||||
("$JAVA_HOME/bin/javac" "$javaSource")
|
|
||||||
fi
|
|
||||||
if [ -e "$javaClass" ]; then
|
|
||||||
log " - Running MavenWrapperDownloader.java ..."
|
|
||||||
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
##########################################################################################
|
|
||||||
# End of extension
|
|
||||||
##########################################################################################
|
|
||||||
|
|
||||||
# If specified, validate the SHA-256 sum of the Maven wrapper jar file
|
|
||||||
wrapperSha256Sum=""
|
|
||||||
while IFS="=" read -r key value; do
|
while IFS="=" read -r key value; do
|
||||||
case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;;
|
case "${key-}" in
|
||||||
|
distributionUrl) distributionUrl=$(trim "${value-}") ;;
|
||||||
|
distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
|
||||||
esac
|
esac
|
||||||
done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
|
done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties"
|
||||||
if [ -n "$wrapperSha256Sum" ]; then
|
[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties"
|
||||||
wrapperSha256Result=false
|
|
||||||
if command -v sha256sum > /dev/null; then
|
case "${distributionUrl##*/}" in
|
||||||
if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then
|
maven-mvnd-*bin.*)
|
||||||
wrapperSha256Result=true
|
MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
|
||||||
|
case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
|
||||||
|
*AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
|
||||||
|
:Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
|
||||||
|
:Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
|
||||||
|
:Linux*x86_64*) distributionPlatform=linux-amd64 ;;
|
||||||
|
*)
|
||||||
|
echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
|
||||||
|
distributionPlatform=linux-amd64
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
|
||||||
|
;;
|
||||||
|
maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
|
||||||
|
*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# apply MVNW_REPOURL and calculate MAVEN_HOME
|
||||||
|
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
|
||||||
|
[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
|
||||||
|
distributionUrlName="${distributionUrl##*/}"
|
||||||
|
distributionUrlNameMain="${distributionUrlName%.*}"
|
||||||
|
distributionUrlNameMain="${distributionUrlNameMain%-bin}"
|
||||||
|
MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
|
||||||
|
MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
|
||||||
|
|
||||||
|
exec_maven() {
|
||||||
|
unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
|
||||||
|
exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -d "$MAVEN_HOME" ]; then
|
||||||
|
verbose "found existing MAVEN_HOME at $MAVEN_HOME"
|
||||||
|
exec_maven "$@"
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "${distributionUrl-}" in
|
||||||
|
*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
|
||||||
|
*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# prepare tmp dir
|
||||||
|
if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
|
||||||
|
clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
|
||||||
|
trap clean HUP INT TERM EXIT
|
||||||
|
else
|
||||||
|
die "cannot create temp dir"
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p -- "${MAVEN_HOME%/*}"
|
||||||
|
|
||||||
|
# Download and Install Apache Maven
|
||||||
|
verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
|
||||||
|
verbose "Downloading from: $distributionUrl"
|
||||||
|
verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
|
||||||
|
|
||||||
|
# select .zip or .tar.gz
|
||||||
|
if ! command -v unzip >/dev/null; then
|
||||||
|
distributionUrl="${distributionUrl%.zip}.tar.gz"
|
||||||
|
distributionUrlName="${distributionUrl##*/}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# verbose opt
|
||||||
|
__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
|
||||||
|
[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
|
||||||
|
|
||||||
|
# normalize http auth
|
||||||
|
case "${MVNW_PASSWORD:+has-password}" in
|
||||||
|
'') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
|
||||||
|
has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
|
||||||
|
verbose "Found wget ... using wget"
|
||||||
|
wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
|
||||||
|
elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
|
||||||
|
verbose "Found curl ... using curl"
|
||||||
|
curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
|
||||||
|
elif set_java_home; then
|
||||||
|
verbose "Falling back to use Java to download"
|
||||||
|
javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
|
||||||
|
targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
|
||||||
|
cat >"$javaSource" <<-END
|
||||||
|
public class Downloader extends java.net.Authenticator
|
||||||
|
{
|
||||||
|
protected java.net.PasswordAuthentication getPasswordAuthentication()
|
||||||
|
{
|
||||||
|
return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
|
||||||
|
}
|
||||||
|
public static void main( String[] args ) throws Exception
|
||||||
|
{
|
||||||
|
setDefault( new Downloader() );
|
||||||
|
java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
END
|
||||||
|
# For Cygwin/MinGW, switch paths to Windows format before running javac and java
|
||||||
|
verbose " - Compiling Downloader.java ..."
|
||||||
|
"$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
|
||||||
|
verbose " - Running Downloader.java ..."
|
||||||
|
"$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If specified, validate the SHA-256 sum of the Maven distribution zip file
|
||||||
|
if [ -n "${distributionSha256Sum-}" ]; then
|
||||||
|
distributionSha256Result=false
|
||||||
|
if [ "$MVN_CMD" = mvnd.sh ]; then
|
||||||
|
echo "Checksum validation is not supported for maven-mvnd." >&2
|
||||||
|
echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
|
||||||
|
exit 1
|
||||||
|
elif command -v sha256sum >/dev/null; then
|
||||||
|
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then
|
||||||
|
distributionSha256Result=true
|
||||||
fi
|
fi
|
||||||
elif command -v shasum > /dev/null; then
|
elif command -v shasum >/dev/null; then
|
||||||
if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then
|
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
|
||||||
wrapperSha256Result=true
|
distributionSha256Result=true
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available."
|
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
|
||||||
echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties."
|
echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
if [ $wrapperSha256Result = false ]; then
|
if [ $distributionSha256Result = false ]; then
|
||||||
echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2
|
echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
|
||||||
echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2
|
echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
|
||||||
echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
|
# unzip and move
|
||||||
|
if command -v unzip >/dev/null; then
|
||||||
# For Cygwin, switch paths to Windows format before running java
|
unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
|
||||||
if $cygwin; then
|
else
|
||||||
[ -n "$JAVA_HOME" ] &&
|
tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
|
||||||
JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME")
|
|
||||||
[ -n "$CLASSPATH" ] &&
|
|
||||||
CLASSPATH=$(cygpath --path --windows "$CLASSPATH")
|
|
||||||
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
|
|
||||||
MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR")
|
|
||||||
fi
|
fi
|
||||||
|
printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url"
|
||||||
|
mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
|
||||||
|
|
||||||
# Provide a "standardized" way to retrieve the CLI args that will
|
clean || :
|
||||||
# work with both Windows and non-Windows executions.
|
exec_maven "$@"
|
||||||
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*"
|
|
||||||
export MAVEN_CMD_LINE_ARGS
|
|
||||||
|
|
||||||
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
|
||||||
|
|
||||||
# shellcheck disable=SC2086 # safe args
|
|
||||||
exec "$JAVACMD" \
|
|
||||||
$MAVEN_OPTS \
|
|
||||||
$MAVEN_DEBUG_OPTS \
|
|
||||||
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
|
|
||||||
"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
|
|
||||||
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
|
|
||||||
|
|
282
mvnw.cmd
vendored
282
mvnw.cmd
vendored
|
@ -1,3 +1,4 @@
|
||||||
|
<# : batch portion
|
||||||
@REM ----------------------------------------------------------------------------
|
@REM ----------------------------------------------------------------------------
|
||||||
@REM Licensed to the Apache Software Foundation (ASF) under one
|
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||||
@REM or more contributor license agreements. See the NOTICE file
|
@REM or more contributor license agreements. See the NOTICE file
|
||||||
|
@ -18,188 +19,131 @@
|
||||||
@REM ----------------------------------------------------------------------------
|
@REM ----------------------------------------------------------------------------
|
||||||
|
|
||||||
@REM ----------------------------------------------------------------------------
|
@REM ----------------------------------------------------------------------------
|
||||||
@REM Apache Maven Wrapper startup batch script, version 3.2.0
|
@REM Apache Maven Wrapper startup batch script, version 3.3.2
|
||||||
@REM
|
|
||||||
@REM Required ENV vars:
|
|
||||||
@REM JAVA_HOME - location of a JDK home dir
|
|
||||||
@REM
|
@REM
|
||||||
@REM Optional ENV vars
|
@REM Optional ENV vars
|
||||||
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
|
@REM MVNW_REPOURL - repo url base for downloading maven distribution
|
||||||
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
|
@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
|
||||||
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
|
||||||
@REM e.g. to debug Maven itself, use
|
|
||||||
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
|
||||||
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
|
||||||
@REM ----------------------------------------------------------------------------
|
@REM ----------------------------------------------------------------------------
|
||||||
|
|
||||||
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
|
@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
|
||||||
@echo off
|
@SET __MVNW_CMD__=
|
||||||
@REM set title of command window
|
@SET __MVNW_ERROR__=
|
||||||
title %0
|
@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
|
||||||
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
|
@SET PSModulePath=
|
||||||
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
|
@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
|
||||||
|
IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
|
||||||
@REM set %HOME% to equivalent of $HOME
|
|
||||||
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
|
|
||||||
|
|
||||||
@REM Execute a user defined script before this one
|
|
||||||
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
|
|
||||||
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
|
|
||||||
if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
|
|
||||||
if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
|
|
||||||
:skipRcPre
|
|
||||||
|
|
||||||
@setlocal
|
|
||||||
|
|
||||||
set ERROR_CODE=0
|
|
||||||
|
|
||||||
@REM To isolate internal variables from possible post scripts, we use another setlocal
|
|
||||||
@setlocal
|
|
||||||
|
|
||||||
@REM ==== START VALIDATION ====
|
|
||||||
if not "%JAVA_HOME%" == "" goto OkJHome
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo Error: JAVA_HOME not found in your environment. >&2
|
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
|
||||||
echo location of your Java installation. >&2
|
|
||||||
echo.
|
|
||||||
goto error
|
|
||||||
|
|
||||||
:OkJHome
|
|
||||||
if exist "%JAVA_HOME%\bin\java.exe" goto init
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo Error: JAVA_HOME is set to an invalid directory. >&2
|
|
||||||
echo JAVA_HOME = "%JAVA_HOME%" >&2
|
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
|
||||||
echo location of your Java installation. >&2
|
|
||||||
echo.
|
|
||||||
goto error
|
|
||||||
|
|
||||||
@REM ==== END VALIDATION ====
|
|
||||||
|
|
||||||
:init
|
|
||||||
|
|
||||||
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
|
|
||||||
@REM Fallback to current working directory if not found.
|
|
||||||
|
|
||||||
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
|
|
||||||
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
|
|
||||||
|
|
||||||
set EXEC_DIR=%CD%
|
|
||||||
set WDIR=%EXEC_DIR%
|
|
||||||
:findBaseDir
|
|
||||||
IF EXIST "%WDIR%"\.mvn goto baseDirFound
|
|
||||||
cd ..
|
|
||||||
IF "%WDIR%"=="%CD%" goto baseDirNotFound
|
|
||||||
set WDIR=%CD%
|
|
||||||
goto findBaseDir
|
|
||||||
|
|
||||||
:baseDirFound
|
|
||||||
set MAVEN_PROJECTBASEDIR=%WDIR%
|
|
||||||
cd "%EXEC_DIR%"
|
|
||||||
goto endDetectBaseDir
|
|
||||||
|
|
||||||
:baseDirNotFound
|
|
||||||
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
|
|
||||||
cd "%EXEC_DIR%"
|
|
||||||
|
|
||||||
:endDetectBaseDir
|
|
||||||
|
|
||||||
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
|
|
||||||
|
|
||||||
@setlocal EnableExtensions EnableDelayedExpansion
|
|
||||||
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
|
|
||||||
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
|
|
||||||
|
|
||||||
:endReadAdditionalConfig
|
|
||||||
|
|
||||||
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
|
|
||||||
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
|
|
||||||
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
|
||||||
|
|
||||||
set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
|
|
||||||
|
|
||||||
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
|
|
||||||
IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B
|
|
||||||
)
|
)
|
||||||
|
@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
|
||||||
|
@SET __MVNW_PSMODULEP_SAVE=
|
||||||
|
@SET __MVNW_ARG0_NAME__=
|
||||||
|
@SET MVNW_USERNAME=
|
||||||
|
@SET MVNW_PASSWORD=
|
||||||
|
@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*)
|
||||||
|
@echo Cannot start maven from wrapper >&2 && exit /b 1
|
||||||
|
@GOTO :EOF
|
||||||
|
: end batch / begin powershell #>
|
||||||
|
|
||||||
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
$ErrorActionPreference = "Stop"
|
||||||
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
|
if ($env:MVNW_VERBOSE -eq "true") {
|
||||||
if exist %WRAPPER_JAR% (
|
$VerbosePreference = "Continue"
|
||||||
if "%MVNW_VERBOSE%" == "true" (
|
}
|
||||||
echo Found %WRAPPER_JAR%
|
|
||||||
)
|
|
||||||
) else (
|
|
||||||
if not "%MVNW_REPOURL%" == "" (
|
|
||||||
SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
|
|
||||||
)
|
|
||||||
if "%MVNW_VERBOSE%" == "true" (
|
|
||||||
echo Couldn't find %WRAPPER_JAR%, downloading it ...
|
|
||||||
echo Downloading from: %WRAPPER_URL%
|
|
||||||
)
|
|
||||||
|
|
||||||
powershell -Command "&{"^
|
# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
|
||||||
"$webclient = new-object System.Net.WebClient;"^
|
$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
|
||||||
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
|
if (!$distributionUrl) {
|
||||||
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
|
Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
|
||||||
"}"^
|
}
|
||||||
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^
|
|
||||||
"}"
|
|
||||||
if "%MVNW_VERBOSE%" == "true" (
|
|
||||||
echo Finished downloading %WRAPPER_JAR%
|
|
||||||
)
|
|
||||||
)
|
|
||||||
@REM End of extension
|
|
||||||
|
|
||||||
@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file
|
switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
|
||||||
SET WRAPPER_SHA_256_SUM=""
|
"maven-mvnd-*" {
|
||||||
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
|
$USE_MVND = $true
|
||||||
IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B
|
$distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
|
||||||
)
|
$MVN_CMD = "mvnd.cmd"
|
||||||
IF NOT %WRAPPER_SHA_256_SUM%=="" (
|
break
|
||||||
powershell -Command "&{"^
|
}
|
||||||
"$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^
|
default {
|
||||||
"If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^
|
$USE_MVND = $false
|
||||||
" Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^
|
$MVN_CMD = $script -replace '^mvnw','mvn'
|
||||||
" Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^
|
break
|
||||||
" Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^
|
}
|
||||||
" exit 1;"^
|
}
|
||||||
"}"^
|
|
||||||
"}"
|
|
||||||
if ERRORLEVEL 1 goto error
|
|
||||||
)
|
|
||||||
|
|
||||||
@REM Provide a "standardized" way to retrieve the CLI args that will
|
# apply MVNW_REPOURL and calculate MAVEN_HOME
|
||||||
@REM work with both Windows and non-Windows executions.
|
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
|
||||||
set MAVEN_CMD_LINE_ARGS=%*
|
if ($env:MVNW_REPOURL) {
|
||||||
|
$MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" }
|
||||||
|
$distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')"
|
||||||
|
}
|
||||||
|
$distributionUrlName = $distributionUrl -replace '^.*/',''
|
||||||
|
$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
|
||||||
|
$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain"
|
||||||
|
if ($env:MAVEN_USER_HOME) {
|
||||||
|
$MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain"
|
||||||
|
}
|
||||||
|
$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
|
||||||
|
$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
|
||||||
|
|
||||||
%MAVEN_JAVA_EXE% ^
|
if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
|
||||||
%JVM_CONFIG_MAVEN_PROPS% ^
|
Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
|
||||||
%MAVEN_OPTS% ^
|
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
|
||||||
%MAVEN_DEBUG_OPTS% ^
|
exit $?
|
||||||
-classpath %WRAPPER_JAR% ^
|
}
|
||||||
"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
|
|
||||||
%WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
|
|
||||||
if ERRORLEVEL 1 goto error
|
|
||||||
goto end
|
|
||||||
|
|
||||||
:error
|
if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
|
||||||
set ERROR_CODE=1
|
Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
|
||||||
|
}
|
||||||
|
|
||||||
:end
|
# prepare tmp dir
|
||||||
@endlocal & set ERROR_CODE=%ERROR_CODE%
|
$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
|
||||||
|
$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
|
||||||
|
$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
|
||||||
|
trap {
|
||||||
|
if ($TMP_DOWNLOAD_DIR.Exists) {
|
||||||
|
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
|
||||||
|
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
|
New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
|
||||||
@REM check for post script, once with legacy .bat ending and once with .cmd ending
|
|
||||||
if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
|
|
||||||
if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
|
|
||||||
:skipRcPost
|
|
||||||
|
|
||||||
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
|
# Download and Install Apache Maven
|
||||||
if "%MAVEN_BATCH_PAUSE%"=="on" pause
|
Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
|
||||||
|
Write-Verbose "Downloading from: $distributionUrl"
|
||||||
|
Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
|
||||||
|
|
||||||
if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
|
$webclient = New-Object System.Net.WebClient
|
||||||
|
if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
|
||||||
|
$webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
|
||||||
|
}
|
||||||
|
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||||
|
$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
|
||||||
|
|
||||||
cmd /C exit /B %ERROR_CODE%
|
# If specified, validate the SHA-256 sum of the Maven distribution zip file
|
||||||
|
$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
|
||||||
|
if ($distributionSha256Sum) {
|
||||||
|
if ($USE_MVND) {
|
||||||
|
Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
|
||||||
|
}
|
||||||
|
Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
|
||||||
|
if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
|
||||||
|
Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# unzip and move
|
||||||
|
Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
|
||||||
|
Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null
|
||||||
|
try {
|
||||||
|
Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
|
||||||
|
} catch {
|
||||||
|
if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
|
||||||
|
Write-Error "fail to move MAVEN_HOME"
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
|
||||||
|
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
|
||||||
|
|
131
pom.xml
131
pom.xml
|
@ -1,17 +1,18 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>org.springframework.samples</groupId>
|
|
||||||
<artifactId>spring-petclinic</artifactId>
|
|
||||||
<version>3.2.0-SNAPSHOT</version>
|
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-parent</artifactId>
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
<version>3.2.1</version>
|
<version>3.4.0</version>
|
||||||
|
<relativePath></relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
<groupId>org.springframework.samples</groupId>
|
||||||
|
<artifactId>spring-petclinic</artifactId>
|
||||||
|
<version>3.4.0-SNAPSHOT</version>
|
||||||
|
|
||||||
<name>petclinic</name>
|
<name>petclinic</name>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
|
@ -20,20 +21,22 @@
|
||||||
<java.version>17</java.version>
|
<java.version>17</java.version>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
<!-- Important for reproducible builds. Update using e.g. ./mvnw versions:set -DnewVersion=... -->
|
<!-- Important for reproducible builds. Update using e.g. ./mvnw versions:set
|
||||||
<project.build.outputTimestamp>2023-05-10T07:42:50Z</project.build.outputTimestamp>
|
-DnewVersion=... -->
|
||||||
|
<project.build.outputTimestamp>2024-11-28T14:37:52Z</project.build.outputTimestamp>
|
||||||
|
|
||||||
<!-- Web dependencies -->
|
<!-- Web dependencies -->
|
||||||
<webjars-bootstrap.version>5.3.2</webjars-bootstrap.version>
|
<webjars-locator.version>1.0.1</webjars-locator.version>
|
||||||
|
<webjars-bootstrap.version>5.3.3</webjars-bootstrap.version>
|
||||||
<webjars-font-awesome.version>4.7.0</webjars-font-awesome.version>
|
<webjars-font-awesome.version>4.7.0</webjars-font-awesome.version>
|
||||||
|
|
||||||
<checkstyle.version>10.12.5</checkstyle.version>
|
<checkstyle.version>10.20.1</checkstyle.version>
|
||||||
<jacoco.version>0.8.11</jacoco.version>
|
<jacoco.version>0.8.12</jacoco.version>
|
||||||
<libsass.version>0.2.29</libsass.version>
|
<libsass.version>0.2.29</libsass.version>
|
||||||
<lifecycle-mapping>1.0.0</lifecycle-mapping>
|
<lifecycle-mapping>1.0.0</lifecycle-mapping>
|
||||||
<maven-checkstyle.version>3.3.1</maven-checkstyle.version>
|
<maven-checkstyle.version>3.6.0</maven-checkstyle.version>
|
||||||
<nohttp-checkstyle.version>0.0.11</nohttp-checkstyle.version>
|
<nohttp-checkstyle.version>0.0.11</nohttp-checkstyle.version>
|
||||||
<spring-format.version>0.0.40</spring-format.version>
|
<spring-format.version>0.0.43</spring-format.version>
|
||||||
|
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
|
@ -68,6 +71,11 @@
|
||||||
<artifactId>spring-boot-starter-test</artifactId>
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<!-- Workaround for AOT issue (https://github.com/spring-projects/spring-framework/pull/33949) -->
|
||||||
|
<groupId>io.projectreactor</groupId>
|
||||||
|
<artifactId>reactor-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- Databases - Uses H2 by default -->
|
<!-- Databases - Uses H2 by default -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -97,6 +105,11 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- Webjars -->
|
<!-- Webjars -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.webjars</groupId>
|
||||||
|
<artifactId>webjars-locator-lite</artifactId>
|
||||||
|
<version>${webjars-locator.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.webjars.npm</groupId>
|
<groupId>org.webjars.npm</groupId>
|
||||||
<artifactId>bootstrap</artifactId>
|
<artifactId>bootstrap</artifactId>
|
||||||
|
@ -139,6 +152,11 @@
|
||||||
<artifactId>jakarta.xml.bind-api</artifactId>
|
<artifactId>jakarta.xml.bind-api</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.micrometer</groupId>
|
||||||
|
<artifactId>micrometer-registry-prometheus</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
@ -155,7 +173,9 @@
|
||||||
<configuration>
|
<configuration>
|
||||||
<rules>
|
<rules>
|
||||||
<requireJavaVersion>
|
<requireJavaVersion>
|
||||||
<message>This build requires at least Java ${java.version}, update your JVM, and run the build again</message>
|
<message>This build requires at least Java ${java.version},
|
||||||
|
update your JVM, and
|
||||||
|
run the build again</message>
|
||||||
<version>${java.version}</version>
|
<version>${java.version}</version>
|
||||||
</requireJavaVersion>
|
</requireJavaVersion>
|
||||||
</rules>
|
</rules>
|
||||||
|
@ -169,10 +189,10 @@
|
||||||
<version>${spring-format.version}</version>
|
<version>${spring-format.version}</version>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<phase>validate</phase>
|
|
||||||
<goals>
|
<goals>
|
||||||
<goal>validate</goal>
|
<goal>validate</goal>
|
||||||
</goals>
|
</goals>
|
||||||
|
<phase>validate</phase>
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
@ -195,17 +215,17 @@
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>nohttp-checkstyle-validation</id>
|
<id>nohttp-checkstyle-validation</id>
|
||||||
<phase>validate</phase>
|
|
||||||
<configuration>
|
|
||||||
<configLocation>src/checkstyle/nohttp-checkstyle.xml</configLocation>
|
|
||||||
<suppressionsLocation>src/checkstyle/nohttp-checkstyle-suppressions.xml</suppressionsLocation>
|
|
||||||
<sourceDirectories>${basedir}</sourceDirectories>
|
|
||||||
<includes>**/*</includes>
|
|
||||||
<excludes>**/.git/**/*,**/.idea/**/*,**/target/**/,**/.flattened-pom.xml,**/*.class</excludes>
|
|
||||||
</configuration>
|
|
||||||
<goals>
|
<goals>
|
||||||
<goal>check</goal>
|
<goal>check</goal>
|
||||||
</goals>
|
</goals>
|
||||||
|
<phase>validate</phase>
|
||||||
|
<configuration>
|
||||||
|
<configLocation>src/checkstyle/nohttp-checkstyle.xml</configLocation>
|
||||||
|
<sourceDirectories>${basedir}</sourceDirectories>
|
||||||
|
<includes>**/*</includes>
|
||||||
|
<excludes>**/.git/**/*,**/.idea/**/*,**/target/**/,**/.flattened-pom.xml,**/*.class</excludes>
|
||||||
|
<propertyExpansion>config_loc=${basedir}/src/checkstyle/</propertyExpansion>
|
||||||
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
@ -246,16 +266,16 @@
|
||||||
</execution>
|
</execution>
|
||||||
<execution>
|
<execution>
|
||||||
<id>report</id>
|
<id>report</id>
|
||||||
<phase>prepare-package</phase>
|
|
||||||
<goals>
|
<goals>
|
||||||
<goal>report</goal>
|
<goal>report</goal>
|
||||||
</goals>
|
</goals>
|
||||||
|
<phase>prepare-package</phase>
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
||||||
<!-- Spring Boot Actuator displays build-related information if a git.properties
|
<!-- Spring Boot Actuator displays build-related information if a git.properties file is
|
||||||
file is present at the classpath -->
|
present at the classpath -->
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>io.github.git-commit-id</groupId>
|
<groupId>io.github.git-commit-id</groupId>
|
||||||
<artifactId>git-commit-id-maven-plugin</artifactId>
|
<artifactId>git-commit-id-maven-plugin</artifactId>
|
||||||
|
@ -264,10 +284,16 @@
|
||||||
<failOnUnableToExtractRepoInfo>false</failOnUnableToExtractRepoInfo>
|
<failOnUnableToExtractRepoInfo>false</failOnUnableToExtractRepoInfo>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<!-- Spring Boot Actuator displays sbom-related information if a CycloneDX SBOM file is
|
||||||
|
present at the classpath -->
|
||||||
|
<plugin>
|
||||||
|
<?m2e ignore?>
|
||||||
|
<groupId>org.cyclonedx</groupId>
|
||||||
|
<artifactId>cyclonedx-maven-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
<licenses>
|
<licenses>
|
||||||
<license>
|
<license>
|
||||||
<name>Apache License, Version 2.0</name>
|
<name>Apache License, Version 2.0</name>
|
||||||
|
@ -277,39 +303,38 @@
|
||||||
|
|
||||||
<repositories>
|
<repositories>
|
||||||
<repository>
|
<repository>
|
||||||
<id>spring-snapshots</id>
|
|
||||||
<name>Spring Snapshots</name>
|
|
||||||
<url>https://repo.spring.io/snapshot</url>
|
|
||||||
<snapshots>
|
<snapshots>
|
||||||
<enabled>true</enabled>
|
<enabled>true</enabled>
|
||||||
</snapshots>
|
</snapshots>
|
||||||
|
<id>spring-snapshots</id>
|
||||||
|
<name>Spring Snapshots</name>
|
||||||
|
<url>https://repo.spring.io/snapshot</url>
|
||||||
</repository>
|
</repository>
|
||||||
<repository>
|
<repository>
|
||||||
<id>spring-milestones</id>
|
|
||||||
<name>Spring Milestones</name>
|
|
||||||
<url>https://repo.spring.io/milestone</url>
|
|
||||||
<snapshots>
|
<snapshots>
|
||||||
<enabled>false</enabled>
|
<enabled>false</enabled>
|
||||||
</snapshots>
|
</snapshots>
|
||||||
|
<id>spring-milestones</id>
|
||||||
|
<name>Spring Milestones</name>
|
||||||
|
<url>https://repo.spring.io/milestone</url>
|
||||||
</repository>
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
<pluginRepositories>
|
<pluginRepositories>
|
||||||
<pluginRepository>
|
<pluginRepository>
|
||||||
<id>spring-snapshots</id>
|
|
||||||
<name>Spring Snapshots</name>
|
|
||||||
<url>https://repo.spring.io/snapshot</url>
|
|
||||||
<snapshots>
|
<snapshots>
|
||||||
<enabled>true</enabled>
|
<enabled>true</enabled>
|
||||||
</snapshots>
|
</snapshots>
|
||||||
|
<id>spring-snapshots</id>
|
||||||
|
<name>Spring Snapshots</name>
|
||||||
|
<url>https://repo.spring.io/snapshot</url>
|
||||||
</pluginRepository>
|
</pluginRepository>
|
||||||
<pluginRepository>
|
<pluginRepository>
|
||||||
<id>spring-milestones</id>
|
|
||||||
<name>Spring Milestones</name>
|
|
||||||
<url>https://repo.spring.io/milestone</url>
|
|
||||||
<snapshots>
|
<snapshots>
|
||||||
<enabled>false</enabled>
|
<enabled>false</enabled>
|
||||||
</snapshots>
|
</snapshots>
|
||||||
|
<id>spring-milestones</id>
|
||||||
|
<name>Spring Milestones</name>
|
||||||
|
<url>https://repo.spring.io/milestone</url>
|
||||||
</pluginRepository>
|
</pluginRepository>
|
||||||
</pluginRepositories>
|
</pluginRepositories>
|
||||||
|
|
||||||
|
@ -324,11 +349,11 @@
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>unpack</id>
|
<id>unpack</id>
|
||||||
<?m2e execute onConfiguration,onIncremental?>
|
|
||||||
<phase>generate-resources</phase>
|
|
||||||
<goals>
|
<goals>
|
||||||
<goal>unpack</goal>
|
<goal>unpack</goal>
|
||||||
</goals>
|
</goals>
|
||||||
|
<?m2e execute onConfiguration,onIncremental?>
|
||||||
|
<phase>generate-resources</phase>
|
||||||
<configuration>
|
<configuration>
|
||||||
<artifactItems>
|
<artifactItems>
|
||||||
<artifactItem>
|
<artifactItem>
|
||||||
|
@ -347,21 +372,20 @@
|
||||||
<groupId>com.gitlab.haynes</groupId>
|
<groupId>com.gitlab.haynes</groupId>
|
||||||
<artifactId>libsass-maven-plugin</artifactId>
|
<artifactId>libsass-maven-plugin</artifactId>
|
||||||
<version>${libsass.version}</version>
|
<version>${libsass.version}</version>
|
||||||
|
<configuration>
|
||||||
|
<inputPath>${basedir}/src/main/scss/</inputPath>
|
||||||
|
<outputPath>${basedir}/src/main/resources/static/resources/css/</outputPath>
|
||||||
|
<includePath>${project.build.directory}/webjars/META-INF/resources/webjars/bootstrap/${webjars-bootstrap.version}/scss/</includePath>
|
||||||
|
</configuration>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<phase>generate-resources</phase>
|
|
||||||
<?m2e execute onConfiguration,onIncremental?>
|
<?m2e execute onConfiguration,onIncremental?>
|
||||||
<goals>
|
<goals>
|
||||||
<goal>compile</goal>
|
<goal>compile</goal>
|
||||||
</goals>
|
</goals>
|
||||||
|
<phase>generate-resources</phase>
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
<configuration>
|
|
||||||
<inputPath>${basedir}/src/main/scss/</inputPath>
|
|
||||||
<outputPath>${basedir}/src/main/resources/static/resources/css/</outputPath>
|
|
||||||
<includePath>
|
|
||||||
${project.build.directory}/webjars/META-INF/resources/webjars/bootstrap/${webjars-bootstrap.version}/scss/</includePath>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
@ -395,7 +419,7 @@
|
||||||
</goals>
|
</goals>
|
||||||
</pluginExecutionFilter>
|
</pluginExecutionFilter>
|
||||||
<action>
|
<action>
|
||||||
<ignore />
|
<ignore></ignore>
|
||||||
</action>
|
</action>
|
||||||
</pluginExecution>
|
</pluginExecution>
|
||||||
<pluginExecution>
|
<pluginExecution>
|
||||||
|
@ -408,7 +432,7 @@
|
||||||
</goals>
|
</goals>
|
||||||
</pluginExecutionFilter>
|
</pluginExecutionFilter>
|
||||||
<action>
|
<action>
|
||||||
<ignore />
|
<ignore></ignore>
|
||||||
</action>
|
</action>
|
||||||
</pluginExecution>
|
</pluginExecution>
|
||||||
<pluginExecution>
|
<pluginExecution>
|
||||||
|
@ -421,7 +445,7 @@
|
||||||
</goals>
|
</goals>
|
||||||
</pluginExecutionFilter>
|
</pluginExecutionFilter>
|
||||||
<action>
|
<action>
|
||||||
<ignore />
|
<ignore></ignore>
|
||||||
</action>
|
</action>
|
||||||
</pluginExecution>
|
</pluginExecution>
|
||||||
</pluginExecutions>
|
</pluginExecutions>
|
||||||
|
@ -433,5 +457,4 @@
|
||||||
</build>
|
</build>
|
||||||
</profile>
|
</profile>
|
||||||
</profiles>
|
</profiles>
|
||||||
|
|
||||||
</project>
|
</project>
|
|
@ -3,8 +3,9 @@
|
||||||
"-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN"
|
"-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN"
|
||||||
"https://checkstyle.org/dtds/suppressions_1_2.dtd">
|
"https://checkstyle.org/dtds/suppressions_1_2.dtd">
|
||||||
<suppressions>
|
<suppressions>
|
||||||
<suppress files="node_modules/.*" checks=".*"/>
|
<suppress files="node_modules[\\/].*" checks=".*"/>
|
||||||
<suppress files="node/.*" checks=".*"/>
|
<suppress files="node[\\/].*" checks=".*"/>
|
||||||
<suppress files="build/.*" checks=".*"/>
|
<suppress files="build[\\/].*" checks=".*"/>
|
||||||
|
<suppress files="target[\\/].*" checks=".*"/>
|
||||||
<suppress files=".+\.(jar|git|ico|p12|gif|jks|jpg|svg|log)" checks="NoHttp"/>
|
<suppress files=".+\.(jar|git|ico|p12|gif|jks|jpg|svg|log)" checks="NoHttp"/>
|
||||||
</suppressions>
|
</suppressions>
|
||||||
|
|
|
@ -4,4 +4,7 @@
|
||||||
"https://checkstyle.org/dtds/configuration_1_2.dtd">
|
"https://checkstyle.org/dtds/configuration_1_2.dtd">
|
||||||
<module name="com.puppycrawl.tools.checkstyle.Checker">
|
<module name="com.puppycrawl.tools.checkstyle.Checker">
|
||||||
<module name="io.spring.nohttp.checkstyle.check.NoHttpCheck"/>
|
<module name="io.spring.nohttp.checkstyle.check.NoHttpCheck"/>
|
||||||
|
<module name="SuppressionFilter">
|
||||||
|
<property name="file" value="${config_loc}/nohttp-checkstyle-suppressions.xml"/>
|
||||||
|
</module>
|
||||||
</module>
|
</module>
|
||||||
|
|
|
@ -28,7 +28,6 @@ public class PetClinicRuntimeHints implements RuntimeHintsRegistrar {
|
||||||
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
|
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
|
||||||
hints.resources().registerPattern("db/*"); // https://github.com/spring-projects/spring-boot/issues/32654
|
hints.resources().registerPattern("db/*"); // https://github.com/spring-projects/spring-boot/issues/32654
|
||||||
hints.resources().registerPattern("messages/*");
|
hints.resources().registerPattern("messages/*");
|
||||||
hints.resources().registerPattern("META-INF/resources/webjars/*");
|
|
||||||
hints.resources().registerPattern("mysql-default-conf");
|
hints.resources().registerPattern("mysql-default-conf");
|
||||||
hints.serialization().registerType(BaseEntity.class);
|
hints.serialization().registerType(BaseEntity.class);
|
||||||
hints.serialization().registerType(Person.class);
|
hints.serialization().registerType(Person.class);
|
||||||
|
|
|
@ -17,6 +17,7 @@ package org.springframework.samples.petclinic.model;
|
||||||
|
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
import jakarta.persistence.MappedSuperclass;
|
import jakarta.persistence.MappedSuperclass;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple JavaBean domain object adds a name property to <code>BaseEntity</code>. Used as
|
* Simple JavaBean domain object adds a name property to <code>BaseEntity</code>. Used as
|
||||||
|
@ -24,11 +25,13 @@ import jakarta.persistence.MappedSuperclass;
|
||||||
*
|
*
|
||||||
* @author Ken Krebs
|
* @author Ken Krebs
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
|
* @author Wick Dynex
|
||||||
*/
|
*/
|
||||||
@MappedSuperclass
|
@MappedSuperclass
|
||||||
public class NamedEntity extends BaseEntity {
|
public class NamedEntity extends BaseEntity {
|
||||||
|
|
||||||
@Column(name = "name")
|
@Column(name = "name")
|
||||||
|
@NotBlank
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
|
|
|
@ -30,7 +30,7 @@ import jakarta.persistence.JoinColumn;
|
||||||
import jakarta.persistence.OneToMany;
|
import jakarta.persistence.OneToMany;
|
||||||
import jakarta.persistence.OrderBy;
|
import jakarta.persistence.OrderBy;
|
||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
import jakarta.validation.constraints.Digits;
|
import jakarta.validation.constraints.Pattern;
|
||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -41,6 +41,7 @@ import jakarta.validation.constraints.NotBlank;
|
||||||
* @author Sam Brannen
|
* @author Sam Brannen
|
||||||
* @author Michael Isvy
|
* @author Michael Isvy
|
||||||
* @author Oliver Drotbohm
|
* @author Oliver Drotbohm
|
||||||
|
* @author Wick Dynex
|
||||||
*/
|
*/
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "owners")
|
@Table(name = "owners")
|
||||||
|
@ -56,13 +57,13 @@ public class Owner extends Person {
|
||||||
|
|
||||||
@Column(name = "telephone")
|
@Column(name = "telephone")
|
||||||
@NotBlank
|
@NotBlank
|
||||||
@Digits(fraction = 0, integer = 10)
|
@Pattern(regexp = "\\d{10}", message = "Telephone must be a 10-digit number")
|
||||||
private String telephone;
|
private String telephone;
|
||||||
|
|
||||||
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
||||||
@JoinColumn(name = "owner_id")
|
@JoinColumn(name = "owner_id")
|
||||||
@OrderBy("name")
|
@OrderBy("name")
|
||||||
private List<Pet> pets = new ArrayList<>();
|
private final List<Pet> pets = new ArrayList<>();
|
||||||
|
|
||||||
public String getAddress() {
|
public String getAddress() {
|
||||||
return this.address;
|
return this.address;
|
||||||
|
@ -101,7 +102,7 @@ public class Owner extends Person {
|
||||||
/**
|
/**
|
||||||
* Return the Pet with the given name, or null if none found for this Owner.
|
* Return the Pet with the given name, or null if none found for this Owner.
|
||||||
* @param name to test
|
* @param name to test
|
||||||
* @return a pet if pet name is already in use
|
* @return the Pet with the given name, or null if no such Pet exists for this Owner
|
||||||
*/
|
*/
|
||||||
public Pet getPet(String name) {
|
public Pet getPet(String name) {
|
||||||
return getPet(name, false);
|
return getPet(name, false);
|
||||||
|
@ -110,7 +111,7 @@ public class Owner extends Person {
|
||||||
/**
|
/**
|
||||||
* Return the Pet with the given id, or null if none found for this Owner.
|
* Return the Pet with the given id, or null if none found for this Owner.
|
||||||
* @param id to test
|
* @param id to test
|
||||||
* @return a pet if pet id is already in use
|
* @return the Pet with the given id, or null if no such Pet exists for this Owner
|
||||||
*/
|
*/
|
||||||
public Pet getPet(Integer id) {
|
public Pet getPet(Integer id) {
|
||||||
for (Pet pet : getPets()) {
|
for (Pet pet : getPets()) {
|
||||||
|
@ -127,10 +128,10 @@ public class Owner extends Person {
|
||||||
/**
|
/**
|
||||||
* Return the Pet with the given name, or null if none found for this Owner.
|
* Return the Pet with the given name, or null if none found for this Owner.
|
||||||
* @param name to test
|
* @param name to test
|
||||||
* @return a pet if pet name is already in use
|
* @param ignoreNew whether to ignore new pets (pets that are not saved yet)
|
||||||
|
* @return the Pet with the given name, or null if no such Pet exists for this Owner
|
||||||
*/
|
*/
|
||||||
public Pet getPet(String name, boolean ignoreNew) {
|
public Pet getPet(String name, boolean ignoreNew) {
|
||||||
name = name.toLowerCase();
|
|
||||||
for (Pet pet : getPets()) {
|
for (Pet pet : getPets()) {
|
||||||
String compName = pet.getName();
|
String compName = pet.getName();
|
||||||
if (compName != null && compName.equalsIgnoreCase(name)) {
|
if (compName != null && compName.equalsIgnoreCase(name)) {
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
package org.springframework.samples.petclinic.owner;
|
package org.springframework.samples.petclinic.owner;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.PageRequest;
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
@ -34,12 +34,14 @@ import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.servlet.ModelAndView;
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
|
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
|
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
* @author Ken Krebs
|
* @author Ken Krebs
|
||||||
* @author Arjen Poutsma
|
* @author Arjen Poutsma
|
||||||
* @author Michael Isvy
|
* @author Michael Isvy
|
||||||
|
* @author Wick Dynex
|
||||||
*/
|
*/
|
||||||
@Controller
|
@Controller
|
||||||
class OwnerController {
|
class OwnerController {
|
||||||
|
@ -48,8 +50,8 @@ class OwnerController {
|
||||||
|
|
||||||
private final OwnerRepository owners;
|
private final OwnerRepository owners;
|
||||||
|
|
||||||
public OwnerController(OwnerRepository clinicService) {
|
public OwnerController(OwnerRepository owners) {
|
||||||
this.owners = clinicService;
|
this.owners = owners;
|
||||||
}
|
}
|
||||||
|
|
||||||
@InitBinder
|
@InitBinder
|
||||||
|
@ -59,23 +61,26 @@ class OwnerController {
|
||||||
|
|
||||||
@ModelAttribute("owner")
|
@ModelAttribute("owner")
|
||||||
public Owner findOwner(@PathVariable(name = "ownerId", required = false) Integer ownerId) {
|
public Owner findOwner(@PathVariable(name = "ownerId", required = false) Integer ownerId) {
|
||||||
return ownerId == null ? new Owner() : this.owners.findById(ownerId);
|
return ownerId == null ? new Owner()
|
||||||
|
: this.owners.findById(ownerId)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("Owner not found with id: " + ownerId
|
||||||
|
+ ". Please ensure the ID is correct " + "and the owner exists in the database."));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/owners/new")
|
@GetMapping("/owners/new")
|
||||||
public String initCreationForm(Map<String, Object> model) {
|
public String initCreationForm() {
|
||||||
Owner owner = new Owner();
|
|
||||||
model.put("owner", owner);
|
|
||||||
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
|
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/owners/new")
|
@PostMapping("/owners/new")
|
||||||
public String processCreationForm(@Valid Owner owner, BindingResult result) {
|
public String processCreationForm(@Valid Owner owner, BindingResult result, RedirectAttributes redirectAttributes) {
|
||||||
if (result.hasErrors()) {
|
if (result.hasErrors()) {
|
||||||
|
redirectAttributes.addFlashAttribute("error", "There was an error in creating the owner.");
|
||||||
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
|
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.owners.save(owner);
|
this.owners.save(owner);
|
||||||
|
redirectAttributes.addFlashAttribute("message", "New Owner Created");
|
||||||
return "redirect:/owners/" + owner.getId();
|
return "redirect:/owners/" + owner.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,25 +127,31 @@ class OwnerController {
|
||||||
private Page<Owner> findPaginatedForOwnersLastName(int page, String lastname) {
|
private Page<Owner> findPaginatedForOwnersLastName(int page, String lastname) {
|
||||||
int pageSize = 5;
|
int pageSize = 5;
|
||||||
Pageable pageable = PageRequest.of(page - 1, pageSize);
|
Pageable pageable = PageRequest.of(page - 1, pageSize);
|
||||||
return owners.findByLastName(lastname, pageable);
|
return owners.findByLastNameStartingWith(lastname, pageable);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/owners/{ownerId}/edit")
|
@GetMapping("/owners/{ownerId}/edit")
|
||||||
public String initUpdateOwnerForm(@PathVariable("ownerId") int ownerId, Model model) {
|
public String initUpdateOwnerForm() {
|
||||||
Owner owner = this.owners.findById(ownerId);
|
|
||||||
model.addAttribute(owner);
|
|
||||||
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
|
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/owners/{ownerId}/edit")
|
@PostMapping("/owners/{ownerId}/edit")
|
||||||
public String processUpdateOwnerForm(@Valid Owner owner, BindingResult result,
|
public String processUpdateOwnerForm(@Valid Owner owner, BindingResult result, @PathVariable("ownerId") int ownerId,
|
||||||
@PathVariable("ownerId") int ownerId) {
|
RedirectAttributes redirectAttributes) {
|
||||||
if (result.hasErrors()) {
|
if (result.hasErrors()) {
|
||||||
|
redirectAttributes.addFlashAttribute("error", "There was an error in updating the owner.");
|
||||||
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
|
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (owner.getId() != ownerId) {
|
||||||
|
result.rejectValue("id", "mismatch", "The owner ID in the form does not match the URL.");
|
||||||
|
redirectAttributes.addFlashAttribute("error", "Owner ID mismatch. Please try again.");
|
||||||
|
return "redirect:/owners/{ownerId}/edit";
|
||||||
|
}
|
||||||
|
|
||||||
owner.setId(ownerId);
|
owner.setId(ownerId);
|
||||||
this.owners.save(owner);
|
this.owners.save(owner);
|
||||||
|
redirectAttributes.addFlashAttribute("message", "Owner Values Updated");
|
||||||
return "redirect:/owners/{ownerId}";
|
return "redirect:/owners/{ownerId}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,7 +163,9 @@ class OwnerController {
|
||||||
@GetMapping("/owners/{ownerId}")
|
@GetMapping("/owners/{ownerId}")
|
||||||
public ModelAndView showOwner(@PathVariable("ownerId") int ownerId) {
|
public ModelAndView showOwner(@PathVariable("ownerId") int ownerId) {
|
||||||
ModelAndView mav = new ModelAndView("owners/ownerDetails");
|
ModelAndView mav = new ModelAndView("owners/ownerDetails");
|
||||||
Owner owner = this.owners.findById(ownerId);
|
Optional<Owner> optionalOwner = this.owners.findById(ownerId);
|
||||||
|
Owner owner = optionalOwner.orElseThrow(() -> new IllegalArgumentException(
|
||||||
|
"Owner not found with id: " + ownerId + ". Please ensure the ID is correct "));
|
||||||
mav.addObject(owner);
|
mav.addObject(owner);
|
||||||
return mav;
|
return mav;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,12 +16,13 @@
|
||||||
package org.springframework.samples.petclinic.owner;
|
package org.springframework.samples.petclinic.owner;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import jakarta.annotation.Nonnull;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.data.repository.Repository;
|
|
||||||
import org.springframework.data.repository.query.Param;
|
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,15 +35,15 @@ import org.springframework.transaction.annotation.Transactional;
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
* @author Sam Brannen
|
* @author Sam Brannen
|
||||||
* @author Michael Isvy
|
* @author Michael Isvy
|
||||||
|
* @author Wick Dynex
|
||||||
*/
|
*/
|
||||||
public interface OwnerRepository extends Repository<Owner, Integer> {
|
public interface OwnerRepository extends JpaRepository<Owner, Integer> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve all {@link PetType}s from the data store.
|
* Retrieve all {@link PetType}s from the data store.
|
||||||
* @return a Collection of {@link PetType}s.
|
* @return a Collection of {@link PetType}s.
|
||||||
*/
|
*/
|
||||||
@Query("SELECT ptype FROM PetType ptype ORDER BY ptype.name")
|
@Query("SELECT ptype FROM PetType ptype ORDER BY ptype.name")
|
||||||
@Transactional(readOnly = true)
|
|
||||||
List<PetType> findPetTypes();
|
List<PetType> findPetTypes();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -52,31 +53,26 @@ public interface OwnerRepository extends Repository<Owner, Integer> {
|
||||||
* @return a Collection of matching {@link Owner}s (or an empty Collection if none
|
* @return a Collection of matching {@link Owner}s (or an empty Collection if none
|
||||||
* found)
|
* found)
|
||||||
*/
|
*/
|
||||||
|
Page<Owner> findByLastNameStartingWith(String lastName, Pageable pageable);
|
||||||
@Query("SELECT DISTINCT owner FROM Owner owner left join owner.pets WHERE owner.lastName LIKE :lastName% ")
|
|
||||||
@Transactional(readOnly = true)
|
|
||||||
Page<Owner> findByLastName(@Param("lastName") String lastName, Pageable pageable);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve an {@link Owner} from the data store by id.
|
* Retrieve an {@link Owner} from the data store by id.
|
||||||
|
* <p>
|
||||||
|
* This method returns an {@link Optional} containing the {@link Owner} if found. If
|
||||||
|
* no {@link Owner} is found with the provided id, it will return an empty
|
||||||
|
* {@link Optional}.
|
||||||
|
* </p>
|
||||||
* @param id the id to search for
|
* @param id the id to search for
|
||||||
* @return the {@link Owner} if found
|
* @return an {@link Optional} containing the {@link Owner} if found, or an empty
|
||||||
|
* {@link Optional} if not found.
|
||||||
|
* @throws IllegalArgumentException if the id is null (assuming null is not a valid
|
||||||
|
* input for id)
|
||||||
*/
|
*/
|
||||||
@Query("SELECT owner FROM Owner owner left join fetch owner.pets WHERE owner.id =:id")
|
Optional<Owner> findById(@Nonnull Integer id);
|
||||||
@Transactional(readOnly = true)
|
|
||||||
Owner findById(@Param("id") Integer id);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save an {@link Owner} to the data store, either inserting or updating it.
|
|
||||||
* @param owner the {@link Owner} to save
|
|
||||||
*/
|
|
||||||
void save(Owner owner);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all the owners from data store
|
* Returns all the owners from data store
|
||||||
**/
|
**/
|
||||||
@Query("SELECT owner FROM Owner owner")
|
|
||||||
@Transactional(readOnly = true)
|
|
||||||
Page<Owner> findAll(Pageable pageable);
|
Page<Owner> findAll(Pageable pageable);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
3
src/main/java/org/springframework/samples/petclinic/owner/Pet.java
Executable file → Normal file
3
src/main/java/org/springframework/samples/petclinic/owner/Pet.java
Executable file → Normal file
|
@ -39,6 +39,7 @@ import jakarta.persistence.Table;
|
||||||
* @author Ken Krebs
|
* @author Ken Krebs
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
* @author Sam Brannen
|
* @author Sam Brannen
|
||||||
|
* @author Wick Dynex
|
||||||
*/
|
*/
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "pets")
|
@Table(name = "pets")
|
||||||
|
@ -55,7 +56,7 @@ public class Pet extends NamedEntity {
|
||||||
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
||||||
@JoinColumn(name = "pet_id")
|
@JoinColumn(name = "pet_id")
|
||||||
@OrderBy("visit_date ASC")
|
@OrderBy("visit_date ASC")
|
||||||
private Set<Visit> visits = new LinkedHashSet<>();
|
private final Set<Visit> visits = new LinkedHashSet<>();
|
||||||
|
|
||||||
public void setBirthDate(LocalDate birthDate) {
|
public void setBirthDate(LocalDate birthDate) {
|
||||||
this.birthDate = birthDate;
|
this.birthDate = birthDate;
|
||||||
|
|
|
@ -17,6 +17,7 @@ package org.springframework.samples.petclinic.owner;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.ModelMap;
|
import org.springframework.ui.ModelMap;
|
||||||
|
@ -31,11 +32,13 @@ import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
|
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
* @author Ken Krebs
|
* @author Ken Krebs
|
||||||
* @author Arjen Poutsma
|
* @author Arjen Poutsma
|
||||||
|
* @author Wick Dynex
|
||||||
*/
|
*/
|
||||||
@Controller
|
@Controller
|
||||||
@RequestMapping("/owners/{ownerId}")
|
@RequestMapping("/owners/{ownerId}")
|
||||||
|
@ -56,11 +59,9 @@ class PetController {
|
||||||
|
|
||||||
@ModelAttribute("owner")
|
@ModelAttribute("owner")
|
||||||
public Owner findOwner(@PathVariable("ownerId") int ownerId) {
|
public Owner findOwner(@PathVariable("ownerId") int ownerId) {
|
||||||
|
Optional<Owner> optionalOwner = this.owners.findById(ownerId);
|
||||||
Owner owner = this.owners.findById(ownerId);
|
Owner owner = optionalOwner.orElseThrow(() -> new IllegalArgumentException(
|
||||||
if (owner == null) {
|
"Owner not found with id: " + ownerId + ". Please ensure the ID is correct "));
|
||||||
throw new IllegalArgumentException("Owner ID not found: " + ownerId);
|
|
||||||
}
|
|
||||||
return owner;
|
return owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,10 +73,9 @@ class PetController {
|
||||||
return new Pet();
|
return new Pet();
|
||||||
}
|
}
|
||||||
|
|
||||||
Owner owner = this.owners.findById(ownerId);
|
Optional<Owner> optionalOwner = this.owners.findById(ownerId);
|
||||||
if (owner == null) {
|
Owner owner = optionalOwner.orElseThrow(() -> new IllegalArgumentException(
|
||||||
throw new IllegalArgumentException("Owner ID not found: " + ownerId);
|
"Owner not found with id: " + ownerId + ". Please ensure the ID is correct "));
|
||||||
}
|
|
||||||
return owner.getPet(petId);
|
return owner.getPet(petId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,47 +93,46 @@ class PetController {
|
||||||
public String initCreationForm(Owner owner, ModelMap model) {
|
public String initCreationForm(Owner owner, ModelMap model) {
|
||||||
Pet pet = new Pet();
|
Pet pet = new Pet();
|
||||||
owner.addPet(pet);
|
owner.addPet(pet);
|
||||||
model.put("pet", pet);
|
|
||||||
return VIEWS_PETS_CREATE_OR_UPDATE_FORM;
|
return VIEWS_PETS_CREATE_OR_UPDATE_FORM;
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/pets/new")
|
@PostMapping("/pets/new")
|
||||||
public String processCreationForm(Owner owner, @Valid Pet pet, BindingResult result, ModelMap model) {
|
public String processCreationForm(Owner owner, @Valid Pet pet, BindingResult result,
|
||||||
if (StringUtils.hasText(pet.getName()) && pet.isNew() && owner.getPet(pet.getName(), true) != null) {
|
RedirectAttributes redirectAttributes) {
|
||||||
|
|
||||||
|
if (StringUtils.hasText(pet.getName()) && pet.isNew() && owner.getPet(pet.getName(), true) != null)
|
||||||
result.rejectValue("name", "duplicate", "already exists");
|
result.rejectValue("name", "duplicate", "already exists");
|
||||||
}
|
|
||||||
|
|
||||||
LocalDate currentDate = LocalDate.now();
|
LocalDate currentDate = LocalDate.now();
|
||||||
if (pet.getBirthDate() != null && pet.getBirthDate().isAfter(currentDate)) {
|
if (pet.getBirthDate() != null && pet.getBirthDate().isAfter(currentDate)) {
|
||||||
result.rejectValue("birthDate", "typeMismatch.birthDate");
|
result.rejectValue("birthDate", "typeMismatch.birthDate");
|
||||||
}
|
}
|
||||||
|
|
||||||
owner.addPet(pet);
|
|
||||||
if (result.hasErrors()) {
|
if (result.hasErrors()) {
|
||||||
model.put("pet", pet);
|
|
||||||
return VIEWS_PETS_CREATE_OR_UPDATE_FORM;
|
return VIEWS_PETS_CREATE_OR_UPDATE_FORM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
owner.addPet(pet);
|
||||||
this.owners.save(owner);
|
this.owners.save(owner);
|
||||||
|
redirectAttributes.addFlashAttribute("message", "New Pet has been Added");
|
||||||
return "redirect:/owners/{ownerId}";
|
return "redirect:/owners/{ownerId}";
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/pets/{petId}/edit")
|
@GetMapping("/pets/{petId}/edit")
|
||||||
public String initUpdateForm(Owner owner, @PathVariable("petId") int petId, ModelMap model) {
|
public String initUpdateForm() {
|
||||||
Pet pet = owner.getPet(petId);
|
|
||||||
model.put("pet", pet);
|
|
||||||
return VIEWS_PETS_CREATE_OR_UPDATE_FORM;
|
return VIEWS_PETS_CREATE_OR_UPDATE_FORM;
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/pets/{petId}/edit")
|
@PostMapping("/pets/{petId}/edit")
|
||||||
public String processUpdateForm(@Valid Pet pet, BindingResult result, Owner owner, ModelMap model) {
|
public String processUpdateForm(Owner owner, @Valid Pet pet, BindingResult result,
|
||||||
|
RedirectAttributes redirectAttributes) {
|
||||||
|
|
||||||
String petName = pet.getName();
|
String petName = pet.getName();
|
||||||
|
|
||||||
// checking if the pet name already exist for the owner
|
// checking if the pet name already exist for the owner
|
||||||
if (StringUtils.hasText(petName)) {
|
if (StringUtils.hasText(petName)) {
|
||||||
Pet existingPet = owner.getPet(petName.toLowerCase(), false);
|
Pet existingPet = owner.getPet(petName, false);
|
||||||
if (existingPet != null && existingPet.getId() != pet.getId()) {
|
if (existingPet != null && !existingPet.getId().equals(pet.getId())) {
|
||||||
result.rejectValue("name", "duplicate", "already exists");
|
result.rejectValue("name", "duplicate", "already exists");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -144,12 +143,12 @@ class PetController {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.hasErrors()) {
|
if (result.hasErrors()) {
|
||||||
model.put("pet", pet);
|
|
||||||
return VIEWS_PETS_CREATE_OR_UPDATE_FORM;
|
return VIEWS_PETS_CREATE_OR_UPDATE_FORM;
|
||||||
}
|
}
|
||||||
|
|
||||||
owner.addPet(pet);
|
owner.addPet(pet);
|
||||||
this.owners.save(owner);
|
this.owners.save(owner);
|
||||||
|
redirectAttributes.addFlashAttribute("message", "Pet details has been edited");
|
||||||
return "redirect:/owners/{ownerId}";
|
return "redirect:/owners/{ownerId}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
0
src/main/java/org/springframework/samples/petclinic/owner/Visit.java
Executable file → Normal file
0
src/main/java/org/springframework/samples/petclinic/owner/Visit.java
Executable file → Normal file
|
@ -16,6 +16,7 @@
|
||||||
package org.springframework.samples.petclinic.owner;
|
package org.springframework.samples.petclinic.owner;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.validation.BindingResult;
|
import org.springframework.validation.BindingResult;
|
||||||
|
@ -27,6 +28,7 @@ import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
|
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
|
@ -34,6 +36,7 @@ import jakarta.validation.Valid;
|
||||||
* @author Arjen Poutsma
|
* @author Arjen Poutsma
|
||||||
* @author Michael Isvy
|
* @author Michael Isvy
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
|
* @author Wick Dynex
|
||||||
*/
|
*/
|
||||||
@Controller
|
@Controller
|
||||||
class VisitController {
|
class VisitController {
|
||||||
|
@ -59,7 +62,9 @@ class VisitController {
|
||||||
@ModelAttribute("visit")
|
@ModelAttribute("visit")
|
||||||
public Visit loadPetWithVisit(@PathVariable("ownerId") int ownerId, @PathVariable("petId") int petId,
|
public Visit loadPetWithVisit(@PathVariable("ownerId") int ownerId, @PathVariable("petId") int petId,
|
||||||
Map<String, Object> model) {
|
Map<String, Object> model) {
|
||||||
Owner owner = this.owners.findById(ownerId);
|
Optional<Owner> optionalOwner = owners.findById(ownerId);
|
||||||
|
Owner owner = optionalOwner.orElseThrow(() -> new IllegalArgumentException(
|
||||||
|
"Owner not found with id: " + ownerId + ". Please ensure the ID is correct "));
|
||||||
|
|
||||||
Pet pet = owner.getPet(petId);
|
Pet pet = owner.getPet(petId);
|
||||||
model.put("pet", pet);
|
model.put("pet", pet);
|
||||||
|
@ -81,13 +86,14 @@ class VisitController {
|
||||||
// called
|
// called
|
||||||
@PostMapping("/owners/{ownerId}/pets/{petId}/visits/new")
|
@PostMapping("/owners/{ownerId}/pets/{petId}/visits/new")
|
||||||
public String processNewVisitForm(@ModelAttribute Owner owner, @PathVariable int petId, @Valid Visit visit,
|
public String processNewVisitForm(@ModelAttribute Owner owner, @PathVariable int petId, @Valid Visit visit,
|
||||||
BindingResult result) {
|
BindingResult result, RedirectAttributes redirectAttributes) {
|
||||||
if (result.hasErrors()) {
|
if (result.hasErrors()) {
|
||||||
return "pets/createOrUpdateVisitForm";
|
return "pets/createOrUpdateVisitForm";
|
||||||
}
|
}
|
||||||
|
|
||||||
owner.addVisit(petId, visit);
|
owner.addVisit(petId, visit);
|
||||||
this.owners.save(owner);
|
this.owners.save(owner);
|
||||||
|
redirectAttributes.addFlashAttribute("message", "Your visit has been booked");
|
||||||
return "redirect:/owners/{ownerId}";
|
return "redirect:/owners/{ownerId}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
0
src/main/java/org/springframework/samples/petclinic/system/CacheConfiguration.java
Executable file → Normal file
0
src/main/java/org/springframework/samples/petclinic/system/CacheConfiguration.java
Executable file → Normal file
|
@ -57,10 +57,6 @@ public class Vet extends Person {
|
||||||
return this.specialties;
|
return this.specialties;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setSpecialtiesInternal(Set<Specialty> specialties) {
|
|
||||||
this.specialties = specialties;
|
|
||||||
}
|
|
||||||
|
|
||||||
@XmlElement
|
@XmlElement
|
||||||
public List<Specialty> getSpecialties() {
|
public List<Specialty> getSpecialties() {
|
||||||
List<Specialty> sortedSpecs = new ArrayList<>(getSpecialtiesInternal());
|
List<Specialty> sortedSpecs = new ArrayList<>(getSpecialtiesInternal());
|
||||||
|
|
|
@ -37,8 +37,8 @@ class VetController {
|
||||||
|
|
||||||
private final VetRepository vetRepository;
|
private final VetRepository vetRepository;
|
||||||
|
|
||||||
public VetController(VetRepository clinicService) {
|
public VetController(VetRepository vetRepository) {
|
||||||
this.vetRepository = clinicService;
|
this.vetRepository = vetRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/vets.html")
|
@GetMapping("/vets.html")
|
||||||
|
|
|
@ -8,7 +8,7 @@ spring.thymeleaf.mode=HTML
|
||||||
|
|
||||||
# JPA
|
# JPA
|
||||||
spring.jpa.hibernate.ddl-auto=none
|
spring.jpa.hibernate.ddl-auto=none
|
||||||
spring.jpa.open-in-view=true
|
spring.jpa.open-in-view=false
|
||||||
|
|
||||||
# Internationalization
|
# Internationalization
|
||||||
spring.messages.basename=messages/messages
|
spring.messages.basename=messages/messages
|
||||||
|
|
|
@ -15,12 +15,12 @@ INSERT INTO vet_specialties VALUES (3, 3) ON CONFLICT (vet_id, specialty_id) DO
|
||||||
INSERT INTO vet_specialties VALUES (4, 2) ON CONFLICT (vet_id, specialty_id) DO NOTHING;
|
INSERT INTO vet_specialties VALUES (4, 2) ON CONFLICT (vet_id, specialty_id) DO NOTHING;
|
||||||
INSERT INTO vet_specialties VALUES (5, 1) ON CONFLICT (vet_id, specialty_id) DO NOTHING;
|
INSERT INTO vet_specialties VALUES (5, 1) ON CONFLICT (vet_id, specialty_id) DO NOTHING;
|
||||||
|
|
||||||
INSERT INTO types (name) SELECT 'cat' WHERE NOT EXISTS (SELECT * FROM specialties WHERE name='cat');
|
INSERT INTO types (name) SELECT 'cat' WHERE NOT EXISTS (SELECT * FROM types WHERE name='cat');
|
||||||
INSERT INTO types (name) SELECT 'dog' WHERE NOT EXISTS (SELECT * FROM specialties WHERE name='dog');
|
INSERT INTO types (name) SELECT 'dog' WHERE NOT EXISTS (SELECT * FROM types WHERE name='dog');
|
||||||
INSERT INTO types (name) SELECT 'lizard' WHERE NOT EXISTS (SELECT * FROM specialties WHERE name='lizard');
|
INSERT INTO types (name) SELECT 'lizard' WHERE NOT EXISTS (SELECT * FROM types WHERE name='lizard');
|
||||||
INSERT INTO types (name) SELECT 'snake' WHERE NOT EXISTS (SELECT * FROM specialties WHERE name='snake');
|
INSERT INTO types (name) SELECT 'snake' WHERE NOT EXISTS (SELECT * FROM types WHERE name='snake');
|
||||||
INSERT INTO types (name) SELECT 'bird' WHERE NOT EXISTS (SELECT * FROM specialties WHERE name='bird');
|
INSERT INTO types (name) SELECT 'bird' WHERE NOT EXISTS (SELECT * FROM types WHERE name='bird');
|
||||||
INSERT INTO types (name) SELECT 'hamster' WHERE NOT EXISTS (SELECT * FROM specialties WHERE name='cat');
|
INSERT INTO types (name) SELECT 'hamster' WHERE NOT EXISTS (SELECT * FROM types WHERE name='hamster');
|
||||||
|
|
||||||
INSERT INTO owners (first_name, last_name, address, city, telephone) SELECT 'George', 'Franklin', '110 W. Liberty St.', 'Madison', '6085551023' WHERE NOT EXISTS (SELECT * FROM owners WHERE id=1);
|
INSERT INTO owners (first_name, last_name, address, city, telephone) SELECT 'George', 'Franklin', '110 W. Liberty St.', 'Madison', '6085551023' WHERE NOT EXISTS (SELECT * FROM owners WHERE id=1);
|
||||||
INSERT INTO owners (first_name, last_name, address, city, telephone) SELECT 'Betty', 'Davis', '638 Cardinal Ave.', 'Sun Prairie', '6085551749' WHERE NOT EXISTS (SELECT * FROM owners WHERE id=2);
|
INSERT INTO owners (first_name, last_name, address, city, telephone) SELECT 'Betty', 'Davis', '638 Cardinal Ave.', 'Sun Prairie', '6085551749' WHERE NOT EXISTS (SELECT * FROM owners WHERE id=2);
|
||||||
|
|
9
src/main/resources/messages/messages_fa.properties
Normal file
9
src/main/resources/messages/messages_fa.properties
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
welcome=خوش آمدید
|
||||||
|
required=الزامی
|
||||||
|
notFound=یافت نشد
|
||||||
|
duplicate=قبلا استفاده شده
|
||||||
|
nonNumeric=باید عددی باشد
|
||||||
|
duplicateFormSubmission=ارسال تکراری فرم مجاز نیست
|
||||||
|
typeMismatch.date=تاریخ نامعتبر
|
||||||
|
typeMismatch.birthDate=تاریخ تولد نامعتبر
|
||||||
|
|
8
src/main/resources/messages/messages_pt.properties
Normal file
8
src/main/resources/messages/messages_pt.properties
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
welcome=Bem-vindo
|
||||||
|
required=E necessario
|
||||||
|
notFound=Nao foi encontrado
|
||||||
|
duplicate=Ja esta em uso
|
||||||
|
nonNumeric=Deve ser tudo numerico
|
||||||
|
duplicateFormSubmission=O envio duplicado de formulario nao e permitido
|
||||||
|
typeMismatch.date=Data invalida
|
||||||
|
typeMismatch.birthDate=Data de nascimento invalida
|
9
src/main/resources/messages/messages_ru.properties
Normal file
9
src/main/resources/messages/messages_ru.properties
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
welcome=Добро пожаловать
|
||||||
|
required=необходимо
|
||||||
|
notFound=не найдено
|
||||||
|
duplicate=уже используется
|
||||||
|
nonNumeric=должно быть все числовое значение
|
||||||
|
duplicateFormSubmission=Дублирование формы не допускается
|
||||||
|
typeMismatch.date=неправильная даные
|
||||||
|
typeMismatch.birthDate=неправильная дата
|
||||||
|
|
9
src/main/resources/messages/messages_tr.properties
Normal file
9
src/main/resources/messages/messages_tr.properties
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
welcome=hoş geldiniz
|
||||||
|
required=gerekli
|
||||||
|
notFound=bulunamadı
|
||||||
|
duplicate=zaten kullanılıyor
|
||||||
|
nonNumeric=sadece sayısal olmalıdır
|
||||||
|
duplicateFormSubmission=Formun tekrar gönderilmesine izin verilmez
|
||||||
|
typeMismatch.date=geçersiz tarih
|
||||||
|
typeMismatch.birthDate=geçersiz tarih
|
||||||
|
|
|
@ -12,8 +12,8 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
/*!
|
/*!
|
||||||
* Bootstrap v5.3.2 (https://getbootstrap.com/)
|
* Bootstrap v5.3.3 (https://getbootstrap.com/)
|
||||||
* Copyright 2011-2023 The Bootstrap Authors
|
* Copyright 2011-2024 The Bootstrap Authors
|
||||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||||
*/
|
*/
|
||||||
:root,
|
:root,
|
||||||
|
@ -2377,6 +2377,8 @@ textarea.form-control-lg {
|
||||||
border-color: var(--bs-btn-active-border-color); }
|
border-color: var(--bs-btn-active-border-color); }
|
||||||
.btn-check:checked + .btn:focus-visible, :not(.btn-check) + .btn:active:focus-visible, .btn:first-child:active:focus-visible, .btn.active:focus-visible, .btn.show:focus-visible {
|
.btn-check:checked + .btn:focus-visible, :not(.btn-check) + .btn:active:focus-visible, .btn:first-child:active:focus-visible, .btn.active:focus-visible, .btn.show:focus-visible {
|
||||||
box-shadow: var(--bs-btn-focus-box-shadow); }
|
box-shadow: var(--bs-btn-focus-box-shadow); }
|
||||||
|
.btn-check:checked:focus-visible + .btn {
|
||||||
|
box-shadow: var(--bs-btn-focus-box-shadow); }
|
||||||
.btn:disabled, .btn.disabled, fieldset:disabled .btn {
|
.btn:disabled, .btn.disabled, fieldset:disabled .btn {
|
||||||
color: var(--bs-btn-disabled-color);
|
color: var(--bs-btn-disabled-color);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
@ -3680,12 +3682,11 @@ textarea.form-control-lg {
|
||||||
--bs-accordion-btn-padding-y: 1rem;
|
--bs-accordion-btn-padding-y: 1rem;
|
||||||
--bs-accordion-btn-color: var(--bs-body-color);
|
--bs-accordion-btn-color: var(--bs-body-color);
|
||||||
--bs-accordion-btn-bg: var(--bs-accordion-bg);
|
--bs-accordion-btn-bg: var(--bs-accordion-bg);
|
||||||
--bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23212529'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");
|
--bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23212529' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M2 5L8 11L14 5'/%3e%3c/svg%3e");
|
||||||
--bs-accordion-btn-icon-width: 1.25rem;
|
--bs-accordion-btn-icon-width: 1.25rem;
|
||||||
--bs-accordion-btn-icon-transform: rotate(-180deg);
|
--bs-accordion-btn-icon-transform: rotate(-180deg);
|
||||||
--bs-accordion-btn-icon-transition: transform 0.2s ease-in-out;
|
--bs-accordion-btn-icon-transition: transform 0.2s ease-in-out;
|
||||||
--bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23052c65'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");
|
--bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23052c65' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M2 5L8 11L14 5'/%3e%3c/svg%3e");
|
||||||
--bs-accordion-btn-focus-border-color: #86b7fe;
|
|
||||||
--bs-accordion-btn-focus-box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
|
--bs-accordion-btn-focus-box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
|
||||||
--bs-accordion-body-padding-x: 1.25rem;
|
--bs-accordion-body-padding-x: 1.25rem;
|
||||||
--bs-accordion-body-padding-y: 1rem;
|
--bs-accordion-body-padding-y: 1rem;
|
||||||
|
@ -3733,7 +3734,6 @@ textarea.form-control-lg {
|
||||||
z-index: 2; }
|
z-index: 2; }
|
||||||
.accordion-button:focus {
|
.accordion-button:focus {
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
border-color: var(--bs-accordion-btn-focus-border-color);
|
|
||||||
outline: 0;
|
outline: 0;
|
||||||
box-shadow: var(--bs-accordion-btn-focus-box-shadow); }
|
box-shadow: var(--bs-accordion-btn-focus-box-shadow); }
|
||||||
|
|
||||||
|
@ -3747,7 +3747,7 @@ textarea.form-control-lg {
|
||||||
.accordion-item:first-of-type {
|
.accordion-item:first-of-type {
|
||||||
border-top-left-radius: var(--bs-accordion-border-radius);
|
border-top-left-radius: var(--bs-accordion-border-radius);
|
||||||
border-top-right-radius: var(--bs-accordion-border-radius); }
|
border-top-right-radius: var(--bs-accordion-border-radius); }
|
||||||
.accordion-item:first-of-type .accordion-button {
|
.accordion-item:first-of-type > .accordion-header .accordion-button {
|
||||||
border-top-left-radius: var(--bs-accordion-inner-border-radius);
|
border-top-left-radius: var(--bs-accordion-inner-border-radius);
|
||||||
border-top-right-radius: var(--bs-accordion-inner-border-radius); }
|
border-top-right-radius: var(--bs-accordion-inner-border-radius); }
|
||||||
.accordion-item:not(:first-of-type) {
|
.accordion-item:not(:first-of-type) {
|
||||||
|
@ -3755,28 +3755,27 @@ textarea.form-control-lg {
|
||||||
.accordion-item:last-of-type {
|
.accordion-item:last-of-type {
|
||||||
border-bottom-right-radius: var(--bs-accordion-border-radius);
|
border-bottom-right-radius: var(--bs-accordion-border-radius);
|
||||||
border-bottom-left-radius: var(--bs-accordion-border-radius); }
|
border-bottom-left-radius: var(--bs-accordion-border-radius); }
|
||||||
.accordion-item:last-of-type .accordion-button.collapsed {
|
.accordion-item:last-of-type > .accordion-header .accordion-button.collapsed {
|
||||||
border-bottom-right-radius: var(--bs-accordion-inner-border-radius);
|
border-bottom-right-radius: var(--bs-accordion-inner-border-radius);
|
||||||
border-bottom-left-radius: var(--bs-accordion-inner-border-radius); }
|
border-bottom-left-radius: var(--bs-accordion-inner-border-radius); }
|
||||||
.accordion-item:last-of-type .accordion-collapse {
|
.accordion-item:last-of-type > .accordion-collapse {
|
||||||
border-bottom-right-radius: var(--bs-accordion-border-radius);
|
border-bottom-right-radius: var(--bs-accordion-border-radius);
|
||||||
border-bottom-left-radius: var(--bs-accordion-border-radius); }
|
border-bottom-left-radius: var(--bs-accordion-border-radius); }
|
||||||
|
|
||||||
.accordion-body {
|
.accordion-body {
|
||||||
padding: var(--bs-accordion-body-padding-y) var(--bs-accordion-body-padding-x); }
|
padding: var(--bs-accordion-body-padding-y) var(--bs-accordion-body-padding-x); }
|
||||||
|
|
||||||
.accordion-flush .accordion-collapse {
|
.accordion-flush > .accordion-item {
|
||||||
border-width: 0; }
|
|
||||||
|
|
||||||
.accordion-flush .accordion-item {
|
|
||||||
border-right: 0;
|
border-right: 0;
|
||||||
border-left: 0;
|
border-left: 0;
|
||||||
border-radius: 0; }
|
border-radius: 0; }
|
||||||
.accordion-flush .accordion-item:first-child {
|
.accordion-flush > .accordion-item:first-child {
|
||||||
border-top: 0; }
|
border-top: 0; }
|
||||||
.accordion-flush .accordion-item:last-child {
|
.accordion-flush > .accordion-item:last-child {
|
||||||
border-bottom: 0; }
|
border-bottom: 0; }
|
||||||
.accordion-flush .accordion-item .accordion-button, .accordion-flush .accordion-item .accordion-button.collapsed {
|
.accordion-flush > .accordion-item > .accordion-header .accordion-button, .accordion-flush > .accordion-item > .accordion-header .accordion-button.collapsed {
|
||||||
|
border-radius: 0; }
|
||||||
|
.accordion-flush > .accordion-item > .accordion-collapse {
|
||||||
border-radius: 0; }
|
border-radius: 0; }
|
||||||
|
|
||||||
[data-bs-theme="dark"] .accordion-button::after {
|
[data-bs-theme="dark"] .accordion-button::after {
|
||||||
|
@ -4524,7 +4523,6 @@ textarea.form-control-lg {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
|
||||||
padding: var(--bs-modal-header-padding);
|
padding: var(--bs-modal-header-padding);
|
||||||
border-bottom: var(--bs-modal-header-border-width) solid var(--bs-modal-header-border-color);
|
border-bottom: var(--bs-modal-header-border-width) solid var(--bs-modal-header-border-color);
|
||||||
border-top-left-radius: var(--bs-modal-inner-border-radius);
|
border-top-left-radius: var(--bs-modal-inner-border-radius);
|
||||||
|
@ -4997,19 +4995,11 @@ textarea.form-control-lg {
|
||||||
background-position: 50%;
|
background-position: 50%;
|
||||||
background-size: 100% 100%; }
|
background-size: 100% 100%; }
|
||||||
|
|
||||||
/* rtl:options: {
|
|
||||||
"autoRename": true,
|
|
||||||
"stringMap":[ {
|
|
||||||
"name" : "prev-next",
|
|
||||||
"search" : "prev",
|
|
||||||
"replace" : "next"
|
|
||||||
} ]
|
|
||||||
} */
|
|
||||||
.carousel-control-prev-icon {
|
.carousel-control-prev-icon {
|
||||||
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e"); }
|
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e") /*rtl:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e")*/; }
|
||||||
|
|
||||||
.carousel-control-next-icon {
|
.carousel-control-next-icon {
|
||||||
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); }
|
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e") /*rtl:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e")*/; }
|
||||||
|
|
||||||
.carousel-indicators {
|
.carousel-indicators {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -5528,13 +5518,10 @@ textarea.form-control-lg {
|
||||||
.offcanvas-header {
|
.offcanvas-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
|
||||||
padding: var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x); }
|
padding: var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x); }
|
||||||
.offcanvas-header .btn-close {
|
.offcanvas-header .btn-close {
|
||||||
padding: calc(var(--bs-offcanvas-padding-y) * .5) calc(var(--bs-offcanvas-padding-x) * .5);
|
padding: calc(var(--bs-offcanvas-padding-y) * .5) calc(var(--bs-offcanvas-padding-x) * .5);
|
||||||
margin-top: calc(-.5 * var(--bs-offcanvas-padding-y));
|
margin: calc(-.5 * var(--bs-offcanvas-padding-y)) calc(-.5 * var(--bs-offcanvas-padding-x)) calc(-.5 * var(--bs-offcanvas-padding-y)) auto; }
|
||||||
margin-right: calc(-.5 * var(--bs-offcanvas-padding-x));
|
|
||||||
margin-bottom: calc(-.5 * var(--bs-offcanvas-padding-y)); }
|
|
||||||
|
|
||||||
.offcanvas-title {
|
.offcanvas-title {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
|
4
src/main/resources/templates/fragments/layout.html
Executable file → Normal file
4
src/main/resources/templates/fragments/layout.html
Executable file → Normal file
|
@ -17,7 +17,7 @@
|
||||||
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
|
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
|
|
||||||
<link th:href="@{/webjars/font-awesome/4.7.0/css/font-awesome.min.css}" rel="stylesheet">
|
<link th:href="@{/webjars/font-awesome/css/font-awesome.min.css}" rel="stylesheet">
|
||||||
<link rel="stylesheet" th:href="@{/resources/css/petclinic.css}" />
|
<link rel="stylesheet" th:href="@{/resources/css/petclinic.css}" />
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
@ -87,7 +87,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script th:src="@{/webjars/bootstrap/5.3.2/dist/js/bootstrap.bundle.min.js}"></script>
|
<script th:src="@{/webjars/bootstrap/dist/js/bootstrap.bundle.min.js}"></script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,16 @@
|
||||||
|
|
||||||
<h2>Owner Information</h2>
|
<h2>Owner Information</h2>
|
||||||
|
|
||||||
|
<div th:if="${message}" class="alert alert-success" id="success-message">
|
||||||
|
<span th:text="${message}"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div th:if="${error}" class="alert alert-danger" id="error-message">
|
||||||
|
<span th:text="${error}"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<table class="table table-striped" th:object="${owner}">
|
<table class="table table-striped" th:object="${owner}">
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -73,7 +83,20 @@
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
</table>
|
</table>
|
||||||
|
<script>
|
||||||
|
// Function to hide the success and error messages after 3 seconds
|
||||||
|
function hideMessages() {
|
||||||
|
setTimeout(function() {
|
||||||
|
document.getElementById("success-message").style.display = "none";
|
||||||
|
document.getElementById("error-message").style.display = "none";
|
||||||
|
}, 3000); // 3000 milliseconds (3 seconds)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the function to hide messages
|
||||||
|
hideMessages();
|
||||||
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -46,7 +46,7 @@ class MySqlIntegrationTests {
|
||||||
|
|
||||||
@ServiceConnection
|
@ServiceConnection
|
||||||
@Container
|
@Container
|
||||||
static MySQLContainer<?> container = new MySQLContainer<>("mysql:8.2");
|
static MySQLContainer<?> container = new MySQLContainer<>("mysql:9.1");
|
||||||
|
|
||||||
@LocalServerPort
|
@LocalServerPort
|
||||||
int port;
|
int port;
|
||||||
|
@ -58,7 +58,7 @@ class MySqlIntegrationTests {
|
||||||
private RestTemplateBuilder builder;
|
private RestTemplateBuilder builder;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testFindAll() throws Exception {
|
void testFindAll() {
|
||||||
vets.findAll();
|
vets.findAll();
|
||||||
vets.findAll(); // served from cache
|
vets.findAll(); // served from cache
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ public class MysqlTestApplication {
|
||||||
@Profile("mysql")
|
@Profile("mysql")
|
||||||
@Bean
|
@Bean
|
||||||
static MySQLContainer<?> container() {
|
static MySQLContainer<?> container() {
|
||||||
return new MySQLContainer<>("mysql:8.2");
|
return new MySQLContainer<>("mysql:9.1");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|
|
@ -44,7 +44,7 @@ public class PetClinicIntegrationTests {
|
||||||
private RestTemplateBuilder builder;
|
private RestTemplateBuilder builder;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testFindAll() throws Exception {
|
void testFindAll() {
|
||||||
vets.findAll();
|
vets.findAll();
|
||||||
vets.findAll(); // served from cache
|
vets.findAll(); // served from cache
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package org.springframework.samples.petclinic;
|
package org.springframework.samples.petclinic;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
@ -48,7 +49,7 @@ import org.springframework.web.client.RestTemplate;
|
||||||
import org.testcontainers.DockerClientFactory;
|
import org.testcontainers.DockerClientFactory;
|
||||||
|
|
||||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { "spring.docker.compose.skip.in-tests=false", //
|
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { "spring.docker.compose.skip.in-tests=false", //
|
||||||
"spring.docker.compose.profiles.active=postgres" })
|
"spring.docker.compose.start.arguments=--force-recreate,--renew-anon-volumes,postgres" })
|
||||||
@ActiveProfiles("postgres")
|
@ActiveProfiles("postgres")
|
||||||
@DisabledInNativeImage
|
@DisabledInNativeImage
|
||||||
public class PostgresIntegrationTests {
|
public class PostgresIntegrationTests {
|
||||||
|
@ -71,7 +72,7 @@ public class PostgresIntegrationTests {
|
||||||
new SpringApplicationBuilder(PetClinicApplication.class) //
|
new SpringApplicationBuilder(PetClinicApplication.class) //
|
||||||
.profiles("postgres") //
|
.profiles("postgres") //
|
||||||
.properties( //
|
.properties( //
|
||||||
"spring.docker.compose.profiles.active=postgres" //
|
"spring.docker.compose.start.arguments=postgres" //
|
||||||
) //
|
) //
|
||||||
.listeners(new PropertiesLogger()) //
|
.listeners(new PropertiesLogger()) //
|
||||||
.run(args);
|
.run(args);
|
||||||
|
@ -114,7 +115,16 @@ public class PostgresIntegrationTests {
|
||||||
Arrays.sort(names);
|
Arrays.sort(names);
|
||||||
for (String name : names) {
|
for (String name : names) {
|
||||||
String resolved = environment.getProperty(name);
|
String resolved = environment.getProperty(name);
|
||||||
String value = source.getProperty(name).toString();
|
|
||||||
|
assertNotNull(resolved, "resolved environment property: " + name + " is null.");
|
||||||
|
|
||||||
|
Object sourceProperty = source.getProperty(name);
|
||||||
|
|
||||||
|
assertNotNull(sourceProperty, "source property was expecting an object but is null.");
|
||||||
|
|
||||||
|
assertNotNull(sourceProperty.toString(), "source property toString() returned null.");
|
||||||
|
|
||||||
|
String value = sourceProperty.toString();
|
||||||
if (resolved.equals(value)) {
|
if (resolved.equals(value)) {
|
||||||
log.info(name + "=" + resolved);
|
log.info(name + "=" + resolved);
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ class ValidatorTests {
|
||||||
|
|
||||||
assertThat(constraintViolations).hasSize(1);
|
assertThat(constraintViolations).hasSize(1);
|
||||||
ConstraintViolation<Person> violation = constraintViolations.iterator().next();
|
ConstraintViolation<Person> violation = constraintViolations.iterator().next();
|
||||||
assertThat(violation.getPropertyPath().toString()).isEqualTo("firstName");
|
assertThat(violation.getPropertyPath()).hasToString("firstName");
|
||||||
assertThat(violation.getMessage()).isEqualTo("must not be blank");
|
assertThat(violation.getMessage()).isEqualTo("must not be blank");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,43 +16,44 @@
|
||||||
|
|
||||||
package org.springframework.samples.petclinic.owner;
|
package org.springframework.samples.petclinic.owner;
|
||||||
|
|
||||||
|
import org.assertj.core.util.Lists;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.condition.DisabledInNativeImage;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.PageImpl;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.test.context.aot.DisabledInAotMode;
|
||||||
|
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||||
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.empty;
|
import static org.hamcrest.Matchers.empty;
|
||||||
|
import static org.hamcrest.Matchers.greaterThan;
|
||||||
|
import static org.hamcrest.Matchers.hasItem;
|
||||||
import static org.hamcrest.Matchers.hasProperty;
|
import static org.hamcrest.Matchers.hasProperty;
|
||||||
|
import static org.hamcrest.Matchers.hasSize;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.hamcrest.Matchers.not;
|
import static org.hamcrest.Matchers.not;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.BDDMockito.given;
|
import static org.mockito.BDDMockito.given;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
|
|
||||||
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.assertj.core.util.Lists;
|
|
||||||
import org.hamcrest.BaseMatcher;
|
|
||||||
import org.hamcrest.Description;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.junit.jupiter.api.condition.DisabledInNativeImage;
|
|
||||||
import org.mockito.Mockito;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
|
||||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
|
||||||
import org.springframework.data.domain.Page;
|
|
||||||
import org.springframework.data.domain.PageImpl;
|
|
||||||
import org.springframework.data.domain.Pageable;
|
|
||||||
import org.springframework.test.context.aot.DisabledInAotMode;
|
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test class for {@link OwnerController}
|
* Test class for {@link OwnerController}
|
||||||
*
|
*
|
||||||
* @author Colin But
|
* @author Colin But
|
||||||
|
* @author Wick Dynex
|
||||||
*/
|
*/
|
||||||
@WebMvcTest(OwnerController.class)
|
@WebMvcTest(OwnerController.class)
|
||||||
@DisabledInNativeImage
|
@DisabledInNativeImage
|
||||||
|
@ -64,7 +65,7 @@ class OwnerControllerTests {
|
||||||
@Autowired
|
@Autowired
|
||||||
private MockMvc mockMvc;
|
private MockMvc mockMvc;
|
||||||
|
|
||||||
@MockBean
|
@MockitoBean
|
||||||
private OwnerRepository owners;
|
private OwnerRepository owners;
|
||||||
|
|
||||||
private Owner george() {
|
private Owner george() {
|
||||||
|
@ -84,18 +85,18 @@ class OwnerControllerTests {
|
||||||
george.addPet(max);
|
george.addPet(max);
|
||||||
max.setId(1);
|
max.setId(1);
|
||||||
return george;
|
return george;
|
||||||
};
|
}
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setup() {
|
void setup() {
|
||||||
|
|
||||||
Owner george = george();
|
Owner george = george();
|
||||||
given(this.owners.findByLastName(eq("Franklin"), any(Pageable.class)))
|
given(this.owners.findByLastNameStartingWith(eq("Franklin"), any(Pageable.class)))
|
||||||
.willReturn(new PageImpl<Owner>(Lists.newArrayList(george)));
|
.willReturn(new PageImpl<>(Lists.newArrayList(george)));
|
||||||
|
|
||||||
given(this.owners.findAll(any(Pageable.class))).willReturn(new PageImpl<Owner>(Lists.newArrayList(george)));
|
given(this.owners.findAll(any(Pageable.class))).willReturn(new PageImpl<>(Lists.newArrayList(george)));
|
||||||
|
|
||||||
given(this.owners.findById(TEST_OWNER_ID)).willReturn(george);
|
given(this.owners.findById(TEST_OWNER_ID)).willReturn(Optional.of(george));
|
||||||
Visit visit = new Visit();
|
Visit visit = new Visit();
|
||||||
visit.setDate(LocalDate.now());
|
visit.setDate(LocalDate.now());
|
||||||
george.getPet("Max").getVisits().add(visit);
|
george.getPet("Max").getVisits().add(visit);
|
||||||
|
@ -117,7 +118,7 @@ class OwnerControllerTests {
|
||||||
.param("lastName", "Bloggs")
|
.param("lastName", "Bloggs")
|
||||||
.param("address", "123 Caramel Street")
|
.param("address", "123 Caramel Street")
|
||||||
.param("city", "London")
|
.param("city", "London")
|
||||||
.param("telephone", "01316761638"))
|
.param("telephone", "1316761638"))
|
||||||
.andExpect(status().is3xxRedirection());
|
.andExpect(status().is3xxRedirection());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,15 +143,15 @@ class OwnerControllerTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testProcessFindFormSuccess() throws Exception {
|
void testProcessFindFormSuccess() throws Exception {
|
||||||
Page<Owner> tasks = new PageImpl<Owner>(Lists.newArrayList(george(), new Owner()));
|
Page<Owner> tasks = new PageImpl<>(Lists.newArrayList(george(), new Owner()));
|
||||||
Mockito.when(this.owners.findByLastName(anyString(), any(Pageable.class))).thenReturn(tasks);
|
when(this.owners.findByLastNameStartingWith(anyString(), any(Pageable.class))).thenReturn(tasks);
|
||||||
mockMvc.perform(get("/owners?page=1")).andExpect(status().isOk()).andExpect(view().name("owners/ownersList"));
|
mockMvc.perform(get("/owners?page=1")).andExpect(status().isOk()).andExpect(view().name("owners/ownersList"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testProcessFindFormByLastName() throws Exception {
|
void testProcessFindFormByLastName() throws Exception {
|
||||||
Page<Owner> tasks = new PageImpl<Owner>(Lists.newArrayList(george()));
|
Page<Owner> tasks = new PageImpl<>(Lists.newArrayList(george()));
|
||||||
Mockito.when(this.owners.findByLastName(eq("Franklin"), any(Pageable.class))).thenReturn(tasks);
|
when(this.owners.findByLastNameStartingWith(eq("Franklin"), any(Pageable.class))).thenReturn(tasks);
|
||||||
mockMvc.perform(get("/owners?page=1").param("lastName", "Franklin"))
|
mockMvc.perform(get("/owners?page=1").param("lastName", "Franklin"))
|
||||||
.andExpect(status().is3xxRedirection())
|
.andExpect(status().is3xxRedirection())
|
||||||
.andExpect(view().name("redirect:/owners/" + TEST_OWNER_ID));
|
.andExpect(view().name("redirect:/owners/" + TEST_OWNER_ID));
|
||||||
|
@ -158,8 +159,8 @@ class OwnerControllerTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testProcessFindFormNoOwnersFound() throws Exception {
|
void testProcessFindFormNoOwnersFound() throws Exception {
|
||||||
Page<Owner> tasks = new PageImpl<Owner>(Lists.newArrayList());
|
Page<Owner> tasks = new PageImpl<>(Lists.newArrayList());
|
||||||
Mockito.when(this.owners.findByLastName(eq("Unknown Surname"), any(Pageable.class))).thenReturn(tasks);
|
when(this.owners.findByLastNameStartingWith(eq("Unknown Surname"), any(Pageable.class))).thenReturn(tasks);
|
||||||
mockMvc.perform(get("/owners?page=1").param("lastName", "Unknown Surname"))
|
mockMvc.perform(get("/owners?page=1").param("lastName", "Unknown Surname"))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(model().attributeHasFieldErrors("owner", "lastName"))
|
.andExpect(model().attributeHasFieldErrors("owner", "lastName"))
|
||||||
|
@ -188,7 +189,7 @@ class OwnerControllerTests {
|
||||||
.param("lastName", "Bloggs")
|
.param("lastName", "Bloggs")
|
||||||
.param("address", "123 Caramel Street")
|
.param("address", "123 Caramel Street")
|
||||||
.param("city", "London")
|
.param("city", "London")
|
||||||
.param("telephone", "01616291589"))
|
.param("telephone", "1616291589"))
|
||||||
.andExpect(status().is3xxRedirection())
|
.andExpect(status().is3xxRedirection())
|
||||||
.andExpect(view().name("redirect:/owners/{ownerId}"));
|
.andExpect(view().name("redirect:/owners/{ownerId}"));
|
||||||
}
|
}
|
||||||
|
@ -224,25 +225,29 @@ class OwnerControllerTests {
|
||||||
.andExpect(model().attribute("owner", hasProperty("city", is("Madison"))))
|
.andExpect(model().attribute("owner", hasProperty("city", is("Madison"))))
|
||||||
.andExpect(model().attribute("owner", hasProperty("telephone", is("6085551023"))))
|
.andExpect(model().attribute("owner", hasProperty("telephone", is("6085551023"))))
|
||||||
.andExpect(model().attribute("owner", hasProperty("pets", not(empty()))))
|
.andExpect(model().attribute("owner", hasProperty("pets", not(empty()))))
|
||||||
.andExpect(model().attribute("owner", hasProperty("pets", new BaseMatcher<List<Pet>>() {
|
.andExpect(model().attribute("owner",
|
||||||
|
hasProperty("pets", hasItem(hasProperty("visits", hasSize(greaterThan(0)))))))
|
||||||
@Override
|
|
||||||
public boolean matches(Object item) {
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
List<Pet> pets = (List<Pet>) item;
|
|
||||||
Pet pet = pets.get(0);
|
|
||||||
if (pet.getVisits().isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void describeTo(Description description) {
|
|
||||||
description.appendText("Max did not have any visits");
|
|
||||||
}
|
|
||||||
})))
|
|
||||||
.andExpect(view().name("owners/ownerDetails"));
|
.andExpect(view().name("owners/ownerDetails"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testProcessUpdateOwnerFormWithIdMismatch() throws Exception {
|
||||||
|
int pathOwnerId = 1;
|
||||||
|
|
||||||
|
Owner owner = new Owner();
|
||||||
|
owner.setId(2);
|
||||||
|
owner.setFirstName("John");
|
||||||
|
owner.setLastName("Doe");
|
||||||
|
owner.setAddress("Center Street");
|
||||||
|
owner.setCity("New York");
|
||||||
|
owner.setTelephone("0123456789");
|
||||||
|
|
||||||
|
when(owners.findById(pathOwnerId)).thenReturn(Optional.of(owner));
|
||||||
|
|
||||||
|
mockMvc.perform(MockMvcRequestBuilders.post("/owners/{ownerId}/edit", pathOwnerId).flashAttr("owner", owner))
|
||||||
|
.andExpect(status().is3xxRedirection())
|
||||||
|
.andExpect(redirectedUrl("/owners/" + pathOwnerId + "/edit"))
|
||||||
|
.andExpect(flash().attributeExists("error"));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
90
src/test/java/org/springframework/samples/petclinic/owner/PetControllerTests.java
Executable file → Normal file
90
src/test/java/org/springframework/samples/petclinic/owner/PetControllerTests.java
Executable file → Normal file
|
@ -18,16 +18,20 @@ package org.springframework.samples.petclinic.owner;
|
||||||
|
|
||||||
import org.assertj.core.util.Lists;
|
import org.assertj.core.util.Lists;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Nested;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.condition.DisabledInNativeImage;
|
import org.junit.jupiter.api.condition.DisabledInNativeImage;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
|
||||||
import org.springframework.context.annotation.ComponentScan;
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
import org.springframework.context.annotation.FilterType;
|
import org.springframework.context.annotation.FilterType;
|
||||||
import org.springframework.test.context.aot.DisabledInAotMode;
|
import org.springframework.test.context.aot.DisabledInAotMode;
|
||||||
|
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import static org.mockito.BDDMockito.given;
|
import static org.mockito.BDDMockito.given;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||||
|
@ -39,6 +43,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||||
* Test class for the {@link PetController}
|
* Test class for the {@link PetController}
|
||||||
*
|
*
|
||||||
* @author Colin But
|
* @author Colin But
|
||||||
|
* @author Wick Dynex
|
||||||
*/
|
*/
|
||||||
@WebMvcTest(value = PetController.class,
|
@WebMvcTest(value = PetController.class,
|
||||||
includeFilters = @ComponentScan.Filter(value = PetTypeFormatter.class, type = FilterType.ASSIGNABLE_TYPE))
|
includeFilters = @ComponentScan.Filter(value = PetTypeFormatter.class, type = FilterType.ASSIGNABLE_TYPE))
|
||||||
|
@ -53,7 +58,7 @@ class PetControllerTests {
|
||||||
@Autowired
|
@Autowired
|
||||||
private MockMvc mockMvc;
|
private MockMvc mockMvc;
|
||||||
|
|
||||||
@MockBean
|
@MockitoBean
|
||||||
private OwnerRepository owners;
|
private OwnerRepository owners;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
|
@ -62,11 +67,17 @@ class PetControllerTests {
|
||||||
cat.setId(3);
|
cat.setId(3);
|
||||||
cat.setName("hamster");
|
cat.setName("hamster");
|
||||||
given(this.owners.findPetTypes()).willReturn(Lists.newArrayList(cat));
|
given(this.owners.findPetTypes()).willReturn(Lists.newArrayList(cat));
|
||||||
|
|
||||||
Owner owner = new Owner();
|
Owner owner = new Owner();
|
||||||
Pet pet = new Pet();
|
Pet pet = new Pet();
|
||||||
|
Pet dog = new Pet();
|
||||||
owner.addPet(pet);
|
owner.addPet(pet);
|
||||||
|
owner.addPet(dog);
|
||||||
pet.setId(TEST_PET_ID);
|
pet.setId(TEST_PET_ID);
|
||||||
given(this.owners.findById(TEST_OWNER_ID)).willReturn(owner);
|
dog.setId(TEST_PET_ID + 1);
|
||||||
|
pet.setName("petty");
|
||||||
|
dog.setName("doggy");
|
||||||
|
given(this.owners.findById(TEST_OWNER_ID)).willReturn(Optional.of(owner));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -87,8 +98,37 @@ class PetControllerTests {
|
||||||
.andExpect(view().name("redirect:/owners/{ownerId}"));
|
.andExpect(view().name("redirect:/owners/{ownerId}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class ProcessCreationFormHasErrors {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testProcessCreationFormHasErrors() throws Exception {
|
void testProcessCreationFormWithBlankName() throws Exception {
|
||||||
|
mockMvc
|
||||||
|
.perform(post("/owners/{ownerId}/pets/new", TEST_OWNER_ID).param("name", "\t \n")
|
||||||
|
.param("birthDate", "2015-02-12"))
|
||||||
|
.andExpect(model().attributeHasNoErrors("owner"))
|
||||||
|
.andExpect(model().attributeHasErrors("pet"))
|
||||||
|
.andExpect(model().attributeHasFieldErrors("pet", "name"))
|
||||||
|
.andExpect(model().attributeHasFieldErrorCode("pet", "name", "required"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(view().name("pets/createOrUpdatePetForm"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testProcessCreationFormWithDuplicateName() throws Exception {
|
||||||
|
mockMvc
|
||||||
|
.perform(post("/owners/{ownerId}/pets/new", TEST_OWNER_ID).param("name", "petty")
|
||||||
|
.param("birthDate", "2015-02-12"))
|
||||||
|
.andExpect(model().attributeHasNoErrors("owner"))
|
||||||
|
.andExpect(model().attributeHasErrors("pet"))
|
||||||
|
.andExpect(model().attributeHasFieldErrors("pet", "name"))
|
||||||
|
.andExpect(model().attributeHasFieldErrorCode("pet", "name", "duplicate"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(view().name("pets/createOrUpdatePetForm"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testProcessCreationFormWithMissingPetType() throws Exception {
|
||||||
mockMvc
|
mockMvc
|
||||||
.perform(post("/owners/{ownerId}/pets/new", TEST_OWNER_ID).param("name", "Betty")
|
.perform(post("/owners/{ownerId}/pets/new", TEST_OWNER_ID).param("name", "Betty")
|
||||||
.param("birthDate", "2015-02-12"))
|
.param("birthDate", "2015-02-12"))
|
||||||
|
@ -100,6 +140,22 @@ class PetControllerTests {
|
||||||
.andExpect(view().name("pets/createOrUpdatePetForm"));
|
.andExpect(view().name("pets/createOrUpdatePetForm"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testProcessCreationFormWithInvalidBirthDate() throws Exception {
|
||||||
|
LocalDate currentDate = LocalDate.now();
|
||||||
|
String futureBirthDate = currentDate.plusMonths(1).toString();
|
||||||
|
|
||||||
|
mockMvc
|
||||||
|
.perform(post("/owners/{ownerId}/pets/new", TEST_OWNER_ID).param("name", "Betty")
|
||||||
|
.param("birthDate", futureBirthDate))
|
||||||
|
.andExpect(model().attributeHasNoErrors("owner"))
|
||||||
|
.andExpect(model().attributeHasErrors("pet"))
|
||||||
|
.andExpect(model().attributeHasFieldErrors("pet", "birthDate"))
|
||||||
|
.andExpect(model().attributeHasFieldErrorCode("pet", "birthDate", "typeMismatch.birthDate"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(view().name("pets/createOrUpdatePetForm"));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testInitUpdateForm() throws Exception {
|
void testInitUpdateForm() throws Exception {
|
||||||
mockMvc.perform(get("/owners/{ownerId}/pets/{petId}/edit", TEST_OWNER_ID, TEST_PET_ID))
|
mockMvc.perform(get("/owners/{ownerId}/pets/{petId}/edit", TEST_OWNER_ID, TEST_PET_ID))
|
||||||
|
@ -108,6 +164,8 @@ class PetControllerTests {
|
||||||
.andExpect(view().name("pets/createOrUpdatePetForm"));
|
.andExpect(view().name("pets/createOrUpdatePetForm"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testProcessUpdateFormSuccess() throws Exception {
|
void testProcessUpdateFormSuccess() throws Exception {
|
||||||
mockMvc
|
mockMvc
|
||||||
|
@ -118,15 +176,33 @@ class PetControllerTests {
|
||||||
.andExpect(view().name("redirect:/owners/{ownerId}"));
|
.andExpect(view().name("redirect:/owners/{ownerId}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class ProcessUpdateFormHasErrors {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testProcessUpdateFormHasErrors() throws Exception {
|
void testProcessUpdateFormWithInvalidBirthDate() throws Exception {
|
||||||
mockMvc
|
mockMvc
|
||||||
.perform(post("/owners/{ownerId}/pets/{petId}/edit", TEST_OWNER_ID, TEST_PET_ID).param("name", "Betty")
|
.perform(post("/owners/{ownerId}/pets/{petId}/edit", TEST_OWNER_ID, TEST_PET_ID).param("name", " ")
|
||||||
.param("birthDate", "2015/02/12"))
|
.param("birthDate", "2015/02/12"))
|
||||||
.andExpect(model().attributeHasNoErrors("owner"))
|
.andExpect(model().attributeHasNoErrors("owner"))
|
||||||
.andExpect(model().attributeHasErrors("pet"))
|
.andExpect(model().attributeHasErrors("pet"))
|
||||||
.andExpect(status().isOk())
|
.andExpect(model().attributeHasFieldErrors("pet", "birthDate"))
|
||||||
|
.andExpect(model().attributeHasFieldErrorCode("pet", "birthDate", "typeMismatch"))
|
||||||
.andExpect(view().name("pets/createOrUpdatePetForm"));
|
.andExpect(view().name("pets/createOrUpdatePetForm"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testProcessUpdateFormWithBlankName() throws Exception {
|
||||||
|
mockMvc
|
||||||
|
.perform(post("/owners/{ownerId}/pets/{petId}/edit", TEST_OWNER_ID, TEST_PET_ID).param("name", " ")
|
||||||
|
.param("birthDate", "2015-02-12"))
|
||||||
|
.andExpect(model().attributeHasNoErrors("owner"))
|
||||||
|
.andExpect(model().attributeHasErrors("pet"))
|
||||||
|
.andExpect(model().attributeHasFieldErrors("pet", "name"))
|
||||||
|
.andExpect(model().attributeHasFieldErrorCode("pet", "name", "required"))
|
||||||
|
.andExpect(view().name("pets/createOrUpdatePetForm"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,7 +68,7 @@ class PetTypeFormatterTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldThrowParseException() throws ParseException {
|
void shouldThrowParseException() {
|
||||||
given(this.pets.findPetTypes()).willReturn(makePetTypes());
|
given(this.pets.findPetTypes()).willReturn(makePetTypes());
|
||||||
Assertions.assertThrows(ParseException.class, () -> {
|
Assertions.assertThrows(ParseException.class, () -> {
|
||||||
petTypeFormatter.parse("Fish", Locale.ENGLISH);
|
petTypeFormatter.parse("Fish", Locale.ENGLISH);
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2012-2024 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.samples.petclinic.owner;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Nested;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.condition.DisabledInNativeImage;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import org.springframework.validation.Errors;
|
||||||
|
import org.springframework.validation.MapBindingResult;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test class for {@link PetValidator}
|
||||||
|
*
|
||||||
|
* @author Wick Dynex
|
||||||
|
*/
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
@DisabledInNativeImage
|
||||||
|
public class PetValidatorTests {
|
||||||
|
|
||||||
|
private PetValidator petValidator;
|
||||||
|
|
||||||
|
private Pet pet;
|
||||||
|
|
||||||
|
private PetType petType;
|
||||||
|
|
||||||
|
private Errors errors;
|
||||||
|
|
||||||
|
private static final String petName = "Buddy";
|
||||||
|
|
||||||
|
private static final String petTypeName = "Dog";
|
||||||
|
|
||||||
|
private static final LocalDate petBirthDate = LocalDate.of(1990, 1, 1);
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
petValidator = new PetValidator();
|
||||||
|
pet = new Pet();
|
||||||
|
petType = new PetType();
|
||||||
|
errors = new MapBindingResult(new HashMap<>(), "pet");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testValidate() {
|
||||||
|
petType.setName(petTypeName);
|
||||||
|
pet.setName(petName);
|
||||||
|
pet.setType(petType);
|
||||||
|
pet.setBirthDate(petBirthDate);
|
||||||
|
|
||||||
|
petValidator.validate(pet, errors);
|
||||||
|
|
||||||
|
assertFalse(errors.hasErrors());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class ValidateHasErrors {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testValidateWithInvalidPetName() {
|
||||||
|
petType.setName(petTypeName);
|
||||||
|
pet.setName("");
|
||||||
|
pet.setType(petType);
|
||||||
|
pet.setBirthDate(petBirthDate);
|
||||||
|
|
||||||
|
petValidator.validate(pet, errors);
|
||||||
|
|
||||||
|
assertTrue(errors.hasFieldErrors("name"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testValidateWithInvalidPetType() {
|
||||||
|
pet.setName(petName);
|
||||||
|
pet.setType(null);
|
||||||
|
pet.setBirthDate(petBirthDate);
|
||||||
|
|
||||||
|
petValidator.validate(pet, errors);
|
||||||
|
|
||||||
|
assertTrue(errors.hasFieldErrors("type"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testValidateWithInvalidBirthDate() {
|
||||||
|
petType.setName(petTypeName);
|
||||||
|
pet.setName(petName);
|
||||||
|
pet.setType(petType);
|
||||||
|
pet.setBirthDate(null);
|
||||||
|
|
||||||
|
petValidator.validate(pet, errors);
|
||||||
|
|
||||||
|
assertTrue(errors.hasFieldErrors("birthDate"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -28,14 +28,17 @@ import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.condition.DisabledInNativeImage;
|
import org.junit.jupiter.api.condition.DisabledInNativeImage;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
|
||||||
import org.springframework.test.context.aot.DisabledInAotMode;
|
import org.springframework.test.context.aot.DisabledInAotMode;
|
||||||
|
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test class for {@link VisitController}
|
* Test class for {@link VisitController}
|
||||||
*
|
*
|
||||||
* @author Colin But
|
* @author Colin But
|
||||||
|
* @author Wick Dynex
|
||||||
*/
|
*/
|
||||||
@WebMvcTest(VisitController.class)
|
@WebMvcTest(VisitController.class)
|
||||||
@DisabledInNativeImage
|
@DisabledInNativeImage
|
||||||
|
@ -49,7 +52,7 @@ class VisitControllerTests {
|
||||||
@Autowired
|
@Autowired
|
||||||
private MockMvc mockMvc;
|
private MockMvc mockMvc;
|
||||||
|
|
||||||
@MockBean
|
@MockitoBean
|
||||||
private OwnerRepository owners;
|
private OwnerRepository owners;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
|
@ -58,7 +61,7 @@ class VisitControllerTests {
|
||||||
Pet pet = new Pet();
|
Pet pet = new Pet();
|
||||||
owner.addPet(pet);
|
owner.addPet(pet);
|
||||||
pet.setId(TEST_PET_ID);
|
pet.setId(TEST_PET_ID);
|
||||||
given(this.owners.findById(TEST_OWNER_ID)).willReturn(owner);
|
given(this.owners.findById(TEST_OWNER_ID)).willReturn(Optional.of(owner));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -20,13 +20,13 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
|
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
|
||||||
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace;
|
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace;
|
||||||
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
|
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
|
||||||
import org.springframework.context.annotation.ComponentScan;
|
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.samples.petclinic.owner.Owner;
|
import org.springframework.samples.petclinic.owner.Owner;
|
||||||
|
@ -36,7 +36,6 @@ import org.springframework.samples.petclinic.owner.PetType;
|
||||||
import org.springframework.samples.petclinic.owner.Visit;
|
import org.springframework.samples.petclinic.owner.Visit;
|
||||||
import org.springframework.samples.petclinic.vet.Vet;
|
import org.springframework.samples.petclinic.vet.Vet;
|
||||||
import org.springframework.samples.petclinic.vet.VetRepository;
|
import org.springframework.samples.petclinic.vet.VetRepository;
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -67,7 +66,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||||
* @author Michael Isvy
|
* @author Michael Isvy
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
*/
|
*/
|
||||||
@DataJpaTest(includeFilters = @ComponentScan.Filter(Service.class))
|
@DataJpaTest
|
||||||
// Ensure that if the mysql profile is active we connect to the real database:
|
// Ensure that if the mysql profile is active we connect to the real database:
|
||||||
@AutoConfigureTestDatabase(replace = Replace.NONE)
|
@AutoConfigureTestDatabase(replace = Replace.NONE)
|
||||||
// @TestPropertySource("/application-postgres.properties")
|
// @TestPropertySource("/application-postgres.properties")
|
||||||
|
@ -83,16 +82,18 @@ class ClinicServiceTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldFindOwnersByLastName() {
|
void shouldFindOwnersByLastName() {
|
||||||
Page<Owner> owners = this.owners.findByLastName("Davis", pageable);
|
Page<Owner> owners = this.owners.findByLastNameStartingWith("Davis", pageable);
|
||||||
assertThat(owners).hasSize(2);
|
assertThat(owners).hasSize(2);
|
||||||
|
|
||||||
owners = this.owners.findByLastName("Daviss", pageable);
|
owners = this.owners.findByLastNameStartingWith("Daviss", pageable);
|
||||||
assertThat(owners).isEmpty();
|
assertThat(owners).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldFindSingleOwnerWithPet() {
|
void shouldFindSingleOwnerWithPet() {
|
||||||
Owner owner = this.owners.findById(1);
|
Optional<Owner> optionalOwner = this.owners.findById(1);
|
||||||
|
assertThat(optionalOwner).isPresent();
|
||||||
|
Owner owner = optionalOwner.get();
|
||||||
assertThat(owner.getLastName()).startsWith("Franklin");
|
assertThat(owner.getLastName()).startsWith("Franklin");
|
||||||
assertThat(owner.getPets()).hasSize(1);
|
assertThat(owner.getPets()).hasSize(1);
|
||||||
assertThat(owner.getPets().get(0).getType()).isNotNull();
|
assertThat(owner.getPets().get(0).getType()).isNotNull();
|
||||||
|
@ -102,7 +103,7 @@ class ClinicServiceTests {
|
||||||
@Test
|
@Test
|
||||||
@Transactional
|
@Transactional
|
||||||
void shouldInsertOwner() {
|
void shouldInsertOwner() {
|
||||||
Page<Owner> owners = this.owners.findByLastName("Schultz", pageable);
|
Page<Owner> owners = this.owners.findByLastNameStartingWith("Schultz", pageable);
|
||||||
int found = (int) owners.getTotalElements();
|
int found = (int) owners.getTotalElements();
|
||||||
|
|
||||||
Owner owner = new Owner();
|
Owner owner = new Owner();
|
||||||
|
@ -112,16 +113,18 @@ class ClinicServiceTests {
|
||||||
owner.setCity("Wollongong");
|
owner.setCity("Wollongong");
|
||||||
owner.setTelephone("4444444444");
|
owner.setTelephone("4444444444");
|
||||||
this.owners.save(owner);
|
this.owners.save(owner);
|
||||||
assertThat(owner.getId().longValue()).isNotEqualTo(0);
|
assertThat(owner.getId()).isNotZero();
|
||||||
|
|
||||||
owners = this.owners.findByLastName("Schultz", pageable);
|
owners = this.owners.findByLastNameStartingWith("Schultz", pageable);
|
||||||
assertThat(owners.getTotalElements()).isEqualTo(found + 1);
|
assertThat(owners.getTotalElements()).isEqualTo(found + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Transactional
|
@Transactional
|
||||||
void shouldUpdateOwner() {
|
void shouldUpdateOwner() {
|
||||||
Owner owner = this.owners.findById(1);
|
Optional<Owner> optionalOwner = this.owners.findById(1);
|
||||||
|
assertThat(optionalOwner).isPresent();
|
||||||
|
Owner owner = optionalOwner.get();
|
||||||
String oldLastName = owner.getLastName();
|
String oldLastName = owner.getLastName();
|
||||||
String newLastName = oldLastName + "X";
|
String newLastName = oldLastName + "X";
|
||||||
|
|
||||||
|
@ -129,7 +132,9 @@ class ClinicServiceTests {
|
||||||
this.owners.save(owner);
|
this.owners.save(owner);
|
||||||
|
|
||||||
// retrieving new name from database
|
// retrieving new name from database
|
||||||
owner = this.owners.findById(1);
|
optionalOwner = this.owners.findById(1);
|
||||||
|
assertThat(optionalOwner).isPresent();
|
||||||
|
owner = optionalOwner.get();
|
||||||
assertThat(owner.getLastName()).isEqualTo(newLastName);
|
assertThat(owner.getLastName()).isEqualTo(newLastName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,7 +151,10 @@ class ClinicServiceTests {
|
||||||
@Test
|
@Test
|
||||||
@Transactional
|
@Transactional
|
||||||
void shouldInsertPetIntoDatabaseAndGenerateId() {
|
void shouldInsertPetIntoDatabaseAndGenerateId() {
|
||||||
Owner owner6 = this.owners.findById(6);
|
Optional<Owner> optionalOwner = this.owners.findById(6);
|
||||||
|
assertThat(optionalOwner).isPresent();
|
||||||
|
Owner owner6 = optionalOwner.get();
|
||||||
|
|
||||||
int found = owner6.getPets().size();
|
int found = owner6.getPets().size();
|
||||||
|
|
||||||
Pet pet = new Pet();
|
Pet pet = new Pet();
|
||||||
|
@ -155,12 +163,14 @@ class ClinicServiceTests {
|
||||||
pet.setType(EntityUtils.getById(types, PetType.class, 2));
|
pet.setType(EntityUtils.getById(types, PetType.class, 2));
|
||||||
pet.setBirthDate(LocalDate.now());
|
pet.setBirthDate(LocalDate.now());
|
||||||
owner6.addPet(pet);
|
owner6.addPet(pet);
|
||||||
assertThat(owner6.getPets().size()).isEqualTo(found + 1);
|
assertThat(owner6.getPets()).hasSize(found + 1);
|
||||||
|
|
||||||
this.owners.save(owner6);
|
this.owners.save(owner6);
|
||||||
|
|
||||||
owner6 = this.owners.findById(6);
|
optionalOwner = this.owners.findById(6);
|
||||||
assertThat(owner6.getPets().size()).isEqualTo(found + 1);
|
assertThat(optionalOwner).isPresent();
|
||||||
|
owner6 = optionalOwner.get();
|
||||||
|
assertThat(owner6.getPets()).hasSize(found + 1);
|
||||||
// checks that id has been generated
|
// checks that id has been generated
|
||||||
pet = owner6.getPet("bowser");
|
pet = owner6.getPet("bowser");
|
||||||
assertThat(pet.getId()).isNotNull();
|
assertThat(pet.getId()).isNotNull();
|
||||||
|
@ -168,8 +178,11 @@ class ClinicServiceTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Transactional
|
@Transactional
|
||||||
void shouldUpdatePetName() throws Exception {
|
void shouldUpdatePetName() {
|
||||||
Owner owner6 = this.owners.findById(6);
|
Optional<Owner> optionalOwner = this.owners.findById(6);
|
||||||
|
assertThat(optionalOwner).isPresent();
|
||||||
|
Owner owner6 = optionalOwner.get();
|
||||||
|
|
||||||
Pet pet7 = owner6.getPet(7);
|
Pet pet7 = owner6.getPet(7);
|
||||||
String oldName = pet7.getName();
|
String oldName = pet7.getName();
|
||||||
|
|
||||||
|
@ -177,7 +190,9 @@ class ClinicServiceTests {
|
||||||
pet7.setName(newName);
|
pet7.setName(newName);
|
||||||
this.owners.save(owner6);
|
this.owners.save(owner6);
|
||||||
|
|
||||||
owner6 = this.owners.findById(6);
|
optionalOwner = this.owners.findById(6);
|
||||||
|
assertThat(optionalOwner).isPresent();
|
||||||
|
owner6 = optionalOwner.get();
|
||||||
pet7 = owner6.getPet(7);
|
pet7 = owner6.getPet(7);
|
||||||
assertThat(pet7.getName()).isEqualTo(newName);
|
assertThat(pet7.getName()).isEqualTo(newName);
|
||||||
}
|
}
|
||||||
|
@ -196,7 +211,10 @@ class ClinicServiceTests {
|
||||||
@Test
|
@Test
|
||||||
@Transactional
|
@Transactional
|
||||||
void shouldAddNewVisitForPet() {
|
void shouldAddNewVisitForPet() {
|
||||||
Owner owner6 = this.owners.findById(6);
|
Optional<Owner> optionalOwner = this.owners.findById(6);
|
||||||
|
assertThat(optionalOwner).isPresent();
|
||||||
|
Owner owner6 = optionalOwner.get();
|
||||||
|
|
||||||
Pet pet7 = owner6.getPet(7);
|
Pet pet7 = owner6.getPet(7);
|
||||||
int found = pet7.getVisits().size();
|
int found = pet7.getVisits().size();
|
||||||
Visit visit = new Visit();
|
Visit visit = new Visit();
|
||||||
|
@ -205,16 +223,17 @@ class ClinicServiceTests {
|
||||||
owner6.addVisit(pet7.getId(), visit);
|
owner6.addVisit(pet7.getId(), visit);
|
||||||
this.owners.save(owner6);
|
this.owners.save(owner6);
|
||||||
|
|
||||||
owner6 = this.owners.findById(6);
|
|
||||||
|
|
||||||
assertThat(pet7.getVisits()) //
|
assertThat(pet7.getVisits()) //
|
||||||
.hasSize(found + 1) //
|
.hasSize(found + 1) //
|
||||||
.allMatch(value -> value.getId() != null);
|
.allMatch(value -> value.getId() != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldFindVisitsByPetId() throws Exception {
|
void shouldFindVisitsByPetId() {
|
||||||
Owner owner6 = this.owners.findById(6);
|
Optional<Owner> optionalOwner = this.owners.findById(6);
|
||||||
|
assertThat(optionalOwner).isPresent();
|
||||||
|
Owner owner6 = optionalOwner.get();
|
||||||
|
|
||||||
Pet pet7 = owner6.getPet(7);
|
Pet pet7 = owner6.getPet(7);
|
||||||
Collection<Visit> visits = pet7.getVisits();
|
Collection<Visit> visits = pet7.getVisits();
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@ import org.springframework.core.ParameterizedTypeReference;
|
||||||
import org.springframework.http.HttpEntity;
|
import org.springframework.http.HttpEntity;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.RequestEntity;
|
import org.springframework.http.RequestEntity;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
|
@ -68,10 +69,10 @@ class CrashControllerIntegrationTests {
|
||||||
new ParameterizedTypeReference<Map<String, Object>>() {
|
new ParameterizedTypeReference<Map<String, Object>>() {
|
||||||
});
|
});
|
||||||
assertThat(resp).isNotNull();
|
assertThat(resp).isNotNull();
|
||||||
assertThat(resp.getStatusCode().is5xxServerError());
|
assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
assertThat(resp.getBody().containsKey("timestamp"));
|
assertThat(resp.getBody()).containsKey("timestamp");
|
||||||
assertThat(resp.getBody().containsKey("status"));
|
assertThat(resp.getBody()).containsKey("status");
|
||||||
assertThat(resp.getBody().containsKey("error"));
|
assertThat(resp.getBody()).containsKey("error");
|
||||||
assertThat(resp.getBody()).containsEntry("message",
|
assertThat(resp.getBody()).containsEntry("message",
|
||||||
"Expected: controller used to showcase what happens when an exception is thrown");
|
"Expected: controller used to showcase what happens when an exception is thrown");
|
||||||
assertThat(resp.getBody()).containsEntry("path", "/oups");
|
assertThat(resp.getBody()).containsEntry("path", "/oups");
|
||||||
|
@ -84,7 +85,7 @@ class CrashControllerIntegrationTests {
|
||||||
ResponseEntity<String> resp = rest.exchange("http://localhost:" + port + "/oups", HttpMethod.GET,
|
ResponseEntity<String> resp = rest.exchange("http://localhost:" + port + "/oups", HttpMethod.GET,
|
||||||
new HttpEntity<>(headers), String.class);
|
new HttpEntity<>(headers), String.class);
|
||||||
assertThat(resp).isNotNull();
|
assertThat(resp).isNotNull();
|
||||||
assertThat(resp.getStatusCode().is5xxServerError());
|
assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
assertThat(resp.getBody()).isNotNull();
|
assertThat(resp.getBody()).isNotNull();
|
||||||
// html:
|
// html:
|
||||||
assertThat(resp.getBody()).containsSubsequence("<body>", "<h2>", "Something happened...", "</h2>", "<p>",
|
assertThat(resp.getBody()).containsSubsequence("<body>", "<h2>", "Something happened...", "</h2>", "<p>",
|
||||||
|
|
|
@ -16,11 +16,10 @@
|
||||||
|
|
||||||
package org.springframework.samples.petclinic.system;
|
package org.springframework.samples.petclinic.system;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test class for {@link CrashController}
|
* Test class for {@link CrashController}
|
||||||
*
|
*
|
||||||
|
@ -31,16 +30,12 @@ import org.junit.jupiter.api.Test;
|
||||||
// luck ((plain(st) UNIT test)! :)
|
// luck ((plain(st) UNIT test)! :)
|
||||||
class CrashControllerTests {
|
class CrashControllerTests {
|
||||||
|
|
||||||
CrashController testee = new CrashController();
|
final CrashController testee = new CrashController();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testTriggerException() throws Exception {
|
void testTriggerException() {
|
||||||
RuntimeException thrown = assertThrows(RuntimeException.class, () -> {
|
assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> testee.triggerException())
|
||||||
testee.triggerException();
|
.withMessageContaining("Expected: controller used to showcase what happens when an exception is thrown");
|
||||||
});
|
|
||||||
|
|
||||||
assertEquals("Expected: controller used to showcase what happens when an exception is thrown",
|
|
||||||
thrown.getMessage());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,11 +22,11 @@ import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.condition.DisabledInNativeImage;
|
import org.junit.jupiter.api.condition.DisabledInNativeImage;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
|
||||||
import org.springframework.data.domain.PageImpl;
|
import org.springframework.data.domain.PageImpl;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.test.context.aot.DisabledInAotMode;
|
import org.springframework.test.context.aot.DisabledInAotMode;
|
||||||
|
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
import org.springframework.test.web.servlet.ResultActions;
|
import org.springframework.test.web.servlet.ResultActions;
|
||||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
|
@ -48,7 +48,7 @@ class VetControllerTests {
|
||||||
@Autowired
|
@Autowired
|
||||||
private MockMvc mockMvc;
|
private MockMvc mockMvc;
|
||||||
|
|
||||||
@MockBean
|
@MockitoBean
|
||||||
private VetRepository vets;
|
private VetRepository vets;
|
||||||
|
|
||||||
private Vet james() {
|
private Vet james() {
|
||||||
|
|
|
@ -156,8 +156,7 @@
|
||||||
<stringProp name="HTTPSampler.response_timeout"></stringProp>
|
<stringProp name="HTTPSampler.response_timeout"></stringProp>
|
||||||
<stringProp name="HTTPSampler.protocol"></stringProp>
|
<stringProp name="HTTPSampler.protocol"></stringProp>
|
||||||
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
|
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
|
||||||
<stringProp name="HTTPSampler.path">
|
<stringProp name="HTTPSampler.path">${CONTEXT_WEB}/webjars/bootstrap/dist/js/bootstrap.bundle.min.js</stringProp>
|
||||||
${CONTEXT_WEB}/webjars/bootstrap/5.3.2/dist/js/bootstrap.bundle.min.js</stringProp>
|
|
||||||
<stringProp name="HTTPSampler.method">GET</stringProp>
|
<stringProp name="HTTPSampler.method">GET</stringProp>
|
||||||
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
|
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
|
||||||
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
|
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
|
||||||
|
@ -420,8 +419,7 @@
|
||||||
<stringProp name="HTTPSampler.port"></stringProp>
|
<stringProp name="HTTPSampler.port"></stringProp>
|
||||||
<stringProp name="HTTPSampler.protocol"></stringProp>
|
<stringProp name="HTTPSampler.protocol"></stringProp>
|
||||||
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
|
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
|
||||||
<stringProp name="HTTPSampler.path">
|
<stringProp name="HTTPSampler.path">${CONTEXT_WEB}/owners/${count}/pets/${petCount}/visits/new</stringProp>
|
||||||
${CONTEXT_WEB}/owners/${count}/pets/${petCount}/visits/new</stringProp>
|
|
||||||
<stringProp name="HTTPSampler.method">GET</stringProp>
|
<stringProp name="HTTPSampler.method">GET</stringProp>
|
||||||
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
|
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
|
||||||
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
|
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
|
||||||
|
@ -458,8 +456,7 @@
|
||||||
<stringProp name="HTTPSampler.port"></stringProp>
|
<stringProp name="HTTPSampler.port"></stringProp>
|
||||||
<stringProp name="HTTPSampler.protocol"></stringProp>
|
<stringProp name="HTTPSampler.protocol"></stringProp>
|
||||||
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
|
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
|
||||||
<stringProp name="HTTPSampler.path">
|
<stringProp name="HTTPSampler.path">${CONTEXT_WEB}/owners/${count}/pets/${petCount}/visits/new</stringProp>
|
||||||
${CONTEXT_WEB}/owners/${count}/pets/${petCount}/visits/new</stringProp>
|
|
||||||
<stringProp name="HTTPSampler.method">POST</stringProp>
|
<stringProp name="HTTPSampler.method">POST</stringProp>
|
||||||
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
|
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
|
||||||
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
|
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
|
||||||
|
|
Loading…
Reference in a new issue