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
|
||||
FROM mcr.microsoft.com/vscode/devcontainers/java:0-${VARIANT}
|
||||
|
||||
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
|
||||
|
||||
ARG USER=vscode
|
||||
VOLUME /home/$USER/.m2
|
||||
VOLUME /home/$USER/.gradle
|
||||
|
||||
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 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",
|
||||
"dockerFile": "Dockerfile",
|
||||
"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": {
|
||||
"docker-in-docker": "latest"
|
||||
},
|
||||
"extensions": [
|
||||
"vscjava.vscode-java-pack",
|
||||
"redhat.vscode-xml",
|
||||
"vmware.vscode-boot-dev-pack",
|
||||
"mhutchie.git-graph"
|
||||
],
|
||||
"forwardPorts": [8080],
|
||||
"settings": {
|
||||
"java.import.gradle.enabled": false,
|
||||
"java.server.launchMode": "Standard"
|
||||
}
|
||||
"name": "Java",
|
||||
"image": "mcr.microsoft.com/devcontainers/base:ubuntu",
|
||||
"features": {
|
||||
"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": [
|
||||
"redhat.vscode-xml",
|
||||
"visualstudioexptteam.vscodeintellicode",
|
||||
"vscjava.vscode-java-pack"
|
||||
]
|
||||
}
|
||||
},
|
||||
"remoteUser": "vscode"
|
||||
}
|
||||
|
|
|
@ -19,3 +19,6 @@ indent_style = space
|
|||
|
||||
[*.{html,sql,less}]
|
||||
indent_size = 2
|
||||
|
||||
[*.gradle]
|
||||
indent_size = 2
|
||||
|
|
7
.gitattributes
vendored
7
.gitattributes
vendored
|
@ -1,2 +1,5 @@
|
|||
mvnw text eol=lf
|
||||
*.java text eol=lf
|
||||
mvnw 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
|
||||
# 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
|
||||
|
||||
|
@ -18,12 +18,12 @@ jobs:
|
|||
java: [ '17' ]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up JDK ${{matrix.java}}
|
||||
uses: actions/setup-java@v2
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: ${{matrix.java}}
|
||||
distribution: 'adopt'
|
||||
cache: maven
|
||||
- name: Build with Maven Wrapper
|
||||
run: ./mvnw -B package
|
||||
run: ./mvnw -B verify
|
||||
|
|
56
.gitignore
vendored
56
.gitignore
vendored
|
@ -1,17 +1,51 @@
|
|||
target/*
|
||||
bin/*
|
||||
build/*
|
||||
.gradle/*
|
||||
.settings/*
|
||||
.classpath
|
||||
.project
|
||||
.factorypath
|
||||
HELP.md
|
||||
pom.xml.bak
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
.gradle
|
||||
build/
|
||||
!gradle/wrapper/gradle-wrapper.jar
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### STS ###
|
||||
.attach_pid*
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
bin/
|
||||
!**/src/main/**/bin/
|
||||
!**/src/test/**/bin/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
/target
|
||||
.sts4-cache/
|
||||
.vscode
|
||||
*.ipr
|
||||
out/
|
||||
!**/src/main/**/out/
|
||||
!**/src/test/**/out/
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
### SDK Man ###
|
||||
.sdkmanrc
|
||||
|
||||
### CSS ###
|
||||
_site/
|
||||
*.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
|
||||
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# 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)
|
||||
|
||||
|
@ -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`,
|
||||
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:
|
||||
|
||||
```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
|
||||
|
||||
```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)
|
||||
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
|
||||
docker-compose --profile mysql up
|
||||
docker compose up mysql
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```bash
|
||||
docker-compose --profile postgres up
|
||||
docker compose up postgres
|
||||
```
|
||||
|
||||
## Test Applications
|
57
build.gradle
57
build.gradle
|
@ -1,32 +1,50 @@
|
|||
plugins {
|
||||
id 'java'
|
||||
id 'org.springframework.boot' version '3.2.0'
|
||||
id 'io.spring.dependency-management' version '1.1.4'
|
||||
id 'org.graalvm.buildtools.native' version '0.9.28'
|
||||
id 'org.springframework.boot' version '3.4.0'
|
||||
id 'io.spring.dependency-management' version '1.1.6'
|
||||
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: 'checkstyle'
|
||||
apply plugin: 'io.spring.javaformat'
|
||||
|
||||
gradle.startParameter.excludedTaskNames += [ "checkFormatAot", "checkFormatAotTest" ]
|
||||
|
||||
group = 'org.springframework.samples'
|
||||
version = '3.2.0'
|
||||
sourceCompatibility = '17'
|
||||
version = '3.4.0'
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
ext.checkstyleVersion = "10.20.1"
|
||||
ext.springJavaformatCheckstyleVersion = "0.0.43"
|
||||
ext.webjarsLocatorLiteVersion = "1.0.1"
|
||||
ext.webjarsFontawesomeVersion = "4.7.0"
|
||||
ext.webjarsBootstrapVersion = "5.3.2"
|
||||
ext.webjarsBootstrapVersion = "5.3.3"
|
||||
|
||||
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-data-jpa'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-validation'
|
||||
implementation 'io.micrometer:micrometer-registry-prometheus'
|
||||
implementation 'javax.cache:cache-api'
|
||||
implementation 'jakarta.xml.bind:jakarta.xml.bind-api'
|
||||
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:font-awesome:${webjarsFontawesomeVersion}"
|
||||
runtimeOnly 'com.github.ben-manes.caffeine:caffeine'
|
||||
|
@ -39,8 +57,35 @@ dependencies {
|
|||
testImplementation 'org.springframework.boot:spring-boot-docker-compose'
|
||||
testImplementation 'org.testcontainers:junit-jupiter'
|
||||
testImplementation 'org.testcontainers:mysql'
|
||||
checkstyle "io.spring.javaformat:spring-javaformat-checkstyle:${springJavaformatCheckstyleVersion}"
|
||||
checkstyle "com.puppycrawl.tools:checkstyle:${checkstyleVersion}"
|
||||
}
|
||||
|
||||
tasks.named('test') {
|
||||
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:
|
||||
mysql:
|
||||
image: mysql:8.2
|
||||
image: mysql:9.1
|
||||
ports:
|
||||
- "3306:3306"
|
||||
environment:
|
||||
|
@ -13,15 +11,11 @@ services:
|
|||
- MYSQL_DATABASE=petclinic
|
||||
volumes:
|
||||
- "./conf.d:/etc/mysql/conf.d:ro"
|
||||
profiles:
|
||||
- mysql
|
||||
postgres:
|
||||
image: postgres:16.1
|
||||
image: postgres:17.0
|
||||
ports:
|
||||
- "5432:5432"
|
||||
environment:
|
||||
- POSTGRES_PASSWORD=petclinic
|
||||
- POSTGRES_USER=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
|
||||
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
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
|
7
gradlew
vendored
7
gradlew
vendored
|
@ -15,6 +15,8 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
|
@ -55,7 +57,7 @@
|
|||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (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.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
|
@ -84,7 +86,8 @@ done
|
|||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# 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.
|
||||
MAX_FD=maximum
|
||||
|
|
186
gradlew.bat
vendored
186
gradlew.bat
vendored
|
@ -1,92 +1,94 @@
|
|||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
|
|
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
|
451
mvnw
vendored
451
mvnw
vendored
|
@ -19,290 +19,241 @@
|
|||
# ----------------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Apache Maven Wrapper startup batch script, version 3.2.0
|
||||
#
|
||||
# Required ENV vars:
|
||||
# ------------------
|
||||
# JAVA_HOME - location of a JDK home dir
|
||||
# Apache Maven Wrapper startup batch script, version 3.3.2
|
||||
#
|
||||
# Optional ENV vars
|
||||
# -----------------
|
||||
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||
# e.g. to debug Maven itself, use
|
||||
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||
# JAVA_HOME - location of a JDK home dir, required when download maven via java source
|
||||
# MVNW_REPOURL - repo url base for downloading maven distribution
|
||||
# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
|
||||
# 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
|
||||
. /usr/local/etc/mavenrc
|
||||
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
|
||||
# OS specific support.
|
||||
native_path() { printf %s\\n "$1"; }
|
||||
case "$(uname)" in
|
||||
CYGWIN*) cygwin=true ;;
|
||||
MINGW*) mingw=true;;
|
||||
Darwin*) darwin=true
|
||||
# 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
|
||||
;;
|
||||
CYGWIN* | MINGW*)
|
||||
[ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
|
||||
native_path() { cygpath --path --windows "$1"; }
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
if [ -r /etc/gentoo-release ] ; then
|
||||
JAVA_HOME=$(java-config --jre-home)
|
||||
fi
|
||||
fi
|
||||
|
||||
# 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
|
||||
# set JAVACMD and JAVACCMD
|
||||
set_java_home() {
|
||||
# For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
|
||||
if [ -n "${JAVA_HOME-}" ]; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ]; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
JAVACCMD="$JAVA_HOME/jre/sh/javac"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
JAVACCMD="$JAVA_HOME/bin/javac"
|
||||
|
||||
if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
|
||||
echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
|
||||
echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
else
|
||||
JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)"
|
||||
fi
|
||||
fi
|
||||
JAVACMD="$(
|
||||
'set' +e
|
||||
'unset' -f command 2>/dev/null
|
||||
'command' -v java
|
||||
)" || :
|
||||
JAVACCMD="$(
|
||||
'set' +e
|
||||
'unset' -f command 2>/dev/null
|
||||
'command' -v javac
|
||||
)" || :
|
||||
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
echo "Error: JAVA_HOME is not defined correctly." >&2
|
||||
echo " We cannot execute $JAVACMD" >&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
|
||||
fi
|
||||
|
||||
basedir="$1"
|
||||
wdir="$1"
|
||||
while [ "$wdir" != '/' ] ; do
|
||||
if [ -d "$wdir"/.mvn ] ; then
|
||||
basedir=$wdir
|
||||
break
|
||||
if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
|
||||
echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
|
||||
return 1
|
||||
fi
|
||||
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
|
||||
if [ -d "${wdir}" ]; then
|
||||
wdir=$(cd "$wdir/.." || exit 1; pwd)
|
||||
fi
|
||||
# end of workaround
|
||||
fi
|
||||
}
|
||||
|
||||
# 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
|
||||
printf '%s' "$(cd "$basedir" || exit 1; pwd)"
|
||||
printf %x\\n $h
|
||||
}
|
||||
|
||||
# concatenates all lines of a file
|
||||
concat_lines() {
|
||||
if [ -f "$1" ]; then
|
||||
# Remove \r in case we run on Windows within Git Bash
|
||||
# and check out the repository with auto CRLF management
|
||||
# enabled. Otherwise, we may read lines that are delimited with
|
||||
# \r\n and produce $'-Xarg\r' rather than -Xarg due to word
|
||||
# splitting rules.
|
||||
tr -s '\r\n' ' ' < "$1"
|
||||
fi
|
||||
verbose() { :; }
|
||||
[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
|
||||
|
||||
die() {
|
||||
printf %s\\n "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
log() {
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
printf '%s\n' "$1"
|
||||
fi
|
||||
trim() {
|
||||
# MWRAPPER-139:
|
||||
# Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
|
||||
# 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")")
|
||||
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=""
|
||||
# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
|
||||
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
|
||||
done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
|
||||
if [ -n "$wrapperSha256Sum" ]; then
|
||||
wrapperSha256Result=false
|
||||
if command -v sha256sum > /dev/null; then
|
||||
if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then
|
||||
wrapperSha256Result=true
|
||||
done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties"
|
||||
[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties"
|
||||
|
||||
case "${distributionUrl##*/}" in
|
||||
maven-mvnd-*bin.*)
|
||||
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
|
||||
elif command -v shasum > /dev/null; then
|
||||
if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then
|
||||
wrapperSha256Result=true
|
||||
elif command -v shasum >/dev/null; then
|
||||
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
|
||||
distributionSha256Result=true
|
||||
fi
|
||||
else
|
||||
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available."
|
||||
echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties."
|
||||
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
|
||||
echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ $wrapperSha256Result = false ]; then
|
||||
echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper 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 wrapperSha256Sum property." >&2
|
||||
if [ $distributionSha256Result = false ]; then
|
||||
echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
|
||||
echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin; then
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME")
|
||||
[ -n "$CLASSPATH" ] &&
|
||||
CLASSPATH=$(cygpath --path --windows "$CLASSPATH")
|
||||
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
|
||||
MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR")
|
||||
# unzip and move
|
||||
if command -v unzip >/dev/null; then
|
||||
unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
|
||||
else
|
||||
tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
|
||||
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
|
||||
# work with both Windows and non-Windows executions.
|
||||
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 "$@"
|
||||
clean || :
|
||||
exec_maven "$@"
|
||||
|
|
282
mvnw.cmd
vendored
282
mvnw.cmd
vendored
|
@ -1,3 +1,4 @@
|
|||
<# : batch portion
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||
@REM or more contributor license agreements. See the NOTICE file
|
||||
|
@ -18,188 +19,131 @@
|
|||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Apache Maven Wrapper startup batch script, version 3.2.0
|
||||
@REM
|
||||
@REM Required ENV vars:
|
||||
@REM JAVA_HOME - location of a JDK home dir
|
||||
@REM Apache Maven Wrapper startup batch script, version 3.3.2
|
||||
@REM
|
||||
@REM Optional ENV vars
|
||||
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
|
||||
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
|
||||
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||
@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 MVNW_REPOURL - repo url base for downloading maven distribution
|
||||
@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
|
||||
@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
|
||||
@echo off
|
||||
@REM set title of command window
|
||||
title %0
|
||||
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
|
||||
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
|
||||
|
||||
@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
|
||||
@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
|
||||
@SET __MVNW_CMD__=
|
||||
@SET __MVNW_ERROR__=
|
||||
@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
|
||||
@SET PSModulePath=
|
||||
@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)
|
||||
)
|
||||
@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
|
||||
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
|
||||
if exist %WRAPPER_JAR% (
|
||||
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%
|
||||
)
|
||||
$ErrorActionPreference = "Stop"
|
||||
if ($env:MVNW_VERBOSE -eq "true") {
|
||||
$VerbosePreference = "Continue"
|
||||
}
|
||||
|
||||
powershell -Command "&{"^
|
||||
"$webclient = new-object System.Net.WebClient;"^
|
||||
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
|
||||
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
|
||||
"}"^
|
||||
"[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
|
||||
# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
|
||||
$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
|
||||
if (!$distributionUrl) {
|
||||
Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
|
||||
}
|
||||
|
||||
@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file
|
||||
SET WRAPPER_SHA_256_SUM=""
|
||||
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
|
||||
IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B
|
||||
)
|
||||
IF NOT %WRAPPER_SHA_256_SUM%=="" (
|
||||
powershell -Command "&{"^
|
||||
"$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^
|
||||
"If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^
|
||||
" Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^
|
||||
" Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^
|
||||
" Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^
|
||||
" exit 1;"^
|
||||
"}"^
|
||||
"}"
|
||||
if ERRORLEVEL 1 goto error
|
||||
)
|
||||
switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
|
||||
"maven-mvnd-*" {
|
||||
$USE_MVND = $true
|
||||
$distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
|
||||
$MVN_CMD = "mvnd.cmd"
|
||||
break
|
||||
}
|
||||
default {
|
||||
$USE_MVND = $false
|
||||
$MVN_CMD = $script -replace '^mvnw','mvn'
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@REM Provide a "standardized" way to retrieve the CLI args that will
|
||||
@REM work with both Windows and non-Windows executions.
|
||||
set MAVEN_CMD_LINE_ARGS=%*
|
||||
# apply MVNW_REPOURL and calculate MAVEN_HOME
|
||||
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
|
||||
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% ^
|
||||
%JVM_CONFIG_MAVEN_PROPS% ^
|
||||
%MAVEN_OPTS% ^
|
||||
%MAVEN_DEBUG_OPTS% ^
|
||||
-classpath %WRAPPER_JAR% ^
|
||||
"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
|
||||
%WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
|
||||
if ERRORLEVEL 1 goto error
|
||||
goto end
|
||||
if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
|
||||
Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
|
||||
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
|
||||
exit $?
|
||||
}
|
||||
|
||||
:error
|
||||
set ERROR_CODE=1
|
||||
if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
|
||||
Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
|
||||
}
|
||||
|
||||
:end
|
||||
@endlocal & set ERROR_CODE=%ERROR_CODE%
|
||||
# prepare tmp dir
|
||||
$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
|
||||
@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
|
||||
New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
|
||||
|
||||
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
|
||||
if "%MAVEN_BATCH_PAUSE%"=="on" pause
|
||||
# Download and Install Apache Maven
|
||||
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"
|
||||
|
|
137
pom.xml
137
pom.xml
|
@ -1,17 +1,18 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
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">
|
||||
<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">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.springframework.samples</groupId>
|
||||
<artifactId>spring-petclinic</artifactId>
|
||||
<version>3.2.0-SNAPSHOT</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.2.1</version>
|
||||
<version>3.4.0</version>
|
||||
<relativePath></relativePath>
|
||||
</parent>
|
||||
|
||||
<groupId>org.springframework.samples</groupId>
|
||||
<artifactId>spring-petclinic</artifactId>
|
||||
<version>3.4.0-SNAPSHOT</version>
|
||||
|
||||
<name>petclinic</name>
|
||||
|
||||
<properties>
|
||||
|
@ -20,20 +21,22 @@
|
|||
<java.version>17</java.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<!-- Important for reproducible builds. Update using e.g. ./mvnw versions:set -DnewVersion=... -->
|
||||
<project.build.outputTimestamp>2023-05-10T07:42:50Z</project.build.outputTimestamp>
|
||||
<!-- Important for reproducible builds. Update using e.g. ./mvnw versions:set
|
||||
-DnewVersion=... -->
|
||||
<project.build.outputTimestamp>2024-11-28T14:37:52Z</project.build.outputTimestamp>
|
||||
|
||||
<!-- 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>
|
||||
|
||||
<checkstyle.version>10.12.5</checkstyle.version>
|
||||
<jacoco.version>0.8.11</jacoco.version>
|
||||
<checkstyle.version>10.20.1</checkstyle.version>
|
||||
<jacoco.version>0.8.12</jacoco.version>
|
||||
<libsass.version>0.2.29</libsass.version>
|
||||
<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>
|
||||
<spring-format.version>0.0.40</spring-format.version>
|
||||
<spring-format.version>0.0.43</spring-format.version>
|
||||
|
||||
</properties>
|
||||
|
||||
|
@ -68,6 +71,11 @@
|
|||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</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 -->
|
||||
<dependency>
|
||||
|
@ -97,6 +105,11 @@
|
|||
</dependency>
|
||||
|
||||
<!-- Webjars -->
|
||||
<dependency>
|
||||
<groupId>org.webjars</groupId>
|
||||
<artifactId>webjars-locator-lite</artifactId>
|
||||
<version>${webjars-locator.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.webjars.npm</groupId>
|
||||
<artifactId>bootstrap</artifactId>
|
||||
|
@ -139,6 +152,11 @@
|
|||
<artifactId>jakarta.xml.bind-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-registry-prometheus</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
@ -155,7 +173,9 @@
|
|||
<configuration>
|
||||
<rules>
|
||||
<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>
|
||||
</requireJavaVersion>
|
||||
</rules>
|
||||
|
@ -169,10 +189,10 @@
|
|||
<version>${spring-format.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>validate</phase>
|
||||
<goals>
|
||||
<goal>validate</goal>
|
||||
</goals>
|
||||
<phase>validate</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
@ -195,17 +215,17 @@
|
|||
<executions>
|
||||
<execution>
|
||||
<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>
|
||||
<goal>check</goal>
|
||||
</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>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
@ -218,7 +238,7 @@
|
|||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<!-- Spring Boot Actuator displays build-related information
|
||||
<!-- Spring Boot Actuator displays build-related information
|
||||
if a META-INF/build-info.properties file is present -->
|
||||
<goals>
|
||||
<goal>build-info</goal>
|
||||
|
@ -246,16 +266,16 @@
|
|||
</execution>
|
||||
<execution>
|
||||
<id>report</id>
|
||||
<phase>prepare-package</phase>
|
||||
<goals>
|
||||
<goal>report</goal>
|
||||
</goals>
|
||||
<phase>prepare-package</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<!-- Spring Boot Actuator displays build-related information if a git.properties
|
||||
file is present at the classpath -->
|
||||
<!-- Spring Boot Actuator displays build-related information if a git.properties file is
|
||||
present at the classpath -->
|
||||
<plugin>
|
||||
<groupId>io.github.git-commit-id</groupId>
|
||||
<artifactId>git-commit-id-maven-plugin</artifactId>
|
||||
|
@ -264,10 +284,16 @@
|
|||
<failOnUnableToExtractRepoInfo>false</failOnUnableToExtractRepoInfo>
|
||||
</configuration>
|
||||
</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>
|
||||
</build>
|
||||
|
||||
<licenses>
|
||||
<license>
|
||||
<name>Apache License, Version 2.0</name>
|
||||
|
@ -277,39 +303,38 @@
|
|||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-snapshots</id>
|
||||
<name>Spring Snapshots</name>
|
||||
<url>https://repo.spring.io/snapshot</url>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
<id>spring-snapshots</id>
|
||||
<name>Spring Snapshots</name>
|
||||
<url>https://repo.spring.io/snapshot</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring Milestones</name>
|
||||
<url>https://repo.spring.io/milestone</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring Milestones</name>
|
||||
<url>https://repo.spring.io/milestone</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>spring-snapshots</id>
|
||||
<name>Spring Snapshots</name>
|
||||
<url>https://repo.spring.io/snapshot</url>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
<id>spring-snapshots</id>
|
||||
<name>Spring Snapshots</name>
|
||||
<url>https://repo.spring.io/snapshot</url>
|
||||
</pluginRepository>
|
||||
<pluginRepository>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring Milestones</name>
|
||||
<url>https://repo.spring.io/milestone</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring Milestones</name>
|
||||
<url>https://repo.spring.io/milestone</url>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
|
||||
|
@ -324,11 +349,11 @@
|
|||
<executions>
|
||||
<execution>
|
||||
<id>unpack</id>
|
||||
<?m2e execute onConfiguration,onIncremental?>
|
||||
<phase>generate-resources</phase>
|
||||
<goals>
|
||||
<goal>unpack</goal>
|
||||
</goals>
|
||||
<?m2e execute onConfiguration,onIncremental?>
|
||||
<phase>generate-resources</phase>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
|
@ -347,21 +372,20 @@
|
|||
<groupId>com.gitlab.haynes</groupId>
|
||||
<artifactId>libsass-maven-plugin</artifactId>
|
||||
<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>
|
||||
<execution>
|
||||
<phase>generate-resources</phase>
|
||||
<?m2e execute onConfiguration,onIncremental?>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
<phase>generate-resources</phase>
|
||||
</execution>
|
||||
</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>
|
||||
</plugins>
|
||||
</build>
|
||||
|
@ -376,7 +400,7 @@
|
|||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<!-- This plugin's configuration is used to store Eclipse m2e settings
|
||||
<!-- This plugin's configuration is used to store Eclipse m2e settings
|
||||
only. It has no influence on the Maven build itself. -->
|
||||
<plugin>
|
||||
<groupId>org.eclipse.m2e</groupId>
|
||||
|
@ -395,7 +419,7 @@
|
|||
</goals>
|
||||
</pluginExecutionFilter>
|
||||
<action>
|
||||
<ignore />
|
||||
<ignore></ignore>
|
||||
</action>
|
||||
</pluginExecution>
|
||||
<pluginExecution>
|
||||
|
@ -408,7 +432,7 @@
|
|||
</goals>
|
||||
</pluginExecutionFilter>
|
||||
<action>
|
||||
<ignore />
|
||||
<ignore></ignore>
|
||||
</action>
|
||||
</pluginExecution>
|
||||
<pluginExecution>
|
||||
|
@ -421,7 +445,7 @@
|
|||
</goals>
|
||||
</pluginExecutionFilter>
|
||||
<action>
|
||||
<ignore />
|
||||
<ignore></ignore>
|
||||
</action>
|
||||
</pluginExecution>
|
||||
</pluginExecutions>
|
||||
|
@ -433,5 +457,4 @@
|
|||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
</project>
|
||||
|
|
|
@ -3,8 +3,9 @@
|
|||
"-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN"
|
||||
"https://checkstyle.org/dtds/suppressions_1_2.dtd">
|
||||
<suppressions>
|
||||
<suppress files="node_modules/.*" checks=".*"/>
|
||||
<suppress files="node/.*" checks=".*"/>
|
||||
<suppress files="build/.*" checks=".*"/>
|
||||
<suppress files="node_modules[\\/].*" checks=".*"/>
|
||||
<suppress files="node[\\/].*" checks=".*"/>
|
||||
<suppress files="build[\\/].*" checks=".*"/>
|
||||
<suppress files="target[\\/].*" checks=".*"/>
|
||||
<suppress files=".+\.(jar|git|ico|p12|gif|jks|jpg|svg|log)" checks="NoHttp"/>
|
||||
</suppressions>
|
||||
|
|
|
@ -4,4 +4,7 @@
|
|||
"https://checkstyle.org/dtds/configuration_1_2.dtd">
|
||||
<module name="com.puppycrawl.tools.checkstyle.Checker">
|
||||
<module name="io.spring.nohttp.checkstyle.check.NoHttpCheck"/>
|
||||
<module name="SuppressionFilter">
|
||||
<property name="file" value="${config_loc}/nohttp-checkstyle-suppressions.xml"/>
|
||||
</module>
|
||||
</module>
|
||||
|
|
|
@ -28,7 +28,6 @@ public class PetClinicRuntimeHints implements RuntimeHintsRegistrar {
|
|||
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
|
||||
hints.resources().registerPattern("db/*"); // https://github.com/spring-projects/spring-boot/issues/32654
|
||||
hints.resources().registerPattern("messages/*");
|
||||
hints.resources().registerPattern("META-INF/resources/webjars/*");
|
||||
hints.resources().registerPattern("mysql-default-conf");
|
||||
hints.serialization().registerType(BaseEntity.class);
|
||||
hints.serialization().registerType(Person.class);
|
||||
|
|
|
@ -17,6 +17,7 @@ package org.springframework.samples.petclinic.model;
|
|||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.MappedSuperclass;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* 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 Juergen Hoeller
|
||||
* @author Wick Dynex
|
||||
*/
|
||||
@MappedSuperclass
|
||||
public class NamedEntity extends BaseEntity {
|
||||
|
||||
@Column(name = "name")
|
||||
@NotBlank
|
||||
private String name;
|
||||
|
||||
public String getName() {
|
||||
|
|
|
@ -30,7 +30,7 @@ import jakarta.persistence.JoinColumn;
|
|||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.OrderBy;
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.validation.constraints.Digits;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
|
@ -41,6 +41,7 @@ import jakarta.validation.constraints.NotBlank;
|
|||
* @author Sam Brannen
|
||||
* @author Michael Isvy
|
||||
* @author Oliver Drotbohm
|
||||
* @author Wick Dynex
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "owners")
|
||||
|
@ -56,13 +57,13 @@ public class Owner extends Person {
|
|||
|
||||
@Column(name = "telephone")
|
||||
@NotBlank
|
||||
@Digits(fraction = 0, integer = 10)
|
||||
@Pattern(regexp = "\\d{10}", message = "Telephone must be a 10-digit number")
|
||||
private String telephone;
|
||||
|
||||
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
||||
@JoinColumn(name = "owner_id")
|
||||
@OrderBy("name")
|
||||
private List<Pet> pets = new ArrayList<>();
|
||||
private final List<Pet> pets = new ArrayList<>();
|
||||
|
||||
public String getAddress() {
|
||||
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.
|
||||
* @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) {
|
||||
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.
|
||||
* @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) {
|
||||
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.
|
||||
* @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) {
|
||||
name = name.toLowerCase();
|
||||
for (Pet pet : getPets()) {
|
||||
String compName = pet.getName();
|
||||
if (compName != null && compName.equalsIgnoreCase(name)) {
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
package org.springframework.samples.petclinic.owner;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
|
@ -34,12 +34,14 @@ import org.springframework.web.bind.annotation.RequestParam;
|
|||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||
|
||||
/**
|
||||
* @author Juergen Hoeller
|
||||
* @author Ken Krebs
|
||||
* @author Arjen Poutsma
|
||||
* @author Michael Isvy
|
||||
* @author Wick Dynex
|
||||
*/
|
||||
@Controller
|
||||
class OwnerController {
|
||||
|
@ -48,8 +50,8 @@ class OwnerController {
|
|||
|
||||
private final OwnerRepository owners;
|
||||
|
||||
public OwnerController(OwnerRepository clinicService) {
|
||||
this.owners = clinicService;
|
||||
public OwnerController(OwnerRepository owners) {
|
||||
this.owners = owners;
|
||||
}
|
||||
|
||||
@InitBinder
|
||||
|
@ -59,23 +61,26 @@ class OwnerController {
|
|||
|
||||
@ModelAttribute("owner")
|
||||
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")
|
||||
public String initCreationForm(Map<String, Object> model) {
|
||||
Owner owner = new Owner();
|
||||
model.put("owner", owner);
|
||||
public String initCreationForm() {
|
||||
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
|
||||
}
|
||||
|
||||
@PostMapping("/owners/new")
|
||||
public String processCreationForm(@Valid Owner owner, BindingResult result) {
|
||||
public String processCreationForm(@Valid Owner owner, BindingResult result, RedirectAttributes redirectAttributes) {
|
||||
if (result.hasErrors()) {
|
||||
redirectAttributes.addFlashAttribute("error", "There was an error in creating the owner.");
|
||||
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
|
||||
}
|
||||
|
||||
this.owners.save(owner);
|
||||
redirectAttributes.addFlashAttribute("message", "New Owner Created");
|
||||
return "redirect:/owners/" + owner.getId();
|
||||
}
|
||||
|
||||
|
@ -122,25 +127,31 @@ class OwnerController {
|
|||
private Page<Owner> findPaginatedForOwnersLastName(int page, String lastname) {
|
||||
int pageSize = 5;
|
||||
Pageable pageable = PageRequest.of(page - 1, pageSize);
|
||||
return owners.findByLastName(lastname, pageable);
|
||||
return owners.findByLastNameStartingWith(lastname, pageable);
|
||||
}
|
||||
|
||||
@GetMapping("/owners/{ownerId}/edit")
|
||||
public String initUpdateOwnerForm(@PathVariable("ownerId") int ownerId, Model model) {
|
||||
Owner owner = this.owners.findById(ownerId);
|
||||
model.addAttribute(owner);
|
||||
public String initUpdateOwnerForm() {
|
||||
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
|
||||
}
|
||||
|
||||
@PostMapping("/owners/{ownerId}/edit")
|
||||
public String processUpdateOwnerForm(@Valid Owner owner, BindingResult result,
|
||||
@PathVariable("ownerId") int ownerId) {
|
||||
public String processUpdateOwnerForm(@Valid Owner owner, BindingResult result, @PathVariable("ownerId") int ownerId,
|
||||
RedirectAttributes redirectAttributes) {
|
||||
if (result.hasErrors()) {
|
||||
redirectAttributes.addFlashAttribute("error", "There was an error in updating the owner.");
|
||||
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);
|
||||
this.owners.save(owner);
|
||||
redirectAttributes.addFlashAttribute("message", "Owner Values Updated");
|
||||
return "redirect:/owners/{ownerId}";
|
||||
}
|
||||
|
||||
|
@ -152,7 +163,9 @@ class OwnerController {
|
|||
@GetMapping("/owners/{ownerId}")
|
||||
public ModelAndView showOwner(@PathVariable("ownerId") int ownerId) {
|
||||
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);
|
||||
return mav;
|
||||
}
|
||||
|
|
|
@ -16,12 +16,13 @@
|
|||
package org.springframework.samples.petclinic.owner;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import jakarta.annotation.Nonnull;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
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;
|
||||
|
||||
/**
|
||||
|
@ -34,15 +35,15 @@ import org.springframework.transaction.annotation.Transactional;
|
|||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @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.
|
||||
* @return a Collection of {@link PetType}s.
|
||||
*/
|
||||
@Query("SELECT ptype FROM PetType ptype ORDER BY ptype.name")
|
||||
@Transactional(readOnly = true)
|
||||
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
|
||||
* found)
|
||||
*/
|
||||
|
||||
@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);
|
||||
Page<Owner> findByLastNameStartingWith(String lastName, Pageable pageable);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @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")
|
||||
@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);
|
||||
Optional<Owner> findById(@Nonnull Integer id);
|
||||
|
||||
/**
|
||||
* Returns all the owners from data store
|
||||
**/
|
||||
@Query("SELECT owner FROM Owner owner")
|
||||
@Transactional(readOnly = true)
|
||||
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 Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @author Wick Dynex
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "pets")
|
||||
|
@ -55,7 +56,7 @@ public class Pet extends NamedEntity {
|
|||
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
||||
@JoinColumn(name = "pet_id")
|
||||
@OrderBy("visit_date ASC")
|
||||
private Set<Visit> visits = new LinkedHashSet<>();
|
||||
private final Set<Visit> visits = new LinkedHashSet<>();
|
||||
|
||||
public void setBirthDate(LocalDate birthDate) {
|
||||
this.birthDate = birthDate;
|
||||
|
|
|
@ -17,6 +17,7 @@ package org.springframework.samples.petclinic.owner;
|
|||
|
||||
import java.time.LocalDate;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.ModelMap;
|
||||
|
@ -31,11 +32,13 @@ import org.springframework.web.bind.annotation.PostMapping;
|
|||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||
|
||||
/**
|
||||
* @author Juergen Hoeller
|
||||
* @author Ken Krebs
|
||||
* @author Arjen Poutsma
|
||||
* @author Wick Dynex
|
||||
*/
|
||||
@Controller
|
||||
@RequestMapping("/owners/{ownerId}")
|
||||
|
@ -56,11 +59,9 @@ class PetController {
|
|||
|
||||
@ModelAttribute("owner")
|
||||
public Owner findOwner(@PathVariable("ownerId") int ownerId) {
|
||||
|
||||
Owner owner = this.owners.findById(ownerId);
|
||||
if (owner == null) {
|
||||
throw new IllegalArgumentException("Owner ID not found: " + 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 "));
|
||||
return owner;
|
||||
}
|
||||
|
||||
|
@ -72,10 +73,9 @@ class PetController {
|
|||
return new Pet();
|
||||
}
|
||||
|
||||
Owner owner = this.owners.findById(ownerId);
|
||||
if (owner == null) {
|
||||
throw new IllegalArgumentException("Owner ID not found: " + 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 "));
|
||||
return owner.getPet(petId);
|
||||
}
|
||||
|
||||
|
@ -93,47 +93,46 @@ class PetController {
|
|||
public String initCreationForm(Owner owner, ModelMap model) {
|
||||
Pet pet = new Pet();
|
||||
owner.addPet(pet);
|
||||
model.put("pet", pet);
|
||||
return VIEWS_PETS_CREATE_OR_UPDATE_FORM;
|
||||
}
|
||||
|
||||
@PostMapping("/pets/new")
|
||||
public String processCreationForm(Owner owner, @Valid Pet pet, BindingResult result, ModelMap model) {
|
||||
if (StringUtils.hasText(pet.getName()) && pet.isNew() && owner.getPet(pet.getName(), true) != null) {
|
||||
public String processCreationForm(Owner owner, @Valid Pet pet, BindingResult result,
|
||||
RedirectAttributes redirectAttributes) {
|
||||
|
||||
if (StringUtils.hasText(pet.getName()) && pet.isNew() && owner.getPet(pet.getName(), true) != null)
|
||||
result.rejectValue("name", "duplicate", "already exists");
|
||||
}
|
||||
|
||||
LocalDate currentDate = LocalDate.now();
|
||||
if (pet.getBirthDate() != null && pet.getBirthDate().isAfter(currentDate)) {
|
||||
result.rejectValue("birthDate", "typeMismatch.birthDate");
|
||||
}
|
||||
|
||||
owner.addPet(pet);
|
||||
if (result.hasErrors()) {
|
||||
model.put("pet", pet);
|
||||
return VIEWS_PETS_CREATE_OR_UPDATE_FORM;
|
||||
}
|
||||
|
||||
owner.addPet(pet);
|
||||
this.owners.save(owner);
|
||||
redirectAttributes.addFlashAttribute("message", "New Pet has been Added");
|
||||
return "redirect:/owners/{ownerId}";
|
||||
}
|
||||
|
||||
@GetMapping("/pets/{petId}/edit")
|
||||
public String initUpdateForm(Owner owner, @PathVariable("petId") int petId, ModelMap model) {
|
||||
Pet pet = owner.getPet(petId);
|
||||
model.put("pet", pet);
|
||||
public String initUpdateForm() {
|
||||
return VIEWS_PETS_CREATE_OR_UPDATE_FORM;
|
||||
}
|
||||
|
||||
@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();
|
||||
|
||||
// checking if the pet name already exist for the owner
|
||||
if (StringUtils.hasText(petName)) {
|
||||
Pet existingPet = owner.getPet(petName.toLowerCase(), false);
|
||||
if (existingPet != null && existingPet.getId() != pet.getId()) {
|
||||
Pet existingPet = owner.getPet(petName, false);
|
||||
if (existingPet != null && !existingPet.getId().equals(pet.getId())) {
|
||||
result.rejectValue("name", "duplicate", "already exists");
|
||||
}
|
||||
}
|
||||
|
@ -144,12 +143,12 @@ class PetController {
|
|||
}
|
||||
|
||||
if (result.hasErrors()) {
|
||||
model.put("pet", pet);
|
||||
return VIEWS_PETS_CREATE_OR_UPDATE_FORM;
|
||||
}
|
||||
|
||||
owner.addPet(pet);
|
||||
this.owners.save(owner);
|
||||
redirectAttributes.addFlashAttribute("message", "Pet details has been edited");
|
||||
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;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.validation.BindingResult;
|
||||
|
@ -27,6 +28,7 @@ import org.springframework.web.bind.annotation.PathVariable;
|
|||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||
|
||||
/**
|
||||
* @author Juergen Hoeller
|
||||
|
@ -34,6 +36,7 @@ import jakarta.validation.Valid;
|
|||
* @author Arjen Poutsma
|
||||
* @author Michael Isvy
|
||||
* @author Dave Syer
|
||||
* @author Wick Dynex
|
||||
*/
|
||||
@Controller
|
||||
class VisitController {
|
||||
|
@ -59,7 +62,9 @@ class VisitController {
|
|||
@ModelAttribute("visit")
|
||||
public Visit loadPetWithVisit(@PathVariable("ownerId") int ownerId, @PathVariable("petId") int petId,
|
||||
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);
|
||||
model.put("pet", pet);
|
||||
|
@ -81,13 +86,14 @@ class VisitController {
|
|||
// called
|
||||
@PostMapping("/owners/{ownerId}/pets/{petId}/visits/new")
|
||||
public String processNewVisitForm(@ModelAttribute Owner owner, @PathVariable int petId, @Valid Visit visit,
|
||||
BindingResult result) {
|
||||
BindingResult result, RedirectAttributes redirectAttributes) {
|
||||
if (result.hasErrors()) {
|
||||
return "pets/createOrUpdateVisitForm";
|
||||
}
|
||||
|
||||
owner.addVisit(petId, visit);
|
||||
this.owners.save(owner);
|
||||
redirectAttributes.addFlashAttribute("message", "Your visit has been booked");
|
||||
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;
|
||||
}
|
||||
|
||||
protected void setSpecialtiesInternal(Set<Specialty> specialties) {
|
||||
this.specialties = specialties;
|
||||
}
|
||||
|
||||
@XmlElement
|
||||
public List<Specialty> getSpecialties() {
|
||||
List<Specialty> sortedSpecs = new ArrayList<>(getSpecialtiesInternal());
|
||||
|
|
|
@ -37,8 +37,8 @@ class VetController {
|
|||
|
||||
private final VetRepository vetRepository;
|
||||
|
||||
public VetController(VetRepository clinicService) {
|
||||
this.vetRepository = clinicService;
|
||||
public VetController(VetRepository vetRepository) {
|
||||
this.vetRepository = vetRepository;
|
||||
}
|
||||
|
||||
@GetMapping("/vets.html")
|
||||
|
|
|
@ -8,7 +8,7 @@ spring.thymeleaf.mode=HTML
|
|||
|
||||
# JPA
|
||||
spring.jpa.hibernate.ddl-auto=none
|
||||
spring.jpa.open-in-view=true
|
||||
spring.jpa.open-in-view=false
|
||||
|
||||
# Internationalization
|
||||
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 (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 'dog' WHERE NOT EXISTS (SELECT * FROM specialties WHERE name='dog');
|
||||
INSERT INTO types (name) SELECT 'lizard' WHERE NOT EXISTS (SELECT * FROM specialties WHERE name='lizard');
|
||||
INSERT INTO types (name) SELECT 'snake' WHERE NOT EXISTS (SELECT * FROM specialties WHERE name='snake');
|
||||
INSERT INTO types (name) SELECT 'bird' WHERE NOT EXISTS (SELECT * FROM specialties WHERE name='bird');
|
||||
INSERT INTO types (name) SELECT 'hamster' 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 types WHERE name='dog');
|
||||
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 types WHERE name='snake');
|
||||
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 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 '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.
|
||||
*/
|
||||
/*!
|
||||
* Bootstrap v5.3.2 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2023 The Bootstrap Authors
|
||||
* Bootstrap v5.3.3 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2024 The Bootstrap Authors
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
:root,
|
||||
|
@ -2377,6 +2377,8 @@ textarea.form-control-lg {
|
|||
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 {
|
||||
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 {
|
||||
color: var(--bs-btn-disabled-color);
|
||||
pointer-events: none;
|
||||
|
@ -3680,12 +3682,11 @@ textarea.form-control-lg {
|
|||
--bs-accordion-btn-padding-y: 1rem;
|
||||
--bs-accordion-btn-color: var(--bs-body-color);
|
||||
--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-transform: rotate(-180deg);
|
||||
--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-focus-border-color: #86b7fe;
|
||||
--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-box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
|
||||
--bs-accordion-body-padding-x: 1.25rem;
|
||||
--bs-accordion-body-padding-y: 1rem;
|
||||
|
@ -3733,7 +3734,6 @@ textarea.form-control-lg {
|
|||
z-index: 2; }
|
||||
.accordion-button:focus {
|
||||
z-index: 3;
|
||||
border-color: var(--bs-accordion-btn-focus-border-color);
|
||||
outline: 0;
|
||||
box-shadow: var(--bs-accordion-btn-focus-box-shadow); }
|
||||
|
||||
|
@ -3747,7 +3747,7 @@ textarea.form-control-lg {
|
|||
.accordion-item:first-of-type {
|
||||
border-top-left-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-right-radius: var(--bs-accordion-inner-border-radius); }
|
||||
.accordion-item:not(:first-of-type) {
|
||||
|
@ -3755,28 +3755,27 @@ textarea.form-control-lg {
|
|||
.accordion-item:last-of-type {
|
||||
border-bottom-right-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-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-left-radius: var(--bs-accordion-border-radius); }
|
||||
|
||||
.accordion-body {
|
||||
padding: var(--bs-accordion-body-padding-y) var(--bs-accordion-body-padding-x); }
|
||||
|
||||
.accordion-flush .accordion-collapse {
|
||||
border-width: 0; }
|
||||
|
||||
.accordion-flush .accordion-item {
|
||||
.accordion-flush > .accordion-item {
|
||||
border-right: 0;
|
||||
border-left: 0;
|
||||
border-radius: 0; }
|
||||
.accordion-flush .accordion-item:first-child {
|
||||
.accordion-flush > .accordion-item:first-child {
|
||||
border-top: 0; }
|
||||
.accordion-flush .accordion-item:last-child {
|
||||
.accordion-flush > .accordion-item:last-child {
|
||||
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; }
|
||||
|
||||
[data-bs-theme="dark"] .accordion-button::after {
|
||||
|
@ -4524,7 +4523,6 @@ textarea.form-control-lg {
|
|||
display: flex;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: var(--bs-modal-header-padding);
|
||||
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);
|
||||
|
@ -4997,19 +4995,11 @@ textarea.form-control-lg {
|
|||
background-position: 50%;
|
||||
background-size: 100% 100%; }
|
||||
|
||||
/* rtl:options: {
|
||||
"autoRename": true,
|
||||
"stringMap":[ {
|
||||
"name" : "prev-next",
|
||||
"search" : "prev",
|
||||
"replace" : "next"
|
||||
} ]
|
||||
} */
|
||||
.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 {
|
||||
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 {
|
||||
position: absolute;
|
||||
|
@ -5528,13 +5518,10 @@ textarea.form-control-lg {
|
|||
.offcanvas-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x); }
|
||||
.offcanvas-header .btn-close {
|
||||
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-right: calc(-.5 * var(--bs-offcanvas-padding-x));
|
||||
margin-bottom: 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; }
|
||||
|
||||
.offcanvas-title {
|
||||
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>
|
||||
<![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}" />
|
||||
|
||||
</head>
|
||||
|
@ -87,7 +87,7 @@
|
|||
</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>
|
||||
|
||||
|
|
|
@ -7,8 +7,18 @@
|
|||
|
||||
|
||||
<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}">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
|
@ -73,7 +83,20 @@
|
|||
</tr>
|
||||
|
||||
</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>
|
||||
|
||||
|
||||
</html>
|
||||
|
|
|
@ -46,7 +46,7 @@ class MySqlIntegrationTests {
|
|||
|
||||
@ServiceConnection
|
||||
@Container
|
||||
static MySQLContainer<?> container = new MySQLContainer<>("mysql:8.2");
|
||||
static MySQLContainer<?> container = new MySQLContainer<>("mysql:9.1");
|
||||
|
||||
@LocalServerPort
|
||||
int port;
|
||||
|
@ -58,7 +58,7 @@ class MySqlIntegrationTests {
|
|||
private RestTemplateBuilder builder;
|
||||
|
||||
@Test
|
||||
void testFindAll() throws Exception {
|
||||
void testFindAll() {
|
||||
vets.findAll();
|
||||
vets.findAll(); // served from cache
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ public class MysqlTestApplication {
|
|||
@Profile("mysql")
|
||||
@Bean
|
||||
static MySQLContainer<?> container() {
|
||||
return new MySQLContainer<>("mysql:8.2");
|
||||
return new MySQLContainer<>("mysql:9.1");
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
|
|
@ -44,7 +44,7 @@ public class PetClinicIntegrationTests {
|
|||
private RestTemplateBuilder builder;
|
||||
|
||||
@Test
|
||||
void testFindAll() throws Exception {
|
||||
void testFindAll() {
|
||||
vets.findAll();
|
||||
vets.findAll(); // served from cache
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package org.springframework.samples.petclinic;
|
||||
|
||||
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 java.util.Arrays;
|
||||
|
@ -48,7 +49,7 @@ import org.springframework.web.client.RestTemplate;
|
|||
import org.testcontainers.DockerClientFactory;
|
||||
|
||||
@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")
|
||||
@DisabledInNativeImage
|
||||
public class PostgresIntegrationTests {
|
||||
|
@ -71,7 +72,7 @@ public class PostgresIntegrationTests {
|
|||
new SpringApplicationBuilder(PetClinicApplication.class) //
|
||||
.profiles("postgres") //
|
||||
.properties( //
|
||||
"spring.docker.compose.profiles.active=postgres" //
|
||||
"spring.docker.compose.start.arguments=postgres" //
|
||||
) //
|
||||
.listeners(new PropertiesLogger()) //
|
||||
.run(args);
|
||||
|
@ -114,7 +115,16 @@ public class PostgresIntegrationTests {
|
|||
Arrays.sort(names);
|
||||
for (String name : names) {
|
||||
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)) {
|
||||
log.info(name + "=" + resolved);
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ class ValidatorTests {
|
|||
|
||||
assertThat(constraintViolations).hasSize(1);
|
||||
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");
|
||||
}
|
||||
|
||||
|
|
|
@ -16,43 +16,44 @@
|
|||
|
||||
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.greaterThan;
|
||||
import static org.hamcrest.Matchers.hasItem;
|
||||
import static org.hamcrest.Matchers.hasProperty;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
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.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
|
||||
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;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||
|
||||
/**
|
||||
* Test class for {@link OwnerController}
|
||||
*
|
||||
* @author Colin But
|
||||
* @author Wick Dynex
|
||||
*/
|
||||
@WebMvcTest(OwnerController.class)
|
||||
@DisabledInNativeImage
|
||||
|
@ -64,7 +65,7 @@ class OwnerControllerTests {
|
|||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@MockBean
|
||||
@MockitoBean
|
||||
private OwnerRepository owners;
|
||||
|
||||
private Owner george() {
|
||||
|
@ -84,18 +85,18 @@ class OwnerControllerTests {
|
|||
george.addPet(max);
|
||||
max.setId(1);
|
||||
return george;
|
||||
};
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
|
||||
Owner george = george();
|
||||
given(this.owners.findByLastName(eq("Franklin"), any(Pageable.class)))
|
||||
.willReturn(new PageImpl<Owner>(Lists.newArrayList(george)));
|
||||
given(this.owners.findByLastNameStartingWith(eq("Franklin"), any(Pageable.class)))
|
||||
.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.setDate(LocalDate.now());
|
||||
george.getPet("Max").getVisits().add(visit);
|
||||
|
@ -117,7 +118,7 @@ class OwnerControllerTests {
|
|||
.param("lastName", "Bloggs")
|
||||
.param("address", "123 Caramel Street")
|
||||
.param("city", "London")
|
||||
.param("telephone", "01316761638"))
|
||||
.param("telephone", "1316761638"))
|
||||
.andExpect(status().is3xxRedirection());
|
||||
}
|
||||
|
||||
|
@ -142,15 +143,15 @@ class OwnerControllerTests {
|
|||
|
||||
@Test
|
||||
void testProcessFindFormSuccess() throws Exception {
|
||||
Page<Owner> tasks = new PageImpl<Owner>(Lists.newArrayList(george(), new Owner()));
|
||||
Mockito.when(this.owners.findByLastName(anyString(), any(Pageable.class))).thenReturn(tasks);
|
||||
Page<Owner> tasks = new PageImpl<>(Lists.newArrayList(george(), new Owner()));
|
||||
when(this.owners.findByLastNameStartingWith(anyString(), any(Pageable.class))).thenReturn(tasks);
|
||||
mockMvc.perform(get("/owners?page=1")).andExpect(status().isOk()).andExpect(view().name("owners/ownersList"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testProcessFindFormByLastName() throws Exception {
|
||||
Page<Owner> tasks = new PageImpl<Owner>(Lists.newArrayList(george()));
|
||||
Mockito.when(this.owners.findByLastName(eq("Franklin"), any(Pageable.class))).thenReturn(tasks);
|
||||
Page<Owner> tasks = new PageImpl<>(Lists.newArrayList(george()));
|
||||
when(this.owners.findByLastNameStartingWith(eq("Franklin"), any(Pageable.class))).thenReturn(tasks);
|
||||
mockMvc.perform(get("/owners?page=1").param("lastName", "Franklin"))
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andExpect(view().name("redirect:/owners/" + TEST_OWNER_ID));
|
||||
|
@ -158,8 +159,8 @@ class OwnerControllerTests {
|
|||
|
||||
@Test
|
||||
void testProcessFindFormNoOwnersFound() throws Exception {
|
||||
Page<Owner> tasks = new PageImpl<Owner>(Lists.newArrayList());
|
||||
Mockito.when(this.owners.findByLastName(eq("Unknown Surname"), any(Pageable.class))).thenReturn(tasks);
|
||||
Page<Owner> tasks = new PageImpl<>(Lists.newArrayList());
|
||||
when(this.owners.findByLastNameStartingWith(eq("Unknown Surname"), any(Pageable.class))).thenReturn(tasks);
|
||||
mockMvc.perform(get("/owners?page=1").param("lastName", "Unknown Surname"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(model().attributeHasFieldErrors("owner", "lastName"))
|
||||
|
@ -188,7 +189,7 @@ class OwnerControllerTests {
|
|||
.param("lastName", "Bloggs")
|
||||
.param("address", "123 Caramel Street")
|
||||
.param("city", "London")
|
||||
.param("telephone", "01616291589"))
|
||||
.param("telephone", "1616291589"))
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.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("telephone", is("6085551023"))))
|
||||
.andExpect(model().attribute("owner", hasProperty("pets", not(empty()))))
|
||||
.andExpect(model().attribute("owner", hasProperty("pets", new BaseMatcher<List<Pet>>() {
|
||||
|
||||
@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(model().attribute("owner",
|
||||
hasProperty("pets", hasItem(hasProperty("visits", hasSize(greaterThan(0)))))))
|
||||
.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"));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
136
src/test/java/org/springframework/samples/petclinic/owner/PetControllerTests.java
Executable file → Normal file
136
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.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.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.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.FilterType;
|
||||
import org.springframework.test.context.aot.DisabledInAotMode;
|
||||
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.Optional;
|
||||
|
||||
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.post;
|
||||
|
@ -39,6 +43,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
|||
* Test class for the {@link PetController}
|
||||
*
|
||||
* @author Colin But
|
||||
* @author Wick Dynex
|
||||
*/
|
||||
@WebMvcTest(value = PetController.class,
|
||||
includeFilters = @ComponentScan.Filter(value = PetTypeFormatter.class, type = FilterType.ASSIGNABLE_TYPE))
|
||||
|
@ -53,7 +58,7 @@ class PetControllerTests {
|
|||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@MockBean
|
||||
@MockitoBean
|
||||
private OwnerRepository owners;
|
||||
|
||||
@BeforeEach
|
||||
|
@ -62,11 +67,17 @@ class PetControllerTests {
|
|||
cat.setId(3);
|
||||
cat.setName("hamster");
|
||||
given(this.owners.findPetTypes()).willReturn(Lists.newArrayList(cat));
|
||||
|
||||
Owner owner = new Owner();
|
||||
Pet pet = new Pet();
|
||||
Pet dog = new Pet();
|
||||
owner.addPet(pet);
|
||||
owner.addPet(dog);
|
||||
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
|
||||
|
@ -87,25 +98,72 @@ class PetControllerTests {
|
|||
.andExpect(view().name("redirect:/owners/{ownerId}"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testProcessCreationFormHasErrors() throws Exception {
|
||||
mockMvc
|
||||
.perform(post("/owners/{ownerId}/pets/new", TEST_OWNER_ID).param("name", "Betty")
|
||||
.param("birthDate", "2015-02-12"))
|
||||
.andExpect(model().attributeHasNoErrors("owner"))
|
||||
.andExpect(model().attributeHasErrors("pet"))
|
||||
.andExpect(model().attributeHasFieldErrors("pet", "type"))
|
||||
.andExpect(model().attributeHasFieldErrorCode("pet", "type", "required"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(view().name("pets/createOrUpdatePetForm"));
|
||||
}
|
||||
@Nested
|
||||
class ProcessCreationFormHasErrors {
|
||||
|
||||
@Test
|
||||
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
|
||||
.perform(post("/owners/{ownerId}/pets/new", TEST_OWNER_ID).param("name", "Betty")
|
||||
.param("birthDate", "2015-02-12"))
|
||||
.andExpect(model().attributeHasNoErrors("owner"))
|
||||
.andExpect(model().attributeHasErrors("pet"))
|
||||
.andExpect(model().attributeHasFieldErrors("pet", "type"))
|
||||
.andExpect(model().attributeHasFieldErrorCode("pet", "type", "required"))
|
||||
.andExpect(status().isOk())
|
||||
.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
|
||||
void testInitUpdateForm() throws Exception {
|
||||
mockMvc.perform(get("/owners/{ownerId}/pets/{petId}/edit", TEST_OWNER_ID, TEST_PET_ID))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(model().attributeExists("pet"))
|
||||
.andExpect(view().name("pets/createOrUpdatePetForm"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInitUpdateForm() throws Exception {
|
||||
mockMvc.perform(get("/owners/{ownerId}/pets/{petId}/edit", TEST_OWNER_ID, TEST_PET_ID))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(model().attributeExists("pet"))
|
||||
.andExpect(view().name("pets/createOrUpdatePetForm"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -118,15 +176,33 @@ class PetControllerTests {
|
|||
.andExpect(view().name("redirect:/owners/{ownerId}"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testProcessUpdateFormHasErrors() throws Exception {
|
||||
mockMvc
|
||||
.perform(post("/owners/{ownerId}/pets/{petId}/edit", TEST_OWNER_ID, TEST_PET_ID).param("name", "Betty")
|
||||
.param("birthDate", "2015/02/12"))
|
||||
.andExpect(model().attributeHasNoErrors("owner"))
|
||||
.andExpect(model().attributeHasErrors("pet"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(view().name("pets/createOrUpdatePetForm"));
|
||||
@Nested
|
||||
class ProcessUpdateFormHasErrors {
|
||||
|
||||
@Test
|
||||
void testProcessUpdateFormWithInvalidBirthDate() 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", "birthDate"))
|
||||
.andExpect(model().attributeHasFieldErrorCode("pet", "birthDate", "typeMismatch"))
|
||||
.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
|
||||
void shouldThrowParseException() throws ParseException {
|
||||
void shouldThrowParseException() {
|
||||
given(this.pets.findPetTypes()).willReturn(makePetTypes());
|
||||
Assertions.assertThrows(ParseException.class, () -> {
|
||||
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.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.test.context.aot.DisabledInAotMode;
|
||||
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Test class for {@link VisitController}
|
||||
*
|
||||
* @author Colin But
|
||||
* @author Wick Dynex
|
||||
*/
|
||||
@WebMvcTest(VisitController.class)
|
||||
@DisabledInNativeImage
|
||||
|
@ -49,7 +52,7 @@ class VisitControllerTests {
|
|||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@MockBean
|
||||
@MockitoBean
|
||||
private OwnerRepository owners;
|
||||
|
||||
@BeforeEach
|
||||
|
@ -58,7 +61,7 @@ class VisitControllerTests {
|
|||
Pet pet = new Pet();
|
||||
owner.addPet(pet);
|
||||
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
|
||||
|
|
|
@ -20,13 +20,13 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||
|
||||
import java.time.LocalDate;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
|
||||
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace;
|
||||
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.Pageable;
|
||||
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.vet.Vet;
|
||||
import org.springframework.samples.petclinic.vet.VetRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
|
@ -67,7 +66,7 @@ import org.springframework.transaction.annotation.Transactional;
|
|||
* @author Michael Isvy
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@DataJpaTest(includeFilters = @ComponentScan.Filter(Service.class))
|
||||
@DataJpaTest
|
||||
// Ensure that if the mysql profile is active we connect to the real database:
|
||||
@AutoConfigureTestDatabase(replace = Replace.NONE)
|
||||
// @TestPropertySource("/application-postgres.properties")
|
||||
|
@ -83,16 +82,18 @@ class ClinicServiceTests {
|
|||
|
||||
@Test
|
||||
void shouldFindOwnersByLastName() {
|
||||
Page<Owner> owners = this.owners.findByLastName("Davis", pageable);
|
||||
Page<Owner> owners = this.owners.findByLastNameStartingWith("Davis", pageable);
|
||||
assertThat(owners).hasSize(2);
|
||||
|
||||
owners = this.owners.findByLastName("Daviss", pageable);
|
||||
owners = this.owners.findByLastNameStartingWith("Daviss", pageable);
|
||||
assertThat(owners).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
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.getPets()).hasSize(1);
|
||||
assertThat(owner.getPets().get(0).getType()).isNotNull();
|
||||
|
@ -102,7 +103,7 @@ class ClinicServiceTests {
|
|||
@Test
|
||||
@Transactional
|
||||
void shouldInsertOwner() {
|
||||
Page<Owner> owners = this.owners.findByLastName("Schultz", pageable);
|
||||
Page<Owner> owners = this.owners.findByLastNameStartingWith("Schultz", pageable);
|
||||
int found = (int) owners.getTotalElements();
|
||||
|
||||
Owner owner = new Owner();
|
||||
|
@ -112,16 +113,18 @@ class ClinicServiceTests {
|
|||
owner.setCity("Wollongong");
|
||||
owner.setTelephone("4444444444");
|
||||
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);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Transactional
|
||||
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 newLastName = oldLastName + "X";
|
||||
|
||||
|
@ -129,7 +132,9 @@ class ClinicServiceTests {
|
|||
this.owners.save(owner);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
@ -146,7 +151,10 @@ class ClinicServiceTests {
|
|||
@Test
|
||||
@Transactional
|
||||
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();
|
||||
|
||||
Pet pet = new Pet();
|
||||
|
@ -155,12 +163,14 @@ class ClinicServiceTests {
|
|||
pet.setType(EntityUtils.getById(types, PetType.class, 2));
|
||||
pet.setBirthDate(LocalDate.now());
|
||||
owner6.addPet(pet);
|
||||
assertThat(owner6.getPets().size()).isEqualTo(found + 1);
|
||||
assertThat(owner6.getPets()).hasSize(found + 1);
|
||||
|
||||
this.owners.save(owner6);
|
||||
|
||||
owner6 = this.owners.findById(6);
|
||||
assertThat(owner6.getPets().size()).isEqualTo(found + 1);
|
||||
optionalOwner = this.owners.findById(6);
|
||||
assertThat(optionalOwner).isPresent();
|
||||
owner6 = optionalOwner.get();
|
||||
assertThat(owner6.getPets()).hasSize(found + 1);
|
||||
// checks that id has been generated
|
||||
pet = owner6.getPet("bowser");
|
||||
assertThat(pet.getId()).isNotNull();
|
||||
|
@ -168,8 +178,11 @@ class ClinicServiceTests {
|
|||
|
||||
@Test
|
||||
@Transactional
|
||||
void shouldUpdatePetName() throws Exception {
|
||||
Owner owner6 = this.owners.findById(6);
|
||||
void shouldUpdatePetName() {
|
||||
Optional<Owner> optionalOwner = this.owners.findById(6);
|
||||
assertThat(optionalOwner).isPresent();
|
||||
Owner owner6 = optionalOwner.get();
|
||||
|
||||
Pet pet7 = owner6.getPet(7);
|
||||
String oldName = pet7.getName();
|
||||
|
||||
|
@ -177,7 +190,9 @@ class ClinicServiceTests {
|
|||
pet7.setName(newName);
|
||||
this.owners.save(owner6);
|
||||
|
||||
owner6 = this.owners.findById(6);
|
||||
optionalOwner = this.owners.findById(6);
|
||||
assertThat(optionalOwner).isPresent();
|
||||
owner6 = optionalOwner.get();
|
||||
pet7 = owner6.getPet(7);
|
||||
assertThat(pet7.getName()).isEqualTo(newName);
|
||||
}
|
||||
|
@ -196,7 +211,10 @@ class ClinicServiceTests {
|
|||
@Test
|
||||
@Transactional
|
||||
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);
|
||||
int found = pet7.getVisits().size();
|
||||
Visit visit = new Visit();
|
||||
|
@ -205,16 +223,17 @@ class ClinicServiceTests {
|
|||
owner6.addVisit(pet7.getId(), visit);
|
||||
this.owners.save(owner6);
|
||||
|
||||
owner6 = this.owners.findById(6);
|
||||
|
||||
assertThat(pet7.getVisits()) //
|
||||
.hasSize(found + 1) //
|
||||
.allMatch(value -> value.getId() != null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFindVisitsByPetId() throws Exception {
|
||||
Owner owner6 = this.owners.findById(6);
|
||||
void shouldFindVisitsByPetId() {
|
||||
Optional<Owner> optionalOwner = this.owners.findById(6);
|
||||
assertThat(optionalOwner).isPresent();
|
||||
Owner owner6 = optionalOwner.get();
|
||||
|
||||
Pet pet7 = owner6.getPet(7);
|
||||
Collection<Visit> visits = pet7.getVisits();
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ import org.springframework.core.ParameterizedTypeReference;
|
|||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
@ -68,10 +69,10 @@ class CrashControllerIntegrationTests {
|
|||
new ParameterizedTypeReference<Map<String, Object>>() {
|
||||
});
|
||||
assertThat(resp).isNotNull();
|
||||
assertThat(resp.getStatusCode().is5xxServerError());
|
||||
assertThat(resp.getBody().containsKey("timestamp"));
|
||||
assertThat(resp.getBody().containsKey("status"));
|
||||
assertThat(resp.getBody().containsKey("error"));
|
||||
assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
assertThat(resp.getBody()).containsKey("timestamp");
|
||||
assertThat(resp.getBody()).containsKey("status");
|
||||
assertThat(resp.getBody()).containsKey("error");
|
||||
assertThat(resp.getBody()).containsEntry("message",
|
||||
"Expected: controller used to showcase what happens when an exception is thrown");
|
||||
assertThat(resp.getBody()).containsEntry("path", "/oups");
|
||||
|
@ -84,7 +85,7 @@ class CrashControllerIntegrationTests {
|
|||
ResponseEntity<String> resp = rest.exchange("http://localhost:" + port + "/oups", HttpMethod.GET,
|
||||
new HttpEntity<>(headers), String.class);
|
||||
assertThat(resp).isNotNull();
|
||||
assertThat(resp.getStatusCode().is5xxServerError());
|
||||
assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
assertThat(resp.getBody()).isNotNull();
|
||||
// html:
|
||||
assertThat(resp.getBody()).containsSubsequence("<body>", "<h2>", "Something happened...", "</h2>", "<p>",
|
||||
|
|
|
@ -16,11 +16,10 @@
|
|||
|
||||
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 static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* Test class for {@link CrashController}
|
||||
*
|
||||
|
@ -31,16 +30,12 @@ import org.junit.jupiter.api.Test;
|
|||
// luck ((plain(st) UNIT test)! :)
|
||||
class CrashControllerTests {
|
||||
|
||||
CrashController testee = new CrashController();
|
||||
final CrashController testee = new CrashController();
|
||||
|
||||
@Test
|
||||
void testTriggerException() throws Exception {
|
||||
RuntimeException thrown = assertThrows(RuntimeException.class, () -> {
|
||||
testee.triggerException();
|
||||
});
|
||||
|
||||
assertEquals("Expected: controller used to showcase what happens when an exception is thrown",
|
||||
thrown.getMessage());
|
||||
void testTriggerException() {
|
||||
assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> testee.triggerException())
|
||||
.withMessageContaining("Expected: controller used to showcase what happens when an exception is thrown");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -22,11 +22,11 @@ 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.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.http.MediaType;
|
||||
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.ResultActions;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
|
@ -48,7 +48,7 @@ class VetControllerTests {
|
|||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@MockBean
|
||||
@MockitoBean
|
||||
private VetRepository vets;
|
||||
|
||||
private Vet james() {
|
||||
|
|
|
@ -156,8 +156,7 @@
|
|||
<stringProp name="HTTPSampler.response_timeout"></stringProp>
|
||||
<stringProp name="HTTPSampler.protocol"></stringProp>
|
||||
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
|
||||
<stringProp name="HTTPSampler.path">
|
||||
${CONTEXT_WEB}/webjars/bootstrap/5.3.2/dist/js/bootstrap.bundle.min.js</stringProp>
|
||||
<stringProp name="HTTPSampler.path">${CONTEXT_WEB}/webjars/bootstrap/dist/js/bootstrap.bundle.min.js</stringProp>
|
||||
<stringProp name="HTTPSampler.method">GET</stringProp>
|
||||
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
|
||||
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
|
||||
|
@ -420,8 +419,7 @@
|
|||
<stringProp name="HTTPSampler.port"></stringProp>
|
||||
<stringProp name="HTTPSampler.protocol"></stringProp>
|
||||
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
|
||||
<stringProp name="HTTPSampler.path">
|
||||
${CONTEXT_WEB}/owners/${count}/pets/${petCount}/visits/new</stringProp>
|
||||
<stringProp name="HTTPSampler.path">${CONTEXT_WEB}/owners/${count}/pets/${petCount}/visits/new</stringProp>
|
||||
<stringProp name="HTTPSampler.method">GET</stringProp>
|
||||
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
|
||||
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
|
||||
|
@ -458,8 +456,7 @@
|
|||
<stringProp name="HTTPSampler.port"></stringProp>
|
||||
<stringProp name="HTTPSampler.protocol"></stringProp>
|
||||
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
|
||||
<stringProp name="HTTPSampler.path">
|
||||
${CONTEXT_WEB}/owners/${count}/pets/${petCount}/visits/new</stringProp>
|
||||
<stringProp name="HTTPSampler.path">${CONTEXT_WEB}/owners/${count}/pets/${petCount}/visits/new</stringProp>
|
||||
<stringProp name="HTTPSampler.method">POST</stringProp>
|
||||
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
|
||||
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
|
||||
|
@ -540,4 +537,4 @@
|
|||
</hashTree>
|
||||
</hashTree>
|
||||
</hashTree>
|
||||
</jmeterTestPlan>
|
||||
</jmeterTestPlan>
|
||||
|
|
Loading…
Reference in a new issue