├── .editorconfig ├── .gitignore ├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── .travis.yml ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── docker-compose.yml ├── mvnw ├── mvnw.cmd ├── pom.xml ├── readme.md ├── sonar-project.properties └── src ├── main ├── docker │ ├── Dockerfile │ ├── Dockerfile.fatjar │ ├── Dockerfile.glibc │ └── Dockerfile.musl ├── java │ ├── module-info.java │ └── org │ │ └── springframework │ │ └── samples │ │ └── petclinic │ │ ├── PetClinicApplication.java │ │ ├── model │ │ ├── BaseEntity.java │ │ ├── NamedEntity.java │ │ ├── Person.java │ │ └── package-info.java │ │ ├── owner │ │ ├── Owner.java │ │ ├── OwnerController.java │ │ ├── OwnerRepository.java │ │ ├── Pet.java │ │ ├── PetController.java │ │ ├── PetRepository.java │ │ ├── PetType.java │ │ ├── PetTypeFormatter.java │ │ ├── PetValidator.java │ │ └── VisitController.java │ │ ├── system │ │ ├── CacheConfiguration.java │ │ ├── CrashController.java │ │ └── WelcomeController.java │ │ ├── vet │ │ ├── Specialty.java │ │ ├── Vet.java │ │ ├── VetController.java │ │ ├── VetRepository.java │ │ └── Vets.java │ │ └── visit │ │ ├── Visit.java │ │ └── VisitRepository.java ├── less │ ├── header.less │ ├── petclinic.less │ ├── responsive.less │ └── typography.less ├── resources │ ├── application-mysql.properties │ ├── application.properties │ ├── banner.txt │ ├── db │ │ ├── hsqldb │ │ │ ├── data.sql │ │ │ └── schema.sql │ │ └── mysql │ │ │ ├── data.sql │ │ │ ├── petclinic_db_setup_mysql.txt │ │ │ └── schema.sql │ ├── messages │ │ ├── messages.properties │ │ ├── messages_de.properties │ │ └── messages_en.properties │ ├── static │ │ └── resources │ │ │ ├── fonts │ │ │ ├── montserrat-webfont.eot │ │ │ ├── montserrat-webfont.svg │ │ │ ├── montserrat-webfont.ttf │ │ │ ├── montserrat-webfont.woff │ │ │ ├── varela_round-webfont.eot │ │ │ ├── varela_round-webfont.svg │ │ │ ├── varela_round-webfont.ttf │ │ │ └── varela_round-webfont.woff │ │ │ └── images │ │ │ ├── favicon.png │ │ │ ├── pets.png │ │ │ ├── platform-bg.png │ │ │ ├── spring-logo-dataflow-mobile.png │ │ │ ├── spring-logo-dataflow.png │ │ │ └── spring-pivotal-logo.png │ └── templates │ │ ├── error.html │ │ ├── fragments │ │ ├── inputField.html │ │ ├── layout.html │ │ └── selectField.html │ │ ├── owners │ │ ├── createOrUpdateOwnerForm.html │ │ ├── findOwners.html │ │ ├── ownerDetails.html │ │ └── ownersList.html │ │ ├── pets │ │ ├── createOrUpdatePetForm.html │ │ └── createOrUpdateVisitForm.html │ │ ├── vets │ │ └── vetList.html │ │ └── welcome.html └── wro │ ├── wro.properties │ └── wro.xml └── test ├── java └── org │ └── springframework │ └── samples │ └── petclinic │ ├── PetclinicIntegrationTests.java │ ├── model │ └── ValidatorTests.java │ ├── owner │ ├── OwnerControllerTests.java │ ├── PetControllerTests.java │ ├── PetTypeFormatterTests.java │ └── VisitControllerTests.java │ ├── service │ ├── ClinicServiceTests.java │ └── EntityUtils.java │ ├── system │ └── CrashControllerTests.java │ └── vet │ ├── VetControllerTests.java │ └── VetTests.java └── jmeter └── petclinic_test_plan.jmx /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | insert_final_newline = true 8 | indent_style = space 9 | 10 | [*.{java,xml}] 11 | indent_size = 4 12 | trim_trailing_whitespace = true 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/* 2 | .settings/* 3 | .classpath 4 | .project 5 | .idea 6 | *.iml 7 | /target 8 | .sts4-cache/ 9 | .vscode/* 10 | !.vscode/settings.json 11 | !.vscode/tasks.json 12 | !.vscode/launch.json 13 | !.vscode/extensions.json 14 | _site/ 15 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panga/spring-petclinic/62fb6aa614b12f7163419ab29513c0e59c4cfdbf/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.3/apache-maven-3.3.3-bin.zip 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: oraclejdk8 3 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "java", 9 | "name": "Debug (Launch)-PetClinicApplication", 10 | "request": "launch", 11 | "cwd": "${workspaceFolder}", 12 | "console": "internalConsole", 13 | "stopOnEntry": false, 14 | "mainClass": "org.springframework.samples.petclinic.PetClinicApplication", 15 | "projectName": "spring-petclinic", 16 | "args": "" 17 | }, 18 | { 19 | "type": "java", 20 | "name": "Debug (Attach)", 21 | "request": "attach", 22 | "hostName": "localhost", 23 | "port": 0 24 | } 25 | ] 26 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "java.configuration.updateBuildConfiguration": "interactive" 3 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "verify", 8 | "type": "shell", 9 | "command": "mvn -B verify", 10 | "group": "build" 11 | }, 12 | { 13 | "label": "test", 14 | "type": "shell", 15 | "command": "mvn -B test", 16 | "group": "test" 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | mysql: 2 | image: mysql:5.7 3 | ports: 4 | - "3306:3306" 5 | environment: 6 | - MYSQL_ROOT_PASSWORD=petclinic 7 | - MYSQL_DATABASE=petclinic 8 | volumes: 9 | - "./conf.d:/etc/mysql/conf.d:ro" 10 | -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # 58 | # Look for the Apple JDKs first to preserve the existing behaviour, and then look 59 | # for the new JDKs provided by Oracle. 60 | # 61 | if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then 62 | # 63 | # Apple JDKs 64 | # 65 | export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home 66 | fi 67 | 68 | if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then 69 | # 70 | # Apple JDKs 71 | # 72 | export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home 73 | fi 74 | 75 | if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then 76 | # 77 | # Oracle JDKs 78 | # 79 | export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home 80 | fi 81 | 82 | if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then 83 | # 84 | # Apple JDKs 85 | # 86 | export JAVA_HOME=`/usr/libexec/java_home` 87 | fi 88 | ;; 89 | esac 90 | 91 | if [ -z "$JAVA_HOME" ] ; then 92 | if [ -r /etc/gentoo-release ] ; then 93 | JAVA_HOME=`java-config --jre-home` 94 | fi 95 | fi 96 | 97 | if [ -z "$M2_HOME" ] ; then 98 | ## resolve links - $0 may be a link to maven's home 99 | PRG="$0" 100 | 101 | # need this for relative symlinks 102 | while [ -h "$PRG" ] ; do 103 | ls=`ls -ld "$PRG"` 104 | link=`expr "$ls" : '.*-> \(.*\)$'` 105 | if expr "$link" : '/.*' > /dev/null; then 106 | PRG="$link" 107 | else 108 | PRG="`dirname "$PRG"`/$link" 109 | fi 110 | done 111 | 112 | saveddir=`pwd` 113 | 114 | M2_HOME=`dirname "$PRG"`/.. 115 | 116 | # make it fully qualified 117 | M2_HOME=`cd "$M2_HOME" && pwd` 118 | 119 | cd "$saveddir" 120 | # echo Using m2 at $M2_HOME 121 | fi 122 | 123 | # For Cygwin, ensure paths are in UNIX format before anything is touched 124 | if $cygwin ; then 125 | [ -n "$M2_HOME" ] && 126 | M2_HOME=`cygpath --unix "$M2_HOME"` 127 | [ -n "$JAVA_HOME" ] && 128 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 129 | [ -n "$CLASSPATH" ] && 130 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 131 | fi 132 | 133 | # For Migwn, ensure paths are in UNIX format before anything is touched 134 | if $mingw ; then 135 | [ -n "$M2_HOME" ] && 136 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 137 | [ -n "$JAVA_HOME" ] && 138 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 139 | # TODO classpath? 140 | fi 141 | 142 | if [ -z "$JAVA_HOME" ]; then 143 | javaExecutable="`which javac`" 144 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 145 | # readlink(1) is not available as standard on Solaris 10. 146 | readLink=`which readlink` 147 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 148 | if $darwin ; then 149 | javaHome="`dirname \"$javaExecutable\"`" 150 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 151 | else 152 | javaExecutable="`readlink -f \"$javaExecutable\"`" 153 | fi 154 | javaHome="`dirname \"$javaExecutable\"`" 155 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 156 | JAVA_HOME="$javaHome" 157 | export JAVA_HOME 158 | fi 159 | fi 160 | fi 161 | 162 | if [ -z "$JAVACMD" ] ; then 163 | if [ -n "$JAVA_HOME" ] ; then 164 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 165 | # IBM's JDK on AIX uses strange locations for the executables 166 | JAVACMD="$JAVA_HOME/jre/sh/java" 167 | else 168 | JAVACMD="$JAVA_HOME/bin/java" 169 | fi 170 | else 171 | JAVACMD="`which java`" 172 | fi 173 | fi 174 | 175 | if [ ! -x "$JAVACMD" ] ; then 176 | echo "Error: JAVA_HOME is not defined correctly." >&2 177 | echo " We cannot execute $JAVACMD" >&2 178 | exit 1 179 | fi 180 | 181 | if [ -z "$JAVA_HOME" ] ; then 182 | echo "Warning: JAVA_HOME environment variable is not set." 183 | fi 184 | 185 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 186 | 187 | # For Cygwin, switch paths to Windows format before running java 188 | if $cygwin; then 189 | [ -n "$M2_HOME" ] && 190 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 191 | [ -n "$JAVA_HOME" ] && 192 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 193 | [ -n "$CLASSPATH" ] && 194 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 195 | fi 196 | 197 | # traverses directory structure from process work directory to filesystem root 198 | # first directory with .mvn subdirectory is considered project base directory 199 | find_maven_basedir() { 200 | local basedir=$(pwd) 201 | local wdir=$(pwd) 202 | while [ "$wdir" != '/' ] ; do 203 | if [ -d "$wdir"/.mvn ] ; then 204 | basedir=$wdir 205 | break 206 | fi 207 | wdir=$(cd "$wdir/.."; pwd) 208 | done 209 | echo "${basedir}" 210 | } 211 | 212 | # concatenates all lines of a file 213 | concat_lines() { 214 | if [ -f "$1" ]; then 215 | echo "$(tr -s '\n' ' ' < "$1")" 216 | fi 217 | } 218 | 219 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)} 220 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 221 | 222 | # Provide a "standardized" way to retrieve the CLI args that will 223 | # work with both Windows and non-Windows executions. 224 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" 225 | export MAVEN_CMD_LINE_ARGS 226 | 227 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 228 | 229 | exec "$JAVACMD" \ 230 | $MAVEN_OPTS \ 231 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 232 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 233 | ${WRAPPER_LAUNCHER} $MAVEN_CMD_LINE_ARGS 234 | 235 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | set MAVEN_CMD_LINE_ARGS=%MAVEN_CONFIG% %* 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | 121 | set WRAPPER_JAR=""%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"" 122 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 123 | 124 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS% 125 | if ERRORLEVEL 1 goto error 126 | goto end 127 | 128 | :error 129 | set ERROR_CODE=1 130 | 131 | :end 132 | @endlocal & set ERROR_CODE=%ERROR_CODE% 133 | 134 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 135 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 136 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 137 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 138 | :skipRcPost 139 | 140 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 141 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 142 | 143 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 144 | 145 | exit /B %ERROR_CODE% 146 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | org.springframework.samples 7 | spring-petclinic 8 | 2.0.0.BUILD-SNAPSHOT 9 | 10 | 11 | org.springframework.boot 12 | spring-boot-starter-parent 13 | 2.0.3.RELEASE 14 | 15 | petclinic 16 | 17 | 18 | 19 | 20 | 11 21 | ${java.version} 22 | ${java.version} 23 | UTF-8 24 | UTF-8 25 | 26 | 27 | 3.3.6 28 | 1.11.4 29 | 2.2.4 30 | 1.8.0 31 | 32 | 5.0.6.RELEASE 33 | 34 | 35 | 36 | 37 | 38 | org.springframework.boot 39 | spring-boot-starter-actuator 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-starter-cache 44 | 45 | 46 | org.springframework.boot 47 | spring-boot-starter-data-jpa 48 | 49 | 50 | javax.transaction-api 51 | javax.transaction 52 | 53 | 54 | 55 | 56 | org.springframework.boot 57 | spring-boot-starter-web 58 | 59 | 60 | org.springframework.boot 61 | spring-boot-starter-thymeleaf 62 | 63 | 64 | org.springframework.boot 65 | spring-boot-starter-test 66 | test 67 | 68 | 69 | 70 | 71 | org.hsqldb 72 | hsqldb 73 | runtime 74 | 75 | 76 | mysql 77 | mysql-connector-java 78 | runtime 79 | 80 | 81 | 82 | 83 | javax.cache 84 | cache-api 85 | 86 | 87 | org.ehcache 88 | ehcache 89 | 90 | 91 | 92 | 93 | org.webjars 94 | webjars-locator-core 95 | 96 | 97 | org.webjars 98 | jquery 99 | ${webjars-jquery.version} 100 | 101 | 102 | org.webjars 103 | jquery-ui 104 | ${webjars-jquery-ui.version} 105 | 106 | 107 | org.webjars 108 | bootstrap 109 | ${webjars-bootstrap.version} 110 | 111 | 112 | 113 | 114 | org.springframework.boot 115 | spring-boot-devtools 116 | true 117 | 118 | 119 | 120 | org.glassfish.jaxb 121 | jaxb-runtime 122 | 2.4.0-b180608.0325 123 | 124 | 125 | org.hibernate.javax.persistence 126 | hibernate-jpa-2.1-api 127 | 1.0.2.Final 128 | 129 | 130 | org.jboss.spec.javax.transaction 131 | jboss-transaction-api_1.2_spec 132 | 1.1.1.Final 133 | 134 | 135 | org.javassist 136 | javassist 137 | 3.23.1-GA 138 | 139 | 140 | org.mockito 141 | mockito-core 142 | 2.20.0 143 | test 144 | 145 | 146 | 147 | 148 | 149 | 150 | org.apache.maven.plugins 151 | maven-compiler-plugin 152 | 3.7.0 153 | 154 | ${java.version} 155 | 156 | 157 | 158 | org.ow2.asm 159 | asm 160 | 6.2 161 | 162 | 163 | 164 | 165 | maven-surefire-plugin 166 | 2.21.0 167 | 168 | 0 169 | 170 | 171 | 172 | org.ow2.asm 173 | asm 174 | 6.2 175 | 176 | 177 | 178 | 179 | org.apache.maven.plugins 180 | maven-dependency-plugin 181 | 3.1.1 182 | 183 | 184 | package 185 | 186 | copy-dependencies 187 | 188 | 189 | ${project.build.directory}/modules 190 | runtime 191 | spring-boot-devtools 192 | 193 | 194 | 195 | 196 | 197 | maven-jar-plugin 198 | 3.1.0 199 | 200 | ${project.build.directory}/modules 201 | 202 | 203 | 204 | org.springframework.boot 205 | spring-boot-maven-plugin 206 | 207 | 208 | 210 | 211 | build-info 212 | 213 | 214 | 215 | ${project.build.sourceEncoding} 216 | ${project.reporting.outputEncoding} 217 | ${maven.compiler.source} 218 | ${maven.compiler.target} 219 | 220 | 221 | 222 | 223 | 224 | 225 | 227 | 228 | pl.project13.maven 229 | git-commit-id-plugin 230 | 231 | 232 | 233 | revision 234 | 235 | 236 | 237 | 238 | true 239 | yyyy-MM-dd'T'HH:mm:ssZ 240 | true 241 | ${project.build.outputDirectory}/git.properties 242 | 243 | false 244 | 245 | 246 | 247 | 248 | ro.isdc.wro4j 249 | wro4j-maven-plugin 250 | ${wro4j.version} 251 | 252 | 253 | generate-resources 254 | 255 | run 256 | 257 | 258 | 259 | 260 | ro.isdc.wro.maven.plugin.manager.factory.ConfigurableWroManagerFactory 261 | ${project.build.directory}/classes/static/resources/css 262 | ${basedir}/src/main/wro/wro.xml 263 | ${basedir}/src/main/wro/wro.properties 264 | ${basedir}/src/main/less 265 | 266 | 267 | 268 | org.webjars 269 | bootstrap 270 | ${webjars-bootstrap.version} 271 | 272 | 273 | org.mockito 274 | mockito-core 275 | 2.20.0 276 | 277 | 278 | 279 | 280 | org.codehaus.mojo 281 | exec-maven-plugin 282 | 1.6.0 283 | 284 | 285 | module-main-class 286 | package 287 | 288 | exec 289 | 290 | 291 | jar 292 | 293 | 294 | --update 295 | 296 | 297 | --file=${project.build.directory}/modules/${project.build.finalName}.jar 298 | 299 | 300 | --main-class=org.springframework.samples.petclinic.PetClinicApplication 301 | 302 | 303 | --module-version=${project.version} 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | docker 316 | 317 | 318 | 319 | org.apache.maven.plugins 320 | maven-resources-plugin 321 | 322 | 323 | filter-dockerfile 324 | validate 325 | 326 | copy-resources 327 | 328 | 329 | ${project.build.directory} 330 | 331 | 332 | src/main/docker 333 | true 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | com.spotify 342 | dockerfile-maven-plugin 343 | 1.4.0 344 | 345 | ${project.build.directory} 346 | ${project.artifactId} 347 | latest 348 | 349 | 350 | -Xmx512m 351 | 352 | 353 | 354 | 355 | org.codehaus.plexus 356 | plexus-archiver 357 | 3.4 358 | 359 | 360 | javax.activation 361 | javax.activation-api 362 | 1.2.0 363 | 364 | 365 | 366 | 367 | build-image 368 | package 369 | 370 | build 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | Apache License, Version 2.0 384 | http://www.apache.org/licenses/LICENSE-2.0 385 | 386 | 387 | 388 | 389 | 390 | jvnet-nexus-staging 391 | http://maven.java.net/content/repositories/staging/ 392 | default 393 | 394 | 395 | 396 | 397 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Spring PetClinic Sample Application [![Build Status](https://travis-ci.org/spring-projects/spring-petclinic.png?branch=master)](https://travis-ci.org/spring-projects/spring-petclinic/) 2 | 3 | ## Understanding the Spring Petclinic application with a few diagrams 4 | See the presentation here 5 | 6 | ## Running petclinic locally 7 | ``` 8 | git clone https://github.com/spring-projects/spring-petclinic.git 9 | cd spring-petclinic 10 | ./mvnw spring-boot:run 11 | ``` 12 | 13 | You can then access petclinic here: http://localhost:8080/ 14 | 15 | petclinic-screenshot 16 | 17 | ## In case you find a bug/suggested improvement for Spring Petclinic 18 | Our issue tracker is available here: https://github.com/spring-projects/spring-petclinic/issues 19 | 20 | 21 | ## Database configuration 22 | 23 | In its default configuration, Petclinic uses an in-memory database (HSQLDB) which 24 | gets populated at startup with data. A similar setup is provided for MySql in case a persistent database configuration is needed. 25 | Note that whenever the database type is changed, the data-access.properties file needs to be updated and the mysql-connector-java artifact from the pom.xml needs to be uncommented. 26 | 27 | You could start a MySql database with docker: 28 | 29 | ``` 30 | docker run -e MYSQL_ROOT_PASSWORD=petclinic -e MYSQL_DATABASE=petclinic -p 3306:3306 mysql:5.7.8 31 | ``` 32 | 33 | ## Working with Petclinic in Eclipse/STS 34 | 35 | ### prerequisites 36 | The following items should be installed in your system: 37 | * Apache Maven (https://maven.apache.org/install.html) 38 | * git command line tool (https://help.github.com/articles/set-up-git) 39 | * Eclipse with the m2e plugin (m2e is installed by default when using the STS (http://www.springsource.org/sts) distribution of Eclipse) 40 | 41 | Note: when m2e is available, there is an m2 icon in Help -> About dialog. 42 | If m2e is not there, just follow the install process here: http://www.eclipse.org/m2e/m2e-downloads.html 43 | 44 | 45 | ### Steps: 46 | 47 | 1) In the command line 48 | ``` 49 | git clone https://github.com/spring-projects/spring-petclinic.git 50 | ``` 51 | 2) Inside Eclipse 52 | ``` 53 | File -> Import -> Maven -> Existing Maven project 54 | ``` 55 | 56 | 57 | ## Looking for something in particular? 58 | 59 | |Spring Boot Configuration | Class or Java property files | 60 | |--------------------------|---| 61 | |The Main Class | [PetClinicApplication](https://github.com/spring-projects/spring-petclinic/blob/master/src/main/java/org/springframework/samples/petclinic/PetClinicApplication.java) | 62 | |Properties Files | [application.properties](https://github.com/spring-projects/spring-petclinic/blob/master/src/main/resources) | 63 | |Caching | [CacheConfiguration](https://github.com/spring-projects/spring-petclinic/blob/master/src/main/java/org/springframework/samples/petclinic/system/CacheConfiguration.java) | 64 | 65 | ## Interesting Spring Petclinic branches and forks 66 | 67 | The Spring Petclinic master branch in the main 68 | [spring-projects](https://github.com/spring-projects/spring-petclinic) 69 | GitHub org is the "canonical" implementation, currently based on 70 | Spring Boot and Thymeleaf. There are quite a few forks in a special 71 | GitHub org [spring-petclinic](https://github.com/spring-petclinic). If 72 | you have a special interest in a different technology stack that could 73 | be used to implement the Pet Clinic then please join the community 74 | there. 75 | 76 | | Link | Main technologies | 77 | |------------------------------------|-------------------| 78 | | [spring-framework-petclinic][] | Spring Framework XML configuration, JSP pages, 3 persistence layers: JDBC, JPA and Spring Data JPA | 79 | | [javaconfig branch][] | Same frameworks as the [spring-framework-petclinic][] but with Java Configuration instead of XML | 80 | | [spring-petclinic-angularjs][] | AngularJS 1.x, Spring Boot and Spring Data JPA | 81 | | [spring-petclinic-angular][] | Angular 4 front-end of the Petclinic REST API [spring-petclinic-rest][] | 82 | | [spring-petclinic-microservices][] | Distributed version of Spring Petclinic built with Spring Cloud | 83 | | [spring-petclinic-reactjs][] | ReactJS (with TypeScript) and Spring Boot | 84 | | [spring-petclinic-graphql][] | GraphQL version based on React Appolo, TypeScript and GraphQL Spring boot starter | 85 | | [spring-petclinic-kotlin][] | Kotlin version of [spring-petclinic][] | 86 | | [spring-petclinic-rest][] | Backend REST API | 87 | 88 | 89 | ## Interaction with other open source projects 90 | 91 | One of the best parts about working on the Spring Petclinic application is that we have the opportunity to work in direct contact with many Open Source projects. We found some bugs/suggested improvements on various topics such as Spring, Spring Data, Bean Validation and even Eclipse! In many cases, they've been fixed/implemented in just a few days. 92 | Here is a list of them: 93 | 94 | | Name | Issue | 95 | |------|-------| 96 | | Spring JDBC: simplify usage of NamedParameterJdbcTemplate | [SPR-10256](https://jira.springsource.org/browse/SPR-10256) and [SPR-10257](https://jira.springsource.org/browse/SPR-10257) | 97 | | Bean Validation / Hibernate Validator: simplify Maven dependencies and backward compatibility |[HV-790](https://hibernate.atlassian.net/browse/HV-790) and [HV-792](https://hibernate.atlassian.net/browse/HV-792) | 98 | | Spring Data: provide more flexibility when working with JPQL queries | [DATAJPA-292](https://jira.springsource.org/browse/DATAJPA-292) | 99 | 100 | 101 | # Contributing 102 | 103 | The [issue tracker](https://github.com/spring-projects/spring-petclinic/issues) is the preferred channel for bug reports, features requests and submitting pull requests. 104 | 105 | For pull requests, editor preferences are available in the [editor config](.editorconfig) for easy use in common text editors. Read more and download plugins at . If you have not previously done so, please fill out and submit the https://cla.pivotal.io/sign/spring[Contributor License Agreement]. 106 | 107 | # License 108 | 109 | The Spring PetClinic sample application is released under version 2.0 of the [Apache License](http://www.apache.org/licenses/LICENSE-2.0). 110 | 111 | [spring-petclinic]: https://github.com/spring-projects/spring-petclinic 112 | [spring-framework-petclinic]: https://github.com/spring-petclinic/spring-framework-petclinic 113 | [spring-petclinic-angularjs]: https://github.com/spring-petclinic/spring-petclinic-angularjs 114 | [javaconfig branch]: https://github.com/spring-petclinic/spring-framework-petclinic/tree/javaconfig 115 | [spring-petclinic-angular]: https://github.com/spring-petclinic/spring-petclinic-angular 116 | [spring-petclinic-microservices]: https://github.com/spring-petclinic/spring-petclinic-microservices 117 | [spring-petclinic-reactjs]: https://github.com/spring-petclinic/spring-petclinic-reactjs 118 | [spring-petclinic-graphql]: https://github.com/spring-petclinic/spring-petclinic-graphql 119 | [spring-petclinic-kotlin]: https://github.com/spring-petclinic/spring-petclinic-kotlin 120 | [spring-petclinic-rest]: https://github.com/spring-petclinic/spring-petclinic-rest 121 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | # Required metadata 2 | sonar.projectKey=java-sonar-runner-simple 3 | sonar.projectName=Simple Java project analyzed with the SonarQube Runner 4 | sonar.projectVersion=1.0 5 | 6 | # Comma-separated paths to directories with sources (required) 7 | sonar.sources=src 8 | 9 | # Language 10 | sonar.language=java 11 | 12 | # Encoding of the source files 13 | sonar.sourceEncoding=UTF-8 -------------------------------------------------------------------------------- /src/main/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | Dockerfile.musl -------------------------------------------------------------------------------- /src/main/docker/Dockerfile.fatjar: -------------------------------------------------------------------------------- 1 | FROM openjdk:11-jre-slim 2 | 3 | ADD spring-petclinic-2.0.0.BUILD-SNAPSHOT.jar app.jar 4 | 5 | ARG JVM_OPTS 6 | ENV JVM_OPTS=${JVM_OPTS} 7 | 8 | CMD java ${JVM_OPTS} -jar app.jar 9 | -------------------------------------------------------------------------------- /src/main/docker/Dockerfile.glibc: -------------------------------------------------------------------------------- 1 | FROM openjdk:11 as builder 2 | 3 | RUN jlink \ 4 | --add-modules java.sql,java.naming,java.management,java.instrument,java.security.jgss,java.desktop,jdk.unsupported \ 5 | --verbose \ 6 | --strip-debug \ 7 | --compress 2 \ 8 | --no-header-files \ 9 | --no-man-pages \ 10 | --output /opt/jre-minimal 11 | 12 | FROM panga/alpine:3.8-glibc2.27 13 | 14 | COPY --from=builder /opt/jre-minimal /opt/jre-minimal 15 | 16 | ENV LANG=C.UTF-8 \ 17 | PATH=${PATH}:/opt/jre-minimal/bin 18 | 19 | ADD modules /opt/app/modules 20 | 21 | ARG JVM_OPTS 22 | ENV JVM_OPTS=${JVM_OPTS} 23 | 24 | CMD java ${JVM_OPTS} \ 25 | --add-opens java.base/java.lang=spring.core,javassist \ 26 | --module-path /opt/app/modules \ 27 | --module spring.petclinic 28 | -------------------------------------------------------------------------------- /src/main/docker/Dockerfile.musl: -------------------------------------------------------------------------------- 1 | FROM panga/openjdk-alpine:11-jdk as builder 2 | 3 | RUN jlink \ 4 | --add-modules java.sql,java.naming,java.management,java.instrument,java.security.jgss,java.desktop,jdk.unsupported \ 5 | --verbose \ 6 | --strip-debug \ 7 | --compress 2 \ 8 | --no-header-files \ 9 | --no-man-pages \ 10 | --output /opt/jre-minimal 11 | 12 | FROM alpine:3.8 13 | 14 | COPY --from=builder /opt/jre-minimal /opt/jre-minimal 15 | 16 | ENV LANG=C.UTF-8 \ 17 | PATH=${PATH}:/opt/jre-minimal/bin 18 | 19 | ADD modules /opt/app/modules 20 | 21 | ARG JVM_OPTS 22 | ENV JVM_OPTS=${JVM_OPTS} 23 | 24 | CMD java ${JVM_OPTS} \ 25 | --add-opens java.base/java.lang=spring.core,javassist \ 26 | --module-path /opt/app/modules \ 27 | --module spring.petclinic 28 | -------------------------------------------------------------------------------- /src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | open module spring.petclinic { 2 | requires cache.api; 3 | 4 | requires java.activation; 5 | requires java.instrument; 6 | requires java.persistence; 7 | requires java.sql; 8 | requires java.transaction; 9 | requires java.validation; 10 | requires java.xml.bind; 11 | 12 | requires org.hibernate.validator; 13 | 14 | requires spring.beans; 15 | requires spring.boot; 16 | requires spring.boot.autoconfigure; 17 | requires spring.context; 18 | requires spring.core; 19 | requires spring.data.commons; 20 | requires spring.data.jpa; 21 | requires spring.tx; 22 | requires spring.web; 23 | requires spring.webmvc; 24 | 25 | requires jdk.unsupported; 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/samples/petclinic/PetClinicApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.samples.petclinic; 18 | 19 | import org.springframework.boot.SpringApplication; 20 | import org.springframework.boot.autoconfigure.SpringBootApplication; 21 | 22 | /** 23 | * PetClinic Spring Boot Application. 24 | * 25 | * @author Dave Syer 26 | * 27 | */ 28 | @SpringBootApplication 29 | public class PetClinicApplication { 30 | 31 | public static void main(String[] args) { 32 | SpringApplication.run(PetClinicApplication.class, args); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/samples/petclinic/model/BaseEntity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2002-2013 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.samples.petclinic.model; 17 | 18 | import java.io.Serializable; 19 | 20 | import javax.persistence.GeneratedValue; 21 | import javax.persistence.GenerationType; 22 | import javax.persistence.Id; 23 | import javax.persistence.MappedSuperclass; 24 | 25 | /** 26 | * Simple JavaBean domain object with an id property. Used as a base class for objects 27 | * needing this property. 28 | * 29 | * @author Ken Krebs 30 | * @author Juergen Hoeller 31 | */ 32 | @MappedSuperclass 33 | public class BaseEntity implements Serializable { 34 | @Id 35 | @GeneratedValue(strategy = GenerationType.IDENTITY) 36 | private Integer id; 37 | 38 | public Integer getId() { 39 | return id; 40 | } 41 | 42 | public void setId(Integer id) { 43 | this.id = id; 44 | } 45 | 46 | public boolean isNew() { 47 | return this.id == null; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/samples/petclinic/model/NamedEntity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2002-2013 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.samples.petclinic.model; 17 | 18 | import javax.persistence.Column; 19 | import javax.persistence.MappedSuperclass; 20 | 21 | 22 | /** 23 | * Simple JavaBean domain object adds a name property to BaseEntity. Used as a base class for objects 24 | * needing these properties. 25 | * 26 | * @author Ken Krebs 27 | * @author Juergen Hoeller 28 | */ 29 | @MappedSuperclass 30 | public class NamedEntity extends BaseEntity { 31 | 32 | @Column(name = "name") 33 | private String name; 34 | 35 | public String getName() { 36 | return this.name; 37 | } 38 | 39 | public void setName(String name) { 40 | this.name = name; 41 | } 42 | 43 | @Override 44 | public String toString() { 45 | return this.getName(); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/samples/petclinic/model/Person.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2002-2013 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.samples.petclinic.model; 17 | 18 | import javax.persistence.Column; 19 | import javax.persistence.MappedSuperclass; 20 | import javax.validation.constraints.NotEmpty; 21 | 22 | /** 23 | * Simple JavaBean domain object representing an person. 24 | * 25 | * @author Ken Krebs 26 | */ 27 | @MappedSuperclass 28 | public class Person extends BaseEntity { 29 | 30 | @Column(name = "first_name") 31 | @NotEmpty 32 | private String firstName; 33 | 34 | @Column(name = "last_name") 35 | @NotEmpty 36 | private String lastName; 37 | 38 | public String getFirstName() { 39 | return this.firstName; 40 | } 41 | 42 | public void setFirstName(String firstName) { 43 | this.firstName = firstName; 44 | } 45 | 46 | public String getLastName() { 47 | return this.lastName; 48 | } 49 | 50 | public void setLastName(String lastName) { 51 | this.lastName = lastName; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/samples/petclinic/model/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * The classes in this package represent utilities used by the domain. 3 | */ 4 | package org.springframework.samples.petclinic.model; 5 | 6 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/samples/petclinic/owner/Owner.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2002-2013 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.samples.petclinic.owner; 17 | 18 | import java.util.ArrayList; 19 | import java.util.Collections; 20 | import java.util.HashSet; 21 | import java.util.List; 22 | import java.util.Set; 23 | 24 | import javax.persistence.CascadeType; 25 | import javax.persistence.Column; 26 | import javax.persistence.Entity; 27 | import javax.persistence.OneToMany; 28 | import javax.persistence.Table; 29 | import javax.validation.constraints.Digits; 30 | import javax.validation.constraints.NotEmpty; 31 | 32 | import org.springframework.beans.support.MutableSortDefinition; 33 | import org.springframework.beans.support.PropertyComparator; 34 | import org.springframework.core.style.ToStringCreator; 35 | import org.springframework.samples.petclinic.model.Person; 36 | 37 | /** 38 | * Simple JavaBean domain object representing an owner. 39 | * 40 | * @author Ken Krebs 41 | * @author Juergen Hoeller 42 | * @author Sam Brannen 43 | * @author Michael Isvy 44 | */ 45 | @Entity 46 | @Table(name = "owners") 47 | public class Owner extends Person { 48 | @Column(name = "address") 49 | @NotEmpty 50 | private String address; 51 | 52 | @Column(name = "city") 53 | @NotEmpty 54 | private String city; 55 | 56 | @Column(name = "telephone") 57 | @NotEmpty 58 | @Digits(fraction = 0, integer = 10) 59 | private String telephone; 60 | 61 | @OneToMany(cascade = CascadeType.ALL, mappedBy = "owner") 62 | private Set pets; 63 | 64 | public String getAddress() { 65 | return this.address; 66 | } 67 | 68 | public void setAddress(String address) { 69 | this.address = address; 70 | } 71 | 72 | public String getCity() { 73 | return this.city; 74 | } 75 | 76 | public void setCity(String city) { 77 | this.city = city; 78 | } 79 | 80 | public String getTelephone() { 81 | return this.telephone; 82 | } 83 | 84 | public void setTelephone(String telephone) { 85 | this.telephone = telephone; 86 | } 87 | 88 | protected Set getPetsInternal() { 89 | if (this.pets == null) { 90 | this.pets = new HashSet<>(); 91 | } 92 | return this.pets; 93 | } 94 | 95 | protected void setPetsInternal(Set pets) { 96 | this.pets = pets; 97 | } 98 | 99 | public List getPets() { 100 | List sortedPets = new ArrayList<>(getPetsInternal()); 101 | PropertyComparator.sort(sortedPets, 102 | new MutableSortDefinition("name", true, true)); 103 | return Collections.unmodifiableList(sortedPets); 104 | } 105 | 106 | public void addPet(Pet pet) { 107 | if (pet.isNew()) { 108 | getPetsInternal().add(pet); 109 | } 110 | pet.setOwner(this); 111 | } 112 | 113 | /** 114 | * Return the Pet with the given name, or null if none found for this Owner. 115 | * 116 | * @param name to test 117 | * @return true if pet name is already in use 118 | */ 119 | public Pet getPet(String name) { 120 | return getPet(name, false); 121 | } 122 | 123 | /** 124 | * Return the Pet with the given name, or null if none found for this Owner. 125 | * 126 | * @param name to test 127 | * @return true if pet name is already in use 128 | */ 129 | public Pet getPet(String name, boolean ignoreNew) { 130 | name = name.toLowerCase(); 131 | for (Pet pet : getPetsInternal()) { 132 | if (!ignoreNew || !pet.isNew()) { 133 | String compName = pet.getName(); 134 | compName = compName.toLowerCase(); 135 | if (compName.equals(name)) { 136 | return pet; 137 | } 138 | } 139 | } 140 | return null; 141 | } 142 | 143 | @Override 144 | public String toString() { 145 | return new ToStringCreator(this) 146 | 147 | .append("id", this.getId()).append("new", this.isNew()) 148 | .append("lastName", this.getLastName()) 149 | .append("firstName", this.getFirstName()).append("address", this.address) 150 | .append("city", this.city).append("telephone", this.telephone).toString(); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.samples.petclinic.owner; 17 | 18 | import org.springframework.stereotype.Controller; 19 | import org.springframework.ui.Model; 20 | import org.springframework.validation.BindingResult; 21 | import org.springframework.web.bind.WebDataBinder; 22 | import org.springframework.web.bind.annotation.GetMapping; 23 | import org.springframework.web.bind.annotation.InitBinder; 24 | import org.springframework.web.bind.annotation.PathVariable; 25 | import org.springframework.web.bind.annotation.PostMapping; 26 | import org.springframework.web.servlet.ModelAndView; 27 | 28 | import javax.validation.Valid; 29 | import java.util.Collection; 30 | import java.util.Map; 31 | 32 | /** 33 | * @author Juergen Hoeller 34 | * @author Ken Krebs 35 | * @author Arjen Poutsma 36 | * @author Michael Isvy 37 | */ 38 | @Controller 39 | class OwnerController { 40 | 41 | private static final String VIEWS_OWNER_CREATE_OR_UPDATE_FORM = "owners/createOrUpdateOwnerForm"; 42 | private final OwnerRepository owners; 43 | 44 | 45 | public OwnerController(OwnerRepository clinicService) { 46 | this.owners = clinicService; 47 | } 48 | 49 | @InitBinder 50 | public void setAllowedFields(WebDataBinder dataBinder) { 51 | dataBinder.setDisallowedFields("id"); 52 | } 53 | 54 | @GetMapping("/owners/new") 55 | public String initCreationForm(Map model) { 56 | Owner owner = new Owner(); 57 | model.put("owner", owner); 58 | return VIEWS_OWNER_CREATE_OR_UPDATE_FORM; 59 | } 60 | 61 | @PostMapping("/owners/new") 62 | public String processCreationForm(@Valid Owner owner, BindingResult result) { 63 | if (result.hasErrors()) { 64 | return VIEWS_OWNER_CREATE_OR_UPDATE_FORM; 65 | } else { 66 | this.owners.save(owner); 67 | return "redirect:/owners/" + owner.getId(); 68 | } 69 | } 70 | 71 | @GetMapping("/owners/find") 72 | public String initFindForm(Map model) { 73 | model.put("owner", new Owner()); 74 | return "owners/findOwners"; 75 | } 76 | 77 | @GetMapping("/owners") 78 | public String processFindForm(Owner owner, BindingResult result, Map model) { 79 | 80 | // allow parameterless GET request for /owners to return all records 81 | if (owner.getLastName() == null) { 82 | owner.setLastName(""); // empty string signifies broadest possible search 83 | } 84 | 85 | // find owners by last name 86 | Collection results = this.owners.findByLastName(owner.getLastName()); 87 | if (results.isEmpty()) { 88 | // no owners found 89 | result.rejectValue("lastName", "notFound", "not found"); 90 | return "owners/findOwners"; 91 | } else if (results.size() == 1) { 92 | // 1 owner found 93 | owner = results.iterator().next(); 94 | return "redirect:/owners/" + owner.getId(); 95 | } else { 96 | // multiple owners found 97 | model.put("selections", results); 98 | return "owners/ownersList"; 99 | } 100 | } 101 | 102 | @GetMapping("/owners/{ownerId}/edit") 103 | public String initUpdateOwnerForm(@PathVariable("ownerId") int ownerId, Model model) { 104 | Owner owner = this.owners.findById(ownerId); 105 | model.addAttribute(owner); 106 | return VIEWS_OWNER_CREATE_OR_UPDATE_FORM; 107 | } 108 | 109 | @PostMapping("/owners/{ownerId}/edit") 110 | public String processUpdateOwnerForm(@Valid Owner owner, BindingResult result, @PathVariable("ownerId") int ownerId) { 111 | if (result.hasErrors()) { 112 | return VIEWS_OWNER_CREATE_OR_UPDATE_FORM; 113 | } else { 114 | owner.setId(ownerId); 115 | this.owners.save(owner); 116 | return "redirect:/owners/{ownerId}"; 117 | } 118 | } 119 | 120 | /** 121 | * Custom handler for displaying an owner. 122 | * 123 | * @param ownerId the ID of the owner to display 124 | * @return a ModelMap with the model attributes for the view 125 | */ 126 | @GetMapping("/owners/{ownerId}") 127 | public ModelAndView showOwner(@PathVariable("ownerId") int ownerId) { 128 | ModelAndView mav = new ModelAndView("owners/ownerDetails"); 129 | mav.addObject(this.owners.findById(ownerId)); 130 | return mav; 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2002-2013 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.samples.petclinic.owner; 17 | 18 | import java.util.Collection; 19 | 20 | import org.springframework.data.jpa.repository.Query; 21 | import org.springframework.data.repository.Repository; 22 | import org.springframework.data.repository.query.Param; 23 | import org.springframework.transaction.annotation.Transactional; 24 | 25 | /** 26 | * Repository class for Owner domain objects All method names are compliant with Spring Data naming 27 | * conventions so this interface can easily be extended for Spring Data See here: http://static.springsource.org/spring-data/jpa/docs/current/reference/html/jpa.repositories.html#jpa.query-methods.query-creation 28 | * 29 | * @author Ken Krebs 30 | * @author Juergen Hoeller 31 | * @author Sam Brannen 32 | * @author Michael Isvy 33 | */ 34 | public interface OwnerRepository extends Repository { 35 | 36 | /** 37 | * Retrieve {@link Owner}s from the data store by last name, returning all owners 38 | * whose last name starts with the given name. 39 | * @param lastName Value to search for 40 | * @return a Collection of matching {@link Owner}s (or an empty Collection if none 41 | * found) 42 | */ 43 | @Query("SELECT DISTINCT owner FROM Owner owner left join fetch owner.pets WHERE owner.lastName LIKE :lastName%") 44 | @Transactional(readOnly = true) 45 | Collection findByLastName(@Param("lastName") String lastName); 46 | 47 | /** 48 | * Retrieve an {@link Owner} from the data store by id. 49 | * @param id the id to search for 50 | * @return the {@link Owner} if found 51 | */ 52 | @Query("SELECT owner FROM Owner owner left join fetch owner.pets WHERE owner.id =:id") 53 | @Transactional(readOnly = true) 54 | Owner findById(@Param("id") Integer id); 55 | 56 | /** 57 | * Save an {@link Owner} to the data store, either inserting or updating it. 58 | * @param owner the {@link Owner} to save 59 | */ 60 | void save(Owner owner); 61 | 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/samples/petclinic/owner/Pet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2002-2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.samples.petclinic.owner; 17 | 18 | import java.time.LocalDate; 19 | import java.util.ArrayList; 20 | import java.util.Collections; 21 | import java.util.HashSet; 22 | import java.util.LinkedHashSet; 23 | import java.util.List; 24 | import java.util.Set; 25 | 26 | import javax.persistence.CascadeType; 27 | import javax.persistence.Column; 28 | import javax.persistence.Entity; 29 | import javax.persistence.FetchType; 30 | import javax.persistence.JoinColumn; 31 | import javax.persistence.ManyToOne; 32 | import javax.persistence.OneToMany; 33 | import javax.persistence.Table; 34 | 35 | import org.springframework.beans.support.MutableSortDefinition; 36 | import org.springframework.beans.support.PropertyComparator; 37 | import org.springframework.format.annotation.DateTimeFormat; 38 | import org.springframework.samples.petclinic.model.NamedEntity; 39 | import org.springframework.samples.petclinic.visit.Visit; 40 | 41 | /** 42 | * Simple business object representing a pet. 43 | * 44 | * @author Ken Krebs 45 | * @author Juergen Hoeller 46 | * @author Sam Brannen 47 | */ 48 | @Entity 49 | @Table(name = "pets") 50 | public class Pet extends NamedEntity { 51 | 52 | @Column(name = "birth_date") 53 | @DateTimeFormat(pattern = "yyyy-MM-dd") 54 | private LocalDate birthDate; 55 | 56 | @ManyToOne 57 | @JoinColumn(name = "type_id") 58 | private PetType type; 59 | 60 | @ManyToOne 61 | @JoinColumn(name = "owner_id") 62 | private Owner owner; 63 | 64 | @OneToMany(cascade = CascadeType.ALL, mappedBy = "petId", fetch = FetchType.EAGER) 65 | private Set visits = new LinkedHashSet<>(); 66 | 67 | public void setBirthDate(LocalDate birthDate) { 68 | this.birthDate = birthDate; 69 | } 70 | 71 | public LocalDate getBirthDate() { 72 | return this.birthDate; 73 | } 74 | 75 | public PetType getType() { 76 | return this.type; 77 | } 78 | 79 | public void setType(PetType type) { 80 | this.type = type; 81 | } 82 | 83 | public Owner getOwner() { 84 | return this.owner; 85 | } 86 | 87 | protected void setOwner(Owner owner) { 88 | this.owner = owner; 89 | } 90 | 91 | protected Set getVisitsInternal() { 92 | if (this.visits == null) { 93 | this.visits = new HashSet<>(); 94 | } 95 | return this.visits; 96 | } 97 | 98 | protected void setVisitsInternal(Set visits) { 99 | this.visits = visits; 100 | } 101 | 102 | public List getVisits() { 103 | List sortedVisits = new ArrayList<>(getVisitsInternal()); 104 | PropertyComparator.sort(sortedVisits, 105 | new MutableSortDefinition("date", false, false)); 106 | return Collections.unmodifiableList(sortedVisits); 107 | } 108 | 109 | public void addVisit(Visit visit) { 110 | getVisitsInternal().add(visit); 111 | visit.setPetId(this.getId()); 112 | } 113 | 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/samples/petclinic/owner/PetController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.samples.petclinic.owner; 17 | 18 | import org.springframework.stereotype.Controller; 19 | import org.springframework.ui.ModelMap; 20 | import org.springframework.util.StringUtils; 21 | import org.springframework.validation.BindingResult; 22 | import org.springframework.web.bind.WebDataBinder; 23 | import org.springframework.web.bind.annotation.*; 24 | 25 | import javax.validation.Valid; 26 | import java.util.Collection; 27 | 28 | /** 29 | * @author Juergen Hoeller 30 | * @author Ken Krebs 31 | * @author Arjen Poutsma 32 | */ 33 | @Controller 34 | @RequestMapping("/owners/{ownerId}") 35 | class PetController { 36 | 37 | private static final String VIEWS_PETS_CREATE_OR_UPDATE_FORM = "pets/createOrUpdatePetForm"; 38 | private final PetRepository pets; 39 | private final OwnerRepository owners; 40 | 41 | public PetController(PetRepository pets, OwnerRepository owners) { 42 | this.pets = pets; 43 | this.owners = owners; 44 | } 45 | 46 | @ModelAttribute("types") 47 | public Collection populatePetTypes() { 48 | return this.pets.findPetTypes(); 49 | } 50 | 51 | @ModelAttribute("owner") 52 | public Owner findOwner(@PathVariable("ownerId") int ownerId) { 53 | return this.owners.findById(ownerId); 54 | } 55 | 56 | @InitBinder("owner") 57 | public void initOwnerBinder(WebDataBinder dataBinder) { 58 | dataBinder.setDisallowedFields("id"); 59 | } 60 | 61 | @InitBinder("pet") 62 | public void initPetBinder(WebDataBinder dataBinder) { 63 | dataBinder.setValidator(new PetValidator()); 64 | } 65 | 66 | @GetMapping("/pets/new") 67 | public String initCreationForm(Owner owner, ModelMap model) { 68 | Pet pet = new Pet(); 69 | owner.addPet(pet); 70 | model.put("pet", pet); 71 | return VIEWS_PETS_CREATE_OR_UPDATE_FORM; 72 | } 73 | 74 | @PostMapping("/pets/new") 75 | public String processCreationForm(Owner owner, @Valid Pet pet, BindingResult result, ModelMap model) { 76 | if (StringUtils.hasLength(pet.getName()) && pet.isNew() && owner.getPet(pet.getName(), true) != null){ 77 | result.rejectValue("name", "duplicate", "already exists"); 78 | } 79 | owner.addPet(pet); 80 | if (result.hasErrors()) { 81 | model.put("pet", pet); 82 | return VIEWS_PETS_CREATE_OR_UPDATE_FORM; 83 | } else { 84 | this.pets.save(pet); 85 | return "redirect:/owners/{ownerId}"; 86 | } 87 | } 88 | 89 | @GetMapping("/pets/{petId}/edit") 90 | public String initUpdateForm(@PathVariable("petId") int petId, ModelMap model) { 91 | Pet pet = this.pets.findById(petId); 92 | model.put("pet", pet); 93 | return VIEWS_PETS_CREATE_OR_UPDATE_FORM; 94 | } 95 | 96 | @PostMapping("/pets/{petId}/edit") 97 | public String processUpdateForm(@Valid Pet pet, BindingResult result, Owner owner, ModelMap model) { 98 | if (result.hasErrors()) { 99 | pet.setOwner(owner); 100 | model.put("pet", pet); 101 | return VIEWS_PETS_CREATE_OR_UPDATE_FORM; 102 | } else { 103 | owner.addPet(pet); 104 | this.pets.save(pet); 105 | return "redirect:/owners/{ownerId}"; 106 | } 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/samples/petclinic/owner/PetRepository.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2002-2013 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.samples.petclinic.owner; 17 | 18 | import java.util.List; 19 | 20 | import org.springframework.data.jpa.repository.Query; 21 | import org.springframework.data.repository.Repository; 22 | import org.springframework.transaction.annotation.Transactional; 23 | 24 | /** 25 | * Repository class for Pet domain objects All method names are compliant with Spring Data naming 26 | * conventions so this interface can easily be extended for Spring Data See here: http://static.springsource.org/spring-data/jpa/docs/current/reference/html/jpa.repositories.html#jpa.query-methods.query-creation 27 | * 28 | * @author Ken Krebs 29 | * @author Juergen Hoeller 30 | * @author Sam Brannen 31 | * @author Michael Isvy 32 | */ 33 | public interface PetRepository extends Repository { 34 | 35 | /** 36 | * Retrieve all {@link PetType}s from the data store. 37 | * @return a Collection of {@link PetType}s. 38 | */ 39 | @Query("SELECT ptype FROM PetType ptype ORDER BY ptype.name") 40 | @Transactional(readOnly = true) 41 | List findPetTypes(); 42 | 43 | /** 44 | * Retrieve a {@link Pet} from the data store by id. 45 | * @param id the id to search for 46 | * @return the {@link Pet} if found 47 | */ 48 | @Transactional(readOnly = true) 49 | Pet findById(Integer id); 50 | 51 | /** 52 | * Save a {@link Pet} to the data store, either inserting or updating it. 53 | * @param pet the {@link Pet} to save 54 | */ 55 | void save(Pet pet); 56 | 57 | } 58 | 59 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/samples/petclinic/owner/PetType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2002-2013 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.samples.petclinic.owner; 17 | 18 | import javax.persistence.Entity; 19 | import javax.persistence.Table; 20 | 21 | import org.springframework.samples.petclinic.model.NamedEntity; 22 | 23 | /** 24 | * @author Juergen Hoeller 25 | * Can be Cat, Dog, Hamster... 26 | */ 27 | @Entity 28 | @Table(name = "types") 29 | public class PetType extends NamedEntity { 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/samples/petclinic/owner/PetTypeFormatter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2002-2013 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.samples.petclinic.owner; 17 | 18 | 19 | import java.text.ParseException; 20 | import java.util.Collection; 21 | import java.util.Locale; 22 | 23 | import org.springframework.beans.factory.annotation.Autowired; 24 | import org.springframework.format.Formatter; 25 | import org.springframework.stereotype.Component; 26 | 27 | /** 28 | * Instructs Spring MVC on how to parse and print elements of type 'PetType'. Starting from Spring 3.0, Formatters have 29 | * come as an improvement in comparison to legacy PropertyEditors. See the following links for more details: - The 30 | * Spring ref doc: http://static.springsource.org/spring/docs/current/spring-framework-reference/html/validation.html#format-Formatter-SPI 31 | * - A nice blog entry from Gordon Dickens: http://gordondickens.com/wordpress/2010/09/30/using-spring-3-0-custom-type-converter/ 32 | *

33 | * 34 | * @author Mark Fisher 35 | * @author Juergen Hoeller 36 | * @author Michael Isvy 37 | */ 38 | @Component 39 | public class PetTypeFormatter implements Formatter { 40 | 41 | private final PetRepository pets; 42 | 43 | 44 | @Autowired 45 | public PetTypeFormatter(PetRepository pets) { 46 | this.pets = pets; 47 | } 48 | 49 | @Override 50 | public String print(PetType petType, Locale locale) { 51 | return petType.getName(); 52 | } 53 | 54 | @Override 55 | public PetType parse(String text, Locale locale) throws ParseException { 56 | Collection findPetTypes = this.pets.findPetTypes(); 57 | for (PetType type : findPetTypes) { 58 | if (type.getName().equals(text)) { 59 | return type; 60 | } 61 | } 62 | throw new ParseException("type not found: " + text, 0); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/samples/petclinic/owner/PetValidator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2002-2013 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.samples.petclinic.owner; 17 | 18 | import org.springframework.util.StringUtils; 19 | import org.springframework.validation.Errors; 20 | import org.springframework.validation.Validator; 21 | 22 | /** 23 | * Validator for Pet forms. 24 | *

25 | * We're not using Bean Validation annotations here because it is easier to define such validation rule in Java. 26 | *

27 | * 28 | * @author Ken Krebs 29 | * @author Juergen Hoeller 30 | */ 31 | public class PetValidator implements Validator { 32 | 33 | private static final String REQUIRED = "required"; 34 | 35 | @Override 36 | public void validate(Object obj, Errors errors) { 37 | Pet pet = (Pet) obj; 38 | String name = pet.getName(); 39 | // name validation 40 | if (!StringUtils.hasLength(name)) { 41 | errors.rejectValue("name", REQUIRED, REQUIRED); 42 | } 43 | 44 | // type validation 45 | if (pet.isNew() && pet.getType() == null) { 46 | errors.rejectValue("type", REQUIRED, REQUIRED); 47 | } 48 | 49 | // birth date validation 50 | if (pet.getBirthDate() == null) { 51 | errors.rejectValue("birthDate", REQUIRED, REQUIRED); 52 | } 53 | } 54 | 55 | /** 56 | * This Validator validates *just* Pet instances 57 | */ 58 | @Override 59 | public boolean supports(Class clazz) { 60 | return Pet.class.isAssignableFrom(clazz); 61 | } 62 | 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/samples/petclinic/owner/VisitController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.samples.petclinic.owner; 17 | 18 | import org.springframework.samples.petclinic.visit.Visit; 19 | import org.springframework.samples.petclinic.visit.VisitRepository; 20 | import org.springframework.stereotype.Controller; 21 | import org.springframework.validation.BindingResult; 22 | import org.springframework.web.bind.WebDataBinder; 23 | import org.springframework.web.bind.annotation.*; 24 | 25 | import javax.validation.Valid; 26 | import java.util.Map; 27 | 28 | /** 29 | * @author Juergen Hoeller 30 | * @author Ken Krebs 31 | * @author Arjen Poutsma 32 | * @author Michael Isvy 33 | * @author Dave Syer 34 | */ 35 | @Controller 36 | class VisitController { 37 | 38 | private final VisitRepository visits; 39 | private final PetRepository pets; 40 | 41 | 42 | public VisitController(VisitRepository visits, PetRepository pets) { 43 | this.visits = visits; 44 | this.pets = pets; 45 | } 46 | 47 | @InitBinder 48 | public void setAllowedFields(WebDataBinder dataBinder) { 49 | dataBinder.setDisallowedFields("id"); 50 | } 51 | 52 | /** 53 | * Called before each and every @RequestMapping annotated method. 54 | * 2 goals: 55 | * - Make sure we always have fresh data 56 | * - Since we do not use the session scope, make sure that Pet object always has an id 57 | * (Even though id is not part of the form fields) 58 | * 59 | * @param petId 60 | * @return Pet 61 | */ 62 | @ModelAttribute("visit") 63 | public Visit loadPetWithVisit(@PathVariable("petId") int petId, Map model) { 64 | Pet pet = this.pets.findById(petId); 65 | model.put("pet", pet); 66 | Visit visit = new Visit(); 67 | pet.addVisit(visit); 68 | return visit; 69 | } 70 | 71 | // Spring MVC calls method loadPetWithVisit(...) before initNewVisitForm is called 72 | @GetMapping("/owners/*/pets/{petId}/visits/new") 73 | public String initNewVisitForm(@PathVariable("petId") int petId, Map model) { 74 | return "pets/createOrUpdateVisitForm"; 75 | } 76 | 77 | // Spring MVC calls method loadPetWithVisit(...) before processNewVisitForm is called 78 | @PostMapping("/owners/{ownerId}/pets/{petId}/visits/new") 79 | public String processNewVisitForm(@Valid Visit visit, BindingResult result) { 80 | if (result.hasErrors()) { 81 | return "pets/createOrUpdateVisitForm"; 82 | } else { 83 | this.visits.save(visit); 84 | return "redirect:/owners/{ownerId}"; 85 | } 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/samples/petclinic/system/CacheConfiguration.java: -------------------------------------------------------------------------------- 1 | package org.springframework.samples.petclinic.system; 2 | 3 | import org.springframework.boot.autoconfigure.cache.JCacheManagerCustomizer; 4 | import org.springframework.cache.annotation.EnableCaching; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | import javax.cache.configuration.MutableConfiguration; 9 | 10 | /** 11 | * Cache configuration intended for caches providing the JCache API. This configuration creates the used cache for the 12 | * application and enables statistics that become accessible via JMX. 13 | */ 14 | @Configuration 15 | @EnableCaching 16 | class CacheConfiguration { 17 | 18 | @Bean 19 | public JCacheManagerCustomizer petclinicCacheConfigurationCustomizer() { 20 | return cm -> { 21 | cm.createCache("vets", cacheConfiguration()); 22 | }; 23 | } 24 | 25 | /** 26 | * Create a simple configuration that enable statistics via the JCache programmatic configuration API. 27 | *

28 | * Within the configuration object that is provided by the JCache API standard, there is only a very limited set of 29 | * configuration options. The really relevant configuration options (like the size limit) must be set via a 30 | * configuration mechanism that is provided by the selected JCache implementation. 31 | */ 32 | private javax.cache.configuration.Configuration cacheConfiguration() { 33 | return new MutableConfiguration<>().setStatisticsEnabled(true); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/samples/petclinic/system/CrashController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2002-2013 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.samples.petclinic.system; 17 | 18 | import org.springframework.stereotype.Controller; 19 | import org.springframework.web.bind.annotation.GetMapping; 20 | 21 | /** 22 | * Controller used to showcase what happens when an exception is thrown 23 | * 24 | * @author Michael Isvy 25 | *

26 | * Also see how a view that resolves to "error" has been added ("error.html"). 27 | */ 28 | @Controller 29 | class CrashController { 30 | 31 | @GetMapping("/oups") 32 | public String triggerException() { 33 | throw new RuntimeException("Expected: controller used to showcase what " 34 | + "happens when an exception is thrown"); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/samples/petclinic/system/WelcomeController.java: -------------------------------------------------------------------------------- 1 | package org.springframework.samples.petclinic.system; 2 | 3 | 4 | import org.springframework.stereotype.Controller; 5 | import org.springframework.web.bind.annotation.GetMapping; 6 | 7 | @Controller 8 | class WelcomeController { 9 | 10 | @GetMapping("/") 11 | public String welcome() { 12 | return "welcome"; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/samples/petclinic/vet/Specialty.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2002-2013 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.samples.petclinic.vet; 17 | 18 | import java.io.Serializable; 19 | 20 | import javax.persistence.Entity; 21 | import javax.persistence.Table; 22 | 23 | import org.springframework.samples.petclinic.model.NamedEntity; 24 | 25 | /** 26 | * Models a {@link Vet Vet's} specialty (for example, dentistry). 27 | * 28 | * @author Juergen Hoeller 29 | */ 30 | @Entity 31 | @Table(name = "specialties") 32 | public class Specialty extends NamedEntity implements Serializable { 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/samples/petclinic/vet/Vet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2002-2013 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.samples.petclinic.vet; 17 | 18 | import java.util.ArrayList; 19 | import java.util.Collections; 20 | import java.util.HashSet; 21 | import java.util.List; 22 | import java.util.Set; 23 | 24 | import javax.persistence.Entity; 25 | import javax.persistence.FetchType; 26 | import javax.persistence.JoinColumn; 27 | import javax.persistence.JoinTable; 28 | import javax.persistence.ManyToMany; 29 | import javax.persistence.Table; 30 | import javax.xml.bind.annotation.XmlElement; 31 | 32 | import org.springframework.beans.support.MutableSortDefinition; 33 | import org.springframework.beans.support.PropertyComparator; 34 | import org.springframework.samples.petclinic.model.Person; 35 | 36 | /** 37 | * Simple JavaBean domain object representing a veterinarian. 38 | * 39 | * @author Ken Krebs 40 | * @author Juergen Hoeller 41 | * @author Sam Brannen 42 | * @author Arjen Poutsma 43 | */ 44 | @Entity 45 | @Table(name = "vets") 46 | public class Vet extends Person { 47 | 48 | @ManyToMany(fetch = FetchType.EAGER) 49 | @JoinTable(name = "vet_specialties", joinColumns = @JoinColumn(name = "vet_id"), inverseJoinColumns = @JoinColumn(name = "specialty_id")) 50 | private Set specialties; 51 | 52 | protected Set getSpecialtiesInternal() { 53 | if (this.specialties == null) { 54 | this.specialties = new HashSet<>(); 55 | } 56 | return this.specialties; 57 | } 58 | 59 | protected void setSpecialtiesInternal(Set specialties) { 60 | this.specialties = specialties; 61 | } 62 | 63 | @XmlElement 64 | public List getSpecialties() { 65 | List sortedSpecs = new ArrayList<>(getSpecialtiesInternal()); 66 | PropertyComparator.sort(sortedSpecs, 67 | new MutableSortDefinition("name", true, true)); 68 | return Collections.unmodifiableList(sortedSpecs); 69 | } 70 | 71 | public int getNrOfSpecialties() { 72 | return getSpecialtiesInternal().size(); 73 | } 74 | 75 | public void addSpecialty(Specialty specialty) { 76 | getSpecialtiesInternal().add(specialty); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/samples/petclinic/vet/VetController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.samples.petclinic.vet; 17 | 18 | import org.springframework.stereotype.Controller; 19 | import org.springframework.web.bind.annotation.GetMapping; 20 | import org.springframework.web.bind.annotation.ResponseBody; 21 | 22 | import java.util.Map; 23 | 24 | /** 25 | * @author Juergen Hoeller 26 | * @author Mark Fisher 27 | * @author Ken Krebs 28 | * @author Arjen Poutsma 29 | */ 30 | @Controller 31 | class VetController { 32 | 33 | private final VetRepository vets; 34 | 35 | public VetController(VetRepository clinicService) { 36 | this.vets = clinicService; 37 | } 38 | 39 | @GetMapping("/vets.html") 40 | public String showVetList(Map model) { 41 | // Here we are returning an object of type 'Vets' rather than a collection of Vet 42 | // objects so it is simpler for Object-Xml mapping 43 | Vets vets = new Vets(); 44 | vets.getVetList().addAll(this.vets.findAll()); 45 | model.put("vets", vets); 46 | return "vets/vetList"; 47 | } 48 | 49 | @GetMapping({ "/vets" }) 50 | public @ResponseBody Vets showResourcesVetList() { 51 | // Here we are returning an object of type 'Vets' rather than a collection of Vet 52 | // objects so it is simpler for JSon/Object mapping 53 | Vets vets = new Vets(); 54 | vets.getVetList().addAll(this.vets.findAll()); 55 | return vets; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2002-2013 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.samples.petclinic.vet; 17 | 18 | import java.util.Collection; 19 | 20 | import org.springframework.cache.annotation.Cacheable; 21 | import org.springframework.dao.DataAccessException; 22 | import org.springframework.data.repository.Repository; 23 | import org.springframework.transaction.annotation.Transactional; 24 | 25 | /** 26 | * Repository class for Vet domain objects All method names are compliant with Spring Data naming 27 | * conventions so this interface can easily be extended for Spring Data See here: http://static.springsource.org/spring-data/jpa/docs/current/reference/html/jpa.repositories.html#jpa.query-methods.query-creation 28 | * 29 | * @author Ken Krebs 30 | * @author Juergen Hoeller 31 | * @author Sam Brannen 32 | * @author Michael Isvy 33 | */ 34 | public interface VetRepository extends Repository { 35 | 36 | /** 37 | * Retrieve all Vets from the data store. 38 | * 39 | * @return a Collection of Vets 40 | */ 41 | @Transactional(readOnly = true) 42 | @Cacheable("vets") 43 | Collection findAll() throws DataAccessException; 44 | 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/samples/petclinic/vet/Vets.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2002-2013 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.samples.petclinic.vet; 17 | 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | import javax.xml.bind.annotation.XmlElement; 22 | import javax.xml.bind.annotation.XmlRootElement; 23 | 24 | /** 25 | * Simple domain object representing a list of veterinarians. Mostly here to be used for the 'vets' {@link 26 | * org.springframework.web.servlet.view.xml.MarshallingView}. 27 | * 28 | * @author Arjen Poutsma 29 | */ 30 | @XmlRootElement 31 | public class Vets { 32 | 33 | private List vets; 34 | 35 | @XmlElement 36 | public List getVetList() { 37 | if (vets == null) { 38 | vets = new ArrayList<>(); 39 | } 40 | return vets; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/samples/petclinic/visit/Visit.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2002-2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.samples.petclinic.visit; 17 | 18 | import java.time.LocalDate; 19 | 20 | import javax.persistence.Column; 21 | import javax.persistence.Entity; 22 | import javax.persistence.Table; 23 | import javax.validation.constraints.NotEmpty; 24 | 25 | import org.springframework.format.annotation.DateTimeFormat; 26 | import org.springframework.samples.petclinic.model.BaseEntity; 27 | 28 | /** 29 | * Simple JavaBean domain object representing a visit. 30 | * 31 | * @author Ken Krebs 32 | * @author Dave Syer 33 | */ 34 | @Entity 35 | @Table(name = "visits") 36 | public class Visit extends BaseEntity { 37 | 38 | @Column(name = "visit_date") 39 | @DateTimeFormat(pattern = "yyyy-MM-dd") 40 | private LocalDate date; 41 | 42 | @NotEmpty 43 | @Column(name = "description") 44 | private String description; 45 | 46 | @Column(name = "pet_id") 47 | private Integer petId; 48 | 49 | /** 50 | * Creates a new instance of Visit for the current date 51 | */ 52 | public Visit() { 53 | this.date = LocalDate.now(); 54 | } 55 | 56 | public LocalDate getDate() { 57 | return this.date; 58 | } 59 | 60 | public void setDate(LocalDate date) { 61 | this.date = date; 62 | } 63 | 64 | public String getDescription() { 65 | return this.description; 66 | } 67 | 68 | public void setDescription(String description) { 69 | this.description = description; 70 | } 71 | 72 | public Integer getPetId() { 73 | return this.petId; 74 | } 75 | 76 | public void setPetId(Integer petId) { 77 | this.petId = petId; 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/samples/petclinic/visit/VisitRepository.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2002-2013 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.samples.petclinic.visit; 17 | 18 | import java.util.List; 19 | 20 | import org.springframework.dao.DataAccessException; 21 | import org.springframework.data.repository.Repository; 22 | import org.springframework.samples.petclinic.model.BaseEntity; 23 | 24 | /** 25 | * Repository class for Visit domain objects All method names are compliant with Spring Data naming 26 | * conventions so this interface can easily be extended for Spring Data See here: http://static.springsource.org/spring-data/jpa/docs/current/reference/html/jpa.repositories.html#jpa.query-methods.query-creation 27 | * 28 | * @author Ken Krebs 29 | * @author Juergen Hoeller 30 | * @author Sam Brannen 31 | * @author Michael Isvy 32 | */ 33 | public interface VisitRepository extends Repository { 34 | 35 | /** 36 | * Save a Visit to the data store, either inserting or updating it. 37 | * 38 | * @param visit the Visit to save 39 | * @see BaseEntity#isNew 40 | */ 41 | void save(Visit visit) throws DataAccessException; 42 | 43 | List findByPetId(Integer petId); 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/less/header.less: -------------------------------------------------------------------------------- 1 | .navbar { 2 | border-top: 4px solid #6db33f; 3 | background-color: #34302d; 4 | margin-bottom: 0px; 5 | border-bottom: 0; 6 | border-left: 0; 7 | border-right: 0; 8 | } 9 | 10 | .navbar a.navbar-brand { 11 | background: url("../images/spring-logo-dataflow.png") -1px -1px no-repeat; 12 | margin: 12px 0 6px; 13 | width: 229px; 14 | height: 46px; 15 | display: inline-block; 16 | text-decoration: none; 17 | padding: 0; 18 | } 19 | 20 | .navbar a.navbar-brand span { 21 | display: block; 22 | width: 229px; 23 | height: 46px; 24 | background: url("../images/spring-logo-dataflow.png") -1px -48px no-repeat; 25 | opacity: 0; 26 | -moz-transition: opacity 0.12s ease-in-out; 27 | -webkit-transition: opacity 0.12s ease-in-out; 28 | -o-transition: opacity 0.12s ease-in-out; 29 | } 30 | 31 | .navbar a:hover.navbar-brand span { 32 | opacity: 1; 33 | } 34 | 35 | .navbar li > a, .navbar-text { 36 | font-family: "montserratregular", sans-serif; 37 | text-shadow: none; 38 | font-size: 14px; 39 | 40 | /* line-height: 14px; */ 41 | padding: 28px 20px; 42 | transition: all 0.15s; 43 | -webkit-transition: all 0.15s; 44 | -moz-transition: all 0.15s; 45 | -o-transition: all 0.15s; 46 | -ms-transition: all 0.15s; 47 | } 48 | 49 | .navbar li > a { 50 | text-transform: uppercase; 51 | } 52 | 53 | .navbar .navbar-text { 54 | margin-top: 0; 55 | margin-bottom: 0; 56 | } 57 | .navbar li:hover > a { 58 | color: #eeeeee; 59 | background-color: #6db33f; 60 | } 61 | 62 | .navbar-toggle { 63 | border-width: 0; 64 | 65 | .icon-bar + .icon-bar { 66 | margin-top: 3px; 67 | } 68 | .icon-bar { 69 | width: 19px; 70 | height: 3px; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/main/less/petclinic.less: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | @icon-font-path: "../../webjars/bootstrap/fonts/"; 15 | 16 | @spring-green: #6db33f; 17 | @spring-dark-green: #5fa134; 18 | @spring-brown: #34302D; 19 | @spring-grey: #838789; 20 | @spring-light-grey: #f1f1f1; 21 | 22 | @body-bg: @spring-light-grey; 23 | @text-color: @spring-brown; 24 | @link-color: @spring-dark-green; 25 | @link-hover-color: @spring-dark-green; 26 | 27 | @navbar-default-link-color: @spring-light-grey; 28 | @navbar-default-link-active-color: @spring-light-grey; 29 | @navbar-default-link-hover-color: @spring-light-grey; 30 | @navbar-default-link-hover-bg: @spring-green; 31 | @navbar-default-toggle-icon-bar-bg: @spring-light-grey; 32 | @navbar-default-toggle-hover-bg: transparent; 33 | @navbar-default-link-active-bg: @spring-green; 34 | 35 | @border-radius-base: 0; 36 | @border-radius-large: 0; 37 | @border-radius-small: 0; 38 | 39 | @btn-default-color: @spring-light-grey; 40 | @btn-default-bg: @spring-brown; 41 | @btn-default-border: @spring-green; 42 | 43 | @nav-tabs-active-link-hover-color: @spring-light-grey; 44 | @nav-tabs-active-link-hover-bg: @spring-brown; 45 | @nav-tabs-active-link-hover-border-color: @spring-brown; 46 | @nav-tabs-border-color: @spring-brown; 47 | 48 | @pagination-active-bg: @spring-brown; 49 | @pagination-active-border: @spring-green; 50 | @table-border-color: @spring-brown; 51 | 52 | .table > thead > tr > th { 53 | background-color: lighten(@spring-brown, 3%); 54 | color: @spring-light-grey; 55 | } 56 | 57 | .table-filter { 58 | background-color: @spring-brown; 59 | padding: 9px 12px; 60 | } 61 | 62 | .nav > li > a { 63 | color: @spring-grey; 64 | } 65 | 66 | .btn-default { 67 | border-width: 2px; 68 | transition: border 0.15s; 69 | -webkit-transition: border 0.15s; 70 | -moz-transition: border 0.15s; 71 | -o-transition: border 0.15s; 72 | -ms-transition: border 0.15s; 73 | 74 | &:hover, 75 | &:focus, 76 | &:active, 77 | &.active, 78 | .open .dropdown-toggle& { 79 | background-color: @spring-brown; 80 | border-color: @spring-brown; 81 | } 82 | } 83 | 84 | 85 | .container .text-muted { 86 | margin: 20px 0; 87 | } 88 | 89 | code { 90 | font-size: 80%; 91 | } 92 | 93 | .xd-container { 94 | margin-top: 40px; 95 | margin-bottom: 100px; 96 | padding-left: 5px; 97 | padding-right: 5px; 98 | } 99 | 100 | h1 { 101 | margin-bottom: 15px 102 | } 103 | 104 | .index-page--subtitle { 105 | font-size: 16px; 106 | line-height: 24px; 107 | margin: 0 0 30px; 108 | } 109 | 110 | .form-horizontal button.btn-inverse { 111 | margin-left: 32px; 112 | } 113 | 114 | #job-params-modal .modal-dialog { 115 | width: 90%; 116 | margin-left:auto; 117 | margin-right:auto; 118 | } 119 | 120 | [ng-cloak].splash { 121 | display: block !important; 122 | } 123 | [ng-cloak] { 124 | display: none; 125 | } 126 | 127 | .splash { 128 | background: @spring-green; 129 | color: @spring-brown; 130 | display: none; 131 | } 132 | 133 | .error-page { 134 | margin-top: 100px; 135 | text-align: center; 136 | } 137 | 138 | .error-page .error-title { 139 | font-size: 24px; 140 | line-height: 24px; 141 | margin: 30px 0 0; 142 | } 143 | 144 | table td { 145 | vertical-align: middle; 146 | } 147 | 148 | table td .progress { 149 | margin-bottom: 0; 150 | } 151 | 152 | table td.action-column { 153 | width: 1px; 154 | } 155 | 156 | .help-block { 157 | color: lighten(@text-color, 50%); // lighten the text some for contrast 158 | } 159 | 160 | .xd-containers { 161 | font-size: 15px; 162 | } 163 | 164 | .cluster-view > table td { 165 | vertical-align: top; 166 | } 167 | 168 | .cluster-view .label, .cluster-view .column-block { 169 | display: block; 170 | } 171 | 172 | .cluster-view .input-group-addon { 173 | width: 0%; 174 | } 175 | 176 | .cluster-view { 177 | margin-bottom: 0; 178 | } 179 | 180 | .deployment-status-deployed { 181 | .label-success; 182 | } 183 | 184 | .deployment-status-incomplete { 185 | .label-warning; 186 | } 187 | 188 | .deployment-status-failed { 189 | .label-danger; 190 | } 191 | 192 | .deployment-status-deploying { 193 | .label-info 194 | } 195 | .deployment-status-na { 196 | } 197 | 198 | .container-details-table th { 199 | background-color: lighten(@spring-brown, 3%); 200 | color: @spring-light-grey; 201 | } 202 | 203 | .status-help-content-table td { 204 | color: @spring-brown; 205 | } 206 | 207 | .alert-success { 208 | .alert-variant(fade(@alert-success-bg, 70%); @alert-success-border; @alert-success-text); 209 | } 210 | .alert-info { 211 | .alert-variant(fade(@alert-info-bg, 70%); @alert-info-border; @alert-info-text); 212 | } 213 | .alert-warning { 214 | .alert-variant(fade(@alert-warning-bg, 70%); @alert-warning-border; @alert-warning-text); 215 | } 216 | .alert-danger { 217 | .alert-variant(fade(@alert-danger-bg, 70%); @alert-danger-border; @alert-danger-text); 218 | } 219 | 220 | .myspinner { 221 | animation-name: spinner; 222 | animation-duration: 2s; 223 | animation-iteration-count: infinite; 224 | animation-timing-function: linear; 225 | 226 | -webkit-transform-origin: 49% 50%; 227 | -webkit-animation-name: spinner; 228 | -webkit-animation-duration: 2s; 229 | -webkit-animation-iteration-count: infinite; 230 | -webkit-animation-timing-function: linear; 231 | } 232 | 233 | hr { 234 | border-top: 1px dotted @spring-brown; 235 | } 236 | 237 | @import "typography.less"; 238 | @import "header.less"; 239 | @import "responsive.less"; 240 | -------------------------------------------------------------------------------- /src/main/less/responsive.less: -------------------------------------------------------------------------------- 1 | @media (max-width: 768px) { 2 | .navbar-toggle { 3 | position:absolute; 4 | z-index: 9999; 5 | left:0px; 6 | top:0px; 7 | } 8 | 9 | .navbar a.navbar-brand { 10 | display: block; 11 | margin: 0 auto 0 auto; 12 | width: 148px; 13 | height: 50px; 14 | float: none; 15 | background: url("../images/spring-logo-dataflow-mobile.png") 0 center no-repeat; 16 | } 17 | 18 | .homepage-billboard .homepage-subtitle { 19 | font-size: 21px; 20 | line-height: 21px; 21 | } 22 | 23 | .navbar a.navbar-brand span { 24 | display: none; 25 | } 26 | 27 | .navbar { 28 | border-top-width: 0; 29 | } 30 | 31 | .xd-container { 32 | margin-top: 20px; 33 | margin-bottom: 30px; 34 | } 35 | 36 | .index-page--subtitle { 37 | margin-top: 10px; 38 | margin-bottom: 30px; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/less/typography.less: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'varela_roundregular'; 3 | 4 | src: url('../fonts/varela_round-webfont.eot'); 5 | src: url('../fonts/varela_round-webfont.eot?#iefix') format('embedded-opentype'), 6 | url('../fonts/varela_round-webfont.woff') format('woff'), 7 | url('../fonts/varela_round-webfont.ttf') format('truetype'), 8 | url('../fonts/varela_round-webfont.svg#varela_roundregular') format('svg'); 9 | font-weight: normal; 10 | font-style: normal; 11 | } 12 | 13 | @font-face { 14 | font-family: 'montserratregular'; 15 | src: url('../fonts/montserrat-webfont.eot'); 16 | src: url('../fonts/montserrat-webfont.eot?#iefix') format('embedded-opentype'), 17 | url('../fonts/montserrat-webfont.woff') format('woff'), 18 | url('../fonts/montserrat-webfont.ttf') format('truetype'), 19 | url('../fonts/montserrat-webfont.svg#montserratregular') format('svg'); 20 | font-weight: normal; 21 | font-style: normal; 22 | } 23 | 24 | body, h1, h2, h3, p, input { 25 | margin: 0; 26 | font-weight: 400; 27 | font-family: "varela_roundregular", sans-serif; 28 | color: #34302d; 29 | } 30 | 31 | h1 { 32 | font-size: 24px; 33 | line-height: 30px; 34 | font-family: "montserratregular", sans-serif; 35 | } 36 | 37 | h2 { 38 | font-size: 18px; 39 | font-weight: 700; 40 | line-height: 24px; 41 | margin-bottom: 10px; 42 | font-family: "montserratregular", sans-serif; 43 | } 44 | 45 | h3 { 46 | font-size: 16px; 47 | line-height: 24px; 48 | margin-bottom: 10px; 49 | font-weight: 700; 50 | } 51 | 52 | p { 53 | //font-size: 15px; 54 | //line-height: 24px; 55 | } 56 | 57 | strong { 58 | font-weight: 700; 59 | font-family: "montserratregular", sans-serif; 60 | } 61 | -------------------------------------------------------------------------------- /src/main/resources/application-mysql.properties: -------------------------------------------------------------------------------- 1 | # database init, supports mysql too 2 | database=mysql 3 | spring.datasource.url=jdbc:mysql://localhost/petclinic 4 | spring.datasource.username=root 5 | spring.datasource.password=petclinic 6 | # Uncomment this the first time the app runs 7 | # spring.datasource.initialization-mode=always 8 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | # database init, supports mysql too 2 | database=hsqldb 3 | spring.datasource.schema=classpath*:db/${database}/schema.sql 4 | spring.datasource.data=classpath*:db/${database}/data.sql 5 | 6 | # Web 7 | spring.thymeleaf.mode=HTML 8 | 9 | # JPA 10 | spring.jpa.hibernate.ddl-auto=none 11 | 12 | # Internationalization 13 | spring.messages.basename=messages/messages 14 | 15 | # Actuator / Management 16 | management.endpoints.web.base-path=/manage 17 | management.endpoints.web.exposure.include=* 18 | 19 | # Logging 20 | logging.level.org.springframework=INFO 21 | # logging.level.org.springframework.web=DEBUG 22 | # logging.level.org.springframework.context.annotation=TRACE 23 | 24 | # Maximum time static resources should be cached 25 | spring.resources.cache.cachecontrol.max-age=12h 26 | -------------------------------------------------------------------------------- /src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | |\ _,,,--,,_ 4 | /,`.-'`' ._ \-;;,_ 5 | _______ __|,4- ) )_ .;.(__`'-'__ ___ __ _ ___ _______ 6 | | | '---''(_/._)-'(_\_) | | | | | | | | | 7 | | _ | ___|_ _| | | | | |_| | | | __ _ _ 8 | | |_| | |___ | | | | | | | | | | \ \ \ \ 9 | | ___| ___| | | | _| |___| | _ | | _| \ \ \ \ 10 | | | | |___ | | | |_| | | | | | | |_ ) ) ) ) 11 | |___| |_______| |___| |_______|_______|___|_| |__|___|_______| / / / / 12 | ==================================================================/_/_/_/ 13 | 14 | :: Built with Spring Boot :: ${spring-boot.version} 15 | 16 | -------------------------------------------------------------------------------- /src/main/resources/db/hsqldb/data.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO vets VALUES (1, 'James', 'Carter'); 2 | INSERT INTO vets VALUES (2, 'Helen', 'Leary'); 3 | INSERT INTO vets VALUES (3, 'Linda', 'Douglas'); 4 | INSERT INTO vets VALUES (4, 'Rafael', 'Ortega'); 5 | INSERT INTO vets VALUES (5, 'Henry', 'Stevens'); 6 | INSERT INTO vets VALUES (6, 'Sharon', 'Jenkins'); 7 | 8 | INSERT INTO specialties VALUES (1, 'radiology'); 9 | INSERT INTO specialties VALUES (2, 'surgery'); 10 | INSERT INTO specialties VALUES (3, 'dentistry'); 11 | 12 | INSERT INTO vet_specialties VALUES (2, 1); 13 | INSERT INTO vet_specialties VALUES (3, 2); 14 | INSERT INTO vet_specialties VALUES (3, 3); 15 | INSERT INTO vet_specialties VALUES (4, 2); 16 | INSERT INTO vet_specialties VALUES (5, 1); 17 | 18 | INSERT INTO types VALUES (1, 'cat'); 19 | INSERT INTO types VALUES (2, 'dog'); 20 | INSERT INTO types VALUES (3, 'lizard'); 21 | INSERT INTO types VALUES (4, 'snake'); 22 | INSERT INTO types VALUES (5, 'bird'); 23 | INSERT INTO types VALUES (6, 'hamster'); 24 | 25 | INSERT INTO owners VALUES (1, 'George', 'Franklin', '110 W. Liberty St.', 'Madison', '6085551023'); 26 | INSERT INTO owners VALUES (2, 'Betty', 'Davis', '638 Cardinal Ave.', 'Sun Prairie', '6085551749'); 27 | INSERT INTO owners VALUES (3, 'Eduardo', 'Rodriquez', '2693 Commerce St.', 'McFarland', '6085558763'); 28 | INSERT INTO owners VALUES (4, 'Harold', 'Davis', '563 Friendly St.', 'Windsor', '6085553198'); 29 | INSERT INTO owners VALUES (5, 'Peter', 'McTavish', '2387 S. Fair Way', 'Madison', '6085552765'); 30 | INSERT INTO owners VALUES (6, 'Jean', 'Coleman', '105 N. Lake St.', 'Monona', '6085552654'); 31 | INSERT INTO owners VALUES (7, 'Jeff', 'Black', '1450 Oak Blvd.', 'Monona', '6085555387'); 32 | INSERT INTO owners VALUES (8, 'Maria', 'Escobito', '345 Maple St.', 'Madison', '6085557683'); 33 | INSERT INTO owners VALUES (9, 'David', 'Schroeder', '2749 Blackhawk Trail', 'Madison', '6085559435'); 34 | INSERT INTO owners VALUES (10, 'Carlos', 'Estaban', '2335 Independence La.', 'Waunakee', '6085555487'); 35 | 36 | INSERT INTO pets VALUES (1, 'Leo', '2010-09-07', 1, 1); 37 | INSERT INTO pets VALUES (2, 'Basil', '2012-08-06', 6, 2); 38 | INSERT INTO pets VALUES (3, 'Rosy', '2011-04-17', 2, 3); 39 | INSERT INTO pets VALUES (4, 'Jewel', '2010-03-07', 2, 3); 40 | INSERT INTO pets VALUES (5, 'Iggy', '2010-11-30', 3, 4); 41 | INSERT INTO pets VALUES (6, 'George', '2010-01-20', 4, 5); 42 | INSERT INTO pets VALUES (7, 'Samantha', '2012-09-04', 1, 6); 43 | INSERT INTO pets VALUES (8, 'Max', '2012-09-04', 1, 6); 44 | INSERT INTO pets VALUES (9, 'Lucky', '2011-08-06', 5, 7); 45 | INSERT INTO pets VALUES (10, 'Mulligan', '2007-02-24', 2, 8); 46 | INSERT INTO pets VALUES (11, 'Freddy', '2010-03-09', 5, 9); 47 | INSERT INTO pets VALUES (12, 'Lucky', '2010-06-24', 2, 10); 48 | INSERT INTO pets VALUES (13, 'Sly', '2012-06-08', 1, 10); 49 | 50 | INSERT INTO visits VALUES (1, 7, '2013-01-01', 'rabies shot'); 51 | INSERT INTO visits VALUES (2, 8, '2013-01-02', 'rabies shot'); 52 | INSERT INTO visits VALUES (3, 8, '2013-01-03', 'neutered'); 53 | INSERT INTO visits VALUES (4, 7, '2013-01-04', 'spayed'); 54 | -------------------------------------------------------------------------------- /src/main/resources/db/hsqldb/schema.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE vet_specialties IF EXISTS; 2 | DROP TABLE vets IF EXISTS; 3 | DROP TABLE specialties IF EXISTS; 4 | DROP TABLE visits IF EXISTS; 5 | DROP TABLE pets IF EXISTS; 6 | DROP TABLE types IF EXISTS; 7 | DROP TABLE owners IF EXISTS; 8 | 9 | 10 | CREATE TABLE vets ( 11 | id INTEGER IDENTITY PRIMARY KEY, 12 | first_name VARCHAR(30), 13 | last_name VARCHAR(30) 14 | ); 15 | CREATE INDEX vets_last_name ON vets (last_name); 16 | 17 | CREATE TABLE specialties ( 18 | id INTEGER IDENTITY PRIMARY KEY, 19 | name VARCHAR(80) 20 | ); 21 | CREATE INDEX specialties_name ON specialties (name); 22 | 23 | CREATE TABLE vet_specialties ( 24 | vet_id INTEGER NOT NULL, 25 | specialty_id INTEGER NOT NULL 26 | ); 27 | ALTER TABLE vet_specialties ADD CONSTRAINT fk_vet_specialties_vets FOREIGN KEY (vet_id) REFERENCES vets (id); 28 | ALTER TABLE vet_specialties ADD CONSTRAINT fk_vet_specialties_specialties FOREIGN KEY (specialty_id) REFERENCES specialties (id); 29 | 30 | CREATE TABLE types ( 31 | id INTEGER IDENTITY PRIMARY KEY, 32 | name VARCHAR(80) 33 | ); 34 | CREATE INDEX types_name ON types (name); 35 | 36 | CREATE TABLE owners ( 37 | id INTEGER IDENTITY PRIMARY KEY, 38 | first_name VARCHAR(30), 39 | last_name VARCHAR_IGNORECASE(30), 40 | address VARCHAR(255), 41 | city VARCHAR(80), 42 | telephone VARCHAR(20) 43 | ); 44 | CREATE INDEX owners_last_name ON owners (last_name); 45 | 46 | CREATE TABLE pets ( 47 | id INTEGER IDENTITY PRIMARY KEY, 48 | name VARCHAR(30), 49 | birth_date DATE, 50 | type_id INTEGER NOT NULL, 51 | owner_id INTEGER NOT NULL 52 | ); 53 | ALTER TABLE pets ADD CONSTRAINT fk_pets_owners FOREIGN KEY (owner_id) REFERENCES owners (id); 54 | ALTER TABLE pets ADD CONSTRAINT fk_pets_types FOREIGN KEY (type_id) REFERENCES types (id); 55 | CREATE INDEX pets_name ON pets (name); 56 | 57 | CREATE TABLE visits ( 58 | id INTEGER IDENTITY PRIMARY KEY, 59 | pet_id INTEGER NOT NULL, 60 | visit_date DATE, 61 | description VARCHAR(255) 62 | ); 63 | ALTER TABLE visits ADD CONSTRAINT fk_visits_pets FOREIGN KEY (pet_id) REFERENCES pets (id); 64 | CREATE INDEX visits_pet_id ON visits (pet_id); 65 | -------------------------------------------------------------------------------- /src/main/resources/db/mysql/data.sql: -------------------------------------------------------------------------------- 1 | INSERT IGNORE INTO vets VALUES (1, 'James', 'Carter'); 2 | INSERT IGNORE INTO vets VALUES (2, 'Helen', 'Leary'); 3 | INSERT IGNORE INTO vets VALUES (3, 'Linda', 'Douglas'); 4 | INSERT IGNORE INTO vets VALUES (4, 'Rafael', 'Ortega'); 5 | INSERT IGNORE INTO vets VALUES (5, 'Henry', 'Stevens'); 6 | INSERT IGNORE INTO vets VALUES (6, 'Sharon', 'Jenkins'); 7 | 8 | INSERT IGNORE INTO specialties VALUES (1, 'radiology'); 9 | INSERT IGNORE INTO specialties VALUES (2, 'surgery'); 10 | INSERT IGNORE INTO specialties VALUES (3, 'dentistry'); 11 | 12 | INSERT IGNORE INTO vet_specialties VALUES (2, 1); 13 | INSERT IGNORE INTO vet_specialties VALUES (3, 2); 14 | INSERT IGNORE INTO vet_specialties VALUES (3, 3); 15 | INSERT IGNORE INTO vet_specialties VALUES (4, 2); 16 | INSERT IGNORE INTO vet_specialties VALUES (5, 1); 17 | 18 | INSERT IGNORE INTO types VALUES (1, 'cat'); 19 | INSERT IGNORE INTO types VALUES (2, 'dog'); 20 | INSERT IGNORE INTO types VALUES (3, 'lizard'); 21 | INSERT IGNORE INTO types VALUES (4, 'snake'); 22 | INSERT IGNORE INTO types VALUES (5, 'bird'); 23 | INSERT IGNORE INTO types VALUES (6, 'hamster'); 24 | 25 | INSERT IGNORE INTO owners VALUES (1, 'George', 'Franklin', '110 W. Liberty St.', 'Madison', '6085551023'); 26 | INSERT IGNORE INTO owners VALUES (2, 'Betty', 'Davis', '638 Cardinal Ave.', 'Sun Prairie', '6085551749'); 27 | INSERT IGNORE INTO owners VALUES (3, 'Eduardo', 'Rodriquez', '2693 Commerce St.', 'McFarland', '6085558763'); 28 | INSERT IGNORE INTO owners VALUES (4, 'Harold', 'Davis', '563 Friendly St.', 'Windsor', '6085553198'); 29 | INSERT IGNORE INTO owners VALUES (5, 'Peter', 'McTavish', '2387 S. Fair Way', 'Madison', '6085552765'); 30 | INSERT IGNORE INTO owners VALUES (6, 'Jean', 'Coleman', '105 N. Lake St.', 'Monona', '6085552654'); 31 | INSERT IGNORE INTO owners VALUES (7, 'Jeff', 'Black', '1450 Oak Blvd.', 'Monona', '6085555387'); 32 | INSERT IGNORE INTO owners VALUES (8, 'Maria', 'Escobito', '345 Maple St.', 'Madison', '6085557683'); 33 | INSERT IGNORE INTO owners VALUES (9, 'David', 'Schroeder', '2749 Blackhawk Trail', 'Madison', '6085559435'); 34 | INSERT IGNORE INTO owners VALUES (10, 'Carlos', 'Estaban', '2335 Independence La.', 'Waunakee', '6085555487'); 35 | 36 | INSERT IGNORE INTO pets VALUES (1, 'Leo', '2000-09-07', 1, 1); 37 | INSERT IGNORE INTO pets VALUES (2, 'Basil', '2002-08-06', 6, 2); 38 | INSERT IGNORE INTO pets VALUES (3, 'Rosy', '2001-04-17', 2, 3); 39 | INSERT IGNORE INTO pets VALUES (4, 'Jewel', '2000-03-07', 2, 3); 40 | INSERT IGNORE INTO pets VALUES (5, 'Iggy', '2000-11-30', 3, 4); 41 | INSERT IGNORE INTO pets VALUES (6, 'George', '2000-01-20', 4, 5); 42 | INSERT IGNORE INTO pets VALUES (7, 'Samantha', '1995-09-04', 1, 6); 43 | INSERT IGNORE INTO pets VALUES (8, 'Max', '1995-09-04', 1, 6); 44 | INSERT IGNORE INTO pets VALUES (9, 'Lucky', '1999-08-06', 5, 7); 45 | INSERT IGNORE INTO pets VALUES (10, 'Mulligan', '1997-02-24', 2, 8); 46 | INSERT IGNORE INTO pets VALUES (11, 'Freddy', '2000-03-09', 5, 9); 47 | INSERT IGNORE INTO pets VALUES (12, 'Lucky', '2000-06-24', 2, 10); 48 | INSERT IGNORE INTO pets VALUES (13, 'Sly', '2002-06-08', 1, 10); 49 | 50 | INSERT IGNORE INTO visits VALUES (1, 7, '2010-03-04', 'rabies shot'); 51 | INSERT IGNORE INTO visits VALUES (2, 8, '2011-03-04', 'rabies shot'); 52 | INSERT IGNORE INTO visits VALUES (3, 8, '2009-06-04', 'neutered'); 53 | INSERT IGNORE INTO visits VALUES (4, 7, '2008-09-04', 'spayed'); 54 | -------------------------------------------------------------------------------- /src/main/resources/db/mysql/petclinic_db_setup_mysql.txt: -------------------------------------------------------------------------------- 1 | ================================================================================ 2 | === Spring PetClinic sample application - MySQL Configuration === 3 | ================================================================================ 4 | 5 | @author Sam Brannen 6 | @author Costin Leau 7 | @author Dave Syer 8 | 9 | -------------------------------------------------------------------------------- 10 | 11 | 1) Download and install the MySQL database (e.g., MySQL Community Server 5.1.x), 12 | which can be found here: http://dev.mysql.com/downloads/. Or run the 13 | "docker-compose.yml" from the root of the project (if you have docker installed 14 | locally). 15 | 16 | 2) Create the PetClinic database and user by executing the "db/mysql/{schema,data}.sql" 17 | scripts (or set "spring.datasource.initialize=true" the first time you run the app). 18 | -------------------------------------------------------------------------------- /src/main/resources/db/mysql/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE IF NOT EXISTS petclinic; 2 | 3 | ALTER DATABASE petclinic 4 | DEFAULT CHARACTER SET utf8 5 | DEFAULT COLLATE utf8_general_ci; 6 | 7 | GRANT ALL PRIVILEGES ON petclinic.* TO pc@localhost IDENTIFIED BY 'pc'; 8 | 9 | USE petclinic; 10 | 11 | CREATE TABLE IF NOT EXISTS vets ( 12 | id INT(4) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 13 | first_name VARCHAR(30), 14 | last_name VARCHAR(30), 15 | INDEX(last_name) 16 | ) engine=InnoDB; 17 | 18 | CREATE TABLE IF NOT EXISTS specialties ( 19 | id INT(4) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 20 | name VARCHAR(80), 21 | INDEX(name) 22 | ) engine=InnoDB; 23 | 24 | CREATE TABLE IF NOT EXISTS vet_specialties ( 25 | vet_id INT(4) UNSIGNED NOT NULL, 26 | specialty_id INT(4) UNSIGNED NOT NULL, 27 | FOREIGN KEY (vet_id) REFERENCES vets(id), 28 | FOREIGN KEY (specialty_id) REFERENCES specialties(id), 29 | UNIQUE (vet_id,specialty_id) 30 | ) engine=InnoDB; 31 | 32 | CREATE TABLE IF NOT EXISTS types ( 33 | id INT(4) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 34 | name VARCHAR(80), 35 | INDEX(name) 36 | ) engine=InnoDB; 37 | 38 | CREATE TABLE IF NOT EXISTS owners ( 39 | id INT(4) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 40 | first_name VARCHAR(30), 41 | last_name VARCHAR(30), 42 | address VARCHAR(255), 43 | city VARCHAR(80), 44 | telephone VARCHAR(20), 45 | INDEX(last_name) 46 | ) engine=InnoDB; 47 | 48 | CREATE TABLE IF NOT EXISTS pets ( 49 | id INT(4) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 50 | name VARCHAR(30), 51 | birth_date DATE, 52 | type_id INT(4) UNSIGNED NOT NULL, 53 | owner_id INT(4) UNSIGNED NOT NULL, 54 | INDEX(name), 55 | FOREIGN KEY (owner_id) REFERENCES owners(id), 56 | FOREIGN KEY (type_id) REFERENCES types(id) 57 | ) engine=InnoDB; 58 | 59 | CREATE TABLE IF NOT EXISTS visits ( 60 | id INT(4) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 61 | pet_id INT(4) UNSIGNED NOT NULL, 62 | visit_date DATE, 63 | description VARCHAR(255), 64 | FOREIGN KEY (pet_id) REFERENCES pets(id) 65 | ) engine=InnoDB; 66 | -------------------------------------------------------------------------------- /src/main/resources/messages/messages.properties: -------------------------------------------------------------------------------- 1 | welcome=Welcome 2 | required=is required 3 | notFound=has not been found 4 | duplicate=is already in use 5 | nonNumeric=must be all numeric 6 | duplicateFormSubmission=Duplicate form submission is not allowed 7 | typeMismatch.date=invalid date 8 | typeMismatch.birthDate=invalid date 9 | -------------------------------------------------------------------------------- /src/main/resources/messages/messages_de.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panga/spring-petclinic/62fb6aa614b12f7163419ab29513c0e59c4cfdbf/src/main/resources/messages/messages_de.properties -------------------------------------------------------------------------------- /src/main/resources/messages/messages_en.properties: -------------------------------------------------------------------------------- 1 | # This file is intentionally empty. Message look-ups will fall back to the default "messages.properties" file. -------------------------------------------------------------------------------- /src/main/resources/static/resources/fonts/montserrat-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panga/spring-petclinic/62fb6aa614b12f7163419ab29513c0e59c4cfdbf/src/main/resources/static/resources/fonts/montserrat-webfont.eot -------------------------------------------------------------------------------- /src/main/resources/static/resources/fonts/montserrat-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panga/spring-petclinic/62fb6aa614b12f7163419ab29513c0e59c4cfdbf/src/main/resources/static/resources/fonts/montserrat-webfont.ttf -------------------------------------------------------------------------------- /src/main/resources/static/resources/fonts/montserrat-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panga/spring-petclinic/62fb6aa614b12f7163419ab29513c0e59c4cfdbf/src/main/resources/static/resources/fonts/montserrat-webfont.woff -------------------------------------------------------------------------------- /src/main/resources/static/resources/fonts/varela_round-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panga/spring-petclinic/62fb6aa614b12f7163419ab29513c0e59c4cfdbf/src/main/resources/static/resources/fonts/varela_round-webfont.eot -------------------------------------------------------------------------------- /src/main/resources/static/resources/fonts/varela_round-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panga/spring-petclinic/62fb6aa614b12f7163419ab29513c0e59c4cfdbf/src/main/resources/static/resources/fonts/varela_round-webfont.ttf -------------------------------------------------------------------------------- /src/main/resources/static/resources/fonts/varela_round-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panga/spring-petclinic/62fb6aa614b12f7163419ab29513c0e59c4cfdbf/src/main/resources/static/resources/fonts/varela_round-webfont.woff -------------------------------------------------------------------------------- /src/main/resources/static/resources/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panga/spring-petclinic/62fb6aa614b12f7163419ab29513c0e59c4cfdbf/src/main/resources/static/resources/images/favicon.png -------------------------------------------------------------------------------- /src/main/resources/static/resources/images/pets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panga/spring-petclinic/62fb6aa614b12f7163419ab29513c0e59c4cfdbf/src/main/resources/static/resources/images/pets.png -------------------------------------------------------------------------------- /src/main/resources/static/resources/images/platform-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panga/spring-petclinic/62fb6aa614b12f7163419ab29513c0e59c4cfdbf/src/main/resources/static/resources/images/platform-bg.png -------------------------------------------------------------------------------- /src/main/resources/static/resources/images/spring-logo-dataflow-mobile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panga/spring-petclinic/62fb6aa614b12f7163419ab29513c0e59c4cfdbf/src/main/resources/static/resources/images/spring-logo-dataflow-mobile.png -------------------------------------------------------------------------------- /src/main/resources/static/resources/images/spring-logo-dataflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panga/spring-petclinic/62fb6aa614b12f7163419ab29513c0e59c4cfdbf/src/main/resources/static/resources/images/spring-logo-dataflow.png -------------------------------------------------------------------------------- /src/main/resources/static/resources/images/spring-pivotal-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panga/spring-petclinic/62fb6aa614b12f7163419ab29513c0e59c4cfdbf/src/main/resources/static/resources/images/spring-pivotal-logo.png -------------------------------------------------------------------------------- /src/main/resources/templates/error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

Something happened...

8 |

Exception message

9 | 10 | 11 | -------------------------------------------------------------------------------- /src/main/resources/templates/fragments/inputField.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |
8 | 9 |
10 |
11 | 12 | 15 |
16 | 19 | 20 | 23 | Error 24 | 25 |
26 |
27 |
28 |
29 | 30 | 31 | -------------------------------------------------------------------------------- /src/main/resources/templates/fragments/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | PetClinic :: a Spring Framework demonstration 14 | 15 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 71 |
72 |
73 | 74 | 75 | 76 |
77 |
78 |
79 |
80 |
81 | Sponsored by Pivotal
83 |
84 |
85 |
86 |
87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /src/main/resources/templates/fragments/selectField.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |
8 | 9 | 10 |
11 | 15 | 18 | 19 | 22 | Error 23 | 24 |
25 |
26 |
27 |
28 | 29 | -------------------------------------------------------------------------------- /src/main/resources/templates/owners/createOrUpdateOwnerForm.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 |

Owner

7 |
8 |
9 | 11 | 13 | 15 | 17 | 19 |
20 |
21 |
22 | 26 |
27 |
28 |
29 | 30 | 31 | -------------------------------------------------------------------------------- /src/main/resources/templates/owners/findOwners.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 |

Find Owners

7 | 8 |
10 |
11 |
12 | 13 |
14 |
17 |

Error

18 |
19 |
20 |
21 |
22 |
23 |
24 | 26 |
27 |
28 | 29 |
30 | 31 |
32 | Add Owner 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/main/resources/templates/owners/ownerDetails.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 |

Owner Information

10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
Name
Address
City
Telephone
30 | 31 | Edit 32 | Owner 33 | Add 34 | New Pet 35 | 36 |
37 |
38 |
39 |

Pets and Visits

40 | 41 | 42 | 43 | 44 | 55 | 77 | 78 | 79 |
45 |
46 |
Name
47 |
48 |
Birth Date
49 |
51 |
Type
52 |
53 |
54 |
56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 71 | 74 | 75 |
Visit DateDescription
Edit 70 | PetAdd 73 | Visit
76 |
80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /src/main/resources/templates/owners/ownersList.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

Owners

8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 24 | 28 | 29 | 30 |
NameAddressCityTelephonePets
22 | 23 | 25 | 26 | 27 |
31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/main/resources/templates/pets/createOrUpdatePetForm.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 |

7 | New 8 | Pet 9 |

10 |
11 | 12 |
13 |
14 | 15 |
16 | 17 |
18 |
19 | 21 | 23 | 25 |
26 |
27 |
28 | 32 |
33 |
34 |
35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/main/resources/templates/pets/createOrUpdateVisitForm.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 |

7 | New 8 | Visit 9 |

10 | 11 | Pet 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 25 | 26 | 28 | 29 |
NameBirth DateTypeOwner
30 | 31 |
32 |
33 | 35 | 37 |
38 | 39 |
40 |
41 | 42 | 43 |
44 |
45 |
46 | 47 |
48 | Previous Visits 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 |
DateDescription
59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /src/main/resources/templates/vets/vetList.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 |

Veterinarians

9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 23 | 24 | 25 |
NameSpecialties
none
26 | 27 | 28 | -------------------------------------------------------------------------------- /src/main/resources/templates/welcome.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

Welcome

8 |
9 |
10 | 11 |
12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/main/wro/wro.properties: -------------------------------------------------------------------------------- 1 | #List of preProcessors 2 | preProcessors=lessCssImport 3 | #List of postProcessors 4 | postProcessors=less4j -------------------------------------------------------------------------------- /src/main/wro/wro.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | classpath:META-INF/resources/webjars/bootstrap/3.3.6/less/bootstrap.less 4 | /petclinic.less 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/samples/petclinic/PetclinicIntegrationTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.samples.petclinic; 18 | 19 | import org.junit.Test; 20 | import org.junit.runner.RunWith; 21 | 22 | import org.springframework.beans.factory.annotation.Autowired; 23 | import org.springframework.boot.test.context.SpringBootTest; 24 | import org.springframework.samples.petclinic.vet.VetRepository; 25 | import org.springframework.test.context.junit4.SpringRunner; 26 | 27 | @RunWith(SpringRunner.class) 28 | @SpringBootTest 29 | public class PetclinicIntegrationTests { 30 | 31 | @Autowired 32 | private VetRepository vets; 33 | 34 | @Test 35 | public void testFindAll() throws Exception { 36 | vets.findAll(); 37 | vets.findAll(); // served from cache 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/samples/petclinic/model/ValidatorTests.java: -------------------------------------------------------------------------------- 1 | package org.springframework.samples.petclinic.model; 2 | 3 | import java.util.Locale; 4 | import java.util.Set; 5 | 6 | import javax.validation.ConstraintViolation; 7 | import javax.validation.Validator; 8 | 9 | import org.junit.Test; 10 | 11 | import org.springframework.context.i18n.LocaleContextHolder; 12 | import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; 13 | 14 | import static org.assertj.core.api.Assertions.assertThat; 15 | 16 | /** 17 | * @author Michael Isvy Simple test to make sure that Bean Validation is working (useful 18 | * when upgrading to a new version of Hibernate Validator/ Bean Validation) 19 | */ 20 | public class ValidatorTests { 21 | 22 | private Validator createValidator() { 23 | LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean(); 24 | localValidatorFactoryBean.afterPropertiesSet(); 25 | return localValidatorFactoryBean; 26 | } 27 | 28 | @Test 29 | public void shouldNotValidateWhenFirstNameEmpty() { 30 | 31 | LocaleContextHolder.setLocale(Locale.ENGLISH); 32 | Person person = new Person(); 33 | person.setFirstName(""); 34 | person.setLastName("smith"); 35 | 36 | Validator validator = createValidator(); 37 | Set> constraintViolations = validator 38 | .validate(person); 39 | 40 | assertThat(constraintViolations.size()).isEqualTo(1); 41 | ConstraintViolation violation = constraintViolations.iterator().next(); 42 | assertThat(violation.getPropertyPath().toString()).isEqualTo("firstName"); 43 | assertThat(violation.getMessage()).isEqualTo("must not be empty"); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/samples/petclinic/owner/OwnerControllerTests.java: -------------------------------------------------------------------------------- 1 | package org.springframework.samples.petclinic.owner; 2 | 3 | import static org.hamcrest.Matchers.hasProperty; 4 | import static org.hamcrest.Matchers.is; 5 | import static org.mockito.BDDMockito.given; 6 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 7 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; 8 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model; 9 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 10 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view; 11 | 12 | import org.assertj.core.util.Lists; 13 | import org.junit.Before; 14 | import org.junit.Test; 15 | import org.junit.runner.RunWith; 16 | import org.springframework.beans.factory.annotation.Autowired; 17 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; 18 | import org.springframework.boot.test.mock.mockito.MockBean; 19 | import org.springframework.samples.petclinic.owner.Owner; 20 | import org.springframework.samples.petclinic.owner.OwnerController; 21 | import org.springframework.samples.petclinic.owner.OwnerRepository; 22 | import org.springframework.test.context.junit4.SpringRunner; 23 | import org.springframework.test.web.servlet.MockMvc; 24 | 25 | /** 26 | * Test class for {@link OwnerController} 27 | * 28 | * @author Colin But 29 | */ 30 | @RunWith(SpringRunner.class) 31 | @WebMvcTest(OwnerController.class) 32 | public class OwnerControllerTests { 33 | 34 | private static final int TEST_OWNER_ID = 1; 35 | 36 | @Autowired 37 | private MockMvc mockMvc; 38 | 39 | @MockBean 40 | private OwnerRepository owners; 41 | 42 | private Owner george; 43 | 44 | @Before 45 | public void setup() { 46 | george = new Owner(); 47 | george.setId(TEST_OWNER_ID); 48 | george.setFirstName("George"); 49 | george.setLastName("Franklin"); 50 | george.setAddress("110 W. Liberty St."); 51 | george.setCity("Madison"); 52 | george.setTelephone("6085551023"); 53 | given(this.owners.findById(TEST_OWNER_ID)).willReturn(george); 54 | } 55 | 56 | @Test 57 | public void testInitCreationForm() throws Exception { 58 | mockMvc.perform(get("/owners/new")) 59 | .andExpect(status().isOk()) 60 | .andExpect(model().attributeExists("owner")) 61 | .andExpect(view().name("owners/createOrUpdateOwnerForm")); 62 | } 63 | 64 | @Test 65 | public void testProcessCreationFormSuccess() throws Exception { 66 | mockMvc.perform(post("/owners/new") 67 | .param("firstName", "Joe") 68 | .param("lastName", "Bloggs") 69 | .param("address", "123 Caramel Street") 70 | .param("city", "London") 71 | .param("telephone", "01316761638") 72 | ) 73 | .andExpect(status().is3xxRedirection()); 74 | } 75 | 76 | @Test 77 | public void testProcessCreationFormHasErrors() throws Exception { 78 | mockMvc.perform(post("/owners/new") 79 | .param("firstName", "Joe") 80 | .param("lastName", "Bloggs") 81 | .param("city", "London") 82 | ) 83 | .andExpect(status().isOk()) 84 | .andExpect(model().attributeHasErrors("owner")) 85 | .andExpect(model().attributeHasFieldErrors("owner", "address")) 86 | .andExpect(model().attributeHasFieldErrors("owner", "telephone")) 87 | .andExpect(view().name("owners/createOrUpdateOwnerForm")); 88 | } 89 | 90 | @Test 91 | public void testInitFindForm() throws Exception { 92 | mockMvc.perform(get("/owners/find")) 93 | .andExpect(status().isOk()) 94 | .andExpect(model().attributeExists("owner")) 95 | .andExpect(view().name("owners/findOwners")); 96 | } 97 | 98 | @Test 99 | public void testProcessFindFormSuccess() throws Exception { 100 | given(this.owners.findByLastName("")).willReturn(Lists.newArrayList(george, new Owner())); 101 | mockMvc.perform(get("/owners")) 102 | .andExpect(status().isOk()) 103 | .andExpect(view().name("owners/ownersList")); 104 | } 105 | 106 | @Test 107 | public void testProcessFindFormByLastName() throws Exception { 108 | given(this.owners.findByLastName(george.getLastName())).willReturn(Lists.newArrayList(george)); 109 | mockMvc.perform(get("/owners") 110 | .param("lastName", "Franklin") 111 | ) 112 | .andExpect(status().is3xxRedirection()) 113 | .andExpect(view().name("redirect:/owners/" + TEST_OWNER_ID)); 114 | } 115 | 116 | @Test 117 | public void testProcessFindFormNoOwnersFound() throws Exception { 118 | mockMvc.perform(get("/owners") 119 | .param("lastName", "Unknown Surname") 120 | ) 121 | .andExpect(status().isOk()) 122 | .andExpect(model().attributeHasFieldErrors("owner", "lastName")) 123 | .andExpect(model().attributeHasFieldErrorCode("owner", "lastName", "notFound")) 124 | .andExpect(view().name("owners/findOwners")); 125 | } 126 | 127 | @Test 128 | public void testInitUpdateOwnerForm() throws Exception { 129 | mockMvc.perform(get("/owners/{ownerId}/edit", TEST_OWNER_ID)) 130 | .andExpect(status().isOk()) 131 | .andExpect(model().attributeExists("owner")) 132 | .andExpect(model().attribute("owner", hasProperty("lastName", is("Franklin")))) 133 | .andExpect(model().attribute("owner", hasProperty("firstName", is("George")))) 134 | .andExpect(model().attribute("owner", hasProperty("address", is("110 W. Liberty St.")))) 135 | .andExpect(model().attribute("owner", hasProperty("city", is("Madison")))) 136 | .andExpect(model().attribute("owner", hasProperty("telephone", is("6085551023")))) 137 | .andExpect(view().name("owners/createOrUpdateOwnerForm")); 138 | } 139 | 140 | @Test 141 | public void testProcessUpdateOwnerFormSuccess() throws Exception { 142 | mockMvc.perform(post("/owners/{ownerId}/edit", TEST_OWNER_ID) 143 | .param("firstName", "Joe") 144 | .param("lastName", "Bloggs") 145 | .param("address", "123 Caramel Street") 146 | .param("city", "London") 147 | .param("telephone", "01616291589") 148 | ) 149 | .andExpect(status().is3xxRedirection()) 150 | .andExpect(view().name("redirect:/owners/{ownerId}")); 151 | } 152 | 153 | @Test 154 | public void testProcessUpdateOwnerFormHasErrors() throws Exception { 155 | mockMvc.perform(post("/owners/{ownerId}/edit", TEST_OWNER_ID) 156 | .param("firstName", "Joe") 157 | .param("lastName", "Bloggs") 158 | .param("city", "London") 159 | ) 160 | .andExpect(status().isOk()) 161 | .andExpect(model().attributeHasErrors("owner")) 162 | .andExpect(model().attributeHasFieldErrors("owner", "address")) 163 | .andExpect(model().attributeHasFieldErrors("owner", "telephone")) 164 | .andExpect(view().name("owners/createOrUpdateOwnerForm")); 165 | } 166 | 167 | @Test 168 | public void testShowOwner() throws Exception { 169 | mockMvc.perform(get("/owners/{ownerId}", TEST_OWNER_ID)) 170 | .andExpect(status().isOk()) 171 | .andExpect(model().attribute("owner", hasProperty("lastName", is("Franklin")))) 172 | .andExpect(model().attribute("owner", hasProperty("firstName", is("George")))) 173 | .andExpect(model().attribute("owner", hasProperty("address", is("110 W. Liberty St.")))) 174 | .andExpect(model().attribute("owner", hasProperty("city", is("Madison")))) 175 | .andExpect(model().attribute("owner", hasProperty("telephone", is("6085551023")))) 176 | .andExpect(view().name("owners/ownerDetails")); 177 | } 178 | 179 | } 180 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/samples/petclinic/owner/PetControllerTests.java: -------------------------------------------------------------------------------- 1 | package org.springframework.samples.petclinic.owner; 2 | 3 | import static org.mockito.BDDMockito.given; 4 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 5 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; 6 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model; 7 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 8 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view; 9 | 10 | import org.assertj.core.util.Lists; 11 | import org.junit.Before; 12 | import org.junit.Test; 13 | import org.junit.runner.RunWith; 14 | import org.springframework.beans.factory.annotation.Autowired; 15 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; 16 | import org.springframework.boot.test.mock.mockito.MockBean; 17 | import org.springframework.context.annotation.ComponentScan; 18 | import org.springframework.context.annotation.FilterType; 19 | import org.springframework.samples.petclinic.owner.Owner; 20 | import org.springframework.samples.petclinic.owner.OwnerRepository; 21 | import org.springframework.samples.petclinic.owner.Pet; 22 | import org.springframework.samples.petclinic.owner.PetController; 23 | import org.springframework.samples.petclinic.owner.PetRepository; 24 | import org.springframework.samples.petclinic.owner.PetType; 25 | import org.springframework.samples.petclinic.owner.PetTypeFormatter; 26 | import org.springframework.test.context.junit4.SpringRunner; 27 | import org.springframework.test.web.servlet.MockMvc; 28 | 29 | /** 30 | * Test class for the {@link PetController} 31 | * 32 | * @author Colin But 33 | */ 34 | @RunWith(SpringRunner.class) 35 | @WebMvcTest(value = PetController.class, 36 | includeFilters = @ComponentScan.Filter( 37 | value = PetTypeFormatter.class, 38 | type = FilterType.ASSIGNABLE_TYPE)) 39 | public class PetControllerTests { 40 | 41 | private static final int TEST_OWNER_ID = 1; 42 | private static final int TEST_PET_ID = 1; 43 | 44 | 45 | @Autowired 46 | private MockMvc mockMvc; 47 | 48 | @MockBean 49 | private PetRepository pets; 50 | 51 | @MockBean 52 | private OwnerRepository owners; 53 | 54 | @Before 55 | public void setup() { 56 | PetType cat = new PetType(); 57 | cat.setId(3); 58 | cat.setName("hamster"); 59 | given(this.pets.findPetTypes()).willReturn(Lists.newArrayList(cat)); 60 | given(this.owners.findById(TEST_OWNER_ID)).willReturn(new Owner()); 61 | given(this.pets.findById(TEST_PET_ID)).willReturn(new Pet()); 62 | 63 | } 64 | 65 | @Test 66 | public void testInitCreationForm() throws Exception { 67 | mockMvc.perform(get("/owners/{ownerId}/pets/new", TEST_OWNER_ID)) 68 | .andExpect(status().isOk()) 69 | .andExpect(view().name("pets/createOrUpdatePetForm")) 70 | .andExpect(model().attributeExists("pet")); 71 | } 72 | 73 | @Test 74 | public void testProcessCreationFormSuccess() throws Exception { 75 | mockMvc.perform(post("/owners/{ownerId}/pets/new", TEST_OWNER_ID) 76 | .param("name", "Betty") 77 | .param("type", "hamster") 78 | .param("birthDate", "2015-02-12") 79 | ) 80 | .andExpect(status().is3xxRedirection()) 81 | .andExpect(view().name("redirect:/owners/{ownerId}")); 82 | } 83 | 84 | @Test 85 | public void testProcessCreationFormHasErrors() throws Exception { 86 | mockMvc.perform(post("/owners/{ownerId}/pets/new", TEST_OWNER_ID) 87 | .param("name", "Betty") 88 | .param("birthDate", "2015-02-12") 89 | ) 90 | .andExpect(model().attributeHasNoErrors("owner")) 91 | .andExpect(model().attributeHasErrors("pet")) 92 | .andExpect(model().attributeHasFieldErrors("pet", "type")) 93 | .andExpect(model().attributeHasFieldErrorCode("pet", "type", "required")) 94 | .andExpect(status().isOk()) 95 | .andExpect(view().name("pets/createOrUpdatePetForm")); 96 | } 97 | 98 | @Test 99 | public void testInitUpdateForm() throws Exception { 100 | mockMvc.perform(get("/owners/{ownerId}/pets/{petId}/edit", TEST_OWNER_ID, TEST_PET_ID)) 101 | .andExpect(status().isOk()) 102 | .andExpect(model().attributeExists("pet")) 103 | .andExpect(view().name("pets/createOrUpdatePetForm")); 104 | } 105 | 106 | @Test 107 | public void testProcessUpdateFormSuccess() throws Exception { 108 | mockMvc.perform(post("/owners/{ownerId}/pets/{petId}/edit", TEST_OWNER_ID, TEST_PET_ID) 109 | .param("name", "Betty") 110 | .param("type", "hamster") 111 | .param("birthDate", "2015-02-12") 112 | ) 113 | .andExpect(status().is3xxRedirection()) 114 | .andExpect(view().name("redirect:/owners/{ownerId}")); 115 | } 116 | 117 | @Test 118 | public void testProcessUpdateFormHasErrors() throws Exception { 119 | mockMvc.perform(post("/owners/{ownerId}/pets/{petId}/edit", TEST_OWNER_ID, TEST_PET_ID) 120 | .param("name", "Betty") 121 | .param("birthDate", "2015/02/12") 122 | ) 123 | .andExpect(model().attributeHasNoErrors("owner")) 124 | .andExpect(model().attributeHasErrors("pet")) 125 | .andExpect(status().isOk()) 126 | .andExpect(view().name("pets/createOrUpdatePetForm")); 127 | } 128 | 129 | } 130 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/samples/petclinic/owner/PetTypeFormatterTests.java: -------------------------------------------------------------------------------- 1 | package org.springframework.samples.petclinic.owner; 2 | 3 | import java.text.ParseException; 4 | import java.util.ArrayList; 5 | import java.util.Collection; 6 | import java.util.List; 7 | import java.util.Locale; 8 | 9 | import org.junit.Before; 10 | import org.junit.Test; 11 | import org.junit.runner.RunWith; 12 | import org.mockito.Mock; 13 | import org.mockito.Mockito; 14 | import org.mockito.junit.MockitoJUnitRunner; 15 | 16 | import static org.junit.Assert.assertEquals; 17 | 18 | /** 19 | * Test class for {@link PetTypeFormatter} 20 | * 21 | * @author Colin But 22 | */ 23 | @RunWith(MockitoJUnitRunner.class) 24 | public class PetTypeFormatterTests { 25 | 26 | @Mock 27 | private PetRepository pets; 28 | 29 | private PetTypeFormatter petTypeFormatter; 30 | 31 | @Before 32 | public void setup() { 33 | this.petTypeFormatter = new PetTypeFormatter(pets); 34 | } 35 | 36 | @Test 37 | public void testPrint() { 38 | PetType petType = new PetType(); 39 | petType.setName("Hamster"); 40 | String petTypeName = this.petTypeFormatter.print(petType, Locale.ENGLISH); 41 | assertEquals("Hamster", petTypeName); 42 | } 43 | 44 | @Test 45 | public void shouldParse() throws ParseException { 46 | Mockito.when(this.pets.findPetTypes()).thenReturn(makePetTypes()); 47 | PetType petType = petTypeFormatter.parse("Bird", Locale.ENGLISH); 48 | assertEquals("Bird", petType.getName()); 49 | } 50 | 51 | @Test(expected = ParseException.class) 52 | public void shouldThrowParseException() throws ParseException { 53 | Mockito.when(this.pets.findPetTypes()).thenReturn(makePetTypes()); 54 | petTypeFormatter.parse("Fish", Locale.ENGLISH); 55 | } 56 | 57 | /** 58 | * Helper method to produce some sample pet types just for test purpose 59 | * 60 | * @return {@link Collection} of {@link PetType} 61 | */ 62 | private List makePetTypes() { 63 | List petTypes = new ArrayList<>(); 64 | petTypes.add(new PetType() { 65 | { 66 | setName("Dog"); 67 | } 68 | }); 69 | petTypes.add(new PetType() { 70 | { 71 | setName("Bird"); 72 | } 73 | }); 74 | return petTypes; 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/samples/petclinic/owner/VisitControllerTests.java: -------------------------------------------------------------------------------- 1 | package org.springframework.samples.petclinic.owner; 2 | 3 | import static org.mockito.BDDMockito.given; 4 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 5 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; 6 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model; 7 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 8 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view; 9 | 10 | import org.junit.Before; 11 | import org.junit.Test; 12 | import org.junit.runner.RunWith; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; 15 | import org.springframework.boot.test.mock.mockito.MockBean; 16 | import org.springframework.samples.petclinic.owner.Pet; 17 | import org.springframework.samples.petclinic.owner.PetRepository; 18 | import org.springframework.samples.petclinic.owner.VisitController; 19 | import org.springframework.samples.petclinic.visit.VisitRepository; 20 | import org.springframework.test.context.junit4.SpringRunner; 21 | import org.springframework.test.web.servlet.MockMvc; 22 | 23 | /** 24 | * Test class for {@link VisitController} 25 | * 26 | * @author Colin But 27 | */ 28 | @RunWith(SpringRunner.class) 29 | @WebMvcTest(VisitController.class) 30 | public class VisitControllerTests { 31 | 32 | private static final int TEST_PET_ID = 1; 33 | 34 | @Autowired 35 | private MockMvc mockMvc; 36 | 37 | @MockBean 38 | private VisitRepository visits; 39 | 40 | @MockBean 41 | private PetRepository pets; 42 | 43 | @Before 44 | public void init() { 45 | given(this.pets.findById(TEST_PET_ID)).willReturn(new Pet()); 46 | } 47 | 48 | @Test 49 | public void testInitNewVisitForm() throws Exception { 50 | mockMvc.perform(get("/owners/*/pets/{petId}/visits/new", TEST_PET_ID)) 51 | .andExpect(status().isOk()) 52 | .andExpect(view().name("pets/createOrUpdateVisitForm")); 53 | } 54 | 55 | @Test 56 | public void testProcessNewVisitFormSuccess() throws Exception { 57 | mockMvc.perform(post("/owners/*/pets/{petId}/visits/new", TEST_PET_ID) 58 | .param("name", "George") 59 | .param("description", "Visit Description") 60 | ) 61 | .andExpect(status().is3xxRedirection()) 62 | .andExpect(view().name("redirect:/owners/{ownerId}")); 63 | } 64 | 65 | @Test 66 | public void testProcessNewVisitFormHasErrors() throws Exception { 67 | mockMvc.perform(post("/owners/*/pets/{petId}/visits/new", TEST_PET_ID) 68 | .param("name", "George") 69 | ) 70 | .andExpect(model().attributeHasErrors("visit")) 71 | .andExpect(status().isOk()) 72 | .andExpect(view().name("pets/createOrUpdateVisitForm")); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/samples/petclinic/service/ClinicServiceTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.samples.petclinic.service; 18 | 19 | import static org.assertj.core.api.Assertions.assertThat; 20 | 21 | import java.time.LocalDate; 22 | import java.util.Collection; 23 | 24 | import org.junit.Test; 25 | import org.junit.runner.RunWith; 26 | import org.springframework.beans.factory.annotation.Autowired; 27 | import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; 28 | import org.springframework.context.annotation.ComponentScan; 29 | import org.springframework.samples.petclinic.owner.Owner; 30 | import org.springframework.samples.petclinic.owner.OwnerRepository; 31 | import org.springframework.samples.petclinic.owner.Pet; 32 | import org.springframework.samples.petclinic.owner.PetRepository; 33 | import org.springframework.samples.petclinic.owner.PetType; 34 | import org.springframework.samples.petclinic.vet.Vet; 35 | import org.springframework.samples.petclinic.vet.VetRepository; 36 | import org.springframework.samples.petclinic.visit.Visit; 37 | import org.springframework.samples.petclinic.visit.VisitRepository; 38 | import org.springframework.stereotype.Service; 39 | import org.springframework.test.context.junit4.SpringRunner; 40 | import org.springframework.transaction.annotation.Transactional; 41 | 42 | /** 43 | * Integration test of the Service and the Repository layer. 44 | *

45 | * ClinicServiceSpringDataJpaTests subclasses benefit from the following services provided by the Spring 46 | * TestContext Framework:

  • Spring IoC container caching which spares us unnecessary set up 47 | * time between test execution.
  • Dependency Injection of test fixture instances, meaning that 48 | * we don't need to perform application context lookups. See the use of {@link Autowired @Autowired} on the {@link 49 | * ClinicServiceTests#clinicService clinicService} instance variable, which uses autowiring by 50 | * type.
  • Transaction management, meaning each test method is executed in its own transaction, 51 | * which is automatically rolled back by default. Thus, even if tests insert or otherwise change database state, there 52 | * is no need for a teardown or cleanup script.
  • An {@link org.springframework.context.ApplicationContext 53 | * ApplicationContext} is also inherited and can be used for explicit bean lookup if necessary.
54 | * 55 | * @author Ken Krebs 56 | * @author Rod Johnson 57 | * @author Juergen Hoeller 58 | * @author Sam Brannen 59 | * @author Michael Isvy 60 | * @author Dave Syer 61 | */ 62 | 63 | @RunWith(SpringRunner.class) 64 | @DataJpaTest(includeFilters = @ComponentScan.Filter(Service.class)) 65 | public class ClinicServiceTests { 66 | 67 | @Autowired 68 | protected OwnerRepository owners; 69 | 70 | @Autowired 71 | protected PetRepository pets; 72 | 73 | @Autowired 74 | protected VisitRepository visits; 75 | 76 | @Autowired 77 | protected VetRepository vets; 78 | 79 | @Test 80 | public void shouldFindOwnersByLastName() { 81 | Collection owners = this.owners.findByLastName("Davis"); 82 | assertThat(owners.size()).isEqualTo(2); 83 | 84 | owners = this.owners.findByLastName("Daviss"); 85 | assertThat(owners.isEmpty()).isTrue(); 86 | } 87 | 88 | @Test 89 | public void shouldFindSingleOwnerWithPet() { 90 | Owner owner = this.owners.findById(1); 91 | assertThat(owner.getLastName()).startsWith("Franklin"); 92 | assertThat(owner.getPets().size()).isEqualTo(1); 93 | assertThat(owner.getPets().get(0).getType()).isNotNull(); 94 | assertThat(owner.getPets().get(0).getType().getName()).isEqualTo("cat"); 95 | } 96 | 97 | @Test 98 | @Transactional 99 | public void shouldInsertOwner() { 100 | Collection owners = this.owners.findByLastName("Schultz"); 101 | int found = owners.size(); 102 | 103 | Owner owner = new Owner(); 104 | owner.setFirstName("Sam"); 105 | owner.setLastName("Schultz"); 106 | owner.setAddress("4, Evans Street"); 107 | owner.setCity("Wollongong"); 108 | owner.setTelephone("4444444444"); 109 | this.owners.save(owner); 110 | assertThat(owner.getId().longValue()).isNotEqualTo(0); 111 | 112 | owners = this.owners.findByLastName("Schultz"); 113 | assertThat(owners.size()).isEqualTo(found + 1); 114 | } 115 | 116 | @Test 117 | @Transactional 118 | public void shouldUpdateOwner() { 119 | Owner owner = this.owners.findById(1); 120 | String oldLastName = owner.getLastName(); 121 | String newLastName = oldLastName + "X"; 122 | 123 | owner.setLastName(newLastName); 124 | this.owners.save(owner); 125 | 126 | // retrieving new name from database 127 | owner = this.owners.findById(1); 128 | assertThat(owner.getLastName()).isEqualTo(newLastName); 129 | } 130 | 131 | @Test 132 | public void shouldFindPetWithCorrectId() { 133 | Pet pet7 = this.pets.findById(7); 134 | assertThat(pet7.getName()).startsWith("Samantha"); 135 | assertThat(pet7.getOwner().getFirstName()).isEqualTo("Jean"); 136 | 137 | } 138 | 139 | @Test 140 | public void shouldFindAllPetTypes() { 141 | Collection petTypes = this.pets.findPetTypes(); 142 | 143 | PetType petType1 = EntityUtils.getById(petTypes, PetType.class, 1); 144 | assertThat(petType1.getName()).isEqualTo("cat"); 145 | PetType petType4 = EntityUtils.getById(petTypes, PetType.class, 4); 146 | assertThat(petType4.getName()).isEqualTo("snake"); 147 | } 148 | 149 | @Test 150 | @Transactional 151 | public void shouldInsertPetIntoDatabaseAndGenerateId() { 152 | Owner owner6 = this.owners.findById(6); 153 | int found = owner6.getPets().size(); 154 | 155 | Pet pet = new Pet(); 156 | pet.setName("bowser"); 157 | Collection types = this.pets.findPetTypes(); 158 | pet.setType(EntityUtils.getById(types, PetType.class, 2)); 159 | pet.setBirthDate(LocalDate.now()); 160 | owner6.addPet(pet); 161 | assertThat(owner6.getPets().size()).isEqualTo(found + 1); 162 | 163 | this.pets.save(pet); 164 | this.owners.save(owner6); 165 | 166 | owner6 = this.owners.findById(6); 167 | assertThat(owner6.getPets().size()).isEqualTo(found + 1); 168 | // checks that id has been generated 169 | assertThat(pet.getId()).isNotNull(); 170 | } 171 | 172 | @Test 173 | @Transactional 174 | public void shouldUpdatePetName() throws Exception { 175 | Pet pet7 = this.pets.findById(7); 176 | String oldName = pet7.getName(); 177 | 178 | String newName = oldName + "X"; 179 | pet7.setName(newName); 180 | this.pets.save(pet7); 181 | 182 | pet7 = this.pets.findById(7); 183 | assertThat(pet7.getName()).isEqualTo(newName); 184 | } 185 | 186 | @Test 187 | public void shouldFindVets() { 188 | Collection vets = this.vets.findAll(); 189 | 190 | Vet vet = EntityUtils.getById(vets, Vet.class, 3); 191 | assertThat(vet.getLastName()).isEqualTo("Douglas"); 192 | assertThat(vet.getNrOfSpecialties()).isEqualTo(2); 193 | assertThat(vet.getSpecialties().get(0).getName()).isEqualTo("dentistry"); 194 | assertThat(vet.getSpecialties().get(1).getName()).isEqualTo("surgery"); 195 | } 196 | 197 | @Test 198 | @Transactional 199 | public void shouldAddNewVisitForPet() { 200 | Pet pet7 = this.pets.findById(7); 201 | int found = pet7.getVisits().size(); 202 | Visit visit = new Visit(); 203 | pet7.addVisit(visit); 204 | visit.setDescription("test"); 205 | this.visits.save(visit); 206 | this.pets.save(pet7); 207 | 208 | pet7 = this.pets.findById(7); 209 | assertThat(pet7.getVisits().size()).isEqualTo(found + 1); 210 | assertThat(visit.getId()).isNotNull(); 211 | } 212 | 213 | @Test 214 | public void shouldFindVisitsByPetId() throws Exception { 215 | Collection visits = this.visits.findByPetId(7); 216 | assertThat(visits.size()).isEqualTo(2); 217 | Visit[] visitArr = visits.toArray(new Visit[visits.size()]); 218 | assertThat(visitArr[0].getDate()).isNotNull(); 219 | assertThat(visitArr[0].getPetId()).isEqualTo(7); 220 | } 221 | 222 | } 223 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/samples/petclinic/service/EntityUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2002-2013 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.samples.petclinic.service; 18 | 19 | import java.util.Collection; 20 | 21 | import org.springframework.orm.ObjectRetrievalFailureException; 22 | import org.springframework.samples.petclinic.model.BaseEntity; 23 | 24 | /** 25 | * Utility methods for handling entities. Separate from the BaseEntity class mainly because of dependency on the 26 | * ORM-associated ObjectRetrievalFailureException. 27 | * 28 | * @author Juergen Hoeller 29 | * @author Sam Brannen 30 | * @see org.springframework.samples.petclinic.model.BaseEntity 31 | * @since 29.10.2003 32 | */ 33 | public abstract class EntityUtils { 34 | 35 | /** 36 | * Look up the entity of the given class with the given id in the given collection. 37 | * 38 | * @param entities the collection to search 39 | * @param entityClass the entity class to look up 40 | * @param entityId the entity id to look up 41 | * @return the found entity 42 | * @throws ObjectRetrievalFailureException if the entity was not found 43 | */ 44 | public static T getById(Collection entities, Class entityClass, int entityId) 45 | throws ObjectRetrievalFailureException { 46 | for (T entity : entities) { 47 | if (entity.getId() == entityId && entityClass.isInstance(entity)) { 48 | return entity; 49 | } 50 | } 51 | throw new ObjectRetrievalFailureException(entityClass, entityId); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/samples/petclinic/system/CrashControllerTests.java: -------------------------------------------------------------------------------- 1 | package org.springframework.samples.petclinic.system; 2 | 3 | import org.junit.Ignore; 4 | import org.junit.Test; 5 | import org.junit.runner.RunWith; 6 | 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; 9 | import org.springframework.test.context.junit4.SpringRunner; 10 | import org.springframework.test.web.servlet.MockMvc; 11 | 12 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 13 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl; 14 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model; 15 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 16 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view; 17 | 18 | /** 19 | * Test class for {@link CrashController} 20 | * 21 | * @author Colin But 22 | */ 23 | @RunWith(SpringRunner.class) 24 | // Waiting https://github.com/spring-projects/spring-boot/issues/5574 25 | @Ignore 26 | @WebMvcTest(controllers = CrashController.class) 27 | public class CrashControllerTests { 28 | 29 | @Autowired 30 | private MockMvc mockMvc; 31 | 32 | @Test 33 | public void testTriggerException() throws Exception { 34 | mockMvc.perform(get("/oups")).andExpect(view().name("exception")) 35 | .andExpect(model().attributeExists("exception")) 36 | .andExpect(forwardedUrl("exception")).andExpect(status().isOk()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/samples/petclinic/vet/VetControllerTests.java: -------------------------------------------------------------------------------- 1 | package org.springframework.samples.petclinic.vet; 2 | 3 | import static org.hamcrest.xml.HasXPath.hasXPath; 4 | import static org.mockito.BDDMockito.given; 5 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 6 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; 7 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; 8 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model; 9 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 10 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view; 11 | 12 | import org.assertj.core.util.Lists; 13 | import org.junit.Before; 14 | import org.junit.Test; 15 | import org.junit.runner.RunWith; 16 | import org.springframework.beans.factory.annotation.Autowired; 17 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; 18 | import org.springframework.boot.test.mock.mockito.MockBean; 19 | import org.springframework.http.MediaType; 20 | import org.springframework.test.context.junit4.SpringRunner; 21 | import org.springframework.test.web.servlet.MockMvc; 22 | import org.springframework.test.web.servlet.ResultActions; 23 | 24 | /** 25 | * Test class for the {@link VetController} 26 | */ 27 | @RunWith(SpringRunner.class) 28 | @WebMvcTest(VetController.class) 29 | public class VetControllerTests { 30 | 31 | @Autowired 32 | private MockMvc mockMvc; 33 | 34 | @MockBean 35 | private VetRepository vets; 36 | 37 | @Before 38 | public void setup() { 39 | Vet james = new Vet(); 40 | james.setFirstName("James"); 41 | james.setLastName("Carter"); 42 | james.setId(1); 43 | Vet helen = new Vet(); 44 | helen.setFirstName("Helen"); 45 | helen.setLastName("Leary"); 46 | helen.setId(2); 47 | Specialty radiology = new Specialty(); 48 | radiology.setId(1); 49 | radiology.setName("radiology"); 50 | helen.addSpecialty(radiology); 51 | given(this.vets.findAll()).willReturn(Lists.newArrayList(james, helen)); 52 | } 53 | 54 | @Test 55 | public void testShowVetListHtml() throws Exception { 56 | mockMvc.perform(get("/vets.html")) 57 | .andExpect(status().isOk()) 58 | .andExpect(model().attributeExists("vets")) 59 | .andExpect(view().name("vets/vetList")); 60 | } 61 | 62 | @Test 63 | public void testShowResourcesVetList() throws Exception { 64 | ResultActions actions = mockMvc.perform(get("/vets") 65 | .accept(MediaType.APPLICATION_JSON)).andExpect(status().isOk()); 66 | actions.andExpect(content().contentType("application/json;charset=UTF-8")) 67 | .andExpect(jsonPath("$.vetList[0].id").value(1)); 68 | } 69 | 70 | @Test 71 | public void testShowVetListXml() throws Exception { 72 | mockMvc.perform(get("/vets").accept(MediaType.APPLICATION_XML)) 73 | .andExpect(status().isOk()) 74 | .andExpect(content().contentType(MediaType.APPLICATION_XML_VALUE)) 75 | .andExpect(content().node(hasXPath("/vets/vetList[id=1]/id"))); 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/samples/petclinic/vet/VetTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2017 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.samples.petclinic.vet; 17 | 18 | import org.junit.Test; 19 | 20 | import org.springframework.util.SerializationUtils; 21 | 22 | import static org.assertj.core.api.Assertions.assertThat; 23 | 24 | /** 25 | * @author Dave Syer 26 | * 27 | */ 28 | public class VetTests { 29 | 30 | @Test 31 | public void testSerialization() { 32 | Vet vet = new Vet(); 33 | vet.setFirstName("Zaphod"); 34 | vet.setLastName("Beeblebrox"); 35 | vet.setId(123); 36 | Vet other = (Vet) SerializationUtils 37 | .deserialize(SerializationUtils.serialize(vet)); 38 | assertThat(other.getFirstName()).isEqualTo(vet.getFirstName()); 39 | assertThat(other.getLastName()).isEqualTo(vet.getLastName()); 40 | assertThat(other.getId()).isEqualTo(vet.getId()); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/test/jmeter/petclinic_test_plan.jmx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | false 7 | false 8 | 9 | 10 | 11 | PETCLINIC_HOST 12 | localhost 13 | = 14 | 15 | 16 | PETCLINIC_PORT 17 | 8080 18 | = 19 | 20 | 21 | CONTEXT_WEB 22 | 23 | = 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | continue 32 | 33 | false 34 | 10 35 | 36 | 500 37 | 10 38 | 1361531541000 39 | 1361531541000 40 | false 41 | 42 | 43 | true 44 | Original : 500 - 10 - 10 45 | 46 | 47 | 48 | 300 49 | 50 | 51 | 52 | 53 | 54 | 55 | ${PETCLINIC_HOST} 56 | ${PETCLINIC_PORT} 57 | 58 | 59 | 60 | 61 | 62 | 4 63 | 64 | 65 | 66 | 67 | true 68 | 69 | 70 | 71 | 1 72 | 10 73 | 1 74 | count 75 | 76 | false 77 | 78 | 79 | 80 | 1 81 | 13 82 | 1 83 | petCount 84 | 85 | false 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | ${CONTEXT_WEB}/ 99 | GET 100 | true 101 | false 102 | true 103 | false 104 | false 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | ${CONTEXT_WEB}/resources/css/petclinic.css 119 | GET 120 | true 121 | false 122 | true 123 | false 124 | false 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | ${CONTEXT_WEB}/webjars/jquery/jquery.min.js 139 | GET 140 | true 141 | false 142 | true 143 | false 144 | false 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | ${CONTEXT_WEB}/vets.html 159 | GET 160 | true 161 | false 162 | true 163 | false 164 | false 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | ${CONTEXT_WEB}/owners/find 179 | GET 180 | true 181 | false 182 | true 183 | false 184 | false 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | ${CONTEXT_WEB}/owners?lastName= 199 | GET 200 | true 201 | false 202 | true 203 | false 204 | false 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | ${CONTEXT_WEB}/owners/${count} 219 | GET 220 | true 221 | false 222 | true 223 | false 224 | false 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | ${CONTEXT_WEB}/owners/${count}/edit 239 | GET 240 | true 241 | false 242 | true 243 | false 244 | false 245 | 246 | 247 | 248 | 249 | true 250 | 251 | 252 | 253 | false 254 | firstName=Test&lastName=${count}&address=1234+Test+St.&city=TestCity&telephone=612345678 255 | = 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | ${CONTEXT_WEB}/owners/${count}/edit 266 | POST 267 | true 268 | false 269 | true 270 | false 271 | false 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | ${CONTEXT_WEB}/owners/${count}/pets/${petCount}/visits/new 286 | GET 287 | true 288 | false 289 | true 290 | false 291 | false 292 | 293 | 294 | 295 | 296 | true 297 | 298 | 299 | 300 | false 301 | date=2013%2F02%2F22&description=visit 302 | = 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | ${CONTEXT_WEB}/owners/${count}/pets/${petCount}/visits/new 313 | POST 314 | true 315 | false 316 | true 317 | false 318 | false 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | ${CONTEXT_WEB}/owners/${count} 333 | GET 334 | true 335 | false 336 | true 337 | false 338 | false 339 | 340 | 341 | 342 | 343 | false 344 | 345 | saveConfig 346 | 347 | 348 | true 349 | true 350 | true 351 | 352 | true 353 | true 354 | true 355 | true 356 | false 357 | true 358 | true 359 | false 360 | false 361 | false 362 | false 363 | false 364 | false 365 | false 366 | false 367 | 0 368 | true 369 | true 370 | 371 | 372 | 373 | 374 | 375 | 376 | false 377 | 378 | saveConfig 379 | 380 | 381 | true 382 | true 383 | true 384 | 385 | true 386 | true 387 | true 388 | true 389 | false 390 | true 391 | true 392 | false 393 | false 394 | false 395 | false 396 | false 397 | false 398 | false 399 | false 400 | 0 401 | true 402 | true 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | --------------------------------------------------------------------------------