diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index f8102bc..804a6d4 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,11 +1,13 @@ -# 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' \ No newline at end of file +RUN bash -lc '. /usr/local/sdkman/bin/sdkman-init.sh && sdk install java $JAVA_VERSION && sdk use java $JAVA_VERSION' diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 3aa67af..a732481 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,25 +1,32 @@ { - "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" + "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" + } } diff --git a/.editorconfig b/.editorconfig index 4e509a4..2513d2a 100644 --- a/.editorconfig +++ b/.editorconfig @@ -19,6 +19,3 @@ indent_style = space [*.{html,sql,less}] indent_size = 2 - -[*.gradle] -indent_size = 2 diff --git a/.gitattributes b/.gitattributes index 31f1bf5..21de586 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,2 @@ -mvnw text eol=lf -*.java text eol=lf - -/gradlew text eol=lf -*.bat text eol=crlf +mvnw text eol=lf +*.java text eol=lf diff --git a/.github/workflows/maven-build.yml b/.github/workflows/maven-build.yml index a1ec4da..5de223f 100644 --- a/.github/workflows/maven-build.yml +++ b/.github/workflows/maven-build.yml @@ -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://docs.github.com/en/actions/use-cases-and-examples/building-and-testing/building-and-testing-java-with-maven +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven name: Java CI with Maven @@ -18,12 +18,12 @@ jobs: java: [ '17' ] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v3 - name: Set up JDK ${{matrix.java}} - uses: actions/setup-java@v4 + uses: actions/setup-java@v2 with: java-version: ${{matrix.java}} distribution: 'adopt' cache: maven - name: Build with Maven Wrapper - run: ./mvnw -B verify + run: ./mvnw -B package diff --git a/.gitignore b/.gitignore index d2767ad..af0cb9b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,51 +1,17 @@ -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 +target/* +bin/* +build/* +.gradle/* +.settings/* .classpath -.factorypath .project -.settings -.springBeans -.sts4-cache -bin/ -!**/src/main/**/bin/ -!**/src/test/**/bin/ - -### IntelliJ IDEA ### +.factorypath +.attach_pid* .idea -*.iws *.iml -*.ipr -out/ -!**/src/main/**/out/ -!**/src/test/**/out/ - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ - -### VS Code ### -.vscode/ - -### SDK Man ### -.sdkmanrc - -### CSS ### +/target +.sts4-cache/ +.vscode _site/ *.css !petclinic.css diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 0000000..89964d1 --- /dev/null +++ b/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,117 @@ +/* + * 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(); + } + +} diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000..cb28b0e Binary files /dev/null and b/.mvn/wrapper/maven-wrapper.jar differ diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index 654af46..5f0536e 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1,19 +1,2 @@ -# 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 +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 diff --git a/build.gradle b/build.gradle index 36de758..a380108 100644 --- a/build.gradle +++ b/build.gradle @@ -1,50 +1,32 @@ plugins { id 'java' - 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" + 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' } apply plugin: 'java' -apply plugin: 'checkstyle' -apply plugin: 'io.spring.javaformat' - -gradle.startParameter.excludedTaskNames += [ "checkFormatAot", "checkFormatAotTest" ] group = 'org.springframework.samples' -version = '3.4.0' - -java { - sourceCompatibility = JavaVersion.VERSION_17 -} +version = '3.2.0' +sourceCompatibility = '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.3" +ext.webjarsBootstrapVersion = "5.3.2" 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' @@ -57,35 +39,8 @@ 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 diff --git a/docker-compose.yml b/docker-compose.yml index 47579bb..3d08a0e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,8 @@ +version: "2.2" + services: mysql: - image: mysql:9.1 + image: mysql:8.2 ports: - "3306:3306" environment: @@ -11,11 +13,15 @@ services: - MYSQL_DATABASE=petclinic volumes: - "./conf.d:/etc/mysql/conf.d:ro" + profiles: + - mysql postgres: - image: postgres:17.0 + image: postgres:16.1 ports: - "5432:5432" environment: - POSTGRES_PASSWORD=petclinic - POSTGRES_USER=petclinic - POSTGRES_DB=petclinic + profiles: + - postgres diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index a4b76b9..7f93135 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index df97d72..3fa8f86 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index f5feea6..1aa94a4 100755 --- a/gradlew +++ b/gradlew @@ -15,8 +15,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # -# SPDX-License-Identifier: Apache-2.0 -# ############################################################################## # @@ -57,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -86,8 +84,7 @@ 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 -P "${APP_HOME:-./}" > /dev/null && printf '%s -' "$PWD" ) || exit +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum diff --git a/gradlew.bat b/gradlew.bat index 9d21a21..6689b85 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,94 +1,92 @@ -@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 +@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 diff --git a/k8s/db.yml b/k8s/db.yml deleted file mode 100644 index c230ddb..0000000 --- a/k8s/db.yml +++ /dev/null @@ -1,73 +0,0 @@ ---- -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 diff --git a/k8s/petclinic.yml b/k8s/petclinic.yml deleted file mode 100644 index a5677cd..0000000 --- a/k8s/petclinic.yml +++ /dev/null @@ -1,64 +0,0 @@ ---- -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 diff --git a/mvnw b/mvnw index d7c358e..66df285 100755 --- a/mvnw +++ b/mvnw @@ -19,241 +19,290 @@ # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- -# Apache Maven Wrapper startup batch script, version 3.3.2 +# Apache Maven Wrapper startup batch script, version 3.2.0 +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir # # Optional ENV vars # ----------------- -# 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 +# 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 # ---------------------------------------------------------------------------- -set -euf -[ "${MVNW_VERBOSE-}" != debug ] || set -x +if [ -z "$MAVEN_SKIP_RC" ] ; then -# OS specific support. -native_path() { printf %s\\n "$1"; } + 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 case "$(uname)" in -CYGWIN* | MINGW*) - [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" - native_path() { cygpath --path --windows "$1"; } - ;; -esac - -# 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 + 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 - else - JAVACMD="$( - 'set' +e - 'unset' -f command 2>/dev/null - 'command' -v java - )" || : - JAVACCMD="$( - 'set' +e - 'unset' -f command 2>/dev/null - 'command' -v javac - )" || : - - 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 - 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 %x\\n $h -} - -verbose() { :; } -[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } - -die() { - printf %s\\n "$1" >&2 - exit 1 -} - -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:]' -} - -# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties -while IFS="=" read -r key value; do - case "${key-}" in - distributionUrl) distributionUrl=$(trim "${value-}") ;; - distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; - esac -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-,maven-mvnd--}/ -[ -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 "$@" +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=$(java-config --jre-home) + fi 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" +# 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 -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##*/}" +# 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 -# 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 +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 - 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 +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" fi else - 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 [ $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 + JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)" fi fi -# 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" +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 + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=$(cd "$wdir/.." || exit 1; pwd) + fi + # end of workaround + done + printf '%s' "$(cd "$basedir" || exit 1; pwd)" +} + +# 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 +} + +log() { + if [ "$MVNW_VERBOSE" = true ]; then + printf '%s\n' "$1" + fi +} + +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 - 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" + log "Couldn't find $wrapperJarPath, downloading it ..." -clean || : -exec_maven "$@" + if [ -n "$MVNW_REPOURL" ]; then + wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + else + wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + fi + while IFS="=" read -r key value; do + # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' ) + safeValue=$(echo "$value" | tr -d '\r') + case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;; + esac + done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" + log "Downloading from: $wrapperUrl" + + if $cygwin; then + wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") + fi + + if command -v wget > /dev/null; then + log "Found wget ... using wget" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + else + wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + log "Found curl ... using curl" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + else + curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + fi + else + log "Falling back to using Java to download" + javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java" + javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaSource=$(cygpath --path --windows "$javaSource") + javaClass=$(cygpath --path --windows "$javaClass") + fi + if [ -e "$javaSource" ]; then + if [ ! -e "$javaClass" ]; then + log " - Compiling MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/javac" "$javaSource") + fi + if [ -e "$javaClass" ]; then + log " - Running MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath" + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +# If specified, validate the SHA-256 sum of the Maven wrapper jar file +wrapperSha256Sum="" +while IFS="=" read -r key value; do + case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;; + 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 + fi + elif command -v shasum > /dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then + wrapperSha256Result=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." + 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 + 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") +fi + +# 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 "$@" diff --git a/mvnw.cmd b/mvnw.cmd index 6f779cf..95ba6f5 100644 --- a/mvnw.cmd +++ b/mvnw.cmd @@ -1,4 +1,3 @@ -<# : batch portion @REM ---------------------------------------------------------------------------- @REM Licensed to the Apache Software Foundation (ASF) under one @REM or more contributor license agreements. See the NOTICE file @@ -19,131 +18,188 @@ @REM ---------------------------------------------------------------------------- @REM ---------------------------------------------------------------------------- -@REM Apache Maven Wrapper startup batch script, version 3.3.2 +@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 @REM Optional ENV vars -@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 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 ---------------------------------------------------------------------------- -@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) +@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 ) -@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 #> -$ErrorActionPreference = "Stop" -if ($env:MVNW_VERBOSE -eq "true") { - $VerbosePreference = "Continue" -} +@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% + ) -# 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" -} + 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 -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 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 +) -# apply MVNW_REPOURL and calculate MAVEN_HOME -# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ -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" +@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=%* -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 $? -} +%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 (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { - Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" -} +:error +set ERROR_CODE=1 -# 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" } - } -} +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% -New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null +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 -# 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" +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause -$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 +if "%MAVEN_TERMINATE_CMD%"=="on" exit %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" +cmd /C exit /B %ERROR_CODE% diff --git a/pom.xml b/pom.xml index ef2dbcc..1609e8e 100644 --- a/pom.xml +++ b/pom.xml @@ -1,18 +1,17 @@ - + 4.0.0 + org.springframework.samples + spring-petclinic + 3.2.0-SNAPSHOT org.springframework.boot spring-boot-starter-parent - 3.4.0 - + 3.2.3-SNAPSHOT - - org.springframework.samples - spring-petclinic - 3.4.0-SNAPSHOT - petclinic @@ -22,21 +21,20 @@ UTF-8 UTF-8 - 2024-11-28T14:37:52Z + -DnewVersion=... --> + 2023-05-10T07:42:50Z - 1.0.1 - 5.3.3 + 5.3.2 4.7.0 - 10.20.1 - 0.8.12 + 10.12.5 + 0.8.11 0.2.29 1.0.0 - 3.6.0 + 3.3.1 0.0.11 - 0.0.43 + 0.0.40 @@ -46,6 +44,11 @@ org.springframework.boot spring-boot-starter-actuator + + org.springframework.data + spring-data-jpa + 3.2.x-3310-SNAPSHOT + org.springframework.boot spring-boot-starter-cache @@ -71,11 +74,6 @@ spring-boot-starter-test test - - - io.projectreactor - reactor-core - @@ -105,11 +103,6 @@ - - org.webjars - webjars-locator-lite - ${webjars-locator.version} - org.webjars.npm bootstrap @@ -152,11 +145,6 @@ jakarta.xml.bind-api - - io.micrometer - micrometer-registry-prometheus - - @@ -174,8 +162,7 @@ This build requires at least Java ${java.version}, - update your JVM, and - run the build again + update your JVM, and run the build again ${java.version} @@ -189,10 +176,10 @@ ${spring-format.version} + validate validate - validate @@ -215,17 +202,18 @@ nohttp-checkstyle-validation - - check - validate src/checkstyle/nohttp-checkstyle.xml + src/checkstyle/nohttp-checkstyle-suppressions.xml ${basedir} **/* - **/.git/**/*,**/.idea/**/*,**/target/**/,**/.flattened-pom.xml,**/*.class - config_loc=${basedir}/src/checkstyle/ + + **/.git/**/*,**/.idea/**/*,**/target/**/,**/.flattened-pom.xml,**/*.class + + check + @@ -238,7 +226,7 @@ spring-boot-maven-plugin - build-info @@ -266,16 +254,16 @@ report + prepare-package report - prepare-package - + io.github.git-commit-id git-commit-id-maven-plugin @@ -284,16 +272,10 @@ false - - - - org.cyclonedx - cyclonedx-maven-plugin - + Apache License, Version 2.0 @@ -303,38 +285,39 @@ - - true - spring-snapshots Spring Snapshots https://repo.spring.io/snapshot + + true + - - false - spring-milestones Spring Milestones https://repo.spring.io/milestone + + false + + - - true - spring-snapshots Spring Snapshots https://repo.spring.io/snapshot + + true + - - false - spring-milestones Spring Milestones https://repo.spring.io/milestone + + false + @@ -349,11 +332,11 @@ unpack + + generate-resources unpack - - generate-resources @@ -372,20 +355,21 @@ com.gitlab.haynes libsass-maven-plugin ${libsass.version} - - ${basedir}/src/main/scss/ - ${basedir}/src/main/resources/static/resources/css/ - ${project.build.directory}/webjars/META-INF/resources/webjars/bootstrap/${webjars-bootstrap.version}/scss/ - + generate-resources compile - generate-resources + + ${basedir}/src/main/scss/ + ${basedir}/src/main/resources/static/resources/css/ + + ${project.build.directory}/webjars/META-INF/resources/webjars/bootstrap/${webjars-bootstrap.version}/scss/ + @@ -400,7 +384,7 @@ - org.eclipse.m2e @@ -419,7 +403,7 @@ - + @@ -432,7 +416,7 @@ - + @@ -445,7 +429,7 @@ - + @@ -457,4 +441,5 @@ - + + \ No newline at end of file diff --git a/README.md b/readme.md similarity index 93% rename from README.md rename to readme.md index f159401..79b0fb6 100644 --- a/README.md +++ b/readme.md @@ -1,4 +1,4 @@ -# Spring PetClinic Sample Application [![Build Status](https://github.com/spring-projects/spring-petclinic/actions/workflows/maven-build.yml/badge.svg)](https://github.com/spring-projects/spring-petclinic/actions/workflows/maven-build.yml)[![Build Status](https://github.com/spring-projects/spring-petclinic/actions/workflows/gradle-build.yml/badge.svg)](https://github.com/spring-projects/spring-petclinic/actions/workflows/gradle-build.yml) +# Spring PetClinic Sample Application [![Build Status](https://github.com/spring-projects/spring-petclinic/actions/workflows/maven-build.yml/badge.svg)](https://github.com/spring-projects/spring-petclinic/actions/workflows/maven-build.yml) [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/spring-projects/spring-petclinic) [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](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:` 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. 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. +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. 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:9.1 +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 ``` or ```bash -docker run -e POSTGRES_USER=petclinic -e POSTGRES_PASSWORD=petclinic -e POSTGRES_DB=petclinic -p 5432:5432 postgres:17.0 +docker run -e POSTGRES_USER=petclinic -e POSTGRES_PASSWORD=petclinic -e POSTGRES_DB=petclinic -p 5432:5432 postgres:16.1 ``` 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 service named after 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 profile just like the Spring profile: ```bash -docker compose up mysql +docker-compose --profile mysql up ``` or ```bash -docker compose up postgres +docker-compose --profile postgres up ``` ## Test Applications diff --git a/src/checkstyle/nohttp-checkstyle-suppressions.xml b/src/checkstyle/nohttp-checkstyle-suppressions.xml index f00599b..7c118a7 100644 --- a/src/checkstyle/nohttp-checkstyle-suppressions.xml +++ b/src/checkstyle/nohttp-checkstyle-suppressions.xml @@ -3,9 +3,8 @@ "-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN" "https://checkstyle.org/dtds/suppressions_1_2.dtd"> - - - - + + + diff --git a/src/checkstyle/nohttp-checkstyle.xml b/src/checkstyle/nohttp-checkstyle.xml index f886411..e620512 100644 --- a/src/checkstyle/nohttp-checkstyle.xml +++ b/src/checkstyle/nohttp-checkstyle.xml @@ -4,7 +4,4 @@ "https://checkstyle.org/dtds/configuration_1_2.dtd"> - - - diff --git a/src/main/java/org/springframework/samples/petclinic/PetClinicRuntimeHints.java b/src/main/java/org/springframework/samples/petclinic/PetClinicRuntimeHints.java index 2975923..7acecf4 100644 --- a/src/main/java/org/springframework/samples/petclinic/PetClinicRuntimeHints.java +++ b/src/main/java/org/springframework/samples/petclinic/PetClinicRuntimeHints.java @@ -28,6 +28,7 @@ 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); diff --git a/src/main/java/org/springframework/samples/petclinic/model/NamedEntity.java b/src/main/java/org/springframework/samples/petclinic/model/NamedEntity.java index 012e8c4..7c2ccb2 100644 --- a/src/main/java/org/springframework/samples/petclinic/model/NamedEntity.java +++ b/src/main/java/org/springframework/samples/petclinic/model/NamedEntity.java @@ -17,7 +17,6 @@ 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 BaseEntity. Used as @@ -25,13 +24,11 @@ import jakarta.validation.constraints.NotBlank; * * @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() { diff --git a/src/main/java/org/springframework/samples/petclinic/owner/Owner.java b/src/main/java/org/springframework/samples/petclinic/owner/Owner.java index 675b214..c739468 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/Owner.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/Owner.java @@ -30,7 +30,7 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.OneToMany; import jakarta.persistence.OrderBy; import jakarta.persistence.Table; -import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Digits; import jakarta.validation.constraints.NotBlank; /** @@ -41,7 +41,6 @@ import jakarta.validation.constraints.NotBlank; * @author Sam Brannen * @author Michael Isvy * @author Oliver Drotbohm - * @author Wick Dynex */ @Entity @Table(name = "owners") @@ -57,13 +56,13 @@ public class Owner extends Person { @Column(name = "telephone") @NotBlank - @Pattern(regexp = "\\d{10}", message = "Telephone must be a 10-digit number") + @Digits(fraction = 0, integer = 10) private String telephone; @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) @JoinColumn(name = "owner_id") @OrderBy("name") - private final List pets = new ArrayList<>(); + private List pets = new ArrayList<>(); public String getAddress() { return this.address; @@ -102,7 +101,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 the Pet with the given name, or null if no such Pet exists for this Owner + * @return a pet if pet name is already in use */ public Pet getPet(String name) { return getPet(name, false); @@ -111,7 +110,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 the Pet with the given id, or null if no such Pet exists for this Owner + * @return a pet if pet id is already in use */ public Pet getPet(Integer id) { for (Pet pet : getPets()) { @@ -128,10 +127,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 - * @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 + * @return a pet if pet name is already in use */ public Pet getPet(String name, boolean ignoreNew) { + name = name.toLowerCase(); for (Pet pet : getPets()) { String compName = pet.getName(); if (compName != null && compName.equalsIgnoreCase(name)) { diff --git a/src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java b/src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java index fa35064..329c823 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java @@ -16,7 +16,7 @@ package org.springframework.samples.petclinic.owner; import java.util.List; -import java.util.Optional; +import java.util.Map; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -34,14 +34,12 @@ 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 { @@ -50,8 +48,8 @@ class OwnerController { private final OwnerRepository owners; - public OwnerController(OwnerRepository owners) { - this.owners = owners; + public OwnerController(OwnerRepository clinicService) { + this.owners = clinicService; } @InitBinder @@ -61,26 +59,23 @@ class OwnerController { @ModelAttribute("owner") public Owner findOwner(@PathVariable(name = "ownerId", required = false) Integer 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.")); + return ownerId == null ? new Owner() : this.owners.findById(ownerId); } @GetMapping("/owners/new") - public String initCreationForm() { + public String initCreationForm(Map model) { + Owner owner = new Owner(); + model.put("owner", owner); return VIEWS_OWNER_CREATE_OR_UPDATE_FORM; } @PostMapping("/owners/new") - public String processCreationForm(@Valid Owner owner, BindingResult result, RedirectAttributes redirectAttributes) { + public String processCreationForm(@Valid Owner owner, BindingResult result) { 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(); } @@ -127,31 +122,25 @@ class OwnerController { private Page findPaginatedForOwnersLastName(int page, String lastname) { int pageSize = 5; Pageable pageable = PageRequest.of(page - 1, pageSize); - return owners.findByLastNameStartingWith(lastname, pageable); + return owners.findByLastName(lastname, pageable); } @GetMapping("/owners/{ownerId}/edit") - public String initUpdateOwnerForm() { + public String initUpdateOwnerForm(@PathVariable("ownerId") int ownerId, Model model) { + Owner owner = this.owners.findById(ownerId); + model.addAttribute(owner); return VIEWS_OWNER_CREATE_OR_UPDATE_FORM; } @PostMapping("/owners/{ownerId}/edit") - public String processUpdateOwnerForm(@Valid Owner owner, BindingResult result, @PathVariable("ownerId") int ownerId, - RedirectAttributes redirectAttributes) { + public String processUpdateOwnerForm(@Valid Owner owner, BindingResult result, + @PathVariable("ownerId") int ownerId) { 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}"; } @@ -163,9 +152,7 @@ class OwnerController { @GetMapping("/owners/{ownerId}") public ModelAndView showOwner(@PathVariable("ownerId") int ownerId) { ModelAndView mav = new ModelAndView("owners/ownerDetails"); - Optional optionalOwner = this.owners.findById(ownerId); - Owner owner = optionalOwner.orElseThrow(() -> new IllegalArgumentException( - "Owner not found with id: " + ownerId + ". Please ensure the ID is correct ")); + Owner owner = this.owners.findById(ownerId); mav.addObject(owner); return mav; } diff --git a/src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java b/src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java index 5d7a40f..f444494 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java @@ -16,13 +16,12 @@ 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; /** @@ -35,15 +34,15 @@ import org.springframework.transaction.annotation.Transactional; * @author Juergen Hoeller * @author Sam Brannen * @author Michael Isvy - * @author Wick Dynex */ -public interface OwnerRepository extends JpaRepository { +public interface OwnerRepository extends Repository { /** * 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 findPetTypes(); /** @@ -53,26 +52,31 @@ public interface OwnerRepository extends JpaRepository { * @return a Collection of matching {@link Owner}s (or an empty Collection if none * found) */ - Page findByLastNameStartingWith(String lastName, Pageable pageable); + + @Query("SELECT DISTINCT owner FROM Owner owner left join owner.pets WHERE owner.lastName LIKE :lastName% ") + @Transactional(readOnly = true) + Page findByLastName(@Param("lastName") String lastName, Pageable pageable); /** * Retrieve an {@link Owner} from the data store by id. - *

- * 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}. - *

* @param id the id to search for - * @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) + * @return the {@link Owner} if found */ - Optional findById(@Nonnull Integer 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); /** * Returns all the owners from data store **/ + @Query("SELECT owner FROM Owner owner") + @Transactional(readOnly = true) Page findAll(Pageable pageable); } diff --git a/src/main/java/org/springframework/samples/petclinic/owner/Pet.java b/src/main/java/org/springframework/samples/petclinic/owner/Pet.java old mode 100644 new mode 100755 index 03fd26c..0b0c08a --- a/src/main/java/org/springframework/samples/petclinic/owner/Pet.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/Pet.java @@ -39,7 +39,6 @@ import jakarta.persistence.Table; * @author Ken Krebs * @author Juergen Hoeller * @author Sam Brannen - * @author Wick Dynex */ @Entity @Table(name = "pets") @@ -56,7 +55,7 @@ public class Pet extends NamedEntity { @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) @JoinColumn(name = "pet_id") @OrderBy("visit_date ASC") - private final Set visits = new LinkedHashSet<>(); + private Set visits = new LinkedHashSet<>(); public void setBirthDate(LocalDate birthDate) { this.birthDate = birthDate; diff --git a/src/main/java/org/springframework/samples/petclinic/owner/PetController.java b/src/main/java/org/springframework/samples/petclinic/owner/PetController.java index fcf431b..0574566 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/PetController.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/PetController.java @@ -17,7 +17,6 @@ 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; @@ -32,13 +31,11 @@ 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}") @@ -59,9 +56,11 @@ class PetController { @ModelAttribute("owner") public Owner findOwner(@PathVariable("ownerId") int ownerId) { - Optional optionalOwner = this.owners.findById(ownerId); - Owner owner = optionalOwner.orElseThrow(() -> new IllegalArgumentException( - "Owner not found with id: " + ownerId + ". Please ensure the ID is correct ")); + + Owner owner = this.owners.findById(ownerId); + if (owner == null) { + throw new IllegalArgumentException("Owner ID not found: " + ownerId); + } return owner; } @@ -73,9 +72,10 @@ class PetController { return new Pet(); } - Optional optionalOwner = this.owners.findById(ownerId); - Owner owner = optionalOwner.orElseThrow(() -> new IllegalArgumentException( - "Owner not found with id: " + ownerId + ". Please ensure the ID is correct ")); + Owner owner = this.owners.findById(ownerId); + if (owner == null) { + throw new IllegalArgumentException("Owner ID not found: " + ownerId); + } return owner.getPet(petId); } @@ -93,46 +93,47 @@ 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, - RedirectAttributes redirectAttributes) { - - if (StringUtils.hasText(pet.getName()) && pet.isNew() && owner.getPet(pet.getName(), true) != null) + 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) { 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() { + public String initUpdateForm(Owner owner, @PathVariable("petId") int petId, ModelMap model) { + Pet pet = owner.getPet(petId); + model.put("pet", pet); return VIEWS_PETS_CREATE_OR_UPDATE_FORM; } @PostMapping("/pets/{petId}/edit") - public String processUpdateForm(Owner owner, @Valid Pet pet, BindingResult result, - RedirectAttributes redirectAttributes) { + public String processUpdateForm(@Valid Pet pet, BindingResult result, Owner owner, ModelMap model) { String petName = pet.getName(); // checking if the pet name already exist for the owner if (StringUtils.hasText(petName)) { - Pet existingPet = owner.getPet(petName, false); - if (existingPet != null && !existingPet.getId().equals(pet.getId())) { + Pet existingPet = owner.getPet(petName.toLowerCase(), false); + if (existingPet != null && existingPet.getId() != pet.getId()) { result.rejectValue("name", "duplicate", "already exists"); } } @@ -143,12 +144,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}"; } diff --git a/src/main/java/org/springframework/samples/petclinic/owner/Visit.java b/src/main/java/org/springframework/samples/petclinic/owner/Visit.java old mode 100644 new mode 100755 diff --git a/src/main/java/org/springframework/samples/petclinic/owner/VisitController.java b/src/main/java/org/springframework/samples/petclinic/owner/VisitController.java index b546f60..c823d91 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/VisitController.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/VisitController.java @@ -16,7 +16,6 @@ package org.springframework.samples.petclinic.owner; import java.util.Map; -import java.util.Optional; import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; @@ -28,7 +27,6 @@ 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 @@ -36,7 +34,6 @@ import org.springframework.web.servlet.mvc.support.RedirectAttributes; * @author Arjen Poutsma * @author Michael Isvy * @author Dave Syer - * @author Wick Dynex */ @Controller class VisitController { @@ -62,9 +59,7 @@ class VisitController { @ModelAttribute("visit") public Visit loadPetWithVisit(@PathVariable("ownerId") int ownerId, @PathVariable("petId") int petId, Map model) { - Optional optionalOwner = owners.findById(ownerId); - Owner owner = optionalOwner.orElseThrow(() -> new IllegalArgumentException( - "Owner not found with id: " + ownerId + ". Please ensure the ID is correct ")); + Owner owner = this.owners.findById(ownerId); Pet pet = owner.getPet(petId); model.put("pet", pet); @@ -86,14 +81,13 @@ class VisitController { // called @PostMapping("/owners/{ownerId}/pets/{petId}/visits/new") public String processNewVisitForm(@ModelAttribute Owner owner, @PathVariable int petId, @Valid Visit visit, - BindingResult result, RedirectAttributes redirectAttributes) { + BindingResult result) { 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}"; } diff --git a/src/main/java/org/springframework/samples/petclinic/system/CacheConfiguration.java b/src/main/java/org/springframework/samples/petclinic/system/CacheConfiguration.java old mode 100644 new mode 100755 diff --git a/src/main/java/org/springframework/samples/petclinic/vet/Vet.java b/src/main/java/org/springframework/samples/petclinic/vet/Vet.java index d8a1fc8..7a70155 100644 --- a/src/main/java/org/springframework/samples/petclinic/vet/Vet.java +++ b/src/main/java/org/springframework/samples/petclinic/vet/Vet.java @@ -57,6 +57,10 @@ public class Vet extends Person { return this.specialties; } + protected void setSpecialtiesInternal(Set specialties) { + this.specialties = specialties; + } + @XmlElement public List getSpecialties() { List sortedSpecs = new ArrayList<>(getSpecialtiesInternal()); diff --git a/src/main/java/org/springframework/samples/petclinic/vet/VetController.java b/src/main/java/org/springframework/samples/petclinic/vet/VetController.java index 29fcecc..3240814 100644 --- a/src/main/java/org/springframework/samples/petclinic/vet/VetController.java +++ b/src/main/java/org/springframework/samples/petclinic/vet/VetController.java @@ -37,8 +37,8 @@ class VetController { private final VetRepository vetRepository; - public VetController(VetRepository vetRepository) { - this.vetRepository = vetRepository; + public VetController(VetRepository clinicService) { + this.vetRepository = clinicService; } @GetMapping("/vets.html") diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 6ed9856..5d3eeed 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -8,7 +8,7 @@ spring.thymeleaf.mode=HTML # JPA spring.jpa.hibernate.ddl-auto=none -spring.jpa.open-in-view=false +spring.jpa.open-in-view=true # Internationalization spring.messages.basename=messages/messages diff --git a/src/main/resources/db/postgres/data.sql b/src/main/resources/db/postgres/data.sql index 5b53366..96c9d46 100644 --- a/src/main/resources/db/postgres/data.sql +++ b/src/main/resources/db/postgres/data.sql @@ -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 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 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 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); diff --git a/src/main/resources/messages/messages_fa.properties b/src/main/resources/messages/messages_fa.properties deleted file mode 100644 index a68a21c..0000000 --- a/src/main/resources/messages/messages_fa.properties +++ /dev/null @@ -1,9 +0,0 @@ -welcome=خوش آمدید -required=الزامی -notFound=یافت نشد -duplicate=قبلا استفاده شده -nonNumeric=باید عددی باشد -duplicateFormSubmission=ارسال تکراری فرم مجاز نیست -typeMismatch.date=تاریخ نامعتبر -typeMismatch.birthDate=تاریخ تولد نامعتبر - diff --git a/src/main/resources/messages/messages_pt.properties b/src/main/resources/messages/messages_pt.properties deleted file mode 100644 index e9bc35a..0000000 --- a/src/main/resources/messages/messages_pt.properties +++ /dev/null @@ -1,8 +0,0 @@ -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 diff --git a/src/main/resources/messages/messages_ru.properties b/src/main/resources/messages/messages_ru.properties deleted file mode 100644 index 7e8d54d..0000000 --- a/src/main/resources/messages/messages_ru.properties +++ /dev/null @@ -1,9 +0,0 @@ -welcome=Добро пожаловать -required=необходимо -notFound=не найдено -duplicate=уже используется -nonNumeric=должно быть все числовое значение -duplicateFormSubmission=Дублирование формы не допускается -typeMismatch.date=неправильная даные -typeMismatch.birthDate=неправильная дата - diff --git a/src/main/resources/messages/messages_tr.properties b/src/main/resources/messages/messages_tr.properties deleted file mode 100644 index 1020566..0000000 --- a/src/main/resources/messages/messages_tr.properties +++ /dev/null @@ -1,9 +0,0 @@ -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 - diff --git a/src/main/resources/static/resources/css/petclinic.css b/src/main/resources/static/resources/css/petclinic.css index bbf3f0d..a2952a6 100644 --- a/src/main/resources/static/resources/css/petclinic.css +++ b/src/main/resources/static/resources/css/petclinic.css @@ -12,8 +12,8 @@ * limitations under the License. */ /*! - * Bootstrap v5.3.3 (https://getbootstrap.com/) - * Copyright 2011-2024 The Bootstrap Authors + * Bootstrap v5.3.2 (https://getbootstrap.com/) + * Copyright 2011-2023 The Bootstrap Authors * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) */ :root, @@ -2377,8 +2377,6 @@ 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; @@ -3682,11 +3680,12 @@ 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='none' stroke='%23212529' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M2 5L8 11L14 5'/%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='%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-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='none' stroke='%23052c65' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M2 5L8 11L14 5'/%3e%3c/svg%3e"); + --bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%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-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; @@ -3734,6 +3733,7 @@ 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-header .accordion-button { + .accordion-item:first-of-type .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,27 +3755,28 @@ 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-header .accordion-button.collapsed { + .accordion-item:last-of-type .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-item { +.accordion-flush .accordion-collapse { + border-width: 0; } + +.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-header .accordion-button, .accordion-flush > .accordion-item > .accordion-header .accordion-button.collapsed { - border-radius: 0; } - .accordion-flush > .accordion-item > .accordion-collapse { + .accordion-flush .accordion-item .accordion-button, .accordion-flush .accordion-item .accordion-button.collapsed { border-radius: 0; } [data-bs-theme="dark"] .accordion-button::after { @@ -4523,6 +4524,7 @@ 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); @@ -4995,11 +4997,19 @@ 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") /*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")*/; } + 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"); } .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") /*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")*/; } + 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"); } .carousel-indicators { position: absolute; @@ -5518,10 +5528,13 @@ 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: calc(-.5 * var(--bs-offcanvas-padding-y)) calc(-.5 * var(--bs-offcanvas-padding-x)) calc(-.5 * var(--bs-offcanvas-padding-y)) auto; } + 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)); } .offcanvas-title { margin-bottom: 0; diff --git a/src/main/resources/templates/fragments/layout.html b/src/main/resources/templates/fragments/layout.html old mode 100644 new mode 100755 index eb54d28..d3250cc --- a/src/main/resources/templates/fragments/layout.html +++ b/src/main/resources/templates/fragments/layout.html @@ -17,7 +17,7 @@ - + @@ -87,7 +87,7 @@ - + diff --git a/src/main/resources/templates/owners/ownerDetails.html b/src/main/resources/templates/owners/ownerDetails.html index 15bca4a..41f7d16 100644 --- a/src/main/resources/templates/owners/ownerDetails.html +++ b/src/main/resources/templates/owners/ownerDetails.html @@ -7,18 +7,8 @@

Owner Information

- -
- -
- -
- -
- - - - + + @@ -83,20 +73,7 @@
Name
- - + - diff --git a/src/test/java/org/springframework/samples/petclinic/MySqlIntegrationTests.java b/src/test/java/org/springframework/samples/petclinic/MySqlIntegrationTests.java index d67e449..867ef90 100644 --- a/src/test/java/org/springframework/samples/petclinic/MySqlIntegrationTests.java +++ b/src/test/java/org/springframework/samples/petclinic/MySqlIntegrationTests.java @@ -46,7 +46,7 @@ class MySqlIntegrationTests { @ServiceConnection @Container - static MySQLContainer container = new MySQLContainer<>("mysql:9.1"); + static MySQLContainer container = new MySQLContainer<>("mysql:8.2"); @LocalServerPort int port; @@ -58,7 +58,7 @@ class MySqlIntegrationTests { private RestTemplateBuilder builder; @Test - void testFindAll() { + void testFindAll() throws Exception { vets.findAll(); vets.findAll(); // served from cache } diff --git a/src/test/java/org/springframework/samples/petclinic/MysqlTestApplication.java b/src/test/java/org/springframework/samples/petclinic/MysqlTestApplication.java index 8c7560a..a3ee84b 100644 --- a/src/test/java/org/springframework/samples/petclinic/MysqlTestApplication.java +++ b/src/test/java/org/springframework/samples/petclinic/MysqlTestApplication.java @@ -36,7 +36,7 @@ public class MysqlTestApplication { @Profile("mysql") @Bean static MySQLContainer container() { - return new MySQLContainer<>("mysql:9.1"); + return new MySQLContainer<>("mysql:8.2"); } public static void main(String[] args) { diff --git a/src/test/java/org/springframework/samples/petclinic/PetClinicIntegrationTests.java b/src/test/java/org/springframework/samples/petclinic/PetClinicIntegrationTests.java index 6d98206..6472a12 100644 --- a/src/test/java/org/springframework/samples/petclinic/PetClinicIntegrationTests.java +++ b/src/test/java/org/springframework/samples/petclinic/PetClinicIntegrationTests.java @@ -44,7 +44,7 @@ public class PetClinicIntegrationTests { private RestTemplateBuilder builder; @Test - void testFindAll() { + void testFindAll() throws Exception { vets.findAll(); vets.findAll(); // served from cache } diff --git a/src/test/java/org/springframework/samples/petclinic/PostgresIntegrationTests.java b/src/test/java/org/springframework/samples/petclinic/PostgresIntegrationTests.java index 0b9e4f9..18945a5 100644 --- a/src/test/java/org/springframework/samples/petclinic/PostgresIntegrationTests.java +++ b/src/test/java/org/springframework/samples/petclinic/PostgresIntegrationTests.java @@ -17,7 +17,6 @@ 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; @@ -49,7 +48,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.start.arguments=--force-recreate,--renew-anon-volumes,postgres" }) + "spring.docker.compose.profiles.active=postgres" }) @ActiveProfiles("postgres") @DisabledInNativeImage public class PostgresIntegrationTests { @@ -72,7 +71,7 @@ public class PostgresIntegrationTests { new SpringApplicationBuilder(PetClinicApplication.class) // .profiles("postgres") // .properties( // - "spring.docker.compose.start.arguments=postgres" // + "spring.docker.compose.profiles.active=postgres" // ) // .listeners(new PropertiesLogger()) // .run(args); @@ -115,16 +114,7 @@ public class PostgresIntegrationTests { Arrays.sort(names); for (String name : names) { String resolved = environment.getProperty(name); - - 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(); + String value = source.getProperty(name).toString(); if (resolved.equals(value)) { log.info(name + "=" + resolved); } diff --git a/src/test/java/org/springframework/samples/petclinic/model/ValidatorTests.java b/src/test/java/org/springframework/samples/petclinic/model/ValidatorTests.java index c753285..9fcd598 100644 --- a/src/test/java/org/springframework/samples/petclinic/model/ValidatorTests.java +++ b/src/test/java/org/springframework/samples/petclinic/model/ValidatorTests.java @@ -53,7 +53,7 @@ class ValidatorTests { assertThat(constraintViolations).hasSize(1); ConstraintViolation violation = constraintViolations.iterator().next(); - assertThat(violation.getPropertyPath()).hasToString("firstName"); + assertThat(violation.getPropertyPath().toString()).isEqualTo("firstName"); assertThat(violation.getMessage()).isEqualTo("must not be blank"); } diff --git a/src/test/java/org/springframework/samples/petclinic/owner/OwnerControllerTests.java b/src/test/java/org/springframework/samples/petclinic/owner/OwnerControllerTests.java index 426ca5c..78b8123 100644 --- a/src/test/java/org/springframework/samples/petclinic/owner/OwnerControllerTests.java +++ b/src/test/java/org/springframework/samples/petclinic/owner/OwnerControllerTests.java @@ -16,44 +16,43 @@ 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.*; +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; /** * Test class for {@link OwnerController} * * @author Colin But - * @author Wick Dynex */ @WebMvcTest(OwnerController.class) @DisabledInNativeImage @@ -65,7 +64,7 @@ class OwnerControllerTests { @Autowired private MockMvc mockMvc; - @MockitoBean + @MockBean private OwnerRepository owners; private Owner george() { @@ -85,18 +84,18 @@ class OwnerControllerTests { george.addPet(max); max.setId(1); return george; - } + }; @BeforeEach void setup() { Owner george = george(); - given(this.owners.findByLastNameStartingWith(eq("Franklin"), any(Pageable.class))) - .willReturn(new PageImpl<>(Lists.newArrayList(george))); + given(this.owners.findByLastName(eq("Franklin"), any(Pageable.class))) + .willReturn(new PageImpl(Lists.newArrayList(george))); - given(this.owners.findAll(any(Pageable.class))).willReturn(new PageImpl<>(Lists.newArrayList(george))); + given(this.owners.findAll(any(Pageable.class))).willReturn(new PageImpl(Lists.newArrayList(george))); - given(this.owners.findById(TEST_OWNER_ID)).willReturn(Optional.of(george)); + given(this.owners.findById(TEST_OWNER_ID)).willReturn(george); Visit visit = new Visit(); visit.setDate(LocalDate.now()); george.getPet("Max").getVisits().add(visit); @@ -118,7 +117,7 @@ class OwnerControllerTests { .param("lastName", "Bloggs") .param("address", "123 Caramel Street") .param("city", "London") - .param("telephone", "1316761638")) + .param("telephone", "01316761638")) .andExpect(status().is3xxRedirection()); } @@ -143,15 +142,15 @@ class OwnerControllerTests { @Test void testProcessFindFormSuccess() throws Exception { - Page tasks = new PageImpl<>(Lists.newArrayList(george(), new Owner())); - when(this.owners.findByLastNameStartingWith(anyString(), any(Pageable.class))).thenReturn(tasks); + Page tasks = new PageImpl(Lists.newArrayList(george(), new Owner())); + Mockito.when(this.owners.findByLastName(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 tasks = new PageImpl<>(Lists.newArrayList(george())); - when(this.owners.findByLastNameStartingWith(eq("Franklin"), any(Pageable.class))).thenReturn(tasks); + Page tasks = new PageImpl(Lists.newArrayList(george())); + Mockito.when(this.owners.findByLastName(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)); @@ -159,8 +158,8 @@ class OwnerControllerTests { @Test void testProcessFindFormNoOwnersFound() throws Exception { - Page tasks = new PageImpl<>(Lists.newArrayList()); - when(this.owners.findByLastNameStartingWith(eq("Unknown Surname"), any(Pageable.class))).thenReturn(tasks); + Page tasks = new PageImpl(Lists.newArrayList()); + Mockito.when(this.owners.findByLastName(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")) @@ -189,7 +188,7 @@ class OwnerControllerTests { .param("lastName", "Bloggs") .param("address", "123 Caramel Street") .param("city", "London") - .param("telephone", "1616291589")) + .param("telephone", "01616291589")) .andExpect(status().is3xxRedirection()) .andExpect(view().name("redirect:/owners/{ownerId}")); } @@ -225,29 +224,25 @@ 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", hasItem(hasProperty("visits", hasSize(greaterThan(0))))))) + .andExpect(model().attribute("owner", hasProperty("pets", new BaseMatcher>() { + + @Override + public boolean matches(Object item) { + @SuppressWarnings("unchecked") + List pets = (List) item; + Pet pet = pets.get(0); + if (pet.getVisits().isEmpty()) { + return false; + } + return true; + } + + @Override + public void describeTo(Description description) { + description.appendText("Max did not have any visits"); + } + }))) .andExpect(view().name("owners/ownerDetails")); } - @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")); - } - } diff --git a/src/test/java/org/springframework/samples/petclinic/owner/PetControllerTests.java b/src/test/java/org/springframework/samples/petclinic/owner/PetControllerTests.java old mode 100644 new mode 100755 index 9a6134c..73b83f9 --- a/src/test/java/org/springframework/samples/petclinic/owner/PetControllerTests.java +++ b/src/test/java/org/springframework/samples/petclinic/owner/PetControllerTests.java @@ -18,20 +18,16 @@ 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; @@ -43,7 +39,6 @@ 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)) @@ -58,7 +53,7 @@ class PetControllerTests { @Autowired private MockMvc mockMvc; - @MockitoBean + @MockBean private OwnerRepository owners; @BeforeEach @@ -67,17 +62,11 @@ 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); - dog.setId(TEST_PET_ID + 1); - pet.setName("petty"); - dog.setName("doggy"); - given(this.owners.findById(TEST_OWNER_ID)).willReturn(Optional.of(owner)); + given(this.owners.findById(TEST_OWNER_ID)).willReturn(owner); } @Test @@ -98,72 +87,25 @@ class PetControllerTests { .andExpect(view().name("redirect:/owners/{ownerId}")); } - @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 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")); + } + @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 @@ -176,33 +118,15 @@ class PetControllerTests { .andExpect(view().name("redirect:/owners/{ownerId}")); } - @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")); - } - + @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")); } } diff --git a/src/test/java/org/springframework/samples/petclinic/owner/PetTypeFormatterTests.java b/src/test/java/org/springframework/samples/petclinic/owner/PetTypeFormatterTests.java index 0295b47..dabade7 100644 --- a/src/test/java/org/springframework/samples/petclinic/owner/PetTypeFormatterTests.java +++ b/src/test/java/org/springframework/samples/petclinic/owner/PetTypeFormatterTests.java @@ -68,7 +68,7 @@ class PetTypeFormatterTests { } @Test - void shouldThrowParseException() { + void shouldThrowParseException() throws ParseException { given(this.pets.findPetTypes()).willReturn(makePetTypes()); Assertions.assertThrows(ParseException.class, () -> { petTypeFormatter.parse("Fish", Locale.ENGLISH); diff --git a/src/test/java/org/springframework/samples/petclinic/owner/PetValidatorTests.java b/src/test/java/org/springframework/samples/petclinic/owner/PetValidatorTests.java deleted file mode 100644 index 1a153bc..0000000 --- a/src/test/java/org/springframework/samples/petclinic/owner/PetValidatorTests.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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")); - } - - } - -} diff --git a/src/test/java/org/springframework/samples/petclinic/owner/VisitControllerTests.java b/src/test/java/org/springframework/samples/petclinic/owner/VisitControllerTests.java index e42e750..3565d92 100644 --- a/src/test/java/org/springframework/samples/petclinic/owner/VisitControllerTests.java +++ b/src/test/java/org/springframework/samples/petclinic/owner/VisitControllerTests.java @@ -28,17 +28,14 @@ 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 @@ -52,7 +49,7 @@ class VisitControllerTests { @Autowired private MockMvc mockMvc; - @MockitoBean + @MockBean private OwnerRepository owners; @BeforeEach @@ -61,7 +58,7 @@ class VisitControllerTests { Pet pet = new Pet(); owner.addPet(pet); pet.setId(TEST_PET_ID); - given(this.owners.findById(TEST_OWNER_ID)).willReturn(Optional.of(owner)); + given(this.owners.findById(TEST_OWNER_ID)).willReturn(owner); } @Test diff --git a/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceTests.java b/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceTests.java index 1736027..d7240f3 100644 --- a/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceTests.java +++ b/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceTests.java @@ -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,6 +36,7 @@ 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; /** @@ -66,7 +67,7 @@ import org.springframework.transaction.annotation.Transactional; * @author Michael Isvy * @author Dave Syer */ -@DataJpaTest +@DataJpaTest(includeFilters = @ComponentScan.Filter(Service.class)) // Ensure that if the mysql profile is active we connect to the real database: @AutoConfigureTestDatabase(replace = Replace.NONE) // @TestPropertySource("/application-postgres.properties") @@ -82,18 +83,16 @@ class ClinicServiceTests { @Test void shouldFindOwnersByLastName() { - Page owners = this.owners.findByLastNameStartingWith("Davis", pageable); + Page owners = this.owners.findByLastName("Davis", pageable); assertThat(owners).hasSize(2); - owners = this.owners.findByLastNameStartingWith("Daviss", pageable); + owners = this.owners.findByLastName("Daviss", pageable); assertThat(owners).isEmpty(); } @Test void shouldFindSingleOwnerWithPet() { - Optional optionalOwner = this.owners.findById(1); - assertThat(optionalOwner).isPresent(); - Owner owner = optionalOwner.get(); + Owner owner = this.owners.findById(1); assertThat(owner.getLastName()).startsWith("Franklin"); assertThat(owner.getPets()).hasSize(1); assertThat(owner.getPets().get(0).getType()).isNotNull(); @@ -103,7 +102,7 @@ class ClinicServiceTests { @Test @Transactional void shouldInsertOwner() { - Page owners = this.owners.findByLastNameStartingWith("Schultz", pageable); + Page owners = this.owners.findByLastName("Schultz", pageable); int found = (int) owners.getTotalElements(); Owner owner = new Owner(); @@ -113,18 +112,16 @@ class ClinicServiceTests { owner.setCity("Wollongong"); owner.setTelephone("4444444444"); this.owners.save(owner); - assertThat(owner.getId()).isNotZero(); + assertThat(owner.getId().longValue()).isNotEqualTo(0); - owners = this.owners.findByLastNameStartingWith("Schultz", pageable); + owners = this.owners.findByLastName("Schultz", pageable); assertThat(owners.getTotalElements()).isEqualTo(found + 1); } @Test @Transactional void shouldUpdateOwner() { - Optional optionalOwner = this.owners.findById(1); - assertThat(optionalOwner).isPresent(); - Owner owner = optionalOwner.get(); + Owner owner = this.owners.findById(1); String oldLastName = owner.getLastName(); String newLastName = oldLastName + "X"; @@ -132,9 +129,7 @@ class ClinicServiceTests { this.owners.save(owner); // retrieving new name from database - optionalOwner = this.owners.findById(1); - assertThat(optionalOwner).isPresent(); - owner = optionalOwner.get(); + owner = this.owners.findById(1); assertThat(owner.getLastName()).isEqualTo(newLastName); } @@ -151,10 +146,7 @@ class ClinicServiceTests { @Test @Transactional void shouldInsertPetIntoDatabaseAndGenerateId() { - Optional optionalOwner = this.owners.findById(6); - assertThat(optionalOwner).isPresent(); - Owner owner6 = optionalOwner.get(); - + Owner owner6 = this.owners.findById(6); int found = owner6.getPets().size(); Pet pet = new Pet(); @@ -163,14 +155,12 @@ class ClinicServiceTests { pet.setType(EntityUtils.getById(types, PetType.class, 2)); pet.setBirthDate(LocalDate.now()); owner6.addPet(pet); - assertThat(owner6.getPets()).hasSize(found + 1); + assertThat(owner6.getPets().size()).isEqualTo(found + 1); this.owners.save(owner6); - optionalOwner = this.owners.findById(6); - assertThat(optionalOwner).isPresent(); - owner6 = optionalOwner.get(); - assertThat(owner6.getPets()).hasSize(found + 1); + owner6 = this.owners.findById(6); + assertThat(owner6.getPets().size()).isEqualTo(found + 1); // checks that id has been generated pet = owner6.getPet("bowser"); assertThat(pet.getId()).isNotNull(); @@ -178,11 +168,8 @@ class ClinicServiceTests { @Test @Transactional - void shouldUpdatePetName() { - Optional optionalOwner = this.owners.findById(6); - assertThat(optionalOwner).isPresent(); - Owner owner6 = optionalOwner.get(); - + void shouldUpdatePetName() throws Exception { + Owner owner6 = this.owners.findById(6); Pet pet7 = owner6.getPet(7); String oldName = pet7.getName(); @@ -190,9 +177,7 @@ class ClinicServiceTests { pet7.setName(newName); this.owners.save(owner6); - optionalOwner = this.owners.findById(6); - assertThat(optionalOwner).isPresent(); - owner6 = optionalOwner.get(); + owner6 = this.owners.findById(6); pet7 = owner6.getPet(7); assertThat(pet7.getName()).isEqualTo(newName); } @@ -211,10 +196,7 @@ class ClinicServiceTests { @Test @Transactional void shouldAddNewVisitForPet() { - Optional optionalOwner = this.owners.findById(6); - assertThat(optionalOwner).isPresent(); - Owner owner6 = optionalOwner.get(); - + Owner owner6 = this.owners.findById(6); Pet pet7 = owner6.getPet(7); int found = pet7.getVisits().size(); Visit visit = new Visit(); @@ -223,17 +205,16 @@ 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() { - Optional optionalOwner = this.owners.findById(6); - assertThat(optionalOwner).isPresent(); - Owner owner6 = optionalOwner.get(); - + void shouldFindVisitsByPetId() throws Exception { + Owner owner6 = this.owners.findById(6); Pet pet7 = owner6.getPet(7); Collection visits = pet7.getVisits(); diff --git a/src/test/java/org/springframework/samples/petclinic/system/CrashControllerIntegrationTests.java b/src/test/java/org/springframework/samples/petclinic/system/CrashControllerIntegrationTests.java index 3cdca47..4edf2af 100644 --- a/src/test/java/org/springframework/samples/petclinic/system/CrashControllerIntegrationTests.java +++ b/src/test/java/org/springframework/samples/petclinic/system/CrashControllerIntegrationTests.java @@ -35,7 +35,6 @@ 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; @@ -69,10 +68,10 @@ class CrashControllerIntegrationTests { new ParameterizedTypeReference>() { }); assertThat(resp).isNotNull(); - 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.getStatusCode().is5xxServerError()); + 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"); @@ -85,7 +84,7 @@ class CrashControllerIntegrationTests { ResponseEntity resp = rest.exchange("http://localhost:" + port + "/oups", HttpMethod.GET, new HttpEntity<>(headers), String.class); assertThat(resp).isNotNull(); - assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); + assertThat(resp.getStatusCode().is5xxServerError()); assertThat(resp.getBody()).isNotNull(); // html: assertThat(resp.getBody()).containsSubsequence("", "

", "Something happened...", "

", "

", diff --git a/src/test/java/org/springframework/samples/petclinic/system/CrashControllerTests.java b/src/test/java/org/springframework/samples/petclinic/system/CrashControllerTests.java index cc2ad67..65d3b89 100644 --- a/src/test/java/org/springframework/samples/petclinic/system/CrashControllerTests.java +++ b/src/test/java/org/springframework/samples/petclinic/system/CrashControllerTests.java @@ -16,9 +16,10 @@ package org.springframework.samples.petclinic.system; -import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import org.junit.jupiter.api.Test; /** * Test class for {@link CrashController} @@ -30,12 +31,16 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; // luck ((plain(st) UNIT test)! :) class CrashControllerTests { - final CrashController testee = new CrashController(); + CrashController testee = new CrashController(); @Test - void testTriggerException() { - assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> testee.triggerException()) - .withMessageContaining("Expected: controller used to showcase what happens when an exception is thrown"); + 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()); } } diff --git a/src/test/java/org/springframework/samples/petclinic/vet/VetControllerTests.java b/src/test/java/org/springframework/samples/petclinic/vet/VetControllerTests.java index 5fffeea..20c3f46 100644 --- a/src/test/java/org/springframework/samples/petclinic/vet/VetControllerTests.java +++ b/src/test/java/org/springframework/samples/petclinic/vet/VetControllerTests.java @@ -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; - @MockitoBean + @MockBean private VetRepository vets; private Vet james() { diff --git a/src/test/jmeter/petclinic_test_plan.jmx b/src/test/jmeter/petclinic_test_plan.jmx index 89c7bf2..013968c 100644 --- a/src/test/jmeter/petclinic_test_plan.jmx +++ b/src/test/jmeter/petclinic_test_plan.jmx @@ -156,7 +156,8 @@ - ${CONTEXT_WEB}/webjars/bootstrap/dist/js/bootstrap.bundle.min.js + + ${CONTEXT_WEB}/webjars/bootstrap/5.3.2/dist/js/bootstrap.bundle.min.js GET true false @@ -419,7 +420,8 @@ - ${CONTEXT_WEB}/owners/${count}/pets/${petCount}/visits/new + + ${CONTEXT_WEB}/owners/${count}/pets/${petCount}/visits/new GET true false @@ -456,7 +458,8 @@ - ${CONTEXT_WEB}/owners/${count}/pets/${petCount}/visits/new + + ${CONTEXT_WEB}/owners/${count}/pets/${petCount}/visits/new POST true false @@ -537,4 +540,4 @@ - + \ No newline at end of file