Java 3 - 2026

Week 1

Lesson 1 - Setup Pet Clinic App. Deployment using Docker.
Lesson 2 - Create, Read, Update, Delete (CRUD) functionality.
Lesson 3 - Homepage with content from the database. Custom not found and error pages.
Lesson 4 - Users, roles, and permissions setup from Java 2. User Registration.
Lesson 5 - Login and logout. Cookies. User permission access.

Lesson 6 - Edit and delete user profile. Password reset.
Lesson 7 - Pagination. Filtering and limiting records by category.
Lesson 8 - Web Sockets.
Lesson 9 - Internationalization. Date and currency formatting.
Lesson 10 - Email and SMS messaging. Failed login attempts.
Lesson 11 - Shopping Cart. Payment processing.

Course Plan

  • Modify the Pet Clinic application based on the user stories and personas you created in Java 2.
  • Derive an entity-relationship diagram and SQL queries for the data you will collect.
  • Create user interface drawings and wireframes for all web layouts.
  • Write use case narratives and build diagrams for each feature you add.
  • Write unit tests as necessary.

Expectations

  • Clone the Spring Pet Clinic project using IntelliJ.
  • Open it as a Gradle project.
  • Close the README.md file.
  • Open src/main/java/.../PetClinicApplication.java
  • A yellow bar will display asking you to install JDK 17.
  • Use Microsoft OpenJDK locally since we will deploy to Azure

Pet Clinic Project

  • Wait while the JDK downloads.

This is correct setup      This is not

Possible solutions if not correct

  • Close and re-open the project.
  • If this warning displays, click "Install required toolchain".
  • Press the Reload Gradle icon if it displays in the upper-right corner.

Run the app

  • Ignore any messages in the bottom-right corner.
  • Click the run button.
  • The Terminal should say that
    Tomcat started on port 8080

View and interact with the app

2027

  • Keep the MYSQL variables. 
  • Add variables to the configurations
# database init, supports mysql too
database=mysql
spring.datasource.url=${MYSQL_URL}
spring.datasource.username=${MYSQL_USER}
spring.datasource.password=${MYSQL_PASS}
# SQL is written to be idempotent so this is safe
spring.sql.init.mode=always
# This tells Spring to run the scripts found in the db folder
spring.sql.init.schema-locations=classpath:db/mysql/schema.sql
spring.sql.init.data-locations=classpath:db/mysql/data.sql
# JPA / Hibernate settings
spring.jpa.hibernate.ddl-auto=none

application-mysql.properties

  • Stop the program.
  • By default, PetClinic uses an in-memory H2 database.
  • You need to tell it to use your external MySQL database instead.
  • In IntelliJ, open the file:
    src/main/resources/application-mysql.properties
  • Replace the default settings with your web-based database details from Java 2.
# database init, supports mysql too
database=mysql
full.jdbc.connectionstring=jdbc:mysql://YOUR_WEB_DB_HOST:3306/YOUR_DB_NAME?user=YOUR_DB_USERNAME&password=YOUR_DB_PASSWORD&sslmode=disabled
spring.datasource.url=jdbc:mysql://YOUR_WEB_DB_HOST8:3306/YOUR_DB_NAME?useSSL=true
spring.datasource.username=YOUR_DB_USERNAME
spring.datasource.password=YOUR_DB_PASSWORD
# SQL is written to be idempotent so this is safe
spring.sql.init.mode=always

MySQL Database

application-mysql.properties

  • Add the following to the application-mysql.properties file.
# This tells Spring to run the scripts found in the db folder
spring.sql.init.schema-locations=classpath:db/mysql/schema.sql
spring.sql.init.data-locations=classpath:db/mysql/data.sql

# JPA / Hibernate settings
spring.jpa.hibernate.ddl-auto=none
  • Because you set spring.sql.init.mode=always, Spring Boot will attempt to run schema.sql and data.sql (located in src/main/resources/db/mysql) when the app starts.
  • Edit the Run configurations.
  •  

application-mysql.properties

  • Edit the Run configurations.
  • Add a Spring Boot configuration with the following settings.
  • Run the app and visit localhost:8080
  • Refresh the database to see the new tables.

Containerize the Application

  • To deploy to all the clouds you listed (Azure, AWS, GCP, etc.), the universal standard is Docker.
  • Create a file named Dockerfile in the root of your project. Add the following.
  • By default, Gradle might generate two jars (a "plain" jar and a "boot" jar). Adding -x jar ensures only the executable "boot" jar is created, so the COPY command doesn't get confused by multiple files.
# Stage 1: Build the application
# We use the full JDK image to compile the code
FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS build
WORKDIR /app
COPY . .
# Gradle build command (skipping tests and plain jar creation)
RUN ./gradlew clean bootJar -x test -x jar

# Stage 2: Run the application
# Microsoft provides a lightweight runtime for deployment
FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu
WORKDIR /app
COPY --from=build /app/build/libs/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-Dspring.profiles.active=mysql", "-jar", "app.jar"]

Build and Run the Image

  • Ensure that your Docker Desktop application is running by looking for the icon in your system tray (near the clock). 
  • Login to DockerHub. https://hub.docker.com/
  • Run this command to login via the terminal
    docker login
  • Go to https://login.docker.com/activate and enter the code shown in the terminal.
  • Run this command in the Terminal to build the Docker image:
    docker build -t dockerhub-username/image-name:version-name .
    for example, I could write this...
    docker build -t mlhaus/petclinic:v1 .
  • Open the Docker Desktop application and click the "Images" tab to confirm it was built. 
  • The -t tags the build. In Docker, a "tag" is like a sticky note on a specific version of your built files.

Build and Run the Image

  • Run the image with this command to map port 8099 on our host machine to port 8080 in the Docker container.
  • docker run -p 8099:8080 image-name
  • Visit http://localhost:8099/ to confirm that the application is running.
  • Press Ctrl + C to terminate the app.

STOP!

  • Our passwords are currently written inside application-mysql.properties. If you push that image to Docker Hub, anyone can download your image and see your passwords.

  • We must remove the secrets from your code, rebuild the image, and then provide the secrets only when the container runs.

  • Open src/main/resources/application-mysql.properties.

  • Replace your hardcoded real passwords with Spring Boot placeholders.

  • Use MYSQL_PASS or MYSQL_PASSWORD consistently

spring.datasource.url=${MYSQL_URL}
spring.datasource.username=${MYSQL_USER}
spring.datasource.password=${MYSQL_PASS}

IntelliJ Environment Variables

  • When running the program the app will look for environment variables with those names. If it doesn't find one, it will fail.

  • Open the Edit Configurations window. Click "Modify options" and choose "Environment Variables".

  • Click the "Edit Environment Variables" button. Enter these variables and your values.
    MYSQL_URL
    MYSQL_USER
    MYSQL_PASS

  • Run the program and visit http://localhost:8080

Rebuild and Rerun

  • Run this command in the Terminal to rebuild the Docker image:
    docker build -t dockerhub-username/image-name:version-name .
  • Run the image locally with this command. You use the -e flag to inject the values for the placeholders
    docker run -p 8099:8080 `
      -e MYSQL_URL="jdbc:mysql://your-db-host:3306/your-db-name?useSSL=true" `
      -e MYSQL_USER="your-db-username" `
      -e MYSQL_PASS="your-db-password
    " `
    dockerhub-username/image-name:version-name
  • The backticks are for multi-line commands in PowerShell.

Rebuild and Rerun

Containerize the Application

  • Run this command to see all of the images built and tagged:
    docker images
  • While you are testing, overwriting v1 over and over is fine. But for real deployments, it is better to increment the number so you can "roll back" if something breaks.
    • Build 1: docker build -t mlhaus/petclinic:v1 .

    • Build 2: docker build -t mlhaus/petclinic:v2 .

    • Build 3: docker build -t mlhaus/petclinic:v3 .

  • This way, if v3 has a bug, you can instantly tell your cloud provider to run v2 instead.

  • Avoid using the static :latest tag; use a unique tag (possibly a Git commit hash or timestamp) for proper tracking and deployment.

2027

No Log Analytics workspace provided.
Generating a Log Analytics workspace with name "workspace-springpatriotthanksrgCNbO"
(RequestDisallowedByAzure) Resource 'workspace-springpatriotthanksrgCNbO' was disallowed by Azure: This policy maintains a set of best available regions where your subscription can deploy resources. The objective of this policy is to ensure that your subscription has full access to Azure services with optimal performance. Should you need additional or different regions, contact support.
Code: RequestDisallowedByAzure                                                                                                                                                
Message: Resource 'workspace-springpatriotthanksrgCNbO' was disallowed by Azure: This policy maintains a set of best available regions where your subscription can deploy resources. The objective of this policy is to ensure that your subscription has full access to Azure services with optimal performance. Should you need additional or different regions, contact support.
Target: workspace-springpatriotthanksrgCNbO          

 

This is what I used to ask Claude to help me find out the issue. The response included this:

This error is happening because your Azure subscription (likely a student/educational subscription) has a policy restriction that limits which regions you can deploy to. The eastus region isn't allowed for your subscription.

az policy assignment list --query "[].{Name:displayName, Parameters:parameters}" -o json | grep -A 20 "Allowed resource deployment regions"

 

 

No Log Analytics workspace provided.
Generating a Log Analytics workspace with name "workspace-favoritebooksrgoT5m"
(ResourceNotFound) The Resource 'Microsoft.OperationalInsights/workspaces/workspace-favoritebooksrgoT5m' under resource group 'favoritebooks-rg' was not found. For more details please go to https://aka.ms/ARMResourceNotFoundFix
Code: ResourceNotFound
Message: The Resource 'Microsoft.OperationalInsights/workspaces/workspace-favoritebooksrgoT5m' under resource group 'favoritebooks-rg' was not found. For more details please go to https://aka.ms/ARMResourceNotFoundFix

i am using westus because i tried eastus, eastus2, and centralus as well and they all had the same result, I made sure to delete and recreate the groups each time with the different location and nothing changed so not sure what to do

Microsoft Azure Student

Microsoft Azure Free Plans

  • Azure does offer a Free F1 web app plan.
    https://azure.microsoft.com/en-us/pricing/details/app-service/linux/
  • Read FAQs about Container App free pricing.
    https://azure.microsoft.com/en-us/pricing/details/container-apps/#faq
  • For a free deployment of your custom Docker image, you should choose Azure Container Apps.
    • The "F1" free tier only supports code deployments, not Docker images. To run a container, you typically need the paid "Basic" tier.
    • Azure Container Apps use a consumption model. You get a monthly free allowance of 180,000 vCPU-seconds and 360,000 GiB-seconds, which is enough for a low-traffic test app. You can scale it to zero to pause your app when no one is using it to save money.

Allowed deployment regions for student subscriptions

Allowed deployment regions for student subscriptions

  • Examine the "Allowed locations" Parameter value for list of regions

  • The regions shown are the ones you are allowed to deploy resources to. Please create resources in one of these regions.

  • Regions in the screenshot may be different than yours.

Allowed deployment regions

  • You can also try one of these commands in the terminal.
    • az policy assignment list
    • az policy assignment list --query "[].{Name:displayName, Parameters:parameters}" -o json | grep -A 20 "Allowed resource deployment regions"
  • If you get an empty array ([]), it might mean there are no explicit allowed-locations policies assigned at the subscription or resource group level, or that this policy is managed internally by Microsoft and is not visible from your end.

Create Resource Group

  • Create a Resource Group. Think of this as a folder for your project. Replace project-name as you continue.
  • az group create \
      --name project-name-rg \
      --location eastus2
  • To split long commands over multiple lines, use a backslash \ for Git Bash or a backtick ` for PowerShell.
  • Replace useast2 with northcentralus or an allowed location.

Create Container App Environment

  • Create the Container App Environment. This is the secure boundary around your app.
  • az containerapp env create \
      --name project-name-env \
      --resource-group project-name-rg \
      --location eastus2
  • To split long commands over multiple lines, use a backslash \ for Git Bash or a backtick ` for PowerShell.
  • This process will take several minutes to deploy.

Create/Deploy Container App

  • This command pulls your image from Docker Hub and launches it as an Azure Container App. Replace dockerhub-username and your actual DB details below. Read the next page before running.
    az containerapp create \
      --name project-name-app \
      --resource-group project-name-rg \
      --environment project-name-env \
      --image dockerhub-username/image-name:v1 \
      --target-port 8080 \
      --ingress 'external' \
      --min-replicas 1 \
      --env-vars MYSQL_URL="jdbc:mysql://your-db-host:3306/your-db-name?useSSL=true" MYSQL_USER="your-db-user" MYSQL_PASS="your-db-password"

Create/Deploy Container App

  • --env-vars: We need to pass the environment variables so your app can connect to your MySQL database.
  • --ingress 'external': Makes your app accessible via a public URL.

  • --target-port 8080: Tells Azure your container listens on port 8080 (standard for Spring Boot).

  • --min-replicas 1: Keeps at least one instance running (so it doesn't "sleep" or take 20 seconds to wake up). If you want to be purely free and don't mind the "cold start" delay, change this to 0.

  • After running the command, yellow text will display in the terminal with a URL like this:
     https://project-name-app.redpond-578f1b00.eastus.azurecontainerapps.io/

  • You can deploy other petclinic apps from the Spring Community.

Container Status

  • Run this command to see the status of the container:
    az containerapp replica list \
      --name project-name-app \
      --resource-group project-name-rg

  • If RunningState is "Running", you are good to go!

  • If you changed min-replicas to 0 and don't like the cold start delay, set it to 1 with this command.
    az containerapp update \
      --name project-name-app \
      --resource-group project-name-rg \
      --min-replicas 1

Live Log Stream

  • Run this command to view the live log stream directly from your terminal.
    az containerapp logs show \
      --name project-name-app \
      --resource-group project-name-rg \
      --follow

  • You might see a prompt to install the containerapp extension if it’s not already there. Type Y and press Enter.
  • After a few seconds, you will see the scrolling logs of your Spring Boot application. Look for any errors or warnings.
  • If the logs command returns an error about "Log Analytics," it means the quick-start command didn't auto-link a workspace for streaming.
  • Press Ctrl + C to stop the stream and return to your command prompt.

Cloud Services

  • You can now move on to deploying this same image to Google Cloud Run or AWS App Runner to compare the experience.
  • Here is the cheat sheet for popular cloud services:

Cloud Provider Service to Use Deployment Strategy
Microsoft Azure Azure Spring Apps Native Spring support. It can build the source directly or run your Docker image.
Google Cloud Cloud Run Serverless. Push image to Google Artifact Registry, then gcloud run deploy.
AWS AWS App Runner Easiest for single containers. Point it to your Docker Hub image, and it handles load balancing/SSL.
Heroku Dynos Use the Heroku CLI: heroku container:push web then heroku container:release web.
Digital Ocean App Platform Connect your GitHub repo; it detects the Dockerfile and deploys automatically.
Red Hat OpenShift OpenShift Deployment Use oc new-app pointing to your Docker image or Source-to-Image (S2I).

Java 3 - Week 1

By Marc Hauschildt

Java 3 - Week 1

  • 167