Stephen's Petitions is a Spring Boot web application for creating, viewing, searching, and signing petitions.
It was developed as part of the CT5209 Cloud DevOps module and demonstrates a Continuous Integration and Continuous Deployment (CI/CD) workflow using GitHub, Jenkins, Maven, Docker, and deployment to a Dockerized Apache Tomcat container on Amazon Web Services (AWS) Elastic Compute Cloud (EC2).
For reviewers: Start with v1.3.0 – Cypress E2E Review Snapshot for the latest stable employer-facing snapshot.
Themainbranch may include later refinements after this release.
Earlier review snapshots remain available as v1.2.0, v1.1.0, and v1.0.0.
The following improvements were bundled into v1.3.0:
- Added Cypress end-to-end browser automation for key petition flows
- Added stable
data-cyselectors to support maintainable browser tests - Added local npm scripts for interactive Cypress runs and CI-style end-to-end execution
- Added a GitHub Actions workflow for Cypress end-to-end testing
- Expanded the README to document Cypress coverage, local execution, and reviewer guidance
The following improvements were bundled into v1.2.0:
- Added Spring Boot Actuator and Micrometer Prometheus registry
- Exposed
/actuator/healthand/actuator/prometheus - Added Prometheus scrape configuration under
observability/prometheus.yml - Added Docker Compose services for Prometheus, Grafana, and Jaeger
- Added OpenTelemetry Spring Boot starter for automatic tracing
- Configured OTLP trace export from the application to Jaeger
- Added observability evidence screenshots for Prometheus, Grafana, and Jaeger
- Expanded the README to document the local observability workflow
-
v1.3.0 – Cypress E2E Review Snapshot
Stable employer-facing snapshot adding Cypress browser automation, local npm-based end-to-end execution, GitHub Actions workflow support, and README updates for reviewer clarity. -
v1.2.0 – Observability Review Snapshot
Stable review snapshot adding Spring Boot Actuator, Prometheus, Grafana, OpenTelemetry, Jaeger, and README evidence for local observability. -
v1.1.0 – Review Snapshot
Stable assessed snapshot with Dockerized Apache Tomcat deployment on AWS EC2, a refined Jenkins pipeline, improved README deployment evidence, refreshed Jenkins screenshots, and a clearer empty-state message on the search results page. -
v1.0.0 – Stable CI/CD Release for Petitions App
Initial stable release of the Spring Boot petitions app with Thymeleaf UI, JUnit and MockMvc testing, WAR packaging, Jenkins-based CI/CD, and deployment to Apache Tomcat on AWS EC2.
- Application URL: http://13.49.44.175:9090/stephenspetitions/
- Create a petition
- View all petitions
- View petition details
- Sign a petition with name and email
- Search petitions by keyword
- Landing page with navigation
- Java 17
- Spring Boot
- Thymeleaf
- Maven
- JUnit and MockMvc
- Cypress
- npm and start-server-and-test
- GitHub Actions
- Jenkins
- GitHub
- Docker
- Apache Tomcat 10
- AWS EC2
- Cloudflare Tunnel
The project now includes a local observability demo using Spring Boot Actuator, Prometheus, Grafana, OpenTelemetry, and Jaeger.
- Spring Boot Actuator and Micrometer Prometheus registry
/actuator/healthand/actuator/prometheusendpoints- Prometheus scrape configuration under
observability/prometheus.yml - Prometheus, Grafana, and Jaeger services defined in
observability/docker-compose.yml - OpenTelemetry Spring Boot starter for automatic tracing
- OTLP trace export from the application to Jaeger
- Manual Grafana dashboard creation using Prometheus as the data source
- Application metrics exposure from Spring Boot
- Prometheus scraping of live application metrics
- Grafana dashboard visualisation for operational monitoring
- Automatic HTTP request tracing with OpenTelemetry
- Trace collection and search in Jaeger
- A practical first step into observability for Java and platform-oriented roles
Prometheus target status:
Grafana dashboard - System CPU Usage:
Grafana dashboard - Two panels:
Jaeger trace proof:
The observability setup is local only and is not part of the EC2 deployment shown elsewhere in this project.
The observability stack is intended for local demonstration and learning. It uses Prometheus, Grafana, and Jaeger alongside the Spring Boot application.
To run these checks locally, make sure the following are installed and working on your machine:
-
Java 17
Required to run the Spring Boot application. -
Docker Desktop
Required to run Prometheus, Grafana, and Jaeger fromobservability/docker-compose.yml. -
A web browser
Required to open the application, Prometheus, Grafana, and Jaeger locally. -
Internet access for the first Docker pull
Docker may need to download the container images the first time you run the observability stack.
You do not need to install Maven separately because this project includes the Maven Wrapper:
mvnw.cmdfor Windowsmvnwfor macOS / Linux
From the project root, you can verify the basics with the following commands.
java -version
docker versionjava -version
docker versionExpected outcome:
java -versionshould show Java 17docker versionshould return both Client and Server
From the project root, run:
docker compose -f observability/docker-compose.yml up -ddocker compose -f observability/docker-compose.yml up -dThis starts:
- Prometheus on
http://localhost:9090 - Grafana on
http://localhost:3000 - Jaeger on
http://localhost:16686
Grafana default local demo credentials for a fresh container startup:
- Username:
admin - Password:
admin
On first login to a fresh Grafana instance, Grafana may prompt you to change the default password.
From the project root, run:
.\mvnw.cmd spring-boot:run./mvnw spring-boot:runThe application runs locally on:
http://localhost:8080
Useful local routes for generating requests and traces:
http://localhost:8080/petitions
http://localhost:8080/create
http://localhost:8080/search
Open:
http://localhost:8080/actuator/health
http://localhost:8080/actuator/prometheus
Then open Prometheus and confirm the Spring Boot application target is up:
http://localhost:9090
Open Jaeger:
http://localhost:16686
Select the service:
ct5209-springboot-war
Then visit a few application routes such as /petitions, /create, and /search.
Return to Jaeger and click Find Traces.
Open one returned trace to confirm the request spans are visible.
When finished, stop the local observability stack with:
docker compose -f observability/docker-compose.yml downThe diagram below shows the application runtime flow, the assessed Jenkins deployment path, and the Cypress browser-automation paths used locally and in GitHub Actions.
flowchart LR
DEV[Developer]
GH[GitHub Repository]
U[User Browser]
subgraph LOCAL["Local browser automation"]
NPM[npm run cy:open<br/>npm run e2e]
LS[Local Spring Boot app<br/>http://localhost:8080]
LCY[Cypress E2E]
end
subgraph CI["GitHub Actions browser automation"]
GHA[GitHub Actions<br/>Cypress E2E workflow]
CIAPP[Temporary Spring Boot app]
CICY[Cypress headless run]
end
subgraph DEPLOY["Assessed deployment path"]
CF[Cloudflare Tunnel]
J[Jenkins Pipeline]
B[Maven build, test, and package]
AT[Automated tests<br/>JUnit, MockMvc, Spring Boot context]
W[WAR artifact<br/>stephenspetitions.war]
DG[Dockerfile]
AP[Manual deployment approval]
end
subgraph EC2["AWS EC2"]
DE[Docker Engine]
IMG[Docker image<br/>stephenspetitions:latest]
T[Tomcat container<br/>host 9090 → container 8080]
subgraph APP["Spring Boot application"]
C[PetitionController]
S[PetitionService<br/>business logic]
M[Petition and Signature models<br/>in-memory seeded list]
V[Thymeleaf templates<br/>petitions.html<br/>petition.html<br/>create.html<br/>search.html<br/>results.html]
end
end
DEV -->|git push| GH
DEV -->|run locally| NPM
NPM -->|starts app| LS
NPM -->|runs browser tests| LCY
LCY -->|checks local flows| LS
GH -->|push or pull request| GHA
GHA -->|npm ci + npm run e2e| CIAPP
GHA --> CICY
CICY -->|checks browser flows| CIAPP
DEV -->|browser access| CF
GH -->|webhook or manual Jenkins run| J
CF -->|public access to Jenkins| J
J --> B
B --> AT
B --> W
J --> AP
DG -->|copied to EC2| DE
W -->|copied to EC2| DE
AP -->|deploy approved| DE
DE -->|docker build| IMG
IMG -->|docker run -p 9090:8080| T
U -->|HTTP request| T
T --> C
C --> S
S --> M
C --> V
T -->|HTML response| U
src/main/java/com/example/demo
├── controller # Web layer (PetitionController)
├── service # Business logic (PetitionService)
└── model # Domain objects (Petition, Signature)
src/test/java/com/example/demo
├── PetitionControllerTest
├── PetitionServiceTest
└── DemoApplicationTests
repo root
├── .github/workflows/cypress-e2e.yml
├── cypress/e2e/petitions/petitions.cy.js
├── cypress.config.js
├── Jenkinsfile
├── Dockerfile
├── package.json
├── pom.xml
└── README.md
Jenkins is used to automate the build, test, packaging, and deployment workflow for the project.
The repository contains a Jenkins pipeline in Jenkinsfile with the following stages: GetProject, Build, Test, Package, Archive, ApproveDeploy, and Deploy.
In addition, the repository now includes a GitHub Actions workflow for Cypress browser-based end-to-end testing. This lightweight workflow complements the existing Jenkins pipeline without changing the original assessed deployment path.
The pipeline workflow is:
- Get the latest code from GitHub
- Build the application with Maven
- Run automated tests
- Package the application as a WAR file
- Archive the WAR artifact in Jenkins
- Pause for manual deployment approval
- Copy the Dockerfile and WAR file to EC2
- Build a Docker image on EC2
- Run the application as a Tomcat container on EC2
Jenkins was made securely reachable from the internet using a Cloudflare Tunnel. This supported browser access to the Jenkins dashboard and webhook configuration without directly exposing the server through open inbound ports.
The screenshot below shows a successful Jenkins pipeline run from the main branch, including manual approval and Docker-based deployment to EC2.
The project includes automated tests across multiple layers.
- Retrieve seeded petitions
- Search petitions by keyword
- Add a new petition
- Add a signature to a petition
- Verify the
/petitionspage loads successfully
- Verify the Spring Boot application context loads
The project now includes a small Cypress browser automation slice for higher-level end-to-end verification.
Current Cypress coverage includes:
- Create a new petition through the browser and confirm it appears in the petitions list
- Search for a newly created petition and confirm it appears in the search results
This Cypress suite uses stable data-cy selectors and is intended as a lightweight browser-level complement to the existing JUnit and MockMvc tests.
The screenshot below shows the Cypress end-to-end browser checks passing locally against the Spring Boot application.
The application can be started locally using the Maven wrapper from the project root directory.
If you want to run the Cypress browser checks, install the Node dependencies once from the project root first:
npm installOpen a terminal in the project root directory and run:
.\mvnw.cmd spring-boot:run./mvnw spring-boot:runThen open:
http://localhost:8080/
Useful local routes:
http://localhost:8080/petitions
http://localhost:8080/create
http://localhost:8080/search
When run locally through Spring Boot, the application is served from the root context, for example /petitions.
When deployed as a WAR file inside Tomcat on EC2, the WAR file name becomes the Tomcat context path, so the deployed application is reached under /stephenspetitions.
From the project root, you can run the Cypress browser checks with the following commands.
npm run cy:opennpm run cy:opennpm run e2enpm run e2eThe Cypress checks target the local Spring Boot root context at http://localhost:8080, not the deployed Tomcat WAR context path under /stephenspetitions.
The deployed version runs in a Dockerized Apache Tomcat container on AWS EC2 and is updated through the Jenkins pipeline after manual approval.
Deployment flow:
- Code is pushed to GitHub, where Jenkins can be triggered by webhook or run manually from Jenkins
- Jenkins retrieves the latest code and runs build and test stages
- The application is packaged as
stephenspetitions.war - The WAR artifact is archived in Jenkins
- Deployment proceeds after manual approval
- Jenkins copies
Dockerfileandstephenspetitions.warto the EC2 host - Jenkins stops and disables the host-level
tomcat10service to free port9090for the containerized deployment - Jenkins builds the Docker image on EC2
- Jenkins runs the container with port mapping
9090:8080
For the deployed version, the WAR file name provides the Tomcat context path, so the live application is reached at /stephenspetitions on port 9090.
This section provides a quick way to review the main features, structure, testing, and deployment evidence of the project.
- Open the live application
- Use the navigation links to:
- View all petitions
- Create a petition
- Search petitions
- Open a petition and sign it
- Review
Jenkinsfilefor the pipeline stages and deployment logic - Review
.github/workflows/cypress-e2e.ymlfor the Cypress browser automation workflow - Review
cypress/e2e/petitions/petitions.cy.jsfor the end-to-end browser checks - Review
Dockerfilefor the Tomcat container image setup - Inspect the commit history to see iterative development
- Review the architecture diagram, testing notes, and Jenkins pipeline evidence in this README
This section summarises the main technical challenges encountered during development and what was learned from resolving them.
Key challenges included:
- Structuring the application pages and navigation cleanly
- Configuring Jenkins pipeline stages correctly
- Packaging the project as a WAR file for Tomcat deployment
- Converting the deployment flow from a host Tomcat service to a Dockerized Tomcat container on EC2
- Debugging GitHub webhook triggering and remote pipeline behaviour
- Validating manual deployment approval and deployment to AWS EC2
- Adding tests across service and controller layers while keeping the application stable
This project provided practical experience in web development, Continuous Integration, Continuous Deployment, testing, containerized deployment, cloud deployment, and troubleshooting.
The following items outline realistic next steps to extend the application beyond the current scope.
- Add a more persistent database option such as MySQL/PostgreSQL or AWS Relational Database Service (RDS), while retaining H2 for lightweight local development
- Improve the user interface styling and usability
- Expand controller and integration test coverage
- Add form validation and error handling
- Add authentication and user accounts





