main edf4db28affc cached
114 files
560.2 KB
163.6k tokens
251 symbols
1 requests
Download .txt
Showing preview only (598K chars total). Download the full file or copy to clipboard to get everything.
Repository: spring-projects/spring-petclinic
Branch: main
Commit: edf4db28affc
Files: 114
Total size: 560.2 KB

Directory structure:
gitextract_lswr63to/

├── .devcontainer/
│   ├── Dockerfile
│   └── devcontainer.json
├── .editorconfig
├── .gitattributes
├── .github/
│   ├── dco.yml
│   └── workflows/
│       ├── deploy-and-test-cluster.yml
│       ├── gradle-build.yml
│       └── maven-build.yml
├── .gitignore
├── .gitpod.yml
├── .mvn/
│   └── wrapper/
│       └── maven-wrapper.properties
├── LICENSE.txt
├── README.md
├── build.gradle
├── docker-compose.yml
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── k8s/
│   ├── db.yml
│   └── petclinic.yml
├── mvnw
├── mvnw.cmd
├── pom.xml
├── settings.gradle
└── src/
    ├── checkstyle/
    │   ├── nohttp-checkstyle-suppressions.xml
    │   └── nohttp-checkstyle.xml
    ├── main/
    │   ├── java/
    │   │   └── org/
    │   │       └── springframework/
    │   │           └── samples/
    │   │               └── petclinic/
    │   │                   ├── PetClinicApplication.java
    │   │                   ├── PetClinicRuntimeHints.java
    │   │                   ├── model/
    │   │                   │   ├── BaseEntity.java
    │   │                   │   ├── NamedEntity.java
    │   │                   │   ├── Person.java
    │   │                   │   └── package-info.java
    │   │                   ├── owner/
    │   │                   │   ├── Owner.java
    │   │                   │   ├── OwnerController.java
    │   │                   │   ├── OwnerRepository.java
    │   │                   │   ├── Pet.java
    │   │                   │   ├── PetController.java
    │   │                   │   ├── PetType.java
    │   │                   │   ├── PetTypeFormatter.java
    │   │                   │   ├── PetTypeRepository.java
    │   │                   │   ├── PetValidator.java
    │   │                   │   ├── Visit.java
    │   │                   │   ├── VisitController.java
    │   │                   │   └── package-info.java
    │   │                   ├── package-info.java
    │   │                   ├── system/
    │   │                   │   ├── CacheConfiguration.java
    │   │                   │   ├── CrashController.java
    │   │                   │   ├── WebConfiguration.java
    │   │                   │   ├── WelcomeController.java
    │   │                   │   └── package-info.java
    │   │                   └── vet/
    │   │                       ├── Specialty.java
    │   │                       ├── Vet.java
    │   │                       ├── VetController.java
    │   │                       ├── VetRepository.java
    │   │                       ├── Vets.java
    │   │                       └── package-info.java
    │   ├── resources/
    │   │   ├── application-mysql.properties
    │   │   ├── application-postgres.properties
    │   │   ├── application.properties
    │   │   ├── banner.txt
    │   │   ├── db/
    │   │   │   ├── h2/
    │   │   │   │   ├── data.sql
    │   │   │   │   └── schema.sql
    │   │   │   ├── mysql/
    │   │   │   │   ├── data.sql
    │   │   │   │   ├── petclinic_db_setup_mysql.txt
    │   │   │   │   ├── schema.sql
    │   │   │   │   └── user.sql
    │   │   │   └── postgres/
    │   │   │       ├── data.sql
    │   │   │       ├── petclinic_db_setup_postgres.txt
    │   │   │       └── schema.sql
    │   │   ├── messages/
    │   │   │   ├── messages.properties
    │   │   │   ├── messages_de.properties
    │   │   │   ├── messages_en.properties
    │   │   │   ├── messages_es.properties
    │   │   │   ├── messages_fa.properties
    │   │   │   ├── messages_ko.properties
    │   │   │   ├── messages_pt.properties
    │   │   │   ├── messages_ru.properties
    │   │   │   └── messages_tr.properties
    │   │   ├── static/
    │   │   │   └── resources/
    │   │   │       └── css/
    │   │   │           └── petclinic.css
    │   │   └── 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
    │   └── scss/
    │       ├── header.scss
    │       ├── petclinic.scss
    │       ├── responsive.scss
    │       └── typography.scss
    └── test/
        ├── java/
        │   └── org/
        │       └── springframework/
        │           └── samples/
        │               └── petclinic/
        │                   ├── MySqlIntegrationTests.java
        │                   ├── MysqlTestApplication.java
        │                   ├── PetClinicIntegrationTests.java
        │                   ├── PostgresIntegrationTests.java
        │                   ├── model/
        │                   │   └── ValidatorTests.java
        │                   ├── owner/
        │                   │   ├── OwnerControllerTests.java
        │                   │   ├── PetControllerTests.java
        │                   │   ├── PetTypeFormatterTests.java
        │                   │   ├── PetValidatorTests.java
        │                   │   └── VisitControllerTests.java
        │                   ├── service/
        │                   │   ├── ClinicServiceTests.java
        │                   │   └── EntityUtils.java
        │                   ├── system/
        │                   │   ├── CrashControllerIntegrationTests.java
        │                   │   ├── CrashControllerTests.java
        │                   │   └── I18nPropertiesSyncTest.java
        │                   └── vet/
        │                       ├── VetControllerTests.java
        │                       └── VetTests.java
        └── jmeter/
            └── petclinic_test_plan.jmx

================================================
FILE CONTENTS
================================================

================================================
FILE: .devcontainer/Dockerfile
================================================
# Not actually used by the devcontainer, but it is used by gitpod
ARG VARIANT=17-bullseye
FROM mcr.microsoft.com/vscode/devcontainers/java:0-${VARIANT}
ARG NODE_VERSION="none"
RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi
ARG USER=vscode
VOLUME /home/$USER/.m2
VOLUME /home/$USER/.gradle
ARG JAVA_VERSION=17.0.7-ms
RUN sudo mkdir /home/$USER/.m2 /home/$USER/.gradle && sudo chown $USER:$USER /home/$USER/.m2 /home/$USER/.gradle
RUN bash -lc '. /usr/local/sdkman/bin/sdkman-init.sh && sdk install java $JAVA_VERSION && sdk use java $JAVA_VERSION'

================================================
FILE: .devcontainer/devcontainer.json
================================================
{
  "name": "Java",
  "image": "mcr.microsoft.com/devcontainers/base:ubuntu",
  "features": {
    "ghcr.io/devcontainers/features/java:1": {
      "version": "21-oracle",
      "jdkDistro": "oracle"
    },
    "ghcr.io/devcontainers/features/azure-cli:1": {},
    "ghcr.io/devcontainers/features/docker-in-docker:2": {},
    "ghcr.io/devcontainers/features/github-cli:1": {}
  },

  "customizations": {
    "vscode": {
      "settings": {},
      "extensions": [
        "redhat.vscode-xml",
        "visualstudioexptteam.vscodeintellicode",
        "vscjava.vscode-java-pack"
      ]
    }
  },
  "remoteUser": "vscode"
}


================================================
FILE: .editorconfig
================================================
# top-most EditorConfig file
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space

[*.{java,xml}]
indent_size = 4
trim_trailing_whitespace = true
indent_style = tab
tab_width = 4

[{pom,wro}.xml]
indent_size = 2
indent_style = space

[*.{html,sql,less}]
indent_size = 2

[*.gradle]
indent_size = 2


================================================
FILE: .gitattributes
================================================
/mvnw           text eol=lf
*.cmd           text eol=crlf
*.java          text eol=lf

/gradlew        text eol=lf
*.bat           text eol=crlf


================================================
FILE: .github/dco.yml
================================================
require:
  members: false

================================================
FILE: .github/workflows/deploy-and-test-cluster.yml
================================================
name: Deploy and Test Cluster

on:
  push:
    branches: [main]
    paths:
      - 'k8s/**'
  pull_request:
    branches: [main]
    paths:
      - 'k8s/**'

jobs:
  deploy-and-test-cluster:
    runs-on: ubuntu-latest
    steps:
      - name: Check out the repository
        uses: actions/checkout@v2

      - name: Create k8s Kind Cluster
        uses: helm/kind-action@v1

      - name: Deploy application
        run: |
          kubectl apply -f k8s/

      - name: Wait for Pods to be ready
        run: |
          kubectl wait --for=condition=ready pod -l app=demo-db --timeout=180s
          kubectl wait --for=condition=ready pod -l app=petclinic --timeout=180s



================================================
FILE: .github/workflows/gradle-build.yml
================================================
# This workflow will build a Java project with Gradle, and cache/restore any dependencies to improve the workflow execution time
# For more information see: https://docs.github.com/en/actions/use-cases-and-examples/building-and-testing/building-and-testing-java-with-gradle

name: Java CI with Gradle

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  build:

    runs-on: ubuntu-latest
    strategy:
      matrix:
        java: [ '17' ]

    steps:
      - uses: actions/checkout@v4
      - name: Set up JDK ${{matrix.java}}
        uses: actions/setup-java@v4
        with:
          java-version: ${{matrix.java}}
          distribution: 'adopt'
          cache: maven
      - name: Setup Gradle
        uses: gradle/actions/setup-gradle@v4
      - name: Build with Gradle
        run: ./gradlew build


================================================
FILE: .github/workflows/maven-build.yml
================================================
# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
# For more information see: https://docs.github.com/en/actions/use-cases-and-examples/building-and-testing/building-and-testing-java-with-maven

name: Java CI with Maven

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  build:

    runs-on: ubuntu-latest
    strategy:
      matrix:
        java: [ '17' ]

    steps:
      - uses: actions/checkout@v4
      - name: Set up JDK ${{matrix.java}}
        uses: actions/setup-java@v4
        with:
          java-version: ${{matrix.java}}
          distribution: 'adopt'
          cache: maven
      - name: Build with Maven Wrapper
        run: ./mvnw -B verify


================================================
FILE: .gitignore
================================================
HELP.md
pom.xml.bak
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
.gradle
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/

### STS ###
.attach_pid*
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/

### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
out/
!**/src/main/**/out/
!**/src/test/**/out/

### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/

### VS Code ###
.vscode/

### CSS ###
_site/
*.css
!petclinic.css


================================================
FILE: .gitpod.yml
================================================
image:
  file: ./.devcontainer/Dockerfile
tasks:
  - before: sudo usermod -a -G sdkman gitpod && sudo usermod -a -G nvm gitpod && sudo chown -R gitpod /usr/local/sdkman /usr/local/share/nvm
  - init: ./mvnw install
vscode:
  extensions:
  - vscjava.vscode-java-pack
  - redhat.vscode-xml

================================================
FILE: .mvn/wrapper/maven-wrapper.properties
================================================
wrapperVersion=3.3.4
distributionType=only-script
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.12/apache-maven-3.9.12-bin.zip


================================================
FILE: LICENSE.txt
================================================

                                 Apache License
                           Version 2.0, January 2004
                        https://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "{}"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright {yyyy} {name of copyright owner}

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       https://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


================================================
FILE: README.md
================================================
# Spring PetClinic Sample Application [![Build Status](https://github.com/spring-projects/spring-petclinic/actions/workflows/maven-build.yml/badge.svg)](https://github.com/spring-projects/spring-petclinic/actions/workflows/maven-build.yml)[![Build Status](https://github.com/spring-projects/spring-petclinic/actions/workflows/gradle-build.yml/badge.svg)](https://github.com/spring-projects/spring-petclinic/actions/workflows/gradle-build.yml)

[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/spring-projects/spring-petclinic) [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/codespaces/new?hide_repo_select=true&ref=main&repo=7517918)

## Understanding the Spring Petclinic application with a few diagrams

See the presentation here:  
[Spring Petclinic Sample Application (legacy slides)](https://speakerdeck.com/michaelisvy/spring-petclinic-sample-application?slide=20)

> **Note:** These slides refer to a legacy, pre–Spring Boot version of Petclinic and may not reflect the current Spring Boot–based implementation.  
> For up-to-date information, please refer to this repository and its documentation.


## Run Petclinic locally

Spring Petclinic is a [Spring Boot](https://spring.io/guides/gs/spring-boot) application built using [Maven](https://spring.io/guides/gs/maven/) or [Gradle](https://spring.io/guides/gs/gradle/).
Java 17 or later is required for the build, and the application can run with Java 17 or newer.

You first need to clone the project locally:

```bash
git clone https://github.com/spring-projects/spring-petclinic.git
cd spring-petclinic
```
If you are using Maven, you can start the application on the command-line as follows:

```bash
./mvnw spring-boot:run
```
With Gradle, the command is as follows:

```bash
./gradlew bootRun
```

You can then access the Petclinic at <http://localhost:8080/>.

<img width="1042" alt="petclinic-screenshot" src="https://cloud.githubusercontent.com/assets/838318/19727082/2aee6d6c-9b8e-11e6-81fe-e889a5ddfded.png">

You can, of course, run Petclinic in your favorite IDE.
See below for more details.

## Building a Container

There is no `Dockerfile` in this project. You can build a container image (if you have a docker daemon) using the Spring Boot build plugin:

```bash
./mvnw spring-boot:build-image
```

## In case you find a bug/suggested improvement for Spring Petclinic

Our issue tracker is available [here](https://github.com/spring-projects/spring-petclinic/issues).

## Database configuration

In its default configuration, Petclinic uses an in-memory database (H2) which
gets populated at startup with data. The h2 console is exposed at `http://localhost:8080/h2-console`,
and it is possible to inspect the content of the database using the `jdbc:h2:mem:<uuid>` URL. The UUID is printed at startup to the console.

A similar setup is provided for MySQL and PostgreSQL if a persistent database configuration is needed. Note that whenever the database type changes, the app needs to run with a different profile: `spring.profiles.active=mysql` for MySQL or `spring.profiles.active=postgres` for PostgreSQL. See the [Spring Boot documentation](https://docs.spring.io/spring-boot/how-to/properties-and-configuration.html#howto.properties-and-configuration.set-active-spring-profiles) for more detail on how to set the active profile.

You can start MySQL or PostgreSQL locally with whatever installer works for your OS or use docker:

```bash
docker run -e MYSQL_USER=petclinic -e MYSQL_PASSWORD=petclinic -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=petclinic -p 3306:3306 mysql:9.6
```

or

```bash
docker run -e POSTGRES_USER=petclinic -e POSTGRES_PASSWORD=petclinic -e POSTGRES_DB=petclinic -p 5432:5432 postgres:18.3
```

Further documentation is provided for [MySQL](https://github.com/spring-projects/spring-petclinic/blob/main/src/main/resources/db/mysql/petclinic_db_setup_mysql.txt)
and [PostgreSQL](https://github.com/spring-projects/spring-petclinic/blob/main/src/main/resources/db/postgres/petclinic_db_setup_postgres.txt).

Instead of vanilla `docker` you can also use the provided `docker-compose.yml` file to start the database containers. Each one has a service named after the Spring profile:

```bash
docker compose up mysql
```

or

```bash
docker compose up postgres
```

## Test Applications

At development time we recommend you use the test applications set up as `main()` methods in `PetClinicIntegrationTests` (using the default H2 database and also adding Spring Boot Devtools), `MySqlTestApplication` and `PostgresIntegrationTests`. These are set up so that you can run the apps in your IDE to get fast feedback and also run the same classes as integration tests against the respective database. The MySql integration tests use Testcontainers to start the database in a Docker container, and the Postgres tests use Docker Compose to do the same thing.

## Compiling the CSS

There is a `petclinic.css` in `src/main/resources/static/resources/css`. It was generated from the `petclinic.scss` source, combined with the [Bootstrap](https://getbootstrap.com/) library. If you make changes to the `scss`, or upgrade Bootstrap, you will need to re-compile the CSS resources using the Maven profile "css", i.e. `./mvnw package -P css`. There is no build profile for Gradle to compile the CSS.

## Working with Petclinic in your IDE

### Prerequisites

The following items should be installed in your system:

- Java 17 or newer (full JDK, not a JRE)
- [Git command line tool](https://help.github.com/articles/set-up-git)
- Your preferred IDE
  - Eclipse with the m2e plugin. Note: when m2e is available, there is a m2 icon in `Help -> About` dialog. If m2e is
  not there, follow the installation process [here](https://www.eclipse.org/m2e/)
  - [Spring Tools Suite](https://spring.io/tools) (STS)
  - [IntelliJ IDEA](https://www.jetbrains.com/idea/)
  - [VS Code](https://code.visualstudio.com)

### Steps

1. On the command line run:

    ```bash
    git clone https://github.com/spring-projects/spring-petclinic.git
    ```

1. Inside Eclipse or STS:

    Open the project via `File -> Import -> Maven -> Existing Maven project`, then select the root directory of the cloned repo.

    Then either build on the command line `./mvnw generate-resources` or use the Eclipse launcher (right-click on project and `Run As -> Maven install`) to generate the CSS. Run the application's main method by right-clicking on it and choosing `Run As -> Java Application`.

1. Inside IntelliJ IDEA:

    In the main menu, choose `File -> Open` and select the Petclinic [pom.xml](pom.xml). Click on the `Open` button.

    - CSS files are generated from the Maven build. You can build them on the command line `./mvnw generate-resources` or right-click on the `spring-petclinic` project then `Maven -> Generates sources and Update Folders`.

    - A run configuration named `PetClinicApplication` should have been created for you if you're using a recent Ultimate version. Otherwise, run the application by right-clicking on the `PetClinicApplication` main class and choosing `Run 'PetClinicApplication'`.

1. Navigate to the Petclinic

    Visit [http://localhost:8080](http://localhost:8080) in your browser.

## Looking for something in particular?

|Spring Boot Configuration | Class or Java property files  |
|--------------------------|---|
|The Main Class | [PetClinicApplication](https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/PetClinicApplication.java) |
|Properties Files | [application.properties](https://github.com/spring-projects/spring-petclinic/blob/main/src/main/resources) |
|Caching | [CacheConfiguration](https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/system/CacheConfiguration.java) |

## Interesting Spring Petclinic branches and forks

The Spring Petclinic "main" branch in the [spring-projects](https://github.com/spring-projects/spring-petclinic)
GitHub org is the "canonical" implementation based on Spring Boot and Thymeleaf. There are
[quite a few forks](https://spring-petclinic.github.io/docs/forks.html) in the GitHub org
[spring-petclinic](https://github.com/spring-petclinic). If you are interested in using a different technology stack to implement the Pet Clinic, please join the community there.

## Interaction with other open-source projects

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 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.
Here is a list of them:

| Name | Issue |
|------|-------|
| Spring JDBC: simplify usage of NamedParameterJdbcTemplate | [SPR-10256](https://github.com/spring-projects/spring-framework/issues/14889) and [SPR-10257](https://github.com/spring-projects/spring-framework/issues/14890) |
| 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) |
| Spring Data: provide more flexibility when working with JPQL queries | [DATAJPA-292](https://github.com/spring-projects/spring-data-jpa/issues/704) |

## Contributing

The [issue tracker](https://github.com/spring-projects/spring-petclinic/issues) is the preferred channel for bug reports, feature requests and submitting pull requests.

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 <https://editorconfig.org>. All commits must include a __Signed-off-by__ trailer at the end of each commit message to indicate that the contributor agrees to the Developer Certificate of Origin.
For additional details, please refer to the blog post [Hello DCO, Goodbye CLA: Simplifying Contributions to Spring](https://spring.io/blog/2025/01/06/hello-dco-goodbye-cla-simplifying-contributions-to-spring).

## License

The Spring PetClinic sample application is released under version 2.0 of the [Apache License](https://www.apache.org/licenses/LICENSE-2.0).


================================================
FILE: build.gradle
================================================
plugins {
  id 'java'
  id 'checkstyle'
  id 'org.springframework.boot' version '4.0.3'
  id 'io.spring.dependency-management' version '1.1.7'
  id 'org.graalvm.buildtools.native' version '0.11.5'
  id 'org.cyclonedx.bom' version '3.2.0'
  id 'io.spring.javaformat' version '0.0.47'
  id "io.spring.nohttp" version "0.0.11"
}

gradle.startParameter.excludedTaskNames += [ "checkFormatAot", "checkFormatAotTest" ]

group = 'org.springframework.samples'
version = '4.0.0-SNAPSHOT'

java {
  toolchain {
    languageVersion = JavaLanguageVersion.of(17)
  }
}

repositories {
  mavenCentral()
}

ext.checkstyleVersion = "12.3.1"
ext.springJavaformatCheckstyleVersion = "0.0.47"
ext.webjarsLocatorLiteVersion = "1.1.3"
ext.webjarsFontawesomeVersion = "4.7.0"
ext.webjarsBootstrapVersion = "5.3.8"

dependencies {
  implementation 'org.springframework.boot:spring-boot-starter-cache'
  implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
  implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
  implementation 'org.springframework.boot:spring-boot-starter-webmvc'
  implementation 'org.springframework.boot:spring-boot-starter-validation'
  implementation 'javax.cache:cache-api'
  implementation 'jakarta.xml.bind:jakarta.xml.bind-api'
  runtimeOnly 'org.springframework.boot:spring-boot-starter-actuator'
  runtimeOnly "org.webjars:webjars-locator-lite:${webjarsLocatorLiteVersion}"
  runtimeOnly "org.webjars.npm:bootstrap:${webjarsBootstrapVersion}"
  runtimeOnly "org.webjars.npm:font-awesome:${webjarsFontawesomeVersion}"
  runtimeOnly 'com.github.ben-manes.caffeine:caffeine'
  runtimeOnly 'com.h2database:h2'
  runtimeOnly 'com.mysql:mysql-connector-j'
  runtimeOnly 'org.postgresql:postgresql'
  developmentOnly 'org.springframework.boot:spring-boot-devtools'
  testImplementation 'org.springframework.boot:spring-boot-starter-data-jpa-test'
  testImplementation 'org.springframework.boot:spring-boot-starter-restclient-test'
  testImplementation 'org.springframework.boot:spring-boot-starter-webmvc-test'
  testImplementation 'org.springframework.boot:spring-boot-testcontainers'
  testImplementation 'org.springframework.boot:spring-boot-docker-compose'
  testImplementation 'org.testcontainers:testcontainers-junit-jupiter'
  testImplementation 'org.testcontainers:testcontainers-mysql'
  checkstyle "io.spring.javaformat:spring-javaformat-checkstyle:${springJavaformatCheckstyleVersion}"
  checkstyle "com.puppycrawl.tools:checkstyle:${checkstyleVersion}"
}

tasks.named('test') {
  useJUnitPlatform()
}

tasks.named("cyclonedxDirectBom").configure {
  dependsOn("compileJava")
  skipConfigs = [".*[Tt]est.*"]
}

checkstyle {
  configDirectory = project.file('src/checkstyle')
  configFile = file('src/checkstyle/nohttp-checkstyle.xml')
}

checkstyleNohttp {
  configDirectory = project.file('src/checkstyle')
  configFile = file('src/checkstyle/nohttp-checkstyle.xml')
}

tasks.named("formatMain").configure { dependsOn("checkstyleMain") }
tasks.named("formatMain").configure { dependsOn("checkstyleNohttp") }

tasks.named("formatTest").configure { dependsOn("checkstyleTest") }
tasks.named("formatTest").configure { dependsOn("checkstyleNohttp") }

checkstyleAot.enabled = false
checkstyleAotTest.enabled = false

checkFormatAot.enabled = false
checkFormatAotTest.enabled = false

formatAot.enabled = false
formatAotTest.enabled = false

================================================
FILE: docker-compose.yml
================================================
services:
  mysql:
    image: mysql:9.6
    ports:
      - "3306:3306"
    environment:
      - MYSQL_ROOT_PASSWORD=
      - MYSQL_ALLOW_EMPTY_PASSWORD=true
      - MYSQL_USER=petclinic
      - MYSQL_PASSWORD=petclinic
      - MYSQL_DATABASE=petclinic
    volumes:
      - "./conf.d:/etc/mysql/conf.d:ro"
  postgres:
    image: postgres:18.3
    ports:
      - "5432:5432"
    environment:
      - POSTGRES_PASSWORD=petclinic
      - POSTGRES_USER=petclinic
      - POSTGRES_DB=petclinic


================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists


================================================
FILE: gradlew
================================================
#!/bin/sh

#
# Copyright © 2015 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#

##############################################################################
#
#   Gradle start up script for POSIX generated by Gradle.
#
#   Important for running:
#
#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
#       noncompliant, but you have some other compliant shell such as ksh or
#       bash, then to run this script, type that shell name before the whole
#       command line, like:
#
#           ksh Gradle
#
#       Busybox and similar reduced shells will NOT work, because this script
#       requires all of these POSIX shell features:
#         * functions;
#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;
#         * compound commands having a testable exit status, especially «case»;
#         * various built-in commands including «command», «set», and «ulimit».
#
#   Important for patching:
#
#   (2) This script targets any POSIX shell, so it avoids extensions provided
#       by Bash, Ksh, etc; in particular arrays are avoided.
#
#       The "traditional" practice of packing multiple parameters into a
#       space-separated string is a well documented source of bugs and security
#       problems, so this is (mostly) avoided, by progressively accumulating
#       options in "$@", and eventually passing that to Java.
#
#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
#       see the in-line comments for details.
#
#       There are tweaks for specific operating systems such as AIX, CygWin,
#       Darwin, MinGW, and NonStop.
#
#   (3) This script is generated from the Groovy template
#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
#       within the Gradle project.
#
#       You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################

# Attempt to set APP_HOME

# Resolve links: $0 may be a link
app_path=$0

# Need this for daisy-chained symlinks.
while
    APP_HOME=${app_path%"${app_path##*/}"}  # leaves a trailing /; empty if no leading path
    [ -h "$app_path" ]
do
    ls=$( ls -ld "$app_path" )
    link=${ls#*' -> '}
    case $link in             #(
      /*)   app_path=$link ;; #(
      *)    app_path=$APP_HOME$link ;;
    esac
done

# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit

# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum

warn () {
    echo "$*"
} >&2

die () {
    echo
    echo "$*"
    echo
    exit 1
} >&2

# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in                #(
  CYGWIN* )         cygwin=true  ;; #(
  Darwin* )         darwin=true  ;; #(
  MSYS* | MINGW* )  msys=true    ;; #(
  NONSTOP* )        nonstop=true ;;
esac



# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
        # IBM's JDK on AIX uses strange locations for the executables
        JAVACMD=$JAVA_HOME/jre/sh/java
    else
        JAVACMD=$JAVA_HOME/bin/java
    fi
    if [ ! -x "$JAVACMD" ] ; then
        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
    fi
else
    JAVACMD=java
    if ! command -v java >/dev/null 2>&1
    then
        die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
    fi
fi

# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
    case $MAX_FD in #(
      max*)
        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
        # shellcheck disable=SC2039,SC3045
        MAX_FD=$( ulimit -H -n ) ||
            warn "Could not query maximum file descriptor limit"
    esac
    case $MAX_FD in  #(
      '' | soft) :;; #(
      *)
        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
        # shellcheck disable=SC2039,SC3045
        ulimit -n "$MAX_FD" ||
            warn "Could not set maximum file descriptor limit to $MAX_FD"
    esac
fi

# Collect all arguments for the java command, stacking in reverse order:
#   * args from the command line
#   * the main class name
#   * -classpath
#   * -D...appname settings
#   * --module-path (only if needed)
#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.

# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
    APP_HOME=$( cygpath --path --mixed "$APP_HOME" )

    JAVACMD=$( cygpath --unix "$JAVACMD" )

    # Now convert the arguments - kludge to limit ourselves to /bin/sh
    for arg do
        if
            case $arg in                                #(
              -*)   false ;;                            # don't mess with options #(
              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath
                    [ -e "$t" ] ;;                      #(
              *)    false ;;
            esac
        then
            arg=$( cygpath --path --ignore --mixed "$arg" )
        fi
        # Roll the args list around exactly as many times as the number of
        # args, so each arg winds up back in the position where it started, but
        # possibly modified.
        #
        # NB: a `for` loop captures its iteration list before it begins, so
        # changing the positional parameters here affects neither the number of
        # iterations, nor the values presented in `arg`.
        shift                   # remove old arg
        set -- "$@" "$arg"      # push replacement arg
    done
fi


# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'

# Collect all arguments for the java command:
#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
#     and any embedded shellness will be escaped.
#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
#     treated as '${Hostname}' itself on the command line.

set -- \
        "-Dorg.gradle.appname=$APP_BASE_NAME" \
        -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
        "$@"

# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
    die "xargs is not available"
fi

# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
#   readarray ARGS < <( xargs -n1 <<<"$var" ) &&
#   set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#

eval "set -- $(
        printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
        xargs -n1 |
        sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
        tr '\n' ' '
    )" '"$@"'

exec "$JAVACMD" "$@"


================================================
FILE: gradlew.bat
================================================
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem      https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem

@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem  Gradle startup script for Windows
@rem
@rem ##########################################################################

@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal

set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%

@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi

@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"

@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome

set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute

echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2

goto fail

:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe

if exist "%JAVA_EXE%" goto execute

echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2

goto fail

:execute
@rem Setup the command line



@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*

:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd

:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%

:mainEnd
if "%OS%"=="Windows_NT" endlocal

:omega


================================================
FILE: k8s/db.yml
================================================
---
apiVersion: v1
kind: Secret
metadata:
  name: demo-db
type: servicebinding.io/postgresql
stringData:
  type: "postgresql"
  provider: "postgresql"
  host: "demo-db"
  port: "5432"
  database: "petclinic"
  username: "user"
  password: "pass"

---
apiVersion: v1
kind: Service
metadata:
  name: demo-db
spec:
  ports:
    - port: 5432
  selector:
    app: demo-db

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-db
  labels:
    app: demo-db
spec:
  selector:
    matchLabels:
      app: demo-db
  template:
    metadata:
      labels:
        app: demo-db
    spec:
      containers:
        - image: postgres:18.3
          name: postgresql
          env:
            - name: POSTGRES_USER
              valueFrom:
                secretKeyRef:
                  name: demo-db
                  key: username
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: demo-db
                  key: password
            - name: POSTGRES_DB
              valueFrom:
                secretKeyRef:
                  name: demo-db
                  key: database
          ports:
            - containerPort: 5432
              name: postgresql
          livenessProbe:
            tcpSocket:
              port: postgresql
          readinessProbe:
            tcpSocket:
              port: postgresql
          startupProbe:
            tcpSocket:
              port: postgresql


================================================
FILE: k8s/petclinic.yml
================================================
---
apiVersion: v1
kind: Service
metadata:
  name: petclinic
spec:
  type: NodePort
  ports:
    - port: 80
      targetPort: 8080
  selector:
    app: petclinic

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: petclinic
  labels:
    app: petclinic
spec:
  replicas: 1
  selector:
    matchLabels:
      app: petclinic
  template:
    metadata:
      labels:
        app: petclinic
    spec:
      containers:
        - name: workload
          image: dsyer/petclinic
          env:
            - name: SPRING_PROFILES_ACTIVE
              value: postgres
            - name: SERVICE_BINDING_ROOT
              value: /bindings
            - name: SPRING_APPLICATION_JSON
              value: |
                {
                  "management.endpoint.health.probes.add-additional-paths": true
                }
          ports:
            - name: http
              containerPort: 8080
          livenessProbe:
            httpGet:
              path: /livez
              port: http
          readinessProbe:
            httpGet:
              path: /readyz
              port: http
          volumeMounts:
            - mountPath: /bindings/secret
              name: binding
              readOnly: true
      volumes:
        - name: binding
          projected:
            sources:
              - secret:
                  name: demo-db


================================================
FILE: mvnw
================================================
#!/bin/sh
# ----------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#    https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.
# ----------------------------------------------------------------------------

# ----------------------------------------------------------------------------
# Apache Maven Wrapper startup batch script, version 3.3.4
#
# Optional ENV vars
# -----------------
#   JAVA_HOME - location of a JDK home dir, required when download maven via java source
#   MVNW_REPOURL - repo url base for downloading maven distribution
#   MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
#   MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
# ----------------------------------------------------------------------------

set -euf
[ "${MVNW_VERBOSE-}" != debug ] || set -x

# OS specific support.
native_path() { printf %s\\n "$1"; }
case "$(uname)" in
CYGWIN* | MINGW*)
  [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
  native_path() { cygpath --path --windows "$1"; }
  ;;
esac

# set JAVACMD and JAVACCMD
set_java_home() {
  # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
  if [ -n "${JAVA_HOME-}" ]; then
    if [ -x "$JAVA_HOME/jre/sh/java" ]; then
      # IBM's JDK on AIX uses strange locations for the executables
      JAVACMD="$JAVA_HOME/jre/sh/java"
      JAVACCMD="$JAVA_HOME/jre/sh/javac"
    else
      JAVACMD="$JAVA_HOME/bin/java"
      JAVACCMD="$JAVA_HOME/bin/javac"

      if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
        echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
        echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
        return 1
      fi
    fi
  else
    JAVACMD="$(
      'set' +e
      'unset' -f command 2>/dev/null
      'command' -v java
    )" || :
    JAVACCMD="$(
      'set' +e
      'unset' -f command 2>/dev/null
      'command' -v javac
    )" || :

    if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
      echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
      return 1
    fi
  fi
}

# hash string like Java String::hashCode
hash_string() {
  str="${1:-}" h=0
  while [ -n "$str" ]; do
    char="${str%"${str#?}"}"
    h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
    str="${str#?}"
  done
  printf %x\\n $h
}

verbose() { :; }
[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }

die() {
  printf %s\\n "$1" >&2
  exit 1
}

trim() {
  # MWRAPPER-139:
  #   Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
  #   Needed for removing poorly interpreted newline sequences when running in more
  #   exotic environments such as mingw bash on Windows.
  printf "%s" "${1}" | tr -d '[:space:]'
}

scriptDir="$(dirname "$0")"
scriptName="$(basename "$0")"

# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
while IFS="=" read -r key value; do
  case "${key-}" in
  distributionUrl) distributionUrl=$(trim "${value-}") ;;
  distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
  esac
done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties"
[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"

case "${distributionUrl##*/}" in
maven-mvnd-*bin.*)
  MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
  case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
  *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
  :Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
  :Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
  :Linux*x86_64*) distributionPlatform=linux-amd64 ;;
  *)
    echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
    distributionPlatform=linux-amd64
    ;;
  esac
  distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
  ;;
maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
*) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
esac

# apply MVNW_REPOURL and calculate MAVEN_HOME
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
distributionUrlName="${distributionUrl##*/}"
distributionUrlNameMain="${distributionUrlName%.*}"
distributionUrlNameMain="${distributionUrlNameMain%-bin}"
MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"

exec_maven() {
  unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
  exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
}

if [ -d "$MAVEN_HOME" ]; then
  verbose "found existing MAVEN_HOME at $MAVEN_HOME"
  exec_maven "$@"
fi

case "${distributionUrl-}" in
*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
esac

# prepare tmp dir
if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
  clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
  trap clean HUP INT TERM EXIT
else
  die "cannot create temp dir"
fi

mkdir -p -- "${MAVEN_HOME%/*}"

# Download and Install Apache Maven
verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
verbose "Downloading from: $distributionUrl"
verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"

# select .zip or .tar.gz
if ! command -v unzip >/dev/null; then
  distributionUrl="${distributionUrl%.zip}.tar.gz"
  distributionUrlName="${distributionUrl##*/}"
fi

# verbose opt
__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v

# normalize http auth
case "${MVNW_PASSWORD:+has-password}" in
'') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
esac

if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
  verbose "Found wget ... using wget"
  wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
  verbose "Found curl ... using curl"
  curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
elif set_java_home; then
  verbose "Falling back to use Java to download"
  javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
  targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
  cat >"$javaSource" <<-END
	public class Downloader extends java.net.Authenticator
	{
	  protected java.net.PasswordAuthentication getPasswordAuthentication()
	  {
	    return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
	  }
	  public static void main( String[] args ) throws Exception
	  {
	    setDefault( new Downloader() );
	    java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
	  }
	}
	END
  # For Cygwin/MinGW, switch paths to Windows format before running javac and java
  verbose " - Compiling Downloader.java ..."
  "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
  verbose " - Running Downloader.java ..."
  "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
fi

# If specified, validate the SHA-256 sum of the Maven distribution zip file
if [ -n "${distributionSha256Sum-}" ]; then
  distributionSha256Result=false
  if [ "$MVN_CMD" = mvnd.sh ]; then
    echo "Checksum validation is not supported for maven-mvnd." >&2
    echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
    exit 1
  elif command -v sha256sum >/dev/null; then
    if echo "$distributionSha256Sum  $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then
      distributionSha256Result=true
    fi
  elif command -v shasum >/dev/null; then
    if echo "$distributionSha256Sum  $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
      distributionSha256Result=true
    fi
  else
    echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
    echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
    exit 1
  fi
  if [ $distributionSha256Result = false ]; then
    echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
    echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
    exit 1
  fi
fi

# unzip and move
if command -v unzip >/dev/null; then
  unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
else
  tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
fi

# Find the actual extracted directory name (handles snapshots where filename != directory name)
actualDistributionDir=""

# First try the expected directory name (for regular distributions)
if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then
  if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then
    actualDistributionDir="$distributionUrlNameMain"
  fi
fi

# If not found, search for any directory with the Maven executable (for snapshots)
if [ -z "$actualDistributionDir" ]; then
  # enable globbing to iterate over items
  set +f
  for dir in "$TMP_DOWNLOAD_DIR"/*; do
    if [ -d "$dir" ]; then
      if [ -f "$dir/bin/$MVN_CMD" ]; then
        actualDistributionDir="$(basename "$dir")"
        break
      fi
    fi
  done
  set -f
fi

if [ -z "$actualDistributionDir" ]; then
  verbose "Contents of $TMP_DOWNLOAD_DIR:"
  verbose "$(ls -la "$TMP_DOWNLOAD_DIR")"
  die "Could not find Maven distribution directory in extracted archive"
fi

verbose "Found extracted Maven distribution directory: $actualDistributionDir"
printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url"
mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"

clean || :
exec_maven "$@"


================================================
FILE: mvnw.cmd
================================================
<# : batch portion
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements.  See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership.  The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License.  You may obtain a copy of the License at
@REM
@REM    https://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied.  See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------

@REM ----------------------------------------------------------------------------
@REM Apache Maven Wrapper startup batch script, version 3.3.4
@REM
@REM Optional ENV vars
@REM   MVNW_REPOURL - repo url base for downloading maven distribution
@REM   MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
@REM   MVNW_VERBOSE - true: enable verbose log; others: silence the output
@REM ----------------------------------------------------------------------------

@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
@SET __MVNW_CMD__=
@SET __MVNW_ERROR__=
@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
@SET PSModulePath=
@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
  IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
)
@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
@SET __MVNW_PSMODULEP_SAVE=
@SET __MVNW_ARG0_NAME__=
@SET MVNW_USERNAME=
@SET MVNW_PASSWORD=
@IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*)
@echo Cannot start maven from wrapper >&2 && exit /b 1
@GOTO :EOF
: end batch / begin powershell #>

$ErrorActionPreference = "Stop"
if ($env:MVNW_VERBOSE -eq "true") {
  $VerbosePreference = "Continue"
}

# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
if (!$distributionUrl) {
  Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
}

switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
  "maven-mvnd-*" {
    $USE_MVND = $true
    $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
    $MVN_CMD = "mvnd.cmd"
    break
  }
  default {
    $USE_MVND = $false
    $MVN_CMD = $script -replace '^mvnw','mvn'
    break
  }
}

# apply MVNW_REPOURL and calculate MAVEN_HOME
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
if ($env:MVNW_REPOURL) {
  $MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" }
  $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')"
}
$distributionUrlName = $distributionUrl -replace '^.*/',''
$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''

$MAVEN_M2_PATH = "$HOME/.m2"
if ($env:MAVEN_USER_HOME) {
  $MAVEN_M2_PATH = "$env:MAVEN_USER_HOME"
}

if (-not (Test-Path -Path $MAVEN_M2_PATH)) {
    New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null
}

$MAVEN_WRAPPER_DISTS = $null
if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) {
  $MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists"
} else {
  $MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists"
}

$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain"
$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"

if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
  Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
  Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
  exit $?
}

if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
  Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
}

# prepare tmp dir
$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
trap {
  if ($TMP_DOWNLOAD_DIR.Exists) {
    try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
    catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
  }
}

New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null

# Download and Install Apache Maven
Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
Write-Verbose "Downloading from: $distributionUrl"
Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"

$webclient = New-Object System.Net.WebClient
if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
  $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
}
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null

# If specified, validate the SHA-256 sum of the Maven distribution zip file
$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
if ($distributionSha256Sum) {
  if ($USE_MVND) {
    Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
  }
  Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
  if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
    Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
  }
}

# unzip and move
Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null

# Find the actual extracted directory name (handles snapshots where filename != directory name)
$actualDistributionDir = ""

# First try the expected directory name (for regular distributions)
$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain"
$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD"
if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) {
  $actualDistributionDir = $distributionUrlNameMain
}

# If not found, search for any directory with the Maven executable (for snapshots)
if (!$actualDistributionDir) {
  Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object {
    $testPath = Join-Path $_.FullName "bin/$MVN_CMD"
    if (Test-Path -Path $testPath -PathType Leaf) {
      $actualDistributionDir = $_.Name
    }
  }
}

if (!$actualDistributionDir) {
  Write-Error "Could not find Maven distribution directory in extracted archive"
}

Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir"
Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null
try {
  Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
} catch {
  if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
    Write-Error "fail to move MAVEN_HOME"
  }
} finally {
  try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
  catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
}

Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"


================================================
FILE: pom.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>4.0.3</version>
  </parent>

  <groupId>org.springframework.samples</groupId>
  <artifactId>spring-petclinic</artifactId>
  <version>4.0.0-SNAPSHOT</version>

  <name>petclinic</name>

  <properties>
    <!-- Generic properties -->
    <java.version>17</java.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <!-- Important for reproducible builds. Update using e.g. ./mvnw versions:set -DnewVersion=... -->
    <project.build.outputTimestamp>2024-11-28T14:37:52Z</project.build.outputTimestamp>

    <!-- Web dependencies -->
    <webjars-locator.version>1.1.3</webjars-locator.version>
    <webjars-bootstrap.version>5.3.8</webjars-bootstrap.version>
    <webjars-font-awesome.version>4.7.0</webjars-font-awesome.version>

    <!-- Checkstyle needs to stay on v12 since v13 sets minimal jdk to 21 - https://checkstyle.org/releasenotes.html#Release_13.0.0 -->
    <checkstyle.version>12.3.1</checkstyle.version>
    <jacoco.version>0.8.14</jacoco.version>
    <libsass.version>0.3.4</libsass.version>
    <lifecycle-mapping>1.0.0</lifecycle-mapping>
    <maven-checkstyle.version>3.6.0</maven-checkstyle.version>
    <nohttp-checkstyle.version>0.0.11</nohttp-checkstyle.version>
    <spring-format.version>0.0.47</spring-format.version>
  </properties>

  <dependencies>
    <!-- Spring and Spring Boot dependencies -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-cache</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-webmvc</artifactId>
    </dependency>

    <dependency>
      <groupId>javax.cache</groupId>
      <artifactId>cache-api</artifactId>
    </dependency>
    <dependency>
      <groupId>jakarta.xml.bind</groupId>
      <artifactId>jakarta.xml.bind-api</artifactId>
    </dependency>

    <dependency>
      <groupId>com.h2database</groupId>
      <artifactId>h2</artifactId>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>com.github.ben-manes.caffeine</groupId>
      <artifactId>caffeine</artifactId>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>com.mysql</groupId>
      <artifactId>mysql-connector-j</artifactId>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>org.postgresql</groupId>
      <artifactId>postgresql</artifactId>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>org.webjars</groupId>
      <artifactId>webjars-locator-lite</artifactId>
      <version>${webjars-locator.version}</version>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>org.webjars.npm</groupId>
      <artifactId>bootstrap</artifactId>
      <version>${webjars-bootstrap.version}</version>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>org.webjars.npm</groupId>
      <artifactId>font-awesome</artifactId>
      <version>${webjars-font-awesome.version}</version>
      <scope>runtime</scope>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-devtools</artifactId>
      <optional>true</optional>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jpa-test</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-restclient</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-restclient-test</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-webmvc-test</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-testcontainers</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-docker-compose</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.testcontainers</groupId>
      <artifactId>testcontainers-junit-jupiter</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.testcontainers</groupId>
      <artifactId>testcontainers-mysql</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-enforcer-plugin</artifactId>
        <executions>
          <execution>
            <id>enforce-java</id>
            <goals>
              <goal>enforce</goal>
            </goals>
            <configuration>
              <rules>
                <requireJavaVersion>
                  <message>This build requires at least Java ${java.version}, update your JVM, and run the build again</message>
                  <version>${java.version}</version>
                </requireJavaVersion>
              </rules>
            </configuration>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <groupId>io.spring.javaformat</groupId>
        <artifactId>spring-javaformat-maven-plugin</artifactId>
        <version>${spring-format.version}</version>
        <executions>
          <execution>
            <goals>
              <goal>validate</goal>
            </goals>
            <phase>validate</phase>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-checkstyle-plugin</artifactId>
        <version>${maven-checkstyle.version}</version>
        <dependencies>
          <dependency>
            <groupId>com.puppycrawl.tools</groupId>
            <artifactId>checkstyle</artifactId>
            <version>${checkstyle.version}</version>
          </dependency>
          <dependency>
            <groupId>io.spring.nohttp</groupId>
            <artifactId>nohttp-checkstyle</artifactId>
            <version>${nohttp-checkstyle.version}</version>
          </dependency>
        </dependencies>
        <executions>
          <execution>
            <id>nohttp-checkstyle-validation</id>
            <goals>
              <goal>check</goal>
            </goals>
            <phase>validate</phase>
            <configuration>
              <configLocation>src/checkstyle/nohttp-checkstyle.xml</configLocation>
              <sourceDirectories>${basedir}</sourceDirectories>
              <includes>**/*</includes>
              <excludes>**/.git/**/*,**/.idea/**/*,**/target/**/,**/.flattened-pom.xml,**/*.class</excludes>
              <propertyExpansion>config_loc=${basedir}/src/checkstyle/</propertyExpansion>
            </configuration>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <groupId>org.graalvm.buildtools</groupId>
        <artifactId>native-maven-plugin</artifactId>
      </plugin>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <executions>
          <execution>
            <!-- Spring Boot Actuator displays build-related information
              if a META-INF/build-info.properties file is present -->
            <goals>
              <goal>build-info</goal>
            </goals>
            <configuration>
              <additionalProperties>
                <encoding.source>${project.build.sourceEncoding}</encoding.source>
                <encoding.reporting>${project.reporting.outputEncoding}</encoding.reporting>
                <java.source>${java.version}</java.source>
                <java.target>${java.version}</java.target>
              </additionalProperties>
            </configuration>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <groupId>org.jacoco</groupId>
        <artifactId>jacoco-maven-plugin</artifactId>
        <version>${jacoco.version}</version>
        <executions>
          <execution>
            <goals>
              <goal>prepare-agent</goal>
            </goals>
          </execution>
          <execution>
            <id>report</id>
            <goals>
              <goal>report</goal>
            </goals>
            <phase>prepare-package</phase>
          </execution>
        </executions>
      </plugin>

      <!-- Spring Boot Actuator displays build-related information if a git.properties file is
      present at the classpath -->
      <plugin>
        <groupId>io.github.git-commit-id</groupId>
        <artifactId>git-commit-id-maven-plugin</artifactId>
        <configuration>
          <failOnNoGitDirectory>false</failOnNoGitDirectory>
          <failOnUnableToExtractRepoInfo>false</failOnUnableToExtractRepoInfo>
        </configuration>
      </plugin>
      <!-- Spring Boot Actuator displays sbom-related information if a CycloneDX SBOM file is
      present at the classpath -->
      <plugin>
        <?m2e ignore?>
        <groupId>org.cyclonedx</groupId>
        <artifactId>cyclonedx-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>
  <licenses>
    <license>
      <name>Apache License, Version 2.0</name>
      <url>https://www.apache.org/licenses/LICENSE-2.0</url>
    </license>
  </licenses>

  <profiles>
    <profile>
      <id>css</id>
      <build>
        <plugins>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-dependency-plugin</artifactId>
            <executions>
              <execution>
                <id>unpack</id>
                <goals>
                  <goal>unpack</goal>
                </goals>
                <?m2e execute onConfiguration,onIncremental?>
                <phase>generate-resources</phase>
                <configuration>
                  <artifactItems>
                    <artifactItem>
                      <groupId>org.webjars.npm</groupId>
                      <artifactId>bootstrap</artifactId>
                      <version>${webjars-bootstrap.version}</version>
                    </artifactItem>
                  </artifactItems>
                  <outputDirectory>${project.build.directory}/webjars</outputDirectory>
                </configuration>
              </execution>
            </executions>
          </plugin>
          <plugin>
            <groupId>com.gitlab.haynes</groupId>
            <artifactId>libsass-maven-plugin</artifactId>
            <version>${libsass.version}</version>
            <configuration>
              <inputPath>${basedir}/src/main/scss/</inputPath>
              <outputPath>${basedir}/src/main/resources/static/resources/css/</outputPath>
              <includePath>${project.build.directory}/webjars/META-INF/resources/webjars/bootstrap/${webjars-bootstrap.version}/scss/</includePath>
            </configuration>
            <executions>
              <execution>
                <?m2e execute onConfiguration,onIncremental?>
                <goals>
                  <goal>compile</goal>
                </goals>
                <phase>generate-resources</phase>
              </execution>
            </executions>
          </plugin>
        </plugins>
      </build>
    </profile>
    <profile>
      <id>m2e</id>
      <activation>
        <property>
          <name>m2e.version</name>
        </property>
      </activation>
      <build>
        <pluginManagement>
          <plugins>
            <!-- This plugin's configuration is used to store Eclipse m2e settings
              only. It has no influence on the Maven build itself. -->
            <plugin>
              <groupId>org.eclipse.m2e</groupId>
              <artifactId>lifecycle-mapping</artifactId>
              <version>${lifecycle-mapping}</version>
              <configuration>
                <lifecycleMappingMetadata>
                  <pluginExecutions>
                    <pluginExecution>
                      <pluginExecutionFilter>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-checkstyle-plugin</artifactId>
                        <versionRange>[1,)</versionRange>
                        <goals>
                          <goal>check</goal>
                        </goals>
                      </pluginExecutionFilter>
                      <action>
                        <ignore></ignore>
                      </action>
                    </pluginExecution>
                    <pluginExecution>
                      <pluginExecutionFilter>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-maven-plugin</artifactId>
                        <versionRange>[1,)</versionRange>
                        <goals>
                          <goal>build-info</goal>
                        </goals>
                      </pluginExecutionFilter>
                      <action>
                        <ignore></ignore>
                      </action>
                    </pluginExecution>
                    <pluginExecution>
                      <pluginExecutionFilter>
                        <groupId>io.spring.javaformat</groupId>
                        <artifactId>spring-javaformat-maven-plugin</artifactId>
                        <versionRange>[0,)</versionRange>
                        <goals>
                          <goal>validate</goal>
                        </goals>
                      </pluginExecutionFilter>
                      <action>
                        <ignore></ignore>
                      </action>
                    </pluginExecution>
                  </pluginExecutions>
                </lifecycleMappingMetadata>
              </configuration>
            </plugin>
          </plugins>
        </pluginManagement>
      </build>
    </profile>
  </profiles>
</project>


================================================
FILE: settings.gradle
================================================
rootProject.name = 'spring-petclinic'


================================================
FILE: src/checkstyle/nohttp-checkstyle-suppressions.xml
================================================
<?xml version="1.0"?>
<!DOCTYPE suppressions PUBLIC
		"-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN"
		"https://checkstyle.org/dtds/suppressions_1_2.dtd">
<suppressions>
	<suppress files="node_modules[\\/].*" checks=".*"/>
	<suppress files="node[\\/].*" checks=".*"/>
	<suppress files="build[\\/].*" checks=".*"/>
	<suppress files="target[\\/].*" checks=".*"/>
	<suppress files=".+\.(jar|git|ico|p12|gif|jks|jpg|svg|log)" checks="NoHttp"/>
</suppressions>


================================================
FILE: src/checkstyle/nohttp-checkstyle.xml
================================================
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
		"-//Puppy Crawl//DTD Check Configuration 1.2//EN"
		"https://checkstyle.org/dtds/configuration_1_2.dtd">
<module name="com.puppycrawl.tools.checkstyle.Checker">
	<module name="io.spring.nohttp.checkstyle.check.NoHttpCheck"/>
	<module name="SuppressionFilter">
		<property name="file" value="${config_loc}/nohttp-checkstyle-suppressions.xml"/>
	</module>
</module>


================================================
FILE: src/main/java/org/springframework/samples/petclinic/PetClinicApplication.java
================================================
/*
 * Copyright 2012-2025 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.samples.petclinic;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportRuntimeHints;

/**
 * PetClinic Spring Boot Application.
 *
 * @author Dave Syer
 */
@SpringBootApplication
@ImportRuntimeHints(PetClinicRuntimeHints.class)
public class PetClinicApplication {

	public static void main(String[] args) {
		SpringApplication.run(PetClinicApplication.class, args);
	}

}


================================================
FILE: src/main/java/org/springframework/samples/petclinic/PetClinicRuntimeHints.java
================================================
/*
 * Copyright 2012-2025 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.samples.petclinic;

import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.samples.petclinic.model.BaseEntity;
import org.springframework.samples.petclinic.model.Person;
import org.springframework.samples.petclinic.vet.Vet;

public class PetClinicRuntimeHints implements RuntimeHintsRegistrar {

	@Override
	public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
		hints.resources().registerPattern("db/*"); // https://github.com/spring-projects/spring-boot/issues/32654
		hints.resources().registerPattern("messages/*");
		hints.resources().registerPattern("mysql-default-conf");
		hints.serialization().registerType(BaseEntity.class);
		hints.serialization().registerType(Person.class);
		hints.serialization().registerType(Vet.class);
	}

}


================================================
FILE: src/main/java/org/springframework/samples/petclinic/model/BaseEntity.java
================================================
/*
 * Copyright 2012-2025 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.samples.petclinic.model;

import java.io.Serializable;

import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.MappedSuperclass;

/**
 * Simple JavaBean domain object with an id property. Used as a base class for objects
 * needing this property.
 *
 * @author Ken Krebs
 * @author Juergen Hoeller
 */
@MappedSuperclass
public class BaseEntity implements Serializable {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Integer id;

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public boolean isNew() {
		return this.id == null;
	}

}


================================================
FILE: src/main/java/org/springframework/samples/petclinic/model/NamedEntity.java
================================================
/*
 * Copyright 2012-2025 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.samples.petclinic.model;

import jakarta.persistence.Column;
import jakarta.persistence.MappedSuperclass;
import jakarta.validation.constraints.NotBlank;

/**
 * Simple JavaBean domain object adds a name property to <code>BaseEntity</code>. Used as
 * a base class for objects needing these properties.
 *
 * @author Ken Krebs
 * @author Juergen Hoeller
 * @author Wick Dynex
 */
@MappedSuperclass
public class NamedEntity extends BaseEntity {

	@Column
	@NotBlank
	private String name;

	public String getName() {
		return this.name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		String name = this.getName();
		return name != null ? name : "<null>";
	}

}


================================================
FILE: src/main/java/org/springframework/samples/petclinic/model/Person.java
================================================
/*
 * Copyright 2012-2025 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.samples.petclinic.model;

import jakarta.persistence.Column;
import jakarta.persistence.MappedSuperclass;
import jakarta.validation.constraints.NotBlank;

/**
 * Simple JavaBean domain object representing an person.
 *
 * @author Ken Krebs
 */
@MappedSuperclass
public class Person extends BaseEntity {

	@Column
	@NotBlank
	private String firstName;

	@Column
	@NotBlank
	private String lastName;

	public String getFirstName() {
		return this.firstName;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public String getLastName() {
		return this.lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

}


================================================
FILE: src/main/java/org/springframework/samples/petclinic/model/package-info.java
================================================
/*
 * Copyright 2012-2025 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * The classes in this package represent utilities used by the domain.
 */
package org.springframework.samples.petclinic.model;


================================================
FILE: src/main/java/org/springframework/samples/petclinic/owner/Owner.java
================================================
/*
 * Copyright 2012-2025 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.samples.petclinic.owner;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import org.springframework.core.style.ToStringCreator;
import org.springframework.samples.petclinic.model.Person;
import org.springframework.util.Assert;

import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OrderBy;
import jakarta.persistence.Table;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.NotBlank;

/**
 * Simple JavaBean domain object representing an owner.
 *
 * @author Ken Krebs
 * @author Juergen Hoeller
 * @author Sam Brannen
 * @author Michael Isvy
 * @author Oliver Drotbohm
 * @author Wick Dynex
 */
@Entity
@Table(name = "owners")
public class Owner extends Person {

	@Column
	@NotBlank
	private String address;

	@Column
	@NotBlank
	private String city;

	@Column
	@NotBlank
	@Pattern(regexp = "\\d{10}", message = "{telephone.invalid}")
	private String telephone;

	@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
	@JoinColumn(name = "owner_id")
	@OrderBy("name")
	private final List<Pet> pets = new ArrayList<>();

	public String getAddress() {
		return this.address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	public String getCity() {
		return this.city;
	}

	public void setCity(String city) {
		this.city = city;
	}

	public String getTelephone() {
		return this.telephone;
	}

	public void setTelephone(String telephone) {
		this.telephone = telephone;
	}

	public List<Pet> getPets() {
		return this.pets;
	}

	public void addPet(Pet pet) {
		if (pet.isNew()) {
			getPets().add(pet);
		}
	}

	/**
	 * Return the Pet with the given name, or null if none found for this Owner.
	 * @param name to test
	 * @return the Pet with the given name, or null if no such Pet exists for this Owner
	 */
	public Pet getPet(String name) {
		return getPet(name, false);
	}

	/**
	 * Return the Pet with the given id, or null if none found for this Owner.
	 * @param id to test
	 * @return the Pet with the given id, or null if no such Pet exists for this Owner
	 */
	public Pet getPet(Integer id) {
		for (Pet pet : getPets()) {
			if (!pet.isNew()) {
				Integer compId = pet.getId();
				if (Objects.equals(compId, id)) {
					return pet;
				}
			}
		}
		return null;
	}

	/**
	 * Return the Pet with the given name, or null if none found for this Owner.
	 * @param name to test
	 * @param ignoreNew whether to ignore new pets (pets that are not saved yet)
	 * @return the Pet with the given name, or null if no such Pet exists for this Owner
	 */
	public Pet getPet(String name, boolean ignoreNew) {
		for (Pet pet : getPets()) {
			String compName = pet.getName();
			if (compName != null && compName.equalsIgnoreCase(name)) {
				if (!ignoreNew || !pet.isNew()) {
					return pet;
				}
			}
		}
		return null;
	}

	@Override
	public String toString() {
		return new ToStringCreator(this).append("id", this.getId())
			.append("new", this.isNew())
			.append("lastName", this.getLastName())
			.append("firstName", this.getFirstName())
			.append("address", this.address)
			.append("city", this.city)
			.append("telephone", this.telephone)
			.toString();
	}

	/**
	 * Adds the given {@link Visit} to the {@link Pet} with the given identifier.
	 * @param petId the identifier of the {@link Pet}, must not be {@literal null}.
	 * @param visit the visit to add, must not be {@literal null}.
	 */
	public void addVisit(Integer petId, Visit visit) {

		Assert.notNull(petId, "Pet identifier must not be null!");
		Assert.notNull(visit, "Visit must not be null!");

		Pet pet = getPet(petId);

		Assert.notNull(pet, "Invalid Pet identifier!");

		pet.addVisit(visit);
	}

}


================================================
FILE: src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java
================================================
/*
 * Copyright 2012-2025 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.samples.petclinic.owner;

import java.util.List;
import java.util.Objects;
import java.util.Optional;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

import jakarta.validation.Valid;

import org.springframework.web.servlet.mvc.support.RedirectAttributes;

/**
 * @author Juergen Hoeller
 * @author Ken Krebs
 * @author Arjen Poutsma
 * @author Michael Isvy
 * @author Wick Dynex
 */
@Controller
class OwnerController {

	private static final String VIEWS_OWNER_CREATE_OR_UPDATE_FORM = "owners/createOrUpdateOwnerForm";

	private final OwnerRepository owners;

	public OwnerController(OwnerRepository owners) {
		this.owners = owners;
	}

	@InitBinder
	public void setAllowedFields(WebDataBinder dataBinder) {
		dataBinder.setDisallowedFields("id");
	}

	@ModelAttribute("owner")
	public Owner findOwner(@PathVariable(name = "ownerId", required = false) Integer ownerId) {
		return ownerId == null ? new Owner()
				: this.owners.findById(ownerId)
					.orElseThrow(() -> new IllegalArgumentException("Owner not found with id: " + ownerId
							+ ". Please ensure the ID is correct " + "and the owner exists in the database."));
	}

	@GetMapping("/owners/new")
	public String initCreationForm() {
		return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
	}

	@PostMapping("/owners/new")
	public String processCreationForm(@Valid Owner owner, BindingResult result, RedirectAttributes redirectAttributes) {
		if (result.hasErrors()) {
			redirectAttributes.addFlashAttribute("error", "There was an error in creating the owner.");
			return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
		}

		this.owners.save(owner);
		redirectAttributes.addFlashAttribute("message", "New Owner Created");
		return "redirect:/owners/" + owner.getId();
	}

	@GetMapping("/owners/find")
	public String initFindForm() {
		return "owners/findOwners";
	}

	@GetMapping("/owners")
	public String processFindForm(@RequestParam(defaultValue = "1") int page, Owner owner, BindingResult result,
			Model model) {
		// allow parameterless GET request for /owners to return all records
		String lastName = owner.getLastName();
		if (lastName == null) {
			lastName = ""; // empty string signifies broadest possible search
		}

		// find owners by last name
		Page<Owner> ownersResults = findPaginatedForOwnersLastName(page, lastName);
		if (ownersResults.isEmpty()) {
			// no owners found
			result.rejectValue("lastName", "notFound", "not found");
			return "owners/findOwners";
		}

		if (ownersResults.getTotalElements() == 1) {
			// 1 owner found
			owner = ownersResults.iterator().next();
			return "redirect:/owners/" + owner.getId();
		}

		// multiple owners found
		return addPaginationModel(page, model, ownersResults);
	}

	private String addPaginationModel(int page, Model model, Page<Owner> paginated) {
		List<Owner> listOwners = paginated.getContent();
		model.addAttribute("currentPage", page);
		model.addAttribute("totalPages", paginated.getTotalPages());
		model.addAttribute("totalItems", paginated.getTotalElements());
		model.addAttribute("listOwners", listOwners);
		return "owners/ownersList";
	}

	private Page<Owner> findPaginatedForOwnersLastName(int page, String lastname) {
		int pageSize = 5;
		Pageable pageable = PageRequest.of(page - 1, pageSize);
		return owners.findByLastNameStartingWith(lastname, pageable);
	}

	@GetMapping("/owners/{ownerId}/edit")
	public String initUpdateOwnerForm() {
		return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
	}

	@PostMapping("/owners/{ownerId}/edit")
	public String processUpdateOwnerForm(@Valid Owner owner, BindingResult result, @PathVariable("ownerId") int ownerId,
			RedirectAttributes redirectAttributes) {
		if (result.hasErrors()) {
			redirectAttributes.addFlashAttribute("error", "There was an error in updating the owner.");
			return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
		}

		if (!Objects.equals(owner.getId(), ownerId)) {
			result.rejectValue("id", "mismatch", "The owner ID in the form does not match the URL.");
			redirectAttributes.addFlashAttribute("error", "Owner ID mismatch. Please try again.");
			return "redirect:/owners/{ownerId}/edit";
		}

		owner.setId(ownerId);
		this.owners.save(owner);
		redirectAttributes.addFlashAttribute("message", "Owner Values Updated");
		return "redirect:/owners/{ownerId}";
	}

	/**
	 * Custom handler for displaying an owner.
	 * @param ownerId the ID of the owner to display
	 * @return a ModelMap with the model attributes for the view
	 */
	@GetMapping("/owners/{ownerId}")
	public ModelAndView showOwner(@PathVariable("ownerId") int ownerId) {
		ModelAndView mav = new ModelAndView("owners/ownerDetails");
		Optional<Owner> optionalOwner = this.owners.findById(ownerId);
		Owner owner = optionalOwner.orElseThrow(() -> new IllegalArgumentException(
				"Owner not found with id: " + ownerId + ". Please ensure the ID is correct "));
		mav.addObject(owner);
		return mav;
	}

}


================================================
FILE: src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java
================================================
/*
 * Copyright 2012-2025 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.samples.petclinic.owner;

import java.util.Optional;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;

/**
 * Repository class for <code>Owner</code> domain objects. All method names are compliant
 * with Spring Data naming conventions so this interface can easily be extended for Spring
 * Data. See:
 * https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods.query-creation
 *
 * @author Ken Krebs
 * @author Juergen Hoeller
 * @author Sam Brannen
 * @author Michael Isvy
 * @author Wick Dynex
 */
public interface OwnerRepository extends JpaRepository<Owner, Integer> {

	/**
	 * Retrieve {@link Owner}s from the data store by last name, returning all owners
	 * whose last name <i>starts</i> with the given name.
	 * @param lastName Value to search for
	 * @return a Collection of matching {@link Owner}s (or an empty Collection if none
	 * found)
	 */
	Page<Owner> findByLastNameStartingWith(String lastName, Pageable pageable);

	/**
	 * Retrieve an {@link Owner} from the data store by id.
	 * <p>
	 * This method returns an {@link Optional} containing the {@link Owner} if found. If
	 * no {@link Owner} is found with the provided id, it will return an empty
	 * {@link Optional}.
	 * </p>
	 * @param id the id to search for
	 * @return an {@link Optional} containing the {@link Owner} if found, or an empty
	 * {@link Optional} if not found.
	 * @throws IllegalArgumentException if the id is null (assuming null is not a valid
	 * input for id)
	 */
	Optional<Owner> findById(Integer id);

}


================================================
FILE: src/main/java/org/springframework/samples/petclinic/owner/Pet.java
================================================
/*
 * Copyright 2012-2025 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.samples.petclinic.owner;

import java.time.LocalDate;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Set;

import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.samples.petclinic.model.NamedEntity;

import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OrderBy;
import jakarta.persistence.Table;

/**
 * Simple business object representing a pet.
 *
 * @author Ken Krebs
 * @author Juergen Hoeller
 * @author Sam Brannen
 * @author Wick Dynex
 */
@Entity
@Table(name = "pets")
public class Pet extends NamedEntity {

	@Column
	@DateTimeFormat(pattern = "yyyy-MM-dd")
	private LocalDate birthDate;

	@ManyToOne
	@JoinColumn(name = "type_id")
	private PetType type;

	@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
	@JoinColumn(name = "pet_id")
	@OrderBy("date ASC")
	private final Set<Visit> visits = new LinkedHashSet<>();

	public void setBirthDate(LocalDate birthDate) {
		this.birthDate = birthDate;
	}

	public LocalDate getBirthDate() {
		return this.birthDate;
	}

	public PetType getType() {
		return this.type;
	}

	public void setType(PetType type) {
		this.type = type;
	}

	public Collection<Visit> getVisits() {
		return this.visits;
	}

	public void addVisit(Visit visit) {
		getVisits().add(visit);
	}

}


================================================
FILE: src/main/java/org/springframework/samples/petclinic/owner/PetController.java
================================================
/*
 * Copyright 2012-2025 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.samples.petclinic.owner;

import java.time.LocalDate;
import java.util.Collection;
import java.util.Objects;
import java.util.Optional;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import jakarta.validation.Valid;

import org.springframework.web.servlet.mvc.support.RedirectAttributes;

/**
 * @author Juergen Hoeller
 * @author Ken Krebs
 * @author Arjen Poutsma
 * @author Wick Dynex
 */
@Controller
@RequestMapping("/owners/{ownerId}")
class PetController {

	private static final String VIEWS_PETS_CREATE_OR_UPDATE_FORM = "pets/createOrUpdatePetForm";

	private final OwnerRepository owners;

	private final PetTypeRepository types;

	public PetController(OwnerRepository owners, PetTypeRepository types) {
		this.owners = owners;
		this.types = types;
	}

	@ModelAttribute("types")
	public Collection<PetType> populatePetTypes() {
		return this.types.findPetTypes();
	}

	@ModelAttribute("owner")
	public Owner findOwner(@PathVariable("ownerId") int ownerId) {
		Optional<Owner> optionalOwner = this.owners.findById(ownerId);
		Owner owner = optionalOwner.orElseThrow(() -> new IllegalArgumentException(
				"Owner not found with id: " + ownerId + ". Please ensure the ID is correct "));
		return owner;
	}

	@ModelAttribute("pet")
	public Pet findPet(@PathVariable("ownerId") int ownerId,
			@PathVariable(name = "petId", required = false) Integer petId) {

		if (petId == null) {
			return new Pet();
		}

		Optional<Owner> optionalOwner = this.owners.findById(ownerId);
		Owner owner = optionalOwner.orElseThrow(() -> new IllegalArgumentException(
				"Owner not found with id: " + ownerId + ". Please ensure the ID is correct "));
		return owner.getPet(petId);
	}

	@InitBinder("owner")
	public void initOwnerBinder(WebDataBinder dataBinder) {
		dataBinder.setDisallowedFields("id");
	}

	@InitBinder("pet")
	public void initPetBinder(WebDataBinder dataBinder) {
		dataBinder.setValidator(new PetValidator());
	}

	@GetMapping("/pets/new")
	public String initCreationForm(Owner owner, ModelMap model) {
		Pet pet = new Pet();
		owner.addPet(pet);
		return VIEWS_PETS_CREATE_OR_UPDATE_FORM;
	}

	@PostMapping("/pets/new")
	public String processCreationForm(Owner owner, @Valid Pet pet, BindingResult result,
			RedirectAttributes redirectAttributes) {

		if (StringUtils.hasText(pet.getName()) && pet.isNew() && owner.getPet(pet.getName(), true) != null) {
			result.rejectValue("name", "duplicate", "already exists");
		}

		LocalDate currentDate = LocalDate.now();
		if (pet.getBirthDate() != null && pet.getBirthDate().isAfter(currentDate)) {
			result.rejectValue("birthDate", "typeMismatch.birthDate");
		}

		if (result.hasErrors()) {
			return VIEWS_PETS_CREATE_OR_UPDATE_FORM;
		}

		owner.addPet(pet);
		this.owners.save(owner);
		redirectAttributes.addFlashAttribute("message", "New Pet has been Added");
		return "redirect:/owners/{ownerId}";
	}

	@GetMapping("/pets/{petId}/edit")
	public String initUpdateForm() {
		return VIEWS_PETS_CREATE_OR_UPDATE_FORM;
	}

	@PostMapping("/pets/{petId}/edit")
	public String processUpdateForm(Owner owner, @Valid Pet pet, BindingResult result,
			RedirectAttributes redirectAttributes) {

		String petName = pet.getName();

		// checking if the pet name already exists for the owner
		if (StringUtils.hasText(petName)) {
			Pet existingPet = owner.getPet(petName, false);
			if (existingPet != null && !Objects.equals(existingPet.getId(), pet.getId())) {
				result.rejectValue("name", "duplicate", "already exists");
			}
		}

		LocalDate currentDate = LocalDate.now();
		if (pet.getBirthDate() != null && pet.getBirthDate().isAfter(currentDate)) {
			result.rejectValue("birthDate", "typeMismatch.birthDate");
		}

		if (result.hasErrors()) {
			return VIEWS_PETS_CREATE_OR_UPDATE_FORM;
		}

		updatePetDetails(owner, pet);
		redirectAttributes.addFlashAttribute("message", "Pet details has been edited");
		return "redirect:/owners/{ownerId}";
	}

	/**
	 * Updates the pet details if it exists or adds a new pet to the owner.
	 * @param owner The owner of the pet
	 * @param pet The pet with updated details
	 */
	private void updatePetDetails(Owner owner, Pet pet) {
		Integer id = pet.getId();
		Assert.state(id != null, "'pet.getId()' must not be null");
		Pet existingPet = owner.getPet(id);
		if (existingPet != null) {
			// Update existing pet's properties
			existingPet.setName(pet.getName());
			existingPet.setBirthDate(pet.getBirthDate());
			existingPet.setType(pet.getType());
		}
		else {
			owner.addPet(pet);
		}
		this.owners.save(owner);
	}

}


================================================
FILE: src/main/java/org/springframework/samples/petclinic/owner/PetType.java
================================================
/*
 * Copyright 2012-2025 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.samples.petclinic.owner;

import org.springframework.samples.petclinic.model.NamedEntity;

import jakarta.persistence.Entity;
import jakarta.persistence.Table;

/**
 * @author Juergen Hoeller Can be Cat, Dog, Hamster...
 */
@Entity
@Table(name = "types")
public class PetType extends NamedEntity {

}


================================================
FILE: src/main/java/org/springframework/samples/petclinic/owner/PetTypeFormatter.java
================================================
/*
 * Copyright 2012-2025 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.samples.petclinic.owner;

import org.springframework.format.Formatter;
import org.springframework.stereotype.Component;

import java.text.ParseException;
import java.util.Collection;
import java.util.Locale;
import java.util.Objects;

/**
 * Instructs Spring MVC on how to parse and print elements of type 'PetType'. Starting
 * from Spring 3.0, Formatters have come as an improvement in comparison to legacy
 * PropertyEditors. See the following links for more details: - The Spring ref doc:
 * https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#format
 *
 * @author Mark Fisher
 * @author Juergen Hoeller
 * @author Michael Isvy
 */
@Component
public class PetTypeFormatter implements Formatter<PetType> {

	private final PetTypeRepository types;

	public PetTypeFormatter(PetTypeRepository types) {
		this.types = types;
	}

	@Override
	public String print(PetType petType, Locale locale) {
		String name = petType.getName();
		return name != null ? name : "<null>";
	}

	@Override
	public PetType parse(String text, Locale locale) throws ParseException {
		Collection<PetType> findPetTypes = this.types.findPetTypes();
		for (PetType type : findPetTypes) {
			if (Objects.equals(type.getName(), text)) {
				return type;
			}
		}
		throw new ParseException("type not found: " + text, 0);
	}

}


================================================
FILE: src/main/java/org/springframework/samples/petclinic/owner/PetTypeRepository.java
================================================
/*
 * Copyright 2012-2025 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.samples.petclinic.owner;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

/**
 * Repository class for <code>PetType</code> domain objects.
 *
 * @author Patrick Baumgartner
 */

public interface PetTypeRepository extends JpaRepository<PetType, Integer> {

	/**
	 * Retrieve all {@link PetType}s from the data store.
	 * @return a Collection of {@link PetType}s.
	 */
	@Query("SELECT ptype FROM PetType ptype ORDER BY ptype.name")
	List<PetType> findPetTypes();

}


================================================
FILE: src/main/java/org/springframework/samples/petclinic/owner/PetValidator.java
================================================
/*
 * Copyright 2012-2025 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.samples.petclinic.owner;

import org.springframework.util.StringUtils;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;

/**
 * <code>Validator</code> for <code>Pet</code> forms.
 * <p>
 * We're not using Bean Validation annotations here because it is easier to define such
 * validation rule in Java.
 * </p>
 *
 * @author Ken Krebs
 * @author Juergen Hoeller
 */
public class PetValidator implements Validator {

	private static final String REQUIRED = "required";

	@Override
	public void validate(Object obj, Errors errors) {
		Pet pet = (Pet) obj;
		String name = pet.getName();
		// name validation
		if (!StringUtils.hasText(name)) {
			errors.rejectValue("name", REQUIRED, REQUIRED);
		}

		// type validation
		if (pet.isNew() && pet.getType() == null) {
			errors.rejectValue("type", REQUIRED, REQUIRED);
		}

		// birth date validation
		if (pet.getBirthDate() == null) {
			errors.rejectValue("birthDate", REQUIRED, REQUIRED);
		}
	}

	/**
	 * This Validator validates *just* Pet instances
	 */
	@Override
	public boolean supports(Class<?> clazz) {
		return Pet.class.isAssignableFrom(clazz);
	}

}


================================================
FILE: src/main/java/org/springframework/samples/petclinic/owner/Visit.java
================================================
/*
 * Copyright 2012-2025 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.samples.petclinic.owner;

import java.time.LocalDate;

import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.samples.petclinic.model.BaseEntity;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotBlank;

/**
 * Simple JavaBean domain object representing a visit.
 *
 * @author Ken Krebs
 * @author Dave Syer
 */
@Entity
@Table(name = "visits")
public class Visit extends BaseEntity {

	@Column(name = "visit_date")
	@DateTimeFormat(pattern = "yyyy-MM-dd")
	private LocalDate date;

	@NotBlank
	private String description;

	/**
	 * Creates a new instance of Visit for the current date
	 */
	public Visit() {
		this.date = LocalDate.now();
	}

	public LocalDate getDate() {
		return this.date;
	}

	public void setDate(LocalDate date) {
		this.date = date;
	}

	public String getDescription() {
		return this.description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

}


================================================
FILE: src/main/java/org/springframework/samples/petclinic/owner/VisitController.java
================================================
/*
 * Copyright 2012-2025 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.samples.petclinic.owner;

import java.util.Map;
import java.util.Optional;

import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;

import jakarta.validation.Valid;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

/**
 * @author Juergen Hoeller
 * @author Ken Krebs
 * @author Arjen Poutsma
 * @author Michael Isvy
 * @author Dave Syer
 * @author Wick Dynex
 */
@Controller
class VisitController {

	private final OwnerRepository owners;

	public VisitController(OwnerRepository owners) {
		this.owners = owners;
	}

	@InitBinder
	public void setAllowedFields(WebDataBinder dataBinder) {
		dataBinder.setDisallowedFields("id");
	}

	/**
	 * Called before each and every @RequestMapping annotated method. 2 goals: - Make sure
	 * we always have fresh data - Since we do not use the session scope, make sure that
	 * Pet object always has an id (Even though id is not part of the form fields)
	 * @param petId
	 * @return Pet
	 */
	@ModelAttribute("visit")
	public Visit loadPetWithVisit(@PathVariable("ownerId") int ownerId, @PathVariable("petId") int petId,
			Map<String, Object> model) {
		Optional<Owner> optionalOwner = owners.findById(ownerId);
		Owner owner = optionalOwner.orElseThrow(() -> new IllegalArgumentException(
				"Owner not found with id: " + ownerId + ". Please ensure the ID is correct "));

		Pet pet = owner.getPet(petId);
		if (pet == null) {
			throw new IllegalArgumentException(
					"Pet with id " + petId + " not found for owner with id " + ownerId + ".");
		}
		model.put("pet", pet);
		model.put("owner", owner);

		Visit visit = new Visit();
		pet.addVisit(visit);
		return visit;
	}

	// Spring MVC calls method loadPetWithVisit(...) before initNewVisitForm is
	// called
	@GetMapping("/owners/{ownerId}/pets/{petId}/visits/new")
	public String initNewVisitForm() {
		return "pets/createOrUpdateVisitForm";
	}

	// Spring MVC calls method loadPetWithVisit(...) before processNewVisitForm is
	// called
	@PostMapping("/owners/{ownerId}/pets/{petId}/visits/new")
	public String processNewVisitForm(@ModelAttribute Owner owner, @PathVariable int petId, @Valid Visit visit,
			BindingResult result, RedirectAttributes redirectAttributes) {
		if (result.hasErrors()) {
			return "pets/createOrUpdateVisitForm";
		}

		owner.addVisit(petId, visit);
		this.owners.save(owner);
		redirectAttributes.addFlashAttribute("message", "Your visit has been booked");
		return "redirect:/owners/{ownerId}";
	}

}


================================================
FILE: src/main/java/org/springframework/samples/petclinic/owner/package-info.java
================================================
/*
 * Copyright 2012-2025 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.samples.petclinic.owner;


================================================
FILE: src/main/java/org/springframework/samples/petclinic/package-info.java
================================================
/*
 * Copyright 2012-2025 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.samples.petclinic;


================================================
FILE: src/main/java/org/springframework/samples/petclinic/system/CacheConfiguration.java
================================================
/*
 * Copyright 2012-2025 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.samples.petclinic.system;

import org.springframework.boot.cache.autoconfigure.JCacheManagerCustomizer;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.cache.configuration.MutableConfiguration;

/**
 * Cache configuration intended for caches providing the JCache API. This configuration
 * creates the used cache for the application and enables statistics that become
 * accessible via JMX.
 */
@Configuration(proxyBeanMethods = false)
@EnableCaching
class CacheConfiguration {

	@Bean
	public JCacheManagerCustomizer petclinicCacheConfigurationCustomizer() {
		return cm -> cm.createCache("vets", cacheConfiguration());
	}

	/**
	 * Create a simple configuration that enable statistics via the JCache programmatic
	 * configuration API.
	 * <p>
	 * Within the configuration object that is provided by the JCache API standard, there
	 * is only a very limited set of configuration options. The really relevant
	 * configuration options (like the size limit) must be set via a configuration
	 * mechanism that is provided by the selected JCache implementation.
	 */
	private javax.cache.configuration.Configuration<Object, Object> cacheConfiguration() {
		return new MutableConfiguration<>().setStatisticsEnabled(true);
	}

}


================================================
FILE: src/main/java/org/springframework/samples/petclinic/system/CrashController.java
================================================
/*
 * Copyright 2012-2025 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.samples.petclinic.system;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

/**
 * Controller used to showcase what happens when an exception is thrown
 *
 * @author Michael Isvy
 * <p/>
 * Also see how a view that resolves to "error" has been added ("error.html").
 */
@Controller
class CrashController {

	@GetMapping("/oups")
	public String triggerException() {
		throw new RuntimeException(
				"Expected: controller used to showcase what " + "happens when an exception is thrown");
	}

}


================================================
FILE: src/main/java/org/springframework/samples/petclinic/system/WebConfiguration.java
================================================
package org.springframework.samples.petclinic.system;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;

import java.util.Locale;

/**
 * Configures internationalization (i18n) support for the application.
 *
 * <p>
 * Handles loading language-specific messages, tracking the user's language, and allowing
 * language changes via the URL parameter (e.g., <code>?lang=de</code>).
 * </p>
 *
 * @author Anuj Ashok Potdar
 */
@Configuration
@SuppressWarnings("unused")
public class WebConfiguration implements WebMvcConfigurer {

	/**
	 * Uses session storage to remember the user’s language setting across requests.
	 * Defaults to English if nothing is specified.
	 * @return session-based {@link LocaleResolver}
	 */
	@Bean
	public LocaleResolver localeResolver() {
		SessionLocaleResolver resolver = new SessionLocaleResolver();
		resolver.setDefaultLocale(Locale.ENGLISH);
		return resolver;
	}

	/**
	 * Allows the app to switch languages using a URL parameter like
	 * <code>?lang=es</code>.
	 * @return a {@link LocaleChangeInterceptor} that handles the change
	 */
	@Bean
	public LocaleChangeInterceptor localeChangeInterceptor() {
		LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor();
		interceptor.setParamName("lang");
		return interceptor;
	}

	/**
	 * Registers the locale change interceptor so it can run on each request.
	 * @param registry where interceptors are added
	 */
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		registry.addInterceptor(localeChangeInterceptor());
	}

}


================================================
FILE: src/main/java/org/springframework/samples/petclinic/system/WelcomeController.java
================================================
/*
 * Copyright 2012-2025 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.samples.petclinic.system;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
class WelcomeController {

	@GetMapping("/")
	public String welcome() {
		return "welcome";
	}

}


================================================
FILE: src/main/java/org/springframework/samples/petclinic/system/package-info.java
================================================
/*
 * Copyright 2012-2025 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.samples.petclinic.system;


================================================
FILE: src/main/java/org/springframework/samples/petclinic/vet/Specialty.java
================================================
/*
 * Copyright 2012-2025 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.samples.petclinic.vet;

import org.springframework.samples.petclinic.model.NamedEntity;

import jakarta.persistence.Entity;
import jakarta.persistence.Table;

/**
 * Models a {@link Vet Vet's} specialty (for example, dentistry).
 *
 * @author Juergen Hoeller
 */
@Entity
@Table(name = "specialties")
public class Specialty extends NamedEntity {

}


================================================
FILE: src/main/java/org/springframework/samples/petclinic/vet/Vet.java
================================================
/*
 * Copyright 2012-2025 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.samples.petclinic.vet;

import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import org.springframework.samples.petclinic.model.NamedEntity;
import org.springframework.samples.petclinic.model.Person;

import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.JoinTable;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.Table;
import jakarta.xml.bind.annotation.XmlElement;

/**
 * Simple JavaBean domain object representing a veterinarian.
 *
 * @author Ken Krebs
 * @author Juergen Hoeller
 * @author Sam Brannen
 * @author Arjen Poutsma
 */
@Entity
@Table(name = "vets")
public class Vet extends Person {

	@ManyToMany(fetch = FetchType.EAGER)
	@JoinTable(name = "vet_specialties", joinColumns = @JoinColumn(name = "vet_id"),
			inverseJoinColumns = @JoinColumn(name = "specialty_id"))
	private Set<Specialty> specialties;

	protected Set<Specialty> getSpecialtiesInternal() {
		if (this.specialties == null) {
			this.specialties = new HashSet<>();
		}
		return this.specialties;
	}

	@XmlElement
	public List<Specialty> getSpecialties() {
		return getSpecialtiesInternal().stream()
			.sorted(Comparator.comparing(NamedEntity::getName))
			.collect(Collectors.toList());
	}

	public int getNrOfSpecialties() {
		return getSpecialtiesInternal().size();
	}

	public void addSpecialty(Specialty specialty) {
		getSpecialtiesInternal().add(specialty);
	}

}


================================================
FILE: src/main/java/org/springframework/samples/petclinic/vet/VetController.java
================================================
/*
 * Copyright 2012-2025 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.samples.petclinic.vet;

import java.util.List;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * @author Juergen Hoeller
 * @author Mark Fisher
 * @author Ken Krebs
 * @author Arjen Poutsma
 */
@Controller
class VetController {

	private final VetRepository vetRepository;

	public VetController(VetRepository vetRepository) {
		this.vetRepository = vetRepository;
	}

	@GetMapping("/vets.html")
	public String showVetList(@RequestParam(defaultValue = "1") int page, Model model) {
		// Here we are returning an object of type 'Vets' rather than a collection of Vet
		// objects so it is simpler for Object-Xml mapping
		Vets vets = new Vets();
		Page<Vet> paginated = findPaginated(page);
		vets.getVetList().addAll(paginated.toList());
		return addPaginationModel(page, paginated, model);
	}

	private String addPaginationModel(int page, Page<Vet> paginated, Model model) {
		List<Vet> listVets = paginated.getContent();
		model.addAttribute("currentPage", page);
		model.addAttribute("totalPages", paginated.getTotalPages());
		model.addAttribute("totalItems", paginated.getTotalElements());
		model.addAttribute("listVets", listVets);
		return "vets/vetList";
	}

	private Page<Vet> findPaginated(int page) {
		int pageSize = 5;
		Pageable pageable = PageRequest.of(page - 1, pageSize);
		return vetRepository.findAll(pageable);
	}

	@GetMapping({ "/vets" })
	public @ResponseBody Vets showResourcesVetList() {
		// Here we are returning an object of type 'Vets' rather than a collection of Vet
		// objects so it is simpler for JSon/Object mapping
		Vets vets = new Vets();
		vets.getVetList().addAll(this.vetRepository.findAll());
		return vets;
	}

}


================================================
FILE: src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java
================================================
/*
 * Copyright 2012-2025 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.samples.petclinic.vet;

import org.springframework.cache.annotation.Cacheable;
import org.springframework.dao.DataAccessException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.Repository;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collection;

/**
 * Repository class for <code>Vet</code> domain objects All method names are compliant
 * with Spring Data naming conventions so this interface can easily be extended for Spring
 * Data. See:
 * https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods.query-creation
 *
 * @author Ken Krebs
 * @author Juergen Hoeller
 * @author Sam Brannen
 * @author Michael Isvy
 */
public interface VetRepository extends Repository<Vet, Integer> {

	/**
	 * Retrieve all <code>Vet</code>s from the data store.
	 * @return a <code>Collection</code> of <code>Vet</code>s
	 */
	@Transactional(readOnly = true)
	@Cacheable("vets")
	Collection<Vet> findAll() throws DataAccessException;

	/**
	 * Retrieve all <code>Vet</code>s from data store in Pages
	 * @param pageable
	 * @return
	 * @throws DataAccessException
	 */
	@Transactional(readOnly = true)
	@Cacheable("vets")
	Page<Vet> findAll(Pageable pageable) throws DataAccessException;

}


================================================
FILE: src/main/java/org/springframework/samples/petclinic/vet/Vets.java
================================================
/*
 * Copyright 2012-2025 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.samples.petclinic.vet;

import java.util.ArrayList;
import java.util.List;

import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlRootElement;

/**
 * Simple domain object representing a list of veterinarians. Mostly here to be used for
 * the 'vets' {@link org.springframework.web.servlet.view.xml.MarshallingView}.
 *
 * @author Arjen Poutsma
 */
@XmlRootElement
public class Vets {

	private List<Vet> vets;

	@XmlElement
	public List<Vet> getVetList() {
		if (vets == null) {
			vets = new ArrayList<>();
		}
		return vets;
	}

}


================================================
FILE: src/main/java/org/springframework/samples/petclinic/vet/package-info.java
================================================
/*
 * Copyright 2012-2025 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.samples.petclinic.vet;


================================================
FILE: src/main/resources/application-mysql.properties
================================================
# database init, supports mysql too
database=mysql
spring.datasource.url=${MYSQL_URL:jdbc:mysql://localhost/petclinic}
spring.datasource.username=${MYSQL_USER:petclinic}
spring.datasource.password=${MYSQL_PASS:petclinic}
# SQL is written to be idempotent so this is safe
spring.sql.init.mode=always


================================================
FILE: src/main/resources/application-postgres.properties
================================================
# database init, supports postgres too
database=postgres
spring.datasource.url=${POSTGRES_URL:jdbc:postgresql://localhost/petclinic}
spring.datasource.username=${POSTGRES_USER:petclinic}
spring.datasource.password=${POSTGRES_PASS:petclinic}
# SQL is written to be idempotent so this is safe
spring.sql.init.mode=always


================================================
FILE: src/main/resources/application.properties
================================================
# database init, supports mysql too
database=h2
spring.sql.init.schema-locations=classpath*:db/${database}/schema.sql
spring.sql.init.data-locations=classpath*:db/${database}/data.sql

# Web
spring.thymeleaf.mode=HTML

# JPA
spring.jpa.hibernate.ddl-auto=none
spring.jpa.open-in-view=false
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategySnakeCaseImpl
spring.jpa.properties.hibernate.default_batch_fetch_size=16

# Internationalization
spring.messages.basename=messages/messages

# Actuator
management.endpoints.web.exposure.include=*

# Logging
logging.level.org.springframework=INFO
# logging.level.org.springframework.web=DEBUG
# logging.level.org.springframework.context.annotation=TRACE

# Maximum time static resources should be cached
spring.web.resources.cache.cachecontrol.max-age=12h


================================================
FILE: src/main/resources/banner.txt
================================================


              |\      _,,,--,,_
             /,`.-'`'   ._  \-;;,_
  _______ __|,4-  ) )_   .;.(__`'-'__     ___ __    _ ___ _______
 |       | '---''(_/._)-'(_\_)   |   |   |   |  |  | |   |       |
 |    _  |    ___|_     _|       |   |   |   |   |_| |   |       | __ _ _
 |   |_| |   |___  |   | |       |   |   |   |       |   |       | \ \ \ \
 |    ___|    ___| |   | |      _|   |___|   |  _    |   |      _|  \ \ \ \
 |   |   |   |___  |   | |     |_|       |   | | |   |   |     |_    ) ) ) )
 |___|   |_______| |___| |_______|_______|___|_|  |__|___|_______|  / / / /
 ==================================================================/_/_/_/

:: Built with Spring Boot :: ${spring-boot.version}



================================================
FILE: src/main/resources/db/h2/data.sql
================================================
INSERT INTO vets VALUES (default, 'James', 'Carter');
INSERT INTO vets VALUES (default, 'Helen', 'Leary');
INSERT INTO vets VALUES (default, 'Linda', 'Douglas');
INSERT INTO vets VALUES (default, 'Rafael', 'Ortega');
INSERT INTO vets VALUES (default, 'Henry', 'Stevens');
INSERT INTO vets VALUES (default, 'Sharon', 'Jenkins');

INSERT INTO specialties VALUES (default, 'radiology');
INSERT INTO specialties VALUES (default, 'surgery');
INSERT INTO specialties VALUES (default, 'dentistry');

INSERT INTO vet_specialties VALUES (2, 1);
INSERT INTO vet_specialties VALUES (3, 2);
INSERT INTO vet_specialties VALUES (3, 3);
INSERT INTO vet_specialties VALUES (4, 2);
INSERT INTO vet_specialties VALUES (5, 1);

INSERT INTO types VALUES (default, 'cat');
INSERT INTO types VALUES (default, 'dog');
INSERT INTO types VALUES (default, 'lizard');
INSERT INTO types VALUES (default, 'snake');
INSERT INTO types VALUES (default, 'bird');
INSERT INTO types VALUES (default, 'hamster');

INSERT INTO owners VALUES (default, 'George', 'Franklin', '110 W. Liberty St.', 'Madison', '6085551023');
INSERT INTO owners VALUES (default, 'Betty', 'Davis', '638 Cardinal Ave.', 'Sun Prairie', '6085551749');
INSERT INTO owners VALUES (default, 'Eduardo', 'Rodriquez', '2693 Commerce St.', 'McFarland', '6085558763');
INSERT INTO owners VALUES (default, 'Harold', 'Davis', '563 Friendly St.', 'Windsor', '6085553198');
INSERT INTO owners VALUES (default, 'Peter', 'McTavish', '2387 S. Fair Way', 'Madison', '6085552765');
INSERT INTO owners VALUES (default, 'Jean', 'Coleman', '105 N. Lake St.', 'Monona', '6085552654');
INSERT INTO owners VALUES (default, 'Jeff', 'Black', '1450 Oak Blvd.', 'Monona', '6085555387');
INSERT INTO owners VALUES (default, 'Maria', 'Escobito', '345 Maple St.', 'Madison', '6085557683');
INSERT INTO owners VALUES (default, 'David', 'Schroeder', '2749 Blackhawk Trail', 'Madison', '6085559435');
INSERT INTO owners VALUES (default, 'Carlos', 'Estaban', '2335 Independence La.', 'Waunakee', '6085555487');

INSERT INTO pets VALUES (default, 'Leo', '2010-09-07', 1, 1);
INSERT INTO pets VALUES (default, 'Basil', '2012-08-06', 6, 2);
INSERT INTO pets VALUES (default, 'Rosy', '2011-04-17', 2, 3);
INSERT INTO pets VALUES (default, 'Jewel', '2010-03-07', 2, 3);
INSERT INTO pets VALUES (default, 'Iggy', '2010-11-30', 3, 4);
INSERT INTO pets VALUES (default, 'George', '2010-01-20', 4, 5);
INSERT INTO pets VALUES (default, 'Samantha', '2012-09-04', 1, 6);
INSERT INTO pets VALUES (default, 'Max', '2012-09-04', 1, 6);
INSERT INTO pets VALUES (default, 'Lucky', '2011-08-06', 5, 7);
INSERT INTO pets VALUES (default, 'Mulligan', '2007-02-24', 2, 8);
INSERT INTO pets VALUES (default, 'Freddy', '2010-03-09', 5, 9);
INSERT INTO pets VALUES (default, 'Lucky', '2010-06-24', 2, 10);
INSERT INTO pets VALUES (default, 'Sly', '2012-06-08', 1, 10);

INSERT INTO visits VALUES (default, 7, '2013-01-01', 'rabies shot');
INSERT INTO visits VALUES (default, 8, '2013-01-02', 'rabies shot');
INSERT INTO visits VALUES (default, 8, '2013-01-03', 'neutered');
INSERT INTO visits VALUES (default, 7, '2013-01-04', 'spayed');


================================================
FILE: src/main/resources/db/h2/schema.sql
================================================
DROP TABLE vet_specialties IF EXISTS;
DROP TABLE vets IF EXISTS;
DROP TABLE specialties IF EXISTS;
DROP TABLE visits IF EXISTS;
DROP TABLE pets IF EXISTS;
DROP TABLE types IF EXISTS;
DROP TABLE owners IF EXISTS;


CREATE TABLE vets (
  id         INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  first_name VARCHAR(30),
  last_name  VARCHAR(30)
);
CREATE INDEX vets_last_name ON vets (last_name);

CREATE TABLE specialties (
  id   INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  name VARCHAR(80)
);
CREATE INDEX specialties_name ON specialties (name);

CREATE TABLE vet_specialties (
  vet_id       INTEGER NOT NULL,
  specialty_id INTEGER NOT NULL
);
ALTER TABLE vet_specialties ADD CONSTRAINT fk_vet_specialties_vets FOREIGN KEY (vet_id) REFERENCES vets (id);
ALTER TABLE vet_specialties ADD CONSTRAINT fk_vet_specialties_specialties FOREIGN KEY (specialty_id) REFERENCES specialties (id);

CREATE TABLE types (
  id   INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  name VARCHAR(80)
);
CREATE INDEX types_name ON types (name);

CREATE TABLE owners (
  id         INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  first_name VARCHAR(30),
  last_name  VARCHAR_IGNORECASE(30),
  address    VARCHAR(255),
  city       VARCHAR(80),
  telephone  VARCHAR(20)
);
CREATE INDEX owners_last_name ON owners (last_name);

CREATE TABLE pets (
  id         INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  name       VARCHAR(30),
  birth_date DATE,
  type_id    INTEGER NOT NULL,
  owner_id   INTEGER
);
ALTER TABLE pets ADD CONSTRAINT fk_pets_owners FOREIGN KEY (owner_id) REFERENCES owners (id);
ALTER TABLE pets ADD CONSTRAINT fk_pets_types FOREIGN KEY (type_id) REFERENCES types (id);
CREATE INDEX pets_name ON pets (name);

CREATE TABLE visits (
  id          INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  pet_id      INTEGER,
  visit_date  DATE,
  description VARCHAR(255)
);
ALTER TABLE visits ADD CONSTRAINT fk_visits_pets FOREIGN KEY (pet_id) REFERENCES pets (id);
CREATE INDEX visits_pet_id ON visits (pet_id);


================================================
FILE: src/main/resources/db/mysql/data.sql
================================================
INSERT IGNORE INTO vets VALUES (1, 'James', 'Carter');
INSERT IGNORE INTO vets VALUES (2, 'Helen', 'Leary');
INSERT IGNORE INTO vets VALUES (3, 'Linda', 'Douglas');
INSERT IGNORE INTO vets VALUES (4, 'Rafael', 'Ortega');
INSERT IGNORE INTO vets VALUES (5, 'Henry', 'Stevens');
INSERT IGNORE INTO vets VALUES (6, 'Sharon', 'Jenkins');

INSERT IGNORE INTO specialties VALUES (1, 'radiology');
INSERT IGNORE INTO specialties VALUES (2, 'surgery');
INSERT IGNORE INTO specialties VALUES (3, 'dentistry');

INSERT IGNORE INTO vet_specialties VALUES (2, 1);
INSERT IGNORE INTO vet_specialties VALUES (3, 2);
INSERT IGNORE INTO vet_specialties VALUES (3, 3);
INSERT IGNORE INTO vet_specialties VALUES (4, 2);
INSERT IGNORE INTO vet_specialties VALUES (5, 1);

INSERT IGNORE INTO types VALUES (1, 'cat');
INSERT IGNORE INTO types VALUES (2, 'dog');
INSERT IGNORE INTO types VALUES (3, 'lizard');
INSERT IGNORE INTO types VALUES (4, 'snake');
INSERT IGNORE INTO types VALUES (5, 'bird');
INSERT IGNORE INTO types VALUES (6, 'hamster');

INSERT IGNORE INTO owners VALUES (1, 'George', 'Franklin', '110 W. Liberty St.', 'Madison', '6085551023');
INSERT IGNORE INTO owners VALUES (2, 'Betty', 'Davis', '638 Cardinal Ave.', 'Sun Prairie', '6085551749');
INSERT IGNORE INTO owners VALUES (3, 'Eduardo', 'Rodriquez', '2693 Commerce St.', 'McFarland', '6085558763');
INSERT IGNORE INTO owners VALUES (4, 'Harold', 'Davis', '563 Friendly St.', 'Windsor', '6085553198');
INSERT IGNORE INTO owners VALUES (5, 'Peter', 'McTavish', '2387 S. Fair Way', 'Madison', '6085552765');
INSERT IGNORE INTO owners VALUES (6, 'Jean', 'Coleman', '105 N. Lake St.', 'Monona', '6085552654');
INSERT IGNORE INTO owners VALUES (7, 'Jeff', 'Black', '1450 Oak Blvd.', 'Monona', '6085555387');
INSERT IGNORE INTO owners VALUES (8, 'Maria', 'Escobito', '345 Maple St.', 'Madison', '6085557683');
INSERT IGNORE INTO owners VALUES (9, 'David', 'Schroeder', '2749 Blackhawk Trail', 'Madison', '6085559435');
INSERT IGNORE INTO owners VALUES (10, 'Carlos', 'Estaban', '2335 Independence La.', 'Waunakee', '6085555487');

INSERT IGNORE INTO pets VALUES (1, 'Leo', '2000-09-07', 1, 1);
INSERT IGNORE INTO pets VALUES (2, 'Basil', '2002-08-06', 6, 2);
INSERT IGNORE INTO pets VALUES (3, 'Rosy', '2001-04-17', 2, 3);
INSERT IGNORE INTO pets VALUES (4, 'Jewel', '2000-03-07', 2, 3);
INSERT IGNORE INTO pets VALUES (5, 'Iggy', '2000-11-30', 3, 4);
INSERT IGNORE INTO pets VALUES (6, 'George', '2000-01-20', 4, 5);
INSERT IGNORE INTO pets VALUES (7, 'Samantha', '1995-09-04', 1, 6);
INSERT IGNORE INTO pets VALUES (8, 'Max', '1995-09-04', 1, 6);
INSERT IGNORE INTO pets VALUES (9, 'Lucky', '1999-08-06', 5, 7);
INSERT IGNORE INTO pets VALUES (10, 'Mulligan', '1997-02-24', 2, 8);
INSERT IGNORE INTO pets VALUES (11, 'Freddy', '2000-03-09', 5, 9);
INSERT IGNORE INTO pets VALUES (12, 'Lucky', '2000-06-24', 2, 10);
INSERT IGNORE INTO pets VALUES (13, 'Sly', '2002-06-08', 1, 10);

INSERT IGNORE INTO visits VALUES (1, 7, '2010-03-04', 'rabies shot');
INSERT IGNORE INTO visits VALUES (2, 8, '2011-03-04', 'rabies shot');
INSERT IGNORE INTO visits VALUES (3, 8, '2009-06-04', 'neutered');
INSERT IGNORE INTO visits VALUES (4, 7, '2008-09-04', 'spayed');


================================================
FILE: src/main/resources/db/mysql/petclinic_db_setup_mysql.txt
================================================
================================================================================
===        Spring PetClinic sample application - MySQL Configuration         ===
================================================================================

@author Sam Brannen
@author Costin Leau
@author Dave Syer

--------------------------------------------------------------------------------

1) Download and install the MySQL database (e.g., MySQL Community Server 5.1.x),
   which can be found here: https://dev.mysql.com/downloads/. Or run the
   "docker-compose.yml" from the root of the project (if you have docker installed
   locally):

        $ docker-compose up
        ...
        mysql_1_eedb4818d817 | MySQL init process done. Ready for start up.
        ...

2) (Once only) create the PetClinic database and user by executing the "db/mysql/user.sql"
   scripts. You can connect to the database running in the docker container using 
   `mysql -u root -h localhost --protocol tcp`, but you don't need to run the script there
   because the petclinic user is already set up if you use the provided `docker-compose.yml`.

3) Run the app with `spring.profiles.active=mysql` (e.g. as a System property via the command
   line, but any way that sets that property in a Spring Boot app should work). For example use
   
   mvn spring-boot:run -Dspring-boot.run.profiles=mysql

   To activate the profile on the command line.

N.B. the "petclinic" database has to exist for the app to work with the JDBC URL value
as it is configured by default. This condition is taken care of automatically by the 
docker-compose configuration provided, or by the `user.sql` script if you run that as
root.


================================================
FILE: src/main/resources/db/mysql/schema.sql
================================================
CREATE TABLE IF NOT EXISTS vets (
  id INT(4) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
  first_name VARCHAR(30),
  last_name VARCHAR(30),
  INDEX(last_name)
) engine=InnoDB;

CREATE TABLE IF NOT EXISTS specialties (
  id INT(4) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(80),
  INDEX(name)
) engine=InnoDB;

CREATE TABLE IF NOT EXISTS vet_specialties (
  vet_id INT(4) UNSIGNED NOT NULL,
  specialty_id INT(4) UNSIGNED NOT NULL,
  FOREIGN KEY (vet_id) REFERENCES vets(id),
  FOREIGN KEY (specialty_id) REFERENCES specialties(id),
  UNIQUE (vet_id,specialty_id)
) engine=InnoDB;

CREATE TABLE IF NOT EXISTS types (
  id INT(4) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(80),
  INDEX(name)
) engine=InnoDB;

CREATE TABLE IF NOT EXISTS owners (
  id INT(4) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
  first_name VARCHAR(30),
  last_name VARCHAR(30),
  address VARCHAR(255),
  city VARCHAR(80),
  telephone VARCHAR(20),
  INDEX(last_name)
) engine=InnoDB;

CREATE TABLE IF NOT EXISTS pets (
  id INT(4) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(30),
  birth_date DATE,
  type_id INT(4) UNSIGNED NOT NULL,
  owner_id INT(4) UNSIGNED,
  INDEX(name),
  FOREIGN KEY (owner_id) REFERENCES owners(id),
  FOREIGN KEY (type_id) REFERENCES types(id)
) engine=InnoDB;

CREATE TABLE IF NOT EXISTS visits (
  id INT(4) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
  pet_id INT(4) UNSIGNED,
  visit_date DATE,
  description VARCHAR(255),
  FOREIGN KEY (pet_id) REFERENCES pets(id)
) engine=InnoDB;


================================================
FILE: src/main/resources/db/mysql/user.sql
================================================
CREATE DATABASE IF NOT EXISTS petclinic;

ALTER DATABASE petclinic
  DEFAULT CHARACTER SET utf8
  DEFAULT COLLATE utf8_general_ci;

CREATE USER IF NOT EXISTS 'petclinic'@'%' IDENTIFIED BY 'petclinic';

GRANT ALL PRIVILEGES ON petclinic.* TO 'petclinic'@'%';

FLUSH PRIVILEGES;


================================================
FILE: src/main/resources/db/postgres/data.sql
================================================
INSERT INTO vets (first_name, last_name) SELECT 'James', 'Carter' WHERE NOT EXISTS (SELECT * FROM vets WHERE id=1);
INSERT INTO vets (first_name, last_name) SELECT 'Helen', 'Leary' WHERE NOT EXISTS (SELECT * FROM vets WHERE id=2);
INSERT INTO vets (first_name, last_name) SELECT 'Linda', 'Douglas' WHERE NOT EXISTS (SELECT * FROM vets WHERE id=3);
INSERT INTO vets (first_name, last_name) SELECT 'Rafael', 'Ortega' WHERE NOT EXISTS (SELECT * FROM vets WHERE id=4);
INSERT INTO vets (first_name, last_name) SELECT 'Henry', 'Stevens' WHERE NOT EXISTS (SELECT * FROM vets WHERE id=5);
INSERT INTO vets (first_name, last_name) SELECT 'Sharon', 'Jenkins' WHERE NOT EXISTS (SELECT * FROM vets WHERE id=6);

INSERT INTO specialties (name) SELECT 'radiology' WHERE NOT EXISTS (SELECT * FROM specialties WHERE name='radiology');
INSERT INTO specialties (name) SELECT 'surgery' WHERE NOT EXISTS (SELECT * FROM specialties WHERE name='surgery'); 
INSERT INTO specialties (name) SELECT 'dentistry' WHERE NOT EXISTS (SELECT * FROM specialties WHERE name='dentistry');

INSERT INTO vet_specialties VALUES (2, 1) ON CONFLICT (vet_id, specialty_id) DO NOTHING;
INSERT INTO vet_specialties VALUES (3, 2) ON CONFLICT (vet_id, specialty_id) DO NOTHING;
INSERT INTO vet_specialties VALUES (3, 3) ON CONFLICT (vet_id, specialty_id) DO NOTHING;
INSERT INTO vet_specialties VALUES (4, 2) ON CONFLICT (vet_id, specialty_id) DO NOTHING;
INSERT INTO vet_specialties VALUES (5, 1) ON CONFLICT (vet_id, specialty_id) DO NOTHING;

INSERT INTO types (name) SELECT 'cat' WHERE NOT EXISTS (SELECT * FROM types WHERE name='cat');
INSERT INTO types (name) SELECT 'dog' WHERE NOT EXISTS (SELECT * FROM types WHERE name='dog');
INSERT INTO types (name) SELECT 'lizard' WHERE NOT EXISTS (SELECT * FROM types WHERE name='lizard');
INSERT INTO types (name) SELECT 'snake' WHERE NOT EXISTS (SELECT * FROM types WHERE name='snake');
INSERT INTO types (name) SELECT 'bird' WHERE NOT EXISTS (SELECT * FROM types WHERE name='bird');
INSERT INTO types (name) SELECT 'hamster' WHERE NOT EXISTS (SELECT * FROM types WHERE name='hamster');

INSERT INTO owners (first_name, last_name, address, city, telephone) SELECT 'George', 'Franklin', '110 W. Liberty St.', 'Madison', '6085551023' WHERE NOT EXISTS (SELECT * FROM owners WHERE id=1);
INSERT INTO owners (first_name, last_name, address, city, telephone) SELECT 'Betty', 'Davis', '638 Cardinal Ave.', 'Sun Prairie', '6085551749' WHERE NOT EXISTS (SELECT * FROM owners WHERE id=2);
INSERT INTO owners (first_name, last_name, address, city, telephone) SELECT 'Eduardo', 'Rodriquez', '2693 Commerce St.', 'McFarland', '6085558763' WHERE NOT EXISTS (SELECT * FROM owners WHERE id=3);
INSERT INTO owners (first_name, last_name, address, city, telephone) SELECT 'Harold', 'Davis', '563 Friendly St.', 'Windsor', '6085553198' WHERE NOT EXISTS (SELECT * FROM owners WHERE id=4);
INSERT INTO owners (first_name, last_name, address, city, telephone) SELECT 'Peter', 'McTavish', '2387 S. Fair Way', 'Madison', '6085552765' WHERE NOT EXISTS (SELECT * FROM owners WHERE id=5);
INSERT INTO owners (first_name, last_name, address, city, telephone) SELECT 'Jean', 'Coleman', '105 N. Lake St.', 'Monona', '6085552654' WHERE NOT EXISTS (SELECT * FROM owners WHERE id=6);
INSERT INTO owners (first_name, last_name, address, city, telephone) SELECT 'Jeff', 'Black', '1450 Oak Blvd.', 'Monona', '6085555387' WHERE NOT EXISTS (SELECT * FROM owners WHERE id=7);
INSERT INTO owners (first_name, last_name, address, city, telephone) SELECT 'Maria', 'Escobito', '345 Maple St.', 'Madison', '6085557683' WHERE NOT EXISTS (SELECT * FROM owners WHERE id=8);
INSERT INTO owners (first_name, last_name, address, city, telephone) SELECT 'David', 'Schroeder', '2749 Blackhawk Trail', 'Madison', '6085559435' WHERE NOT EXISTS (SELECT * FROM owners WHERE id=9);
INSERT INTO owners (first_name, last_name, address, city, telephone) SELECT 'Carlos', 'Estaban', '2335 Independence La.', 'Waunakee', '6085555487' WHERE NOT EXISTS (SELECT * FROM owners WHERE id=10);

INSERT INTO pets (name, birth_date, type_id, owner_id) SELECT 'Leo', '2000-09-07', 1, 1 WHERE NOT EXISTS (SELECT * FROM pets WHERE id=1);
INSERT INTO pets (name, birth_date, type_id, owner_id) SELECT 'Basil', '2002-08-06', 6, 2 WHERE NOT EXISTS (SELECT * FROM pets WHERE id=2);
INSERT INTO pets (name, birth_date, type_id, owner_id) SELECT 'Rosy', '2001-04-17', 2, 3 WHERE NOT EXISTS (SELECT * FROM pets WHERE id=3);
INSERT INTO pets (name, birth_date, type_id, owner_id) SELECT 'Jewel', '2000-03-07', 2, 3 WHERE NOT EXISTS (SELECT * FROM pets WHERE id=4);
INSERT INTO pets (name, birth_date, type_id, owner_id) SELECT 'Iggy', '2000-11-30', 3, 4 WHERE NOT EXISTS (SELECT * FROM pets WHERE id=5);
INSERT INTO pets (name, birth_date, type_id, owner_id) SELECT 'George', '2000-01-20', 4, 5 WHERE NOT EXISTS (SELECT * FROM pets WHERE id=6);
INSERT INTO pets (name, birth_date, type_id, owner_id) SELECT 'Samantha', '1995-09-04', 1, 6 WHERE NOT EXISTS (SELECT * FROM pets WHERE id=7);
INSERT INTO pets (name, birth_date, type_id, owner_id) SELECT 'Max', '1995-09-04', 1, 6 WHERE NOT EXISTS (SELECT * FROM pets WHERE id=8);
INSERT INTO pets (name, birth_date, type_id, owner_id) SELECT 'Lucky', '1999-08-06', 5, 7 WHERE NOT EXISTS (SELECT * FROM pets WHERE id=9);
INSERT INTO pets (name, birth_date, type_id, owner_id) SELECT 'Mulligan', '1997-02-24', 2, 8 WHERE NOT EXISTS (SELECT * FROM pets WHERE id=10);
INSERT INTO pets (name, birth_date, type_id, owner_id) SELECT 'Freddy', '2000-03-09', 5, 9 WHERE NOT EXISTS (SELECT * FROM pets WHERE id=11);
INSERT INTO pets (name, birth_date, type_id, owner_id) SELECT 'Lucky', '2000-06-24', 2, 10 WHERE NOT EXISTS (SELECT * FROM pets WHERE id=12);
INSERT INTO pets (name, birth_date, type_id, owner_id) SELECT 'Sly', '2002-06-08', 1, 10 WHERE NOT EXISTS (SELECT * FROM pets WHERE id=13);

INSERT INTO visits (pet_id, visit_date, description) SELECT 7, '2010-03-04', 'rabies shot' WHERE NOT EXISTS (SELECT * FROM visits WHERE id=1);
INSERT INTO visits (pet_id, visit_date, description) SELECT 8, '2011-03-04', 'rabies shot' WHERE NOT EXISTS (SELECT * FROM visits WHERE id=2);
INSERT INTO visits (pet_id, visit_date, description) SELECT 8, '2009-06-04', 'neutered' WHERE NOT EXISTS (SELECT * FROM visits WHERE id=3);
INSERT INTO visits (pet_id, visit_date, description) SELECT 7, '2008-09-04', 'spayed' WHERE NOT EXISTS (SELECT * FROM visits WHERE id=4);


================================================
FILE: src/main/resources/db/postgres/petclinic_db_setup_postgres.txt
================================================
===============================================================================
===     Spring PetClinic sample application - PostgreSQL Configuration     ===
===============================================================================

--------------------------------------------------------------------------------

1) Run the "docker-compose.yml" from the root of the project:

        $ docker-compose up
        ...
        spring-petclinic-postgres-1  | The files belonging to this database system will be owned by user "postgres".
        ...

2) Run the app with `spring.profiles.active=postgres` (e.g. as a System property via the command
   line, but any way that sets that property in a Spring Boot app should work). For example use

   mvn spring-boot:run -Dspring-boot.run.profiles=postgres

   To activate the profile on the command line.


================================================
FILE: src/main/resources/db/postgres/schema.sql
================================================
CREATE TABLE IF NOT EXISTS vets (
  id         INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  first_name TEXT,
  last_name  TEXT
);
CREATE INDEX ON vets (last_name);

CREATE TABLE IF NOT EXISTS specialties (
  id   INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  name TEXT
);
CREATE INDEX ON specialties (name);

CREATE TABLE IF NOT EXISTS vet_specialties (
  vet_id       INT NOT NULL REFERENCES vets (id),
  specialty_id INT NOT NULL REFERENCES specialties (id),
  UNIQUE (vet_id, specialty_id)
);

CREATE TABLE IF NOT EXISTS types (
  id   INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  name TEXT
);
CREATE INDEX ON types (name);

CREATE TABLE IF NOT EXISTS owners (
  id         INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  first_name TEXT,
  last_name  TEXT,
  address    TEXT,
  city       TEXT,
  telephone  TEXT
);
CREATE INDEX ON owners (last_name);

CREATE TABLE IF NOT EXISTS pets (
  id         INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  name       TEXT,
  birth_date DATE,
  type_id    INT NOT NULL REFERENCES types (id),
  owner_id   INT REFERENCES owners (id)
);
CREATE INDEX ON pets (name);
CREATE INDEX ON pets (owner_id);

CREATE TABLE IF NOT EXISTS visits (
  id          INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  pet_id      INT REFERENCES pets (id),
  visit_date  DATE,
  description TEXT
);
CREATE INDEX ON visits (pet_id);


================================================
FILE: src/main/resources/messages/messages.properties
================================================
welcome=Welcome
required=is required
notFound=has not been found
duplicate=is already in use
nonNumeric=must be all numeric
duplicateFormSubmission=Duplicate form submission is not allowed
typeMismatch.date=invalid date
typeMismatch.birthDate=invalid date
owner=Owner
firstName=First Name
lastName=Last Name
address=Address
city=City
telephone=Telephone
owners=Owners
addOwner=Add Owner
findOwner=Find Owner
findOwners=Find Owners
updateOwner=Update Owner
vets=Veterinarians
name=Name
specialties=Specialties
none=none
pages=pages
first=First
next=Next
previous=Previous
last=Last
somethingHappened=Something happened...
pets=Pets
home=Home
error=Error
telephone.invalid=Telephone must be a 10-digit number
layoutTitle=PetClinic :: a Spring Framework demonstration
pet=Pet
birthDate=Birth Date
type=Type
previousVisits=Previous Visits
date=Date
description=Description
new=New 
addVisit=Add Visit
editPet=Edit Pet
ownerInformation=Owner Information
visitDate=Visit Date
editOwner=Edit Owner
addNewPet=Add New Pet
petsAndVisits=Pets and Visits
error.404=The requested page was not found.
error.500=An internal server error occurred.
error.general=An unexpected error occurred.


================================================
FILE: src/main/resources/messages/messages_de.properties
================================================
welcome=Willkommen
required=muss angegeben werden
notFound=wurde nicht gefunden
duplicate=ist bereits vergeben
nonNumeric=darf nur numerisch sein
duplicateFormSubmission=Wiederholtes Absenden des Formulars ist nicht erlaubt
typeMismatch.date=ung�ltiges Datum
typeMismatch.birthDate=ung�ltiges Datum
owner=Besitzer
firstName=Vorname
lastName=Nachname
address=Adresse
city=Stadt
telephone=Telefon
owners=Besitzer
addOwner=Besitzer hinzufügen
findOwner=Besitzer finden
findOwners=Besitzer suchen
updateOwner=Besitzer aktualisieren
vets=Tierärzte
name=Name
specialties=Fachgebiete
none=keine
pages=Seiten
first=Erste
next=Nächste
previous=Vorherige
last=Letzte
somethingHappened=Etwas ist passiert...
pets=Haustiere
home=Startseite
error=Fehler
telephone.invalid=Telefonnummer muss aus 10 Ziffern bestehen
layoutTitle=PetClinic :: eine Demonstration des Spring Frameworks
pet=Haustier
birthDate=Geburtsdatum
type=Typ
previousVisits=Frühere Besuche
date=Datum
description=Beschreibung
new=Neu
addVisit=Besuch hinzufügen
editPet=Haustier bearbeiten
ownerInformation=Besitzerinformationen
visitDate=Besuchsdatum
editOwner=Besitzer bearbeiten
addNewPet=Neues Haustier hinzufügen
petsAndVisits=Haustiere und Besuche
error.404=Die angeforderte Seite wurde nicht gefunden.
error.500=Ein interner Serverfehler ist aufgetreten.
error.general=Ein unerwarteter Fehler ist aufgetreten.


================================================
FILE: src/main/resources/messages/messages_en.properties
================================================
# This file is intentionally empty. Message look-ups will fall back to the default "messages.properties" file.

================================================
FILE: src/main/resources/messages/messages_es.properties
================================================
welcome=Bienvenido
required=Es requerido
notFound=No ha sido encontrado
duplicate=Ya se encuentra en uso
nonNumeric=Sólo debe contener numeros
duplicateFormSubmission=No se permite el envío de formularios duplicados
typeMismatch.date=Fecha invalida
typeMismatch.birthDate=Fecha invalida
owner=Propietario
firstName=Nombre
lastName=Apellido
address=Dirección
city=Ciudad
telephone=Teléfono
owners=Propietarios
addOwner=Añadir propietario
findOwner=Buscar propietario
findOwners=Buscar propietarios
updateOwner=Actualizar propietario
vets=Veterinarios
name=Nombre
specialties=Especialidades
none=ninguno
pages=páginas
first=Primero
next=Siguiente
previous=Anterior
last=Último
somethingHappened=Algo pasó...
pets=Mascotas
home=Inicio
error=Error
telephone.invalid=El número de teléfono debe tener 10 dígitos
layoutTitle=PetClinic :: una demostración de Spring Framework
pet=Mascota
birthDate=Fecha de nacimiento
type=Tipo
previousVisits=Visitas anteriores
date=Fecha
description=Descripción
new=Nuevo
addVisit=Agregar visita
editPet=Editar mascota
ownerInformation=Información del propietario
visitDate=Fecha de visita
editOwner=Editar propietario
addNewPet=Agregar nueva mascota
petsAndVisits=Mascotas y visitas
error.404=La página solicitada no fue encontrada.
error.500=Ocurrió un error interno del servidor.
error.general=Ocurrió un error inesperado.


================================================
FILE: src/main/resources/messages/messages_fa.properties
================================================
welcome=خوش آمدید
required=الزامی
notFound=یافت نشد
duplicate=قبلا استفاده شده
nonNumeric=باید عددی باشد
duplicateFormSubmission=ارسال تکراری فرم مجاز نیست
typeMismatch.date=تاریخ نامعتبر
typeMismatch.birthDate=تاریخ تولد نامعتبر
owner=مالک
firstName=نام
lastName=نام خانوادگی
address=آدرس
city=شهر
telephone=تلفن
owners=مالکان
addOwner=افزودن مالک
findOwner=یافتن مالک
findOwners=یافتن مالکان
updateOwner=ویرایش مالک
vets=دامپزشکان
name=نام
specialties=تخصص‌ها
none=هیچ‌کدام
pages=صفحات
first=اول
next=بعدی
previous=قبلی
last=آخر
somethingHappened=مشکلی پیش آمد...
pets=حیوانات خانگی
home=خانه
error=خطا
telephone.invalid=شماره تلفن باید ۱۰ رقمی باشد
layoutTitle=PetClinic :: یک نمایش از Spring Framework
pet=حیوان خانگی
birthDate=تاریخ تولد
type=نوع
previousVisits=ویزیت‌های قبلی
date=تاریخ
description=توضیحات
new=جدید
addVisit=افزودن ویزیت
editPet=ویرایش حیوان خانگی
ownerInformation=اطلاعات مالک
visitDate=تاریخ ویزیت
editOwner=ویرایش مالک
addNewPet=افزودن حیوان خانگی جدید
petsAndVisits=حیوانات و ویزیت‌ها
error.404=صفحه درخواستی پیدا نشد.
error.500=خطای داخلی سرور رخ داد.
error.general=خطای غیرمنتظره‌ای رخ داد.


================================================
FILE: src/main/resources/messages/messages_ko.properties
================================================
welcome=환영합니다
required=입력이 필요합니다
notFound=찾을 수 없습니다
duplicate=이미 존재합니다
nonNumeric=모두 숫자로 입력해야 합니다
duplicateFormSubmission=중복 제출은 허용되지 않습니다
typeMismatch.date=잘못된 날짜입니다
typeMismatch.birthDate=잘못된 날짜입니다
owner=소유자
firstName=이름
lastName=성
address=주소
city=도시
telephone=전화번호
owners=소유자 목록
addOwner=소유자 추가
findOwner=소유자 찾기
findOwners=소유자들 찾기
updateOwner=소유자 수정
vets=수의사
name=이름
specialties=전문 분야
none=없음
pages=페이지
first=첫 번째
next=다음
previous=이전
last=마지막
somethingHappened=문제가 발생했습니다...
pets=반려동물
home=홈
error=오류
telephone.invalid=전화번호는 10자리 숫자여야 합니다
layoutTitle=PetClinic :: Spring Framework 데모
pet=반려동물
birthDate=생년월일
type=종류
previousVisits=이전 방문
date=날짜
description=설명
new=새로운
addVisit=방문 추가
editPet=반려동물 수정
ownerInformation=소유자 정보
visitDate=방문 날짜
editOwner=소유자 수정
addNewPet=새 반려동물 추가
petsAndVisits=반려동물 및 방문
error.404=요청하신 페이지를 찾을 수 없습니다.
error.500=서버 내부 오류가 발생했습니다.
error.general=알 수 없는 오류가 발생했습니다.


================================================
FILE: src/main/resources/messages/messages_pt.properties
================================================
welcome=Bem-vindo
required=E necessario
notFound=Nao foi encontrado
duplicate=Ja esta em uso
nonNumeric=Deve ser tudo numerico
duplicateFormSubmission=O envio duplicado de formulario nao e permitido
typeMismatch.date=Data invalida
typeMismatch.birthDate=Data de nascimento invalida
owner=Proprietário
firstName=Primeiro Nome
lastName=Sobrenome
address=Endereço
city=Cidade
telephone=Telefone
owners=Proprietários
addOwner=Adicionar proprietário
findOwner=Encontrar proprietário
findOwners=Encontrar proprietários
updateOwner=Atualizar proprietário
vets=Veterinários
name=Nome
specialties=Especialidades
none=nenhum
pages=páginas
first=Primeiro
next=Próximo
previous=Anterior
last=Último
somethingHappened=Algo aconteceu...
pets=Animais de estimação
home=Início
error=Erro
telephone.invalid=O número de telefone deve conter 10 dígitos
layoutTitle=PetClinic :: uma demonstração do Spring Framework
pet=Animal de estimação
birthDate=Data de nascimento
type=Tipo
previousVisits=Visitas anteriores
date=Data
description=Descrição
new=Novo
addVisit=Adicionar visita
editPet=Editar animal
ownerInformation=Informações do proprietário
visitDate=Data da visita
editOwner=Editar proprietário
addNewPet=Adicionar novo animal
petsAndVisits=Animais e visitas
error.404=A página solicitada não foi encontrada.
error.500=Ocorreu um erro interno no servidor.
error.general=Ocorreu um erro inesperado.


================================================
FILE: src/main/resources/messages/messages_ru.properties
================================================
welcome=Добро пожаловать
required=необходимо
notFound=не найдено
duplicate=уже используется
nonNumeric=должно быть все числовое значение
duplicateFormSubmission=Дублирование формы не допускается
typeMismatch.date=неправильная даные
typeMismatch.birthDate=неправильная дата
owner=Владелец
firstName=Имя
lastName=Фамилия
address=Адрес
city=Город
telephone=Телефон
owners=Владельцы
addOwner=Добавить владельца
findOwner=Найти владельца
findOwners=Найти владельцев
updateOwner=Обновить владельца
vets=Ветеринары
name=Имя
specialties=Специальности
none=нет
pages=страницы
first=Первый
next=Следующий
previous=Предыдущий
last=Последний
somethingHappened=Что-то пошло не так...
pets=Питомцы
home=Главная
error=Ошибка
telephone.invalid=Телефон должен содержать 10 цифр
layoutTitle=PetClinic :: демонстрация Spring Framework
pet=Питомец
birthDate=Дата рождения
type=Тип
previousVisits=Предыдущие визиты
date=Дата
description=Описание
new=Новый
addVisit=Добавить визит
editPet=Редактировать питомца
ownerInformation=Информация о владельце
visitDate=Дата визита
editOwner=Редактировать владельца
addNewPet=Добавить нового питомца
petsAndVisits=Питомцы и визиты
error.404=Запрашиваемая страница не найдена.
error.500=Произошла внутренняя ошибка сервера.
error.general=Произошла непредвиденная ошибка.


================================================
FILE: src/main/resources/messages/messages_tr.properties
================================================
welcome=hoş geldiniz
required=gerekli
notFound=bulunamadı
duplicate=zaten kullanılıyor
nonNumeric=sadece sayısal olmalıdır
duplicateFormSubmission=Formun tekrar gönderilmesine izin verilmez
typeMismatch.date=geçersiz tarih
typeMismatch.birthDate=geçersiz tarih
owner=Sahip
firstName=Ad
lastName=Soyad
address=Adres
city=Şehir
telephone=Telefon
owners=Sahipler
addOwner=Sahip Ekle
findOwner=Sahip Bul
findOwners=Sahipleri Bul
updateOwner=Sahip Güncelle
vets=Veterinerler
name=İsim
specialties=Uzmanlıklar
none=yok
pages=sayfalar
first=İlk
next=Sonraki
previous=Önceki
last=Son
somethingHappened=Bir şey oldu...
pets=Evcil Hayvanlar
home=Ana Sayfa
error=Hata
telephone.invalid=Telefon numarası 10 basamaklı olmalıdır
layoutTitle=PetClinic :: bir Spring Framework demosu
pet=Evcil Hayvan
birthDate=Doğum Tarihi
type=Tür
previousVisits=Önceki Ziyaretler
date=Tarih
description=Açıklama
new=Yeni
addVisit=Ziyaret Ekle
editPet=Evcil Hayvanı Düzenle
ownerInformation=Sahip Bilgileri
visitDate=Ziyaret Tarihi
editOwner=Sahibi Düzenle
addNewPet=Yeni Evcil Hayvan Ekle
petsAndVisits=Evcil Hayvanlar ve Ziyaretler
error.404=İstenen sayfa bulunamadı.
error.500=Sunucuda dahili bir hata oluştu.
error.general=Beklenmeyen bir hata oluştu.


================================================
FILE: src/main/resources/static/resources/css/petclinic.css
================================================
/*
 * Copyright 2016 the original author or authors.
 *
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*!
   * Bootstrap  v5.3.8 (https://getbootstrap.com/)
   * Copyright 2011-2025 The Bootstrap Authors
   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
   */
:root,
[data-bs-theme="light"] {
  --bs-blue: #0d6efd;
  --bs-indigo: #6610f2;
  --bs-purple: #6f42c1;
  --bs-pink: #d63384;
  --bs-red: #dc3545;
  --bs-orange: #fd7e14;
  --bs-yellow: #ffc107;
  --bs-green: #198754;
  --bs-teal: #20c997;
  --bs-cyan: #0dcaf0;
  --bs-black: #000;
  --bs-white: #fff;
  --bs-gray: #6c757d;
  --bs-gray-dark: #343a40;
  --bs-gray-100: #f8f9fa;
  --bs-gray-200: #e9ecef;
  --bs-gray-300: #dee2e6;
  --bs-gray-400: #ced4da;
  --bs-gray-500: #adb5bd;
  --bs-gray-600: #6c757d;
  --bs-gray-700: #495057;
  --bs-gray-800: #343a40;
  --bs-gray-900: #212529;
  --bs-primary: #0d6efd;
  --bs-secondary: #6c757d;
  --bs-success: #198754;
  --bs-info: #0dcaf0;
  --bs-warning: #ffc107;
  --bs-danger: #dc3545;
  --bs-light: #f8f9fa;
  --bs-dark: #212529;
  --bs-primary-rgb: 13, 110, 253;
  --bs-secondary-rgb: 108, 117, 125;
  --bs-success-rgb: 25, 135, 84;
  --bs-info-rgb: 13, 202, 240;
  --bs-warning-rgb: 255, 193, 7;
  --bs-danger-rgb: 220, 53, 69;
  --bs-light-rgb: 248, 249, 250;
  --bs-dark-rgb: 33, 37, 41;
  --bs-primary-text-emphasis: #052c65;
  --bs-secondary-text-emphasis: #2b2f32;
  --bs-success-text-emphasis: #0a3622;
  --bs-info-text-emphasis: #055160;
  --bs-warning-text-emphasis: #664d03;
  --bs-danger-text-emphasis: #58151c;
  --bs-light-text-emphasis: #495057;
  --bs-dark-text-emphasis: #495057;
  --bs-primary-bg-subtle: #cfe2ff;
  --bs-secondary-bg-subtle: #e2e3e5;
  --bs-success-bg-subtle: #d1e7dd;
  --bs-info-bg-subtle: #cff4fc;
  --bs-warning-bg-subtle: #fff3cd;
  --bs-danger-bg-subtle: #f8d7da;
  --bs-light-bg-subtle: #fcfcfd;
  --bs-dark-bg-subtle: #ced4da;
  --bs-primary-border-subtle: #9ec5fe;
  --bs-secondary-border-subtle: #c4c8cb;
  --bs-success-border-subtle: #a3cfbb;
  --bs-info-border-subtle: #9eeaf9;
  --bs-warning-border-subtle: #ffe69c;
  --bs-danger-border-subtle: #f1aeb5;
  --bs-light-border-subtle: #e9ecef;
  --bs-dark-border-subtle: #adb5bd;
  --bs-white-rgb: 255, 255, 255;
  --bs-black-rgb: 0, 0, 0;
  --bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
  --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
  --bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
  --bs-body-font-family: var(--bs-font-sans-serif);
  --bs-body-font-size: 1rem;
  --bs-body-font-weight: 400;
  --bs-body-line-height: 1.5;
  --bs-body-color: #212529;
  --bs-body-color-rgb: 33, 37, 41;
  --bs-body-bg: #fff;
  --bs-body-bg-rgb: 255, 255, 255;
  --bs-emphasis-color: #000;
  --bs-emphasis-color-rgb: 0, 0, 0;
  --bs-secondary-color: rgba(33, 37, 41, 0.75);
  --bs-secondary-color-rgb: 33, 37, 41;
  --bs-secondary-bg: #e9ecef;
  --bs-secondary-bg-rgb: 233, 236, 239;
  --bs-tertiary-color: rgba(33, 37, 41, 0.5);
  --bs-tertiary-color-rgb: 33, 37, 41;
  --bs-tertiary-bg: #f8f9fa;
  --bs-tertiary-bg-rgb: 248, 249, 250;
  --bs-heading-color: inherit;
  --bs-link-color: #0d6efd;
  --bs-link-color-rgb: 13, 110, 253;
  --bs-link-decoration: underline;
  --bs-link-hover-color: #0a58ca;
  --bs-link-hover-color-rgb: 10, 88, 202;
  --bs-code-color: #d63384;
  --bs-highlight-color: #212529;
  --bs-highlight-bg: #fff3cd;
  --bs-border-width: 1px;
  --bs-border-style: solid;
  --bs-border-color: #dee2e6;
  --bs-border-color-translucent: rgba(0, 0, 0, 0.175);
  --bs-border-radius: 0.375rem;
  --bs-border-radius-sm: 0.25rem;
  --bs-border-radius-lg: 0.5rem;
  --bs-border-radius-xl: 1rem;
  --bs-border-radius-xxl: 2rem;
  --bs-border-radius-2xl: var(--bs-border-radius-xxl);
  --bs-border-radius-pill: 50rem;
  --bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
  --bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
  --bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);
  --bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075);
  --bs-focus-ring-width: 0.25rem;
  --bs-focus-ring-opacity: 0.25;
  --bs-focus-ring-color: rgba(13, 110, 253, 0.25);
  --bs-form-valid-color: #198754;
  --bs-form-valid-border-color: #198754;
  --bs-form-invalid-color: #dc3545;
  --bs-form-invalid-border-color: #dc3545; }

[data-bs-theme="dark"] {
  color-scheme: dark;
  --bs-body-color: #dee2e6;
  --bs-body-color-rgb: 222, 226, 230;
  --bs-body-bg: #212529;
  --bs-body-bg-rgb: 33, 37, 41;
  --bs-emphasis-color: #fff;
  --bs-emphasis-color-rgb: 255, 255, 255;
  --bs-secondary-color: rgba(222, 226, 230, 0.75);
  --bs-secondary-color-rgb: 222, 226, 230;
  --bs-secondary-bg: #343a40;
  --bs-secondary-bg-rgb: 52, 58, 64;
  --bs-tertiary-color: rgba(222, 226, 230, 0.5);
  --bs-tertiary-color-rgb: 222, 226, 230;
  --bs-tertiary-bg: #2b3035;
  --bs-tertiary-bg-rgb: 43, 48, 53;
  --bs-primary-text-emphasis: #6ea8fe;
  --bs-secondary-text-emphasis: #a7acb1;
  --bs-success-text-emphasis: #75b798;
  --bs-info-text-emphasis: #6edff6;
  --bs-warning-text-emphasis: #ffda6a;
  --bs-danger-text-emphasis: #ea868f;
  --bs-light-text-emphasis: #f8f9fa;
  --bs-dark-text-emphasis: #dee2e6;
  --bs-primary-bg-subtle: #031633;
  --bs-secondary-bg-subtle: #161719;
  --bs-success-bg-subtle: #051b11;
  --bs-info-bg-subtle: #032830;
  --bs-warning-bg-subtle: #332701;
  --bs-danger-bg-subtle: #2c0b0e;
  --bs-light-bg-subtle: #343a40;
  --bs-dark-bg-subtle: #1a1d20;
  --bs-primary-border-subtle: #084298;
  --bs-secondary-border-subtle: #41464b;
  --bs-success-border-subtle: #0f5132;
  --bs-info-border-subtle: #087990;
  --bs-warning-border-subtle: #997404;
  --bs-danger-border-subtle: #842029;
  --bs-light-border-subtle: #495057;
  --bs-dark-border-subtle: #343a40;
  --bs-heading-color: inherit;
  --bs-link-color: #6ea8fe;
  --bs-link-hover-color: #8bb9fe;
  --bs-link-color-rgb: 110, 168, 254;
  --bs-link-hover-color-rgb: 139, 185, 254;
  --bs-code-color: #e685b5;
  --bs-highlight-color: #dee2e6;
  --bs-highlight-bg: #664d03;
  --bs-border-color: #495057;
  --bs-border-color-translucent: rgba(255, 255, 255, 0.15);
  --bs-form-valid-color: #75b798;
  --bs-form-valid-border-color: #75b798;
  --bs-form-invalid-color: #ea868f;
  --bs-form-invalid-border-color: #ea868f; }

*,
*::before,
*::after {
  box-sizing: border-box; }

@media (prefers-reduced-motion: no-preference) {
  :root {
    scroll-behavior: smooth; } }

body {
  margin: 0;
  font-family: var(--bs-body-font-family);
  font-size: var(--bs-body-font-size);
  font-weight: var(--bs-body-font-weight);
  line-height: var(--bs-body-line-height);
  color: var(--bs-body-color);
  text-align: var(--bs-body-text-align);
  background-color: var(--bs-body-bg);
  -webkit-text-size-adjust: 100%;
  -webkit-tap-highlight-color: rgba(0, 0, 0, 0); }

hr {
  margin: 1rem 0;
  color: inherit;
  border: 0;
  border-top: var(--bs-border-width) solid;
  opacity: 0.25; }

h6, .h6, h5, .h5, h4, .h4, h3, .h3, h2, .h2, h1, .h1 {
  margin-top: 0;
  margin-bottom: 0.5rem;
  font-weight: 500;
  line-height: 1.2;
  color: var(--bs-heading-color); }

h1, .h1 {
  font-size: calc(1.375rem + 1.5vw); }
  @media (min-width: 1200px) {
    h1, .h1 {
      font-size: 2.5rem; } }
h2, .h2 {
  font-size: calc(1.325rem + 0.9vw); }
  @media (min-width: 1200px) {
    h2, .h2 {
      font-size: 2rem; } }
h3, .h3 {
  font-size: calc(1.3rem + 0.6vw); }
  @media (min-width: 1200px) {
    h3, .h3 {
      font-size: 1.75rem; } }
h4, .h4 {
  font-size: calc(1.275rem + 0.3vw); }
  @media (min-width: 1200px) {
    h4, .h4 {
      font-size: 1.5rem; } }
h5, .h5 {
  font-size: 1.25rem; }

h6, .h6 {
  font-size: 1rem; }

p {
  margin-top: 0;
  margin-bottom: 1rem; }

abbr[title] {
  text-decoration: underline dotted;
  cursor: help;
  text-decoration-skip-ink: none; }

address {
  margin-bottom: 1rem;
  font-style: normal;
  line-height: inherit; }

ol,
ul {
  padding-left: 2rem; }

ol,
ul,
dl {
  margin-top: 0;
  margin-bottom: 1rem; }

ol ol,
ul ul,
ol ul,
ul ol {
  margin-bottom: 0; }

dt {
  font-weight: 700; }

dd {
  margin-bottom: .5rem;
  margin-left: 0; }

blockquote {
  margin: 0 0 1rem; }

b,
strong {
  font-weight: bolder; }

small, .small {
  font-size: 0.875em; }

mark, .mark {
  padding: 0.1875em;
  color: var(--bs-highlight-color);
  background-color: var(--bs-highlight-bg); }

sub,
sup {
  position: relative;
  font-size: 0.75em;
  line-height: 0;
  vertical-align: baseline; }

sub {
  bottom: -.25em; }

sup {
  top: -.5em; }

a {
  color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));
  text-decoration: underline; }
  a:hover {
    --bs-link-color-rgb: var(--bs-link-hover-color-rgb); }

a:not([href]):not([class]), a:not([href]):not([class]):hover {
  color: inherit;
  text-decoration: none; }

pre,
code,
kbd,
samp {
  font-family: var(--bs-font-monospace);
  font-size: 1em; }

pre {
  display: block;
  margin-top: 0;
  margin-bottom: 1rem;
  overflow: auto;
  font-size: 0.875em; }
  pre code {
    font-size: inherit;
    color: inherit;
    word-break: normal; }

code {
  font-size: 0.875em;
  color: var(--bs-code-color);
  word-wrap: break-word; }
  a > code {
    color: inherit; }

kbd {
  padding: 0.1875rem 0.375rem;
  font-size: 0.875em;
  color: var(--bs-body-bg);
  background-color: var(--bs-body-color);
  border-radius: 0.25rem; }
  kbd kbd {
    padding: 0;
    font-size: 1em; }

figure {
  margin: 0 0 1rem; }

img,
svg {
  vertical-align: middle; }

table {
  caption-side: bottom;
  border-collapse: collapse; }

caption {
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
  color: var(--bs-secondary-color);
  text-align: left; }

th {
  text-align: inherit;
  text-align: -webkit-match-parent; }

thead,
tbody,
tfoot,
tr,
td,
th {
  border-color: inherit;
  border-style: solid;
  border-width: 0; }

label {
  display: inline-block; }

button {
  border-radius: 0; }

button:focus:not(:focus-visible) {
  outline: 0; }

input,
button,
select,
optgroup,
textarea {
  margin: 0;
  font-family: inherit;
  font-size: inherit;
  line-height: inherit; }

button,
select {
  text-transform: none; }

[role="button"] {
  cursor: pointer; }

select {
  word-wrap: normal; }
  select:disabled {
    opacity: 1; }

[list]:not([type="date"]):not([type="datetime-local"]):not([type="month"]):not([type="week"]):not([type="time"])::-webkit-calendar-picker-indicator {
  display: none !important; }

button,
[type="button"],
[type="reset"],
[type="submit"] {
  -webkit-appearance: button; }
  button:not(:disabled),
  [type="button"]:not(:disabled),
  [type="reset"]:not(:disabled),
  [type="submit"]:not(:disabled) {
    cursor: pointer; }

::-moz-focus-inner {
  padding: 0;
  border-style: none; }

textarea {
  resize: vertical; }

fieldset {
  min-width: 0;
  padding: 0;
  margin: 0;
  border: 0; }

legend {
  float: left;
  width: 100%;
  padding: 0;
  margin-bottom: 0.5rem;
  line-height: inherit;
  font-size: calc(1.275rem + 0.3vw); }
  @media (min-width: 1200px) {
    legend {
      font-size: 1.5rem; } }
  legend + * {
    clear: left; }

::-webkit-datetime-edit-fields-wrapper,
::-webkit-datetime-edit-text,
::-webkit-datetime-edit-minute,
::-webkit-datetime-edit-hour-field,
::-webkit-datetime-edit-day-field,
::-webkit-datetime-edit-month-field,
::-webkit-datetime-edit-year-field {
  padding: 0; }

::-webkit-inner-spin-button {
  height: auto; }

[type="search"] {
  -webkit-appearance: textfield;
  outline-offset: -2px; }
  [type="search"]::-webkit-search-cancel-button {
    cursor: pointer;
    filter: grayscale(1); }

/* rtl:raw:
[type="tel"],
[type="url"],
[type="email"],
[type="number"] {
  direction: ltr;
}
*/
::-webkit-search-decoration {
  -webkit-appearance: none; }

::-webkit-color-swatch-wrapper {
  padding: 0; }

::file-selector-button {
  font: inherit;
  -webkit-appearance: button; }

output {
  display: inline-block; }

iframe {
  border: 0; }

summary {
  display: list-item;
  cursor: pointer; }

progress {
  vertical-align: baseline; }

[hidden] {
  display: none !important; }

.lead {
  font-size: 1.25rem;
  font-weight: 300; }

.display-1 {
  font-weight: 300;
  line-height: 1.2;
  font-size: calc(1.625rem + 4.5vw); }
  @media (min-width: 1200px) {
    .display-1 {
      font-size: 5rem; } }
.display-2 {
  font-weight: 300;
  line-height: 1.2;
  font-size: calc(1.575rem + 3.9vw); }
  @media (min-width: 1200px) {
    .display-2 {
      font-size: 4.5rem; } }
.display-3 {
  font-weight: 300;
  line-height: 1.2;
  font-size: calc(1.525rem + 3.3vw); }
  @media (min-width: 1200px) {
    .display-3 {
      font-size: 4rem; } }
.display-4 {
  font-weight: 300;
  line-height: 1.2;
  font-size: calc(1.475rem + 2.7vw); }
  @media (min-width: 1200px) {
    .display-4 {
      font-size: 3.5rem; } }
.display-5 {
  font-weight: 300;
  line-height: 1.2;
  font-size: calc(1.425rem + 2.1vw); }
  @media (min-width: 1200px) {
    .display-5 {
      font-size: 3rem; } }
.display-6 {
  font-weight: 300;
  line-height: 1.2;
  font-size: calc(1.375rem + 1.5vw); }
  @media (min-width: 1200px) {
    .display-6 {
      font-size: 2.5rem; } }
.list-unstyled {
  padding-left: 0;
  list-style: none; }

.list-inline {
  padding-left: 0;
  list-style: none; }

.list-inline-item {
  display: inline-block; }
  .list-inline-item:not(:last-child) {
    margin-right: 0.5rem; }

.initialism {
  font-size: 0.875em;
  text-transform: uppercase; }

.blockquote {
  margin-bottom: 1rem;
  font-size: 1.25rem; }
  .blockquote > :last-child {
    margin-bottom: 0; }

.blockquote-footer {
  margin-top: -1rem;
  margin-bottom: 1rem;
  font-size: 0.875em;
  color: #6c757d; }
  .blockquote-footer::before {
    content: "\2014\00A0"; }

.img-fluid {
  max-width: 100%;
  height: auto; }

.img-thumbnail {
  padding: 0.25rem;
  background-color: var(--bs-body-bg);
  border: var(--bs-border-width) solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  max-width: 100%;
  height: auto; }

.figure {
  display: inline-block; }

.figure-img {
  margin-bottom: 0.5rem;
  line-height: 1; }

.figure-caption {
  font-size: 0.875em;
  color: var(--bs-secondary-color); }

.container,
.container-fluid,
.container-xxl,
.container-xl,
.container-lg,
.container-md,
.container-sm {
  --bs-gutter-x: 1.5rem;
  --bs-gutter-y: 0;
  width: 100%;
  padding-right: calc(var(--bs-gutter-x) * .5);
  padding-left: calc(var(--bs-gutter-x) * .5);
  margin-right: auto;
  margin-left: auto; }

@media (min-width: 576px) {
  .container-sm, .container {
    max-width: 540px; } }

@media (min-width: 768px) {
  .container-md, .container-sm, .container {
    max-width: 720px; } }

@media (min-width: 992px) {
  .container-lg, .container-md, .container-sm, .container {
    max-width: 960px; } }

@media (min-width: 1200px) {
  .container-xl, .container-lg, .container-md, .container-sm, .container {
    max-width: 1140px; } }

@media (min-width: 1400px) {
  .container-xxl, .container-xl, .container-lg, .container-md, .container-sm, .container {
    max-width: 1320px; } }

:root {
  --bs-breakpoint-xs: 0;
  --bs-breakpoint-sm: 576px;
  --bs-breakpoint-md: 768px;
  --bs-breakpoint-lg: 992px;
  --bs-breakpoint-xl: 1200px;
  --bs-breakpoint-xxl: 1400px; }

.row {
  --bs-gutter-x: 1.5rem;
  --bs-gutter-y: 0;
  display: flex;
  flex-wrap: wrap;
  margin-top: calc(-1 * var(--bs-gutter-y));
  margin-right: calc(-.5 * var(--bs-gutter-x)
Download .txt
gitextract_lswr63to/

├── .devcontainer/
│   ├── Dockerfile
│   └── devcontainer.json
├── .editorconfig
├── .gitattributes
├── .github/
│   ├── dco.yml
│   └── workflows/
│       ├── deploy-and-test-cluster.yml
│       ├── gradle-build.yml
│       └── maven-build.yml
├── .gitignore
├── .gitpod.yml
├── .mvn/
│   └── wrapper/
│       └── maven-wrapper.properties
├── LICENSE.txt
├── README.md
├── build.gradle
├── docker-compose.yml
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── k8s/
│   ├── db.yml
│   └── petclinic.yml
├── mvnw
├── mvnw.cmd
├── pom.xml
├── settings.gradle
└── src/
    ├── checkstyle/
    │   ├── nohttp-checkstyle-suppressions.xml
    │   └── nohttp-checkstyle.xml
    ├── main/
    │   ├── java/
    │   │   └── org/
    │   │       └── springframework/
    │   │           └── samples/
    │   │               └── petclinic/
    │   │                   ├── PetClinicApplication.java
    │   │                   ├── PetClinicRuntimeHints.java
    │   │                   ├── model/
    │   │                   │   ├── BaseEntity.java
    │   │                   │   ├── NamedEntity.java
    │   │                   │   ├── Person.java
    │   │                   │   └── package-info.java
    │   │                   ├── owner/
    │   │                   │   ├── Owner.java
    │   │                   │   ├── OwnerController.java
    │   │                   │   ├── OwnerRepository.java
    │   │                   │   ├── Pet.java
    │   │                   │   ├── PetController.java
    │   │                   │   ├── PetType.java
    │   │                   │   ├── PetTypeFormatter.java
    │   │                   │   ├── PetTypeRepository.java
    │   │                   │   ├── PetValidator.java
    │   │                   │   ├── Visit.java
    │   │                   │   ├── VisitController.java
    │   │                   │   └── package-info.java
    │   │                   ├── package-info.java
    │   │                   ├── system/
    │   │                   │   ├── CacheConfiguration.java
    │   │                   │   ├── CrashController.java
    │   │                   │   ├── WebConfiguration.java
    │   │                   │   ├── WelcomeController.java
    │   │                   │   └── package-info.java
    │   │                   └── vet/
    │   │                       ├── Specialty.java
    │   │                       ├── Vet.java
    │   │                       ├── VetController.java
    │   │                       ├── VetRepository.java
    │   │                       ├── Vets.java
    │   │                       └── package-info.java
    │   ├── resources/
    │   │   ├── application-mysql.properties
    │   │   ├── application-postgres.properties
    │   │   ├── application.properties
    │   │   ├── banner.txt
    │   │   ├── db/
    │   │   │   ├── h2/
    │   │   │   │   ├── data.sql
    │   │   │   │   └── schema.sql
    │   │   │   ├── mysql/
    │   │   │   │   ├── data.sql
    │   │   │   │   ├── petclinic_db_setup_mysql.txt
    │   │   │   │   ├── schema.sql
    │   │   │   │   └── user.sql
    │   │   │   └── postgres/
    │   │   │       ├── data.sql
    │   │   │       ├── petclinic_db_setup_postgres.txt
    │   │   │       └── schema.sql
    │   │   ├── messages/
    │   │   │   ├── messages.properties
    │   │   │   ├── messages_de.properties
    │   │   │   ├── messages_en.properties
    │   │   │   ├── messages_es.properties
    │   │   │   ├── messages_fa.properties
    │   │   │   ├── messages_ko.properties
    │   │   │   ├── messages_pt.properties
    │   │   │   ├── messages_ru.properties
    │   │   │   └── messages_tr.properties
    │   │   ├── static/
    │   │   │   └── resources/
    │   │   │       └── css/
    │   │   │           └── petclinic.css
    │   │   └── 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
    │   └── scss/
    │       ├── header.scss
    │       ├── petclinic.scss
    │       ├── responsive.scss
    │       └── typography.scss
    └── test/
        ├── java/
        │   └── org/
        │       └── springframework/
        │           └── samples/
        │               └── petclinic/
        │                   ├── MySqlIntegrationTests.java
        │                   ├── MysqlTestApplication.java
        │                   ├── PetClinicIntegrationTests.java
        │                   ├── PostgresIntegrationTests.java
        │                   ├── model/
        │                   │   └── ValidatorTests.java
        │                   ├── owner/
        │                   │   ├── OwnerControllerTests.java
        │                   │   ├── PetControllerTests.java
        │                   │   ├── PetTypeFormatterTests.java
        │                   │   ├── PetValidatorTests.java
        │                   │   └── VisitControllerTests.java
        │                   ├── service/
        │                   │   ├── ClinicServiceTests.java
        │                   │   └── EntityUtils.java
        │                   ├── system/
        │                   │   ├── CrashControllerIntegrationTests.java
        │                   │   ├── CrashControllerTests.java
        │                   │   └── I18nPropertiesSyncTest.java
        │                   └── vet/
        │                       ├── VetControllerTests.java
        │                       └── VetTests.java
        └── jmeter/
            └── petclinic_test_plan.jmx
Download .txt
SYMBOL INDEX (251 symbols across 45 files)

FILE: src/main/java/org/springframework/samples/petclinic/PetClinicApplication.java
  class PetClinicApplication (line 28) | @SpringBootApplication
    method main (line 32) | public static void main(String[] args) {

FILE: src/main/java/org/springframework/samples/petclinic/PetClinicRuntimeHints.java
  class PetClinicRuntimeHints (line 25) | public class PetClinicRuntimeHints implements RuntimeHintsRegistrar {
    method registerHints (line 27) | @Override

FILE: src/main/java/org/springframework/samples/petclinic/model/BaseEntity.java
  class BaseEntity (line 32) | @MappedSuperclass
    method getId (line 39) | public Integer getId() {
    method setId (line 43) | public void setId(Integer id) {
    method isNew (line 47) | public boolean isNew() {

FILE: src/main/java/org/springframework/samples/petclinic/model/NamedEntity.java
  class NamedEntity (line 30) | @MappedSuperclass
    method getName (line 37) | public String getName() {
    method setName (line 41) | public void setName(String name) {
    method toString (line 45) | @Override

FILE: src/main/java/org/springframework/samples/petclinic/model/Person.java
  class Person (line 27) | @MappedSuperclass
    method getFirstName (line 38) | public String getFirstName() {
    method setFirstName (line 42) | public void setFirstName(String firstName) {
    method getLastName (line 46) | public String getLastName() {
    method setLastName (line 50) | public void setLastName(String lastName) {

FILE: src/main/java/org/springframework/samples/petclinic/owner/Owner.java
  class Owner (line 47) | @Entity
    method getAddress (line 69) | public String getAddress() {
    method setAddress (line 73) | public void setAddress(String address) {
    method getCity (line 77) | public String getCity() {
    method setCity (line 81) | public void setCity(String city) {
    method getTelephone (line 85) | public String getTelephone() {
    method setTelephone (line 89) | public void setTelephone(String telephone) {
    method getPets (line 93) | public List<Pet> getPets() {
    method addPet (line 97) | public void addPet(Pet pet) {
    method getPet (line 108) | public Pet getPet(String name) {
    method getPet (line 117) | public Pet getPet(Integer id) {
    method getPet (line 135) | public Pet getPet(String name, boolean ignoreNew) {
    method toString (line 147) | @Override
    method addVisit (line 164) | public void addVisit(Integer petId, Visit visit) {

FILE: src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java
  class OwnerController (line 48) | @Controller
    method OwnerController (line 55) | public OwnerController(OwnerRepository owners) {
    method setAllowedFields (line 59) | @InitBinder
    method findOwner (line 64) | @ModelAttribute("owner")
    method initCreationForm (line 72) | @GetMapping("/owners/new")
    method processCreationForm (line 77) | @PostMapping("/owners/new")
    method initFindForm (line 89) | @GetMapping("/owners/find")
    method processFindForm (line 94) | @GetMapping("/owners")
    method addPaginationModel (line 121) | private String addPaginationModel(int page, Model model, Page<Owner> p...
    method findPaginatedForOwnersLastName (line 130) | private Page<Owner> findPaginatedForOwnersLastName(int page, String la...
    method initUpdateOwnerForm (line 136) | @GetMapping("/owners/{ownerId}/edit")
    method processUpdateOwnerForm (line 141) | @PostMapping("/owners/{ownerId}/edit")
    method showOwner (line 166) | @GetMapping("/owners/{ownerId}")

FILE: src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java
  type OwnerRepository (line 36) | public interface OwnerRepository extends JpaRepository<Owner, Integer> {
    method findByLastNameStartingWith (line 45) | Page<Owner> findByLastNameStartingWith(String lastName, Pageable pagea...
    method findById (line 60) | Optional<Owner> findById(Integer id);

FILE: src/main/java/org/springframework/samples/petclinic/owner/Pet.java
  class Pet (line 44) | @Entity
    method setBirthDate (line 61) | public void setBirthDate(LocalDate birthDate) {
    method getBirthDate (line 65) | public LocalDate getBirthDate() {
    method getType (line 69) | public PetType getType() {
    method setType (line 73) | public void setType(PetType type) {
    method getVisits (line 77) | public Collection<Visit> getVisits() {
    method addVisit (line 81) | public void addVisit(Visit visit) {

FILE: src/main/java/org/springframework/samples/petclinic/owner/PetController.java
  class PetController (line 46) | @Controller
    method PetController (line 56) | public PetController(OwnerRepository owners, PetTypeRepository types) {
    method populatePetTypes (line 61) | @ModelAttribute("types")
    method findOwner (line 66) | @ModelAttribute("owner")
    method findPet (line 74) | @ModelAttribute("pet")
    method initOwnerBinder (line 88) | @InitBinder("owner")
    method initPetBinder (line 93) | @InitBinder("pet")
    method initCreationForm (line 98) | @GetMapping("/pets/new")
    method processCreationForm (line 105) | @PostMapping("/pets/new")
    method initUpdateForm (line 128) | @GetMapping("/pets/{petId}/edit")
    method processUpdateForm (line 133) | @PostMapping("/pets/{petId}/edit")
    method updatePetDetails (line 166) | private void updatePetDetails(Owner owner, Pet pet) {

FILE: src/main/java/org/springframework/samples/petclinic/owner/PetType.java
  class PetType (line 26) | @Entity

FILE: src/main/java/org/springframework/samples/petclinic/owner/PetTypeFormatter.java
  class PetTypeFormatter (line 36) | @Component
    method PetTypeFormatter (line 41) | public PetTypeFormatter(PetTypeRepository types) {
    method print (line 45) | @Override
    method parse (line 51) | @Override

FILE: src/main/java/org/springframework/samples/petclinic/owner/PetTypeRepository.java
  type PetTypeRepository (line 30) | public interface PetTypeRepository extends JpaRepository<PetType, Intege...
    method findPetTypes (line 36) | @Query("SELECT ptype FROM PetType ptype ORDER BY ptype.name")

FILE: src/main/java/org/springframework/samples/petclinic/owner/PetValidator.java
  class PetValidator (line 32) | public class PetValidator implements Validator {
    method validate (line 36) | @Override
    method supports (line 59) | @Override

FILE: src/main/java/org/springframework/samples/petclinic/owner/Visit.java
  class Visit (line 34) | @Entity
    method Visit (line 48) | public Visit() {
    method getDate (line 52) | public LocalDate getDate() {
    method setDate (line 56) | public void setDate(LocalDate date) {
    method getDescription (line 60) | public String getDescription() {
    method setDescription (line 64) | public void setDescription(String description) {

FILE: src/main/java/org/springframework/samples/petclinic/owner/VisitController.java
  class VisitController (line 41) | @Controller
    method VisitController (line 46) | public VisitController(OwnerRepository owners) {
    method setAllowedFields (line 50) | @InitBinder
    method loadPetWithVisit (line 62) | @ModelAttribute("visit")
    method initNewVisitForm (line 84) | @GetMapping("/owners/{ownerId}/pets/{petId}/visits/new")
    method processNewVisitForm (line 91) | @PostMapping("/owners/{ownerId}/pets/{petId}/visits/new")

FILE: src/main/java/org/springframework/samples/petclinic/system/CacheConfiguration.java
  class CacheConfiguration (line 31) | @Configuration(proxyBeanMethods = false)
    method petclinicCacheConfigurationCustomizer (line 35) | @Bean
    method cacheConfiguration (line 49) | private javax.cache.configuration.Configuration<Object, Object> cacheC...

FILE: src/main/java/org/springframework/samples/petclinic/system/CrashController.java
  class CrashController (line 28) | @Controller
    method triggerException (line 31) | @GetMapping("/oups")

FILE: src/main/java/org/springframework/samples/petclinic/system/WebConfiguration.java
  class WebConfiguration (line 23) | @Configuration
    method localeResolver (line 32) | @Bean
    method localeChangeInterceptor (line 44) | @Bean
    method addInterceptors (line 55) | @Override

FILE: src/main/java/org/springframework/samples/petclinic/system/WelcomeController.java
  class WelcomeController (line 22) | @Controller
    method welcome (line 25) | @GetMapping("/")

FILE: src/main/java/org/springframework/samples/petclinic/vet/Specialty.java
  class Specialty (line 28) | @Entity

FILE: src/main/java/org/springframework/samples/petclinic/vet/Vet.java
  class Vet (line 43) | @Entity
    method getSpecialtiesInternal (line 52) | protected Set<Specialty> getSpecialtiesInternal() {
    method getSpecialties (line 59) | @XmlElement
    method getNrOfSpecialties (line 66) | public int getNrOfSpecialties() {
    method addSpecialty (line 70) | public void addSpecialty(Specialty specialty) {

FILE: src/main/java/org/springframework/samples/petclinic/vet/VetController.java
  class VetController (line 35) | @Controller
    method VetController (line 40) | public VetController(VetRepository vetRepository) {
    method showVetList (line 44) | @GetMapping("/vets.html")
    method addPaginationModel (line 54) | private String addPaginationModel(int page, Page<Vet> paginated, Model...
    method findPaginated (line 63) | private Page<Vet> findPaginated(int page) {
    method showResourcesVetList (line 69) | @GetMapping({ "/vets" })

FILE: src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java
  type VetRepository (line 38) | public interface VetRepository extends Repository<Vet, Integer> {
    method findAll (line 44) | @Transactional(readOnly = true)
    method findAll (line 54) | @Transactional(readOnly = true)

FILE: src/main/java/org/springframework/samples/petclinic/vet/Vets.java
  class Vets (line 30) | @XmlRootElement
    method getVetList (line 35) | @XmlElement

FILE: src/main/resources/db/h2/schema.sql
  type vets (line 10) | CREATE TABLE vets (
  type vets_last_name (line 15) | CREATE INDEX vets_last_name ON vets (last_name)
  type specialties (line 17) | CREATE TABLE specialties (
  type specialties_name (line 21) | CREATE INDEX specialties_name ON specialties (name)
  type vet_specialties (line 23) | CREATE TABLE vet_specialties (
  type types (line 30) | CREATE TABLE types (
  type types_name (line 34) | CREATE INDEX types_name ON types (name)
  type owners (line 36) | CREATE TABLE owners (
  type owners_last_name (line 44) | CREATE INDEX owners_last_name ON owners (last_name)
  type pets (line 46) | CREATE TABLE pets (
  type pets_name (line 55) | CREATE INDEX pets_name ON pets (name)
  type visits (line 57) | CREATE TABLE visits (
  type visits_pet_id (line 64) | CREATE INDEX visits_pet_id ON visits (pet_id)

FILE: src/main/resources/db/mysql/schema.sql
  type vets (line 1) | CREATE TABLE IF NOT EXISTS vets (
  type specialties (line 8) | CREATE TABLE IF NOT EXISTS specialties (
  type vet_specialties (line 14) | CREATE TABLE IF NOT EXISTS vet_specialties (
  type types (line 22) | CREATE TABLE IF NOT EXISTS types (
  type owners (line 28) | CREATE TABLE IF NOT EXISTS owners (
  type pets (line 38) | CREATE TABLE IF NOT EXISTS pets (
  type visits (line 49) | CREATE TABLE IF NOT EXISTS visits (

FILE: src/main/resources/db/postgres/schema.sql
  type vets (line 1) | CREATE TABLE IF NOT EXISTS vets (
  type vets (line 6) | CREATE INDEX ON vets (last_name)
  type specialties (line 8) | CREATE TABLE IF NOT EXISTS specialties (
  type specialties (line 12) | CREATE INDEX ON specialties (name)
  type vet_specialties (line 14) | CREATE TABLE IF NOT EXISTS vet_specialties (
  type types (line 20) | CREATE TABLE IF NOT EXISTS types (
  type types (line 24) | CREATE INDEX ON types (name)
  type owners (line 26) | CREATE TABLE IF NOT EXISTS owners (
  type owners (line 34) | CREATE INDEX ON owners (last_name)
  type pets (line 36) | CREATE TABLE IF NOT EXISTS pets (
  type pets (line 43) | CREATE INDEX ON pets (name)
  type pets (line 44) | CREATE INDEX ON pets (owner_id)
  type visits (line 46) | CREATE TABLE IF NOT EXISTS visits (
  type visits (line 52) | CREATE INDEX ON visits (pet_id)

FILE: src/test/java/org/springframework/samples/petclinic/MySqlIntegrationTests.java
  class MySqlIntegrationTests (line 41) | @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
    method findAll (line 61) | @Test
    method ownerDetails (line 67) | @Test

FILE: src/test/java/org/springframework/samples/petclinic/MysqlTestApplication.java
  class MysqlTestApplication (line 32) | @Configuration
    method container (line 35) | @ServiceConnection
    method main (line 42) | public static void main(String[] args) {

FILE: src/test/java/org/springframework/samples/petclinic/PetClinicIntegrationTests.java
  class PetClinicIntegrationTests (line 34) | @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties ...
    method findAll (line 46) | @Test
    method ownerDetails (line 52) | @Test
    method ownerList (line 59) | @Test
    method main (line 66) | public static void main(String[] args) {

FILE: src/test/java/org/springframework/samples/petclinic/PostgresIntegrationTests.java
  class PostgresIntegrationTests (line 51) | @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties ...
    method available (line 66) | @BeforeAll
    method main (line 71) | public static void main(String[] args) {
    method findAll (line 81) | @Test
    method ownerDetails (line 87) | @Test
    class PropertiesLogger (line 94) | static class PropertiesLogger implements ApplicationListener<Applicati...
      method onApplicationEvent (line 102) | @Override
      method printProperties (line 111) | public void printProperties() {
      method findPropertiesPropertySources (line 138) | private List<EnumerablePropertySource<?>> findPropertiesPropertySour...

FILE: src/test/java/org/springframework/samples/petclinic/model/ValidatorTests.java
  class ValidatorTests (line 35) | class ValidatorTests {
    method createValidator (line 37) | private Validator createValidator() {
    method shouldNotValidateWhenFirstNameEmpty (line 43) | @Test

FILE: src/test/java/org/springframework/samples/petclinic/owner/OwnerControllerTests.java
  class OwnerControllerTests (line 58) | @WebMvcTest(OwnerController.class)
    method george (line 71) | private Owner george() {
    method setup (line 90) | @BeforeEach
    method initCreationForm (line 104) | @Test
    method processCreationFormSuccess (line 112) | @Test
    method processCreationFormHasErrors (line 123) | @Test
    method initFindForm (line 134) | @Test
    method processFindFormSuccess (line 142) | @Test
    method processFindFormByLastName (line 149) | @Test
    method processFindFormNoOwnersFound (line 158) | @Test
    method initUpdateOwnerForm (line 170) | @Test
    method processUpdateOwnerFormSuccess (line 183) | @Test
    method processUpdateOwnerFormUnchangedSuccess (line 195) | @Test
    method processUpdateOwnerFormHasErrors (line 202) | @Test
    method showOwner (line 216) | @Test
    method processUpdateOwnerFormWithIdMismatch (line 231) | @Test

FILE: src/test/java/org/springframework/samples/petclinic/owner/PetControllerTests.java
  class PetControllerTests (line 48) | @WebMvcTest(value = PetController.class,
    method setup (line 67) | @BeforeEach
    method initCreationForm (line 86) | @Test
    method processCreationFormSuccess (line 94) | @Test
    class ProcessCreationFormHasErrors (line 104) | @Nested
      method processCreationFormWithBlankName (line 107) | @Test
      method processCreationFormWithDuplicateName (line 120) | @Test
      method processCreationFormWithMissingPetType (line 133) | @Test
      method processCreationFormWithInvalidBirthDate (line 146) | @Test
      method initUpdateForm (line 162) | @Test
    method processUpdateFormSuccess (line 172) | @Test
    class ProcessUpdateFormHasErrors (line 182) | @Nested
      method processUpdateFormWithInvalidBirthDate (line 185) | @Test
      method processUpdateFormWithBlankName (line 197) | @Test

FILE: src/test/java/org/springframework/samples/petclinic/owner/PetTypeFormatterTests.java
  class PetTypeFormatterTests (line 41) | @ExtendWith(MockitoExtension.class)
    method setup (line 50) | @BeforeEach
    method testPrint (line 55) | @Test
    method shouldParse (line 63) | @Test
    method shouldThrowParseException (line 70) | @Test
    method makePetTypes (line 82) | private List<PetType> makePetTypes() {

FILE: src/test/java/org/springframework/samples/petclinic/owner/PetValidatorTests.java
  class PetValidatorTests (line 39) | @ExtendWith(MockitoExtension.class)
    method setUp (line 57) | @BeforeEach
    method validate (line 65) | @Test
    class ValidateHasErrors (line 77) | @Nested
      method validateWithInvalidPetName (line 80) | @Test
      method validateWithInvalidPetType (line 92) | @Test
      method validateWithInvalidBirthDate (line 103) | @Test

FILE: src/test/java/org/springframework/samples/petclinic/owner/VisitControllerTests.java
  class VisitControllerTests (line 43) | @WebMvcTest(VisitController.class)
    method init (line 58) | @BeforeEach
    method initNewVisitForm (line 67) | @Test
    method processNewVisitFormSuccess (line 74) | @Test
    method processNewVisitFormHasErrors (line 84) | @Test

FILE: src/test/java/org/springframework/samples/petclinic/service/ClinicServiceTests.java
  class ClinicServiceTests (line 70) | @DataJpaTest
    method shouldFindOwnersByLastName (line 87) | @Test
    method shouldFindSingleOwnerWithPet (line 96) | @Test
    method shouldInsertOwner (line 107) | @Test
    method shouldUpdateOwner (line 126) | @Test
    method shouldFindAllPetTypes (line 145) | @Test
    method shouldInsertPetIntoDatabaseAndGenerateId (line 155) | @Test
    method shouldUpdatePetName (line 183) | @Test
    method shouldFindVets (line 204) | @Test
    method shouldAddNewVisitForPet (line 215) | @Test
    method shouldFindVisitsByPetId (line 235) | @Test

FILE: src/test/java/org/springframework/samples/petclinic/service/EntityUtils.java
  class EntityUtils (line 33) | public abstract class EntityUtils {
    method getById (line 43) | public static <T extends BaseEntity> T getById(Collection<T> entities,...

FILE: src/test/java/org/springframework/samples/petclinic/system/CrashControllerIntegrationTests.java
  class CrashControllerIntegrationTests (line 50) | @SpringBootTest(webEnvironment = RANDOM_PORT,
    method triggerExceptionJson (line 61) | @Test
    method triggerExceptionHtml (line 77) | @Test
    class TestConfiguration (line 95) | @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class,

FILE: src/test/java/org/springframework/samples/petclinic/system/CrashControllerTests.java
  class CrashControllerTests (line 31) | class CrashControllerTests {
    method triggerException (line 35) | @Test

FILE: src/test/java/org/springframework/samples/petclinic/system/I18nPropertiesSyncTest.java
  class I18nPropertiesSyncTest (line 25) | public class I18nPropertiesSyncTest {
    method checkNonInternationalizedStrings (line 39) | @Test
    method checkI18nPropertyFilesAreInSync (line 86) | @Test

FILE: src/test/java/org/springframework/samples/petclinic/vet/VetControllerTests.java
  class VetControllerTests (line 43) | @WebMvcTest(VetController.class)
    method james (line 54) | private Vet james() {
    method helen (line 62) | private Vet helen() {
    method setup (line 74) | @BeforeEach
    method showVetListHtml (line 82) | @Test
    method showResourcesVetList (line 92) | @Test

FILE: src/test/java/org/springframework/samples/petclinic/vet/VetTests.java
  class VetTests (line 26) | class VetTests {
    method serialization (line 28) | @Test
Condensed preview — 114 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (608K chars).
[
  {
    "path": ".devcontainer/Dockerfile",
    "chars": 643,
    "preview": "# Not actually used by the devcontainer, but it is used by gitpod\nARG VARIANT=17-bullseye\nFROM mcr.microsoft.com/vscode/"
  },
  {
    "path": ".devcontainer/devcontainer.json",
    "chars": 623,
    "preview": "{\n  \"name\": \"Java\",\n  \"image\": \"mcr.microsoft.com/devcontainers/base:ubuntu\",\n  \"features\": {\n    \"ghcr.io/devcontainers"
  },
  {
    "path": ".editorconfig",
    "chars": 344,
    "preview": "# top-most EditorConfig file\nroot = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\nindent_style "
  },
  {
    "path": ".gitattributes",
    "chars": 145,
    "preview": "/mvnw           text eol=lf\n*.cmd           text eol=crlf\n*.java          text eol=lf\n\n/gradlew        text eol=lf\n*.bat"
  },
  {
    "path": ".github/dco.yml",
    "chars": 25,
    "preview": "require:\n  members: false"
  },
  {
    "path": ".github/workflows/deploy-and-test-cluster.yml",
    "chars": 673,
    "preview": "name: Deploy and Test Cluster\n\non:\n  push:\n    branches: [main]\n    paths:\n      - 'k8s/**'\n  pull_request:\n    branches"
  },
  {
    "path": ".github/workflows/gradle-build.yml",
    "chars": 838,
    "preview": "# This workflow will build a Java project with Gradle, and cache/restore any dependencies to improve the workflow execut"
  },
  {
    "path": ".github/workflows/maven-build.yml",
    "chars": 771,
    "preview": "# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow executi"
  },
  {
    "path": ".gitignore",
    "chars": 598,
    "preview": "HELP.md\npom.xml.bak\ntarget/\n!.mvn/wrapper/maven-wrapper.jar\n!**/src/main/**/target/\n!**/src/test/**/target/\n.gradle\nbuil"
  },
  {
    "path": ".gitpod.yml",
    "chars": 287,
    "preview": "image:\n  file: ./.devcontainer/Dockerfile\ntasks:\n  - before: sudo usermod -a -G sdkman gitpod && sudo usermod -a -G nvm "
  },
  {
    "path": ".mvn/wrapper/maven-wrapper.properties",
    "chars": 168,
    "preview": "wrapperVersion=3.3.4\ndistributionType=only-script\ndistributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/"
  },
  {
    "path": "LICENSE.txt",
    "chars": 11360,
    "preview": "\n                                 Apache License\n                           Version 2.0, January 2004\n                  "
  },
  {
    "path": "README.md",
    "chars": 10399,
    "preview": "# Spring PetClinic Sample Application [![Build Status](https://github.com/spring-projects/spring-petclinic/actions/workf"
  },
  {
    "path": "build.gradle",
    "chars": 3383,
    "preview": "plugins {\n  id 'java'\n  id 'checkstyle'\n  id 'org.springframework.boot' version '4.0.3'\n  id 'io.spring.dependency-manag"
  },
  {
    "path": "docker-compose.yml",
    "chars": 488,
    "preview": "services:\n  mysql:\n    image: mysql:9.6\n    ports:\n      - \"3306:3306\"\n    environment:\n      - MYSQL_ROOT_PASSWORD=\n   "
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "chars": 252,
    "preview": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributi"
  },
  {
    "path": "gradlew",
    "chars": 8595,
    "preview": "#!/bin/sh\n\n#\n# Copyright © 2015 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "gradlew.bat",
    "chars": 2896,
    "preview": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (th"
  },
  {
    "path": "k8s/db.yml",
    "chars": 1458,
    "preview": "---\napiVersion: v1\nkind: Secret\nmetadata:\n  name: demo-db\ntype: servicebinding.io/postgresql\nstringData:\n  type: \"postgr"
  },
  {
    "path": "k8s/petclinic.yml",
    "chars": 1358,
    "preview": "---\napiVersion: v1\nkind: Service\nmetadata:\n  name: petclinic\nspec:\n  type: NodePort\n  ports:\n    - port: 80\n      target"
  },
  {
    "path": "mvnw",
    "chars": 11791,
    "preview": "#!/bin/sh\n# ----------------------------------------------------------------------------\n# Licensed to the Apache Softwa"
  },
  {
    "path": "mvnw.cmd",
    "chars": 8482,
    "preview": "<# : batch portion\r\n@REM ----------------------------------------------------------------------------\r\n@REM Licensed to "
  },
  {
    "path": "pom.xml",
    "chars": 15251,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2"
  },
  {
    "path": "settings.gradle",
    "chars": 38,
    "preview": "rootProject.name = 'spring-petclinic'\n"
  },
  {
    "path": "src/checkstyle/nohttp-checkstyle-suppressions.xml",
    "chars": 470,
    "preview": "<?xml version=\"1.0\"?>\n<!DOCTYPE suppressions PUBLIC\n\t\t\"-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN\"\n\t\t\"ht"
  },
  {
    "path": "src/checkstyle/nohttp-checkstyle.xml",
    "chars": 412,
    "preview": "<?xml version=\"1.0\"?>\n<!DOCTYPE module PUBLIC\n\t\t\"-//Puppy Crawl//DTD Check Configuration 1.2//EN\"\n\t\t\"https://checkstyle."
  },
  {
    "path": "src/main/java/org/springframework/samples/petclinic/PetClinicApplication.java",
    "chars": 1144,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/java/org/springframework/samples/petclinic/PetClinicRuntimeHints.java",
    "chars": 1494,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/java/org/springframework/samples/petclinic/model/BaseEntity.java",
    "chars": 1344,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/java/org/springframework/samples/petclinic/model/NamedEntity.java",
    "chars": 1367,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/java/org/springframework/samples/petclinic/model/Person.java",
    "chars": 1327,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/java/org/springframework/samples/petclinic/model/package-info.java",
    "chars": 755,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/java/org/springframework/samples/petclinic/owner/Owner.java",
    "chars": 4533,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java",
    "chars": 6211,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java",
    "chars": 2298,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/java/org/springframework/samples/petclinic/owner/Pet.java",
    "chars": 2186,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/java/org/springframework/samples/petclinic/owner/PetController.java",
    "chars": 5775,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/java/org/springframework/samples/petclinic/owner/PetType.java",
    "chars": 951,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/java/org/springframework/samples/petclinic/owner/PetTypeFormatter.java",
    "chars": 1987,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/java/org/springframework/samples/petclinic/owner/PetTypeRepository.java",
    "chars": 1208,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/java/org/springframework/samples/petclinic/owner/PetValidator.java",
    "chars": 1807,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/java/org/springframework/samples/petclinic/owner/Visit.java",
    "chars": 1694,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/java/org/springframework/samples/petclinic/owner/VisitController.java",
    "chars": 3503,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/java/org/springframework/samples/petclinic/owner/package-info.java",
    "chars": 675,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/java/org/springframework/samples/petclinic/package-info.java",
    "chars": 669,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/java/org/springframework/samples/petclinic/system/CacheConfiguration.java",
    "chars": 2014,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/java/org/springframework/samples/petclinic/system/CrashController.java",
    "chars": 1204,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/java/org/springframework/samples/petclinic/system/WebConfiguration.java",
    "chars": 1945,
    "preview": "package org.springframework.samples.petclinic.system;\n\nimport org.springframework.context.annotation.Bean;\nimport org.sp"
  },
  {
    "path": "src/main/java/org/springframework/samples/petclinic/system/WelcomeController.java",
    "chars": 898,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/java/org/springframework/samples/petclinic/system/package-info.java",
    "chars": 676,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/java/org/springframework/samples/petclinic/vet/Specialty.java",
    "chars": 998,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/java/org/springframework/samples/petclinic/vet/Vet.java",
    "chars": 2190,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/java/org/springframework/samples/petclinic/vet/VetController.java",
    "chars": 2681,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java",
    "chars": 1994,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/java/org/springframework/samples/petclinic/vet/Vets.java",
    "chars": 1215,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/java/org/springframework/samples/petclinic/vet/package-info.java",
    "chars": 673,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/resources/application-mysql.properties",
    "chars": 299,
    "preview": "# database init, supports mysql too\ndatabase=mysql\nspring.datasource.url=${MYSQL_URL:jdbc:mysql://localhost/petclinic}\ns"
  },
  {
    "path": "src/main/resources/application-postgres.properties",
    "chars": 319,
    "preview": "# database init, supports postgres too\ndatabase=postgres\nspring.datasource.url=${POSTGRES_URL:jdbc:postgresql://localhos"
  },
  {
    "path": "src/main/resources/application.properties",
    "chars": 846,
    "preview": "# database init, supports mysql too\ndatabase=h2\nspring.sql.init.schema-locations=classpath*:db/${database}/schema.sql\nsp"
  },
  {
    "path": "src/main/resources/banner.txt",
    "chars": 709,
    "preview": "\n\n              |\\      _,,,--,,_\n             /,`.-'`'   ._  \\-;;,_\n  _______ __|,4-  ) )_   .;.(__`'-'__     ___ __   "
  },
  {
    "path": "src/main/resources/db/h2/data.sql",
    "chars": 3118,
    "preview": "INSERT INTO vets VALUES (default, 'James', 'Carter');\nINSERT INTO vets VALUES (default, 'Helen', 'Leary');\nINSERT INTO v"
  },
  {
    "path": "src/main/resources/db/h2/schema.sql",
    "chars": 2056,
    "preview": "DROP TABLE vet_specialties IF EXISTS;\nDROP TABLE vets IF EXISTS;\nDROP TABLE specialties IF EXISTS;\nDROP TABLE visits IF "
  },
  {
    "path": "src/main/resources/db/mysql/data.sql",
    "chars": 3200,
    "preview": "INSERT IGNORE INTO vets VALUES (1, 'James', 'Carter');\nINSERT IGNORE INTO vets VALUES (2, 'Helen', 'Leary');\nINSERT IGNO"
  },
  {
    "path": "src/main/resources/db/mysql/petclinic_db_setup_mysql.txt",
    "chars": 1690,
    "preview": "================================================================================\n===        Spring PetClinic sample appl"
  },
  {
    "path": "src/main/resources/db/mysql/schema.sql",
    "chars": 1546,
    "preview": "CREATE TABLE IF NOT EXISTS vets (\n  id INT(4) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,\n  first_name VARCHAR(30),\n  "
  },
  {
    "path": "src/main/resources/db/mysql/user.sql",
    "chars": 277,
    "preview": "CREATE DATABASE IF NOT EXISTS petclinic;\n\nALTER DATABASE petclinic\n  DEFAULT CHARACTER SET utf8\n  DEFAULT COLLATE utf8_g"
  },
  {
    "path": "src/main/resources/db/postgres/data.sql",
    "chars": 6422,
    "preview": "INSERT INTO vets (first_name, last_name) SELECT 'James', 'Carter' WHERE NOT EXISTS (SELECT * FROM vets WHERE id=1);\nINSE"
  },
  {
    "path": "src/main/resources/db/postgres/petclinic_db_setup_postgres.txt",
    "chars": 857,
    "preview": "===============================================================================\n===     Spring PetClinic sample applicat"
  },
  {
    "path": "src/main/resources/db/postgres/schema.sql",
    "chars": 1386,
    "preview": "CREATE TABLE IF NOT EXISTS vets (\n  id         INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,\n  first_name TEXT,\n  la"
  },
  {
    "path": "src/main/resources/messages/messages.properties",
    "chars": 1176,
    "preview": "welcome=Welcome\nrequired=is required\nnotFound=has not been found\nduplicate=is already in use\nnonNumeric=must be all nume"
  },
  {
    "path": "src/main/resources/messages/messages_de.properties",
    "chars": 1370,
    "preview": "welcome=Willkommen\nrequired=muss angegeben werden\nnotFound=wurde nicht gefunden\nduplicate=ist bereits vergeben\nnonNumeri"
  },
  {
    "path": "src/main/resources/messages/messages_en.properties",
    "chars": 110,
    "preview": "# This file is intentionally empty. Message look-ups will fall back to the default \"messages.properties\" file."
  },
  {
    "path": "src/main/resources/messages/messages_es.properties",
    "chars": 1353,
    "preview": "welcome=Bienvenido\nrequired=Es requerido\nnotFound=No ha sido encontrado\nduplicate=Ya se encuentra en uso\nnonNumeric=Sólo"
  },
  {
    "path": "src/main/resources/messages/messages_fa.properties",
    "chars": 1120,
    "preview": "welcome=خوش آمدید\nrequired=الزامی\nnotFound=یافت نشد\nduplicate=قبلا استفاده شده\nnonNumeric=باید عددی باشد\nduplicateFormSu"
  },
  {
    "path": "src/main/resources/messages/messages_ko.properties",
    "chars": 895,
    "preview": "welcome=환영합니다\nrequired=입력이 필요합니다\nnotFound=찾을 수 없습니다\nduplicate=이미 존재합니다\nnonNumeric=모두 숫자로 입력해야 합니다\nduplicateFormSubmissio"
  },
  {
    "path": "src/main/resources/messages/messages_pt.properties",
    "chars": 1385,
    "preview": "welcome=Bem-vindo\nrequired=E necessario\nnotFound=Nao foi encontrado\nduplicate=Ja esta em uso\nnonNumeric=Deve ser tudo nu"
  },
  {
    "path": "src/main/resources/messages/messages_ru.properties",
    "chars": 1289,
    "preview": "welcome=Добро пожаловать\nrequired=необходимо\nnotFound=не найдено\nduplicate=уже используется\nnonNumeric=должно быть все ч"
  },
  {
    "path": "src/main/resources/messages/messages_tr.properties",
    "chars": 1225,
    "preview": "welcome=hoş geldiniz\nrequired=gerekli\nnotFound=bulunamadı\nduplicate=zaten kullanılıyor\nnonNumeric=sadece sayısal olmalıd"
  },
  {
    "path": "src/main/resources/static/resources/css/petclinic.css",
    "chars": 278931,
    "preview": "/*\n * Copyright 2016 the original author or authors.\n *\n * You may obtain a copy of the License at\n *\n *      https://ww"
  },
  {
    "path": "src/main/resources/templates/error.html",
    "chars": 776,
    "preview": "<!DOCTYPE html>\n\n<html xmlns:th=\"https://www.thymeleaf.org\" th:replace=\"~{fragments/layout :: layout (~{::body},'error')"
  },
  {
    "path": "src/main/resources/templates/fragments/inputField.html",
    "chars": 1065,
    "preview": "<!DOCTYPE html>\n\n<html xmlns:th=\"https://www.thymeleaf.org\">\n\n<body>\n  <form>\n    <th:block th:fragment=\"input (label, n"
  },
  {
    "path": "src/main/resources/templates/fragments/layout.html",
    "chars": 3211,
    "preview": "<!DOCTYPE html>\n\n<html th:fragment=\"layout (template, menu)\" xmlns:th=\"https://www.thymeleaf.org\">\n\n<head>\n\n  <meta http"
  },
  {
    "path": "src/main/resources/templates/fragments/selectField.html",
    "chars": 980,
    "preview": "<!DOCTYPE html>\n\n<html xmlns:th=\"https://www.thymeleaf.org\">\n\n<body>\n  <form>\n    <th:block th:fragment=\"select (label, "
  },
  {
    "path": "src/main/resources/templates/owners/createOrUpdateOwnerForm.html",
    "chars": 1073,
    "preview": "<!DOCTYPE html>\n\n<html xmlns:th=\"https://www.thymeleaf.org\" th:replace=\"~{fragments/layout :: layout (~{::body},'owners'"
  },
  {
    "path": "src/main/resources/templates/owners/findOwners.html",
    "chars": 1150,
    "preview": "<!DOCTYPE html>\n\n<html xmlns:th=\"https://www.thymeleaf.org\" th:replace=\"~{fragments/layout :: layout (~{::body},'owners'"
  },
  {
    "path": "src/main/resources/templates/owners/ownerDetails.html",
    "chars": 2887,
    "preview": "<!DOCTYPE html>\n\n<html xmlns:th=\"https://www.thymeleaf.org\" th:replace=\"~{fragments/layout :: layout (~{::body},'owners'"
  },
  {
    "path": "src/main/resources/templates/owners/ownersList.html",
    "chars": 2423,
    "preview": "<!DOCTYPE html>\n\n<html xmlns:th=\"https://www.thymeleaf.org\" th:replace=\"~{fragments/layout :: layout (~{::body},'owners'"
  },
  {
    "path": "src/main/resources/templates/pets/createOrUpdatePetForm.html",
    "chars": 1230,
    "preview": "<!DOCTYPE html>\n\n<html xmlns:th=\"https://www.thymeleaf.org\" th:replace=\"~{fragments/layout :: layout (~{::body},'owners'"
  },
  {
    "path": "src/main/resources/templates/pets/createOrUpdateVisitForm.html",
    "chars": 1788,
    "preview": "<!DOCTYPE html>\n\n<html xmlns:th=\"https://www.thymeleaf.org\" th:replace=\"~{fragments/layout :: layout (~{::body},'owners'"
  },
  {
    "path": "src/main/resources/templates/vets/vetList.html",
    "chars": 2192,
    "preview": "<!DOCTYPE html>\n\n<html xmlns:th=\"https://www.thymeleaf.org\" th:replace=\"~{fragments/layout :: layout (~{::body},'vets')}"
  },
  {
    "path": "src/main/resources/templates/welcome.html",
    "chars": 375,
    "preview": "<!DOCTYPE html>\n\n<html xmlns:th=\"https://www.thymeleaf.org\" th:replace=\"~{fragments/layout :: layout (~{::body},'home')}"
  },
  {
    "path": "src/main/scss/header.scss",
    "chars": 1386,
    "preview": ".navbar {\n  border-top: 4px solid #6db33f;\n  background-color: #34302d;\n  margin-bottom: 0px;\n  border-bottom: 0;\n  bord"
  },
  {
    "path": "src/main/scss/petclinic.scss",
    "chars": 4403,
    "preview": "/*\n * Copyright 2016 the original author or authors.\n *\n * You may obtain a copy of the License at\n *\n *      https://ww"
  },
  {
    "path": "src/main/scss/responsive.scss",
    "chars": 678,
    "preview": "@media (max-width: 768px) {\n  .navbar-toggle {\n    position:absolute;\n    z-index: 9999;\n    left:0px;\n    top:0px;\n  }\n"
  },
  {
    "path": "src/main/scss/typography.scss",
    "chars": 1472,
    "preview": "@font-face {\n  font-family: 'varela_roundregular';\n\n  src: url('../fonts/varela_round-webfont.eot');\n  src: url('../font"
  },
  {
    "path": "src/test/java/org/springframework/samples/petclinic/MySqlIntegrationTests.java",
    "chars": 2645,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/test/java/org/springframework/samples/petclinic/MysqlTestApplication.java",
    "chars": 1529,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/test/java/org/springframework/samples/petclinic/PetClinicIntegrationTests.java",
    "chars": 2478,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/test/java/org/springframework/samples/petclinic/PostgresIntegrationTests.java",
    "chars": 5148,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/test/java/org/springframework/samples/petclinic/model/ValidatorTests.java",
    "chars": 2057,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/test/java/org/springframework/samples/petclinic/owner/OwnerControllerTests.java",
    "chars": 9292,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/test/java/org/springframework/samples/petclinic/owner/PetControllerTests.java",
    "chars": 7620,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/test/java/org/springframework/samples/petclinic/owner/PetTypeFormatterTests.java",
    "chars": 2616,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/test/java/org/springframework/samples/petclinic/owner/PetValidatorTests.java",
    "chars": 2833,
    "preview": "/*\n * Copyright 2012-2024 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/test/java/org/springframework/samples/petclinic/owner/VisitControllerTests.java",
    "chars": 3163,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/test/java/org/springframework/samples/petclinic/service/ClinicServiceTests.java",
    "chars": 8289,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/test/java/org/springframework/samples/petclinic/service/EntityUtils.java",
    "chars": 1901,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/test/java/org/springframework/samples/petclinic/system/CrashControllerIntegrationTests.java",
    "chars": 4213,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/test/java/org/springframework/samples/petclinic/system/CrashControllerTests.java",
    "chars": 1326,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/test/java/org/springframework/samples/petclinic/system/I18nPropertiesSyncTest.java",
    "chars": 4231,
    "preview": "package org.springframework.samples.petclinic.system;\n\nimport org.junit.jupiter.api.Test;\n\nimport java.nio.file.Files;\ni"
  },
  {
    "path": "src/test/java/org/springframework/samples/petclinic/vet/VetControllerTests.java",
    "chars": 3168,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/test/java/org/springframework/samples/petclinic/vet/VetTests.java",
    "chars": 1313,
    "preview": "/*\n * Copyright 2012-2025 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/test/jmeter/petclinic_test_plan.jmx",
    "chars": 32036,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<jmeterTestPlan version=\"1.2\" properties=\"5.0\" jmeter=\"5.5\">\n  <hashTree>\n    <Te"
  }
]

// ... and 1 more files (download for full content)

About this extraction

This page contains the full source code of the spring-projects/spring-petclinic GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 114 files (560.2 KB), approximately 163.6k tokens, and a symbol index with 251 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!