JAX-WS Loan Approval System
A complete JAX-WS SOAP web services application for loan processing, featuring customer registration, credit score evaluation, and loan approval workflows with SQLite database persistence.
Quick Reference Card
🚀 Three Ways to Use This Project
| Task | Command | Result |
|---|---|---|
| Run Tests | docker-compose -f docker-compose.test.yml up --build |
Tests run, results in ./target/surefire-reports/ |
| Generate Coverage | docker-compose -f docker-compose.coverage.yml up --build |
Coverage report in ./target/site/jacoco/index.html |
| Start Server | docker-compose up -d |
Server running at http://localhost:8080 |
📝 Convenience Scripts
| Platform | Run Tests | Generate Coverage |
|---|---|---|
| Windows | run-tests.bat |
run-coverage.bat |
| Linux/Mac | ./run-tests.sh |
./run-coverage.sh |
Project Details
Overview
This project demonstrates a production-ready JAX-WS web service implementation using Java 8, Maven, and Apache Tomcat. The system provides three main SOAP services:
- Hello World Service - A simple demonstration service for testing SOAP connectivity
- Loan Approval Service - Complete loan processing system with:
- Customer registration with blacklist management
- Automated credit score evaluation
- Risk-based interest rate calculation
- Loan application processing and approval workflows
Technology Stack
- Java: 1.8 (JDK 8)
- Web Services: JAX-WS 2.3.1 (SOAP)
- Build Tool: Apache Maven 3.x
- Application Server: Apache Tomcat 9+
- Database: SQLite 3.43.0
- Containerization: Docker & Docker Compose
- Unit Testing: JUnit 5 (Jupiter), Mockito, JaCoCo
- Integration Testing: Python 3 with requests library
Key Features
- RESTful SOAP Services: Industry-standard JAX-WS implementation
- Database Persistence: SQLite database with automatic schema initialization
- Credit Risk Assessment: Tiered approval logic based on credit scores
- Blacklist Management: Customer screening and rejection system
- Docker Support: Fully containerized with one-command deployment
- Unit Testing: JUnit 5 tests with JaCoCo code coverage
- Integration Testing: Python test clients for all services
- Production-Ready: Singleton patterns, prepared statements, error handling
How to Run and Rebuild Using Docker Compose
Prerequisites
- Docker Desktop (Windows/Mac) or Docker Engine (Linux)
- Docker Compose (included with Docker Desktop)
Quick Start
Three Docker Compose Configurations
This project provides three Docker Compose files for different purposes:
| Purpose | File | Command | Output Location |
|---|---|---|---|
| 🚀 Start Server | docker-compose.yml |
docker-compose up -d |
http://localhost:8080 |
| ✅ Run Tests | docker-compose.test.yml |
docker-compose -f docker-compose.test.yml up --build |
./target/surefire-reports/ |
| 📊 Coverage Report | docker-compose.coverage.yml |
docker-compose -f docker-compose.coverage.yml up --build |
./target/site/jacoco/index.html |
Visual Workflow:
┌─────────────────────────────────────────────────────────────────┐
│ Development Workflow │
└─────────────────────────────────────────────────────────────────┘
1. Write Code
│
▼
┌─────────────────────────┐
│ Run Unit Tests │ ──► docker-compose -f docker-compose.test.yml up --build
│ (docker-compose.test) │
└──────────┬──────────────┘
│ Tests Pass ✓
▼
┌─────────────────────────┐
│ Generate Coverage │ ──► docker-compose -f docker-compose.coverage.yml up --build
│ (docker-compose.coverage│
└──────────┬──────────────┘ Open: ./target/site/jacoco/index.html
│ Coverage OK ✓
▼
┌─────────────────────────┐
│ Deploy Application │ ──► docker-compose up -d
│ (docker-compose.yml) │
└──────────┬──────────────┘ Access: http://localhost:8080
│
▼
Production Ready!
Quick Commands:
# Run all tests
docker-compose -f docker-compose.test.yml up --build && docker-compose -f docker-compose.test.yml down
# Generate coverage report
docker-compose -f docker-compose.coverage.yml up --build && docker-compose -f docker-compose.coverage.yml down
# Start application server
docker-compose up -d
Or use convenience scripts:
REM Windows
run-tests.bat
run-coverage.bat
docker-compose up -d
# Linux/Mac
./run-tests.sh
./run-coverage.sh
docker-compose up -d
1. Start the Application
docker-compose up -d
This command will:
- Build the Docker image with Maven multi-stage build
- Compile the Java application
- Package it as a WAR file
- Deploy to Tomcat
- Start the container in detached mode
Wait 30-40 seconds for Tomcat to fully start
2. Verify the Deployment
Access the following URLs in your browser:
- Hello World WSDL: http://localhost:8080/jaxws-hello-world/hello?wsdl
- Loan Service WSDL: http://localhost:8080/jaxws-hello-world/loan?wsdl
- Tomcat Manager: http://localhost:8080/manager (username:
admin, password:admin123)
3. View Application Logs
# Follow logs in real-time
docker-compose logs -f
# View recent logs
docker-compose logs --tail=100
# View logs for specific service
docker-compose logs jaxws-service
4. Stop the Application
docker-compose down
Docker Compose Patterns
This project includes three Docker Compose configurations for different purposes:
Pattern 1: Start Application Server
File: docker-compose.yml
Purpose: Deploy and run the JAX-WS application on Tomcat
# Start the server in detached mode
docker-compose up -d
# Start with build (after code changes)
docker-compose up -d --build
# View logs in real-time
docker-compose logs -f
# Stop the server
docker-compose down
What happens:
- Runs unit tests (Stage 1: tester)
- Builds WAR file (Stage 2: builder)
- Deploys to Tomcat (Stage 3)
- Exposes port 8080
- Creates/persists SQLite database
Access points:
- Hello World Service: http://localhost:8080/jaxws-hello-world/hello?wsdl
- Loan Service: http://localhost:8080/jaxws-hello-world/loan?wsdl
- Tomcat Manager: http://localhost:8080/manager (admin/admin123)
Pattern 2: Run Unit Tests
File: docker-compose.test.yml
Purpose: Execute JUnit tests and export results to ./target
# Run tests
docker-compose -f docker-compose.test.yml up --build
# Run tests in background
docker-compose -f docker-compose.test.yml up -d
# View test output
docker-compose -f docker-compose.test.yml logs
# Clean up
docker-compose -f docker-compose.test.yml down
What happens:
- Builds test environment (tester stage)
- Runs
mvn test - Executes all *Test.java files
- Exports test reports to
./target/surefire-reports/ - Exports coverage data to
./target/jacoco.exec
Test results location:
- Console output: Test summary with pass/fail counts
- Reports:
./target/surefire-reports/(text and XML)
Example output:
Tests run: 10, Failures: 0, Errors: 0, Skipped: 0
BUILD SUCCESS
Pattern 3: Generate Code Coverage Report
File: docker-compose.coverage.yml
Purpose: Run tests and generate JaCoCo coverage reports in ./target/site/jacoco/
# Generate coverage reports
docker-compose -f docker-compose.coverage.yml up --build
# Generate coverage in background
docker-compose -f docker-compose.coverage.yml up -d
# View coverage generation logs
docker-compose -f docker-compose.coverage.yml logs
# Clean up
docker-compose -f docker-compose.coverage.yml down
What happens:
- Builds test environment
- Runs
mvn testwith JaCoCo agent - Generates coverage reports in multiple formats
- Exports to
./target/site/jacoco/
Coverage reports location:
- HTML Report:
./target/site/jacoco/index.html← Open this in browser - XML Report:
./target/site/jacoco/jacoco.xml(for CI/CD) - CSV Report:
./target/site/jacoco/jacoco.csv(for spreadsheets)
Example output:
=================================================================================
Coverage report generated successfully!
=================================================================================
HTML Report: ./target/site/jacoco/index.html
XML Report: ./target/site/jacoco/jacoco.xml
CSV Report: ./target/site/jacoco/jacoco.csv
=================================================================================
Complete Workflow Example
Here's a typical development workflow using all three patterns:
# 1. Run tests to ensure code works
docker-compose -f docker-compose.test.yml up --build
docker-compose -f docker-compose.test.yml down
# 2. Generate coverage report to check test quality
docker-compose -f docker-compose.coverage.yml up --build
docker-compose -f docker-compose.coverage.yml down
# 3. View coverage report
# Open target/site/jacoco/index.html in browser
# 4. Deploy application if tests pass
docker-compose up -d --build
# 5. Verify deployment
curl http://localhost:8080/jaxws-hello-world/hello?wsdl
# 6. View application logs
docker-compose logs -f
# 7. Stop when done
docker-compose down
Quick Reference Scripts
For convenience, use the provided scripts instead of typing full commands:
| Task | Windows | Linux/Mac | Docker Compose Equivalent |
|---|---|---|---|
| Run Tests | run-tests.bat |
./run-tests.sh |
docker-compose -f docker-compose.test.yml up --build |
| Generate Coverage | run-coverage.bat |
./run-coverage.sh |
docker-compose -f docker-compose.coverage.yml up --build |
| Start Server | N/A | N/A | docker-compose up -d |
Example:
REM Windows
run-tests.bat
run-coverage.bat
REM Then start server
docker-compose up -d
# Linux/Mac
./run-tests.sh
./run-coverage.sh
# Then start server
docker-compose up -d
Rebuild After Code Changes
When you modify the source code, rebuild and restart:
# Rebuild and restart
docker-compose up -d --build
# Or rebuild without cache (clean build)
docker-compose build --no-cache
docker-compose up -d
Docker Commands Reference
Application Server Commands
| Command | Description |
|---|---|
docker-compose up -d |
Start application server in detached mode |
docker-compose up -d --build |
Rebuild and start application server |
docker-compose down |
Stop and remove application containers |
docker-compose logs -f |
Follow application logs in real-time |
docker-compose ps |
List running containers |
docker-compose restart |
Restart application services |
docker-compose exec jaxws-service bash |
Access application container shell |
Test Commands
| Command | Description |
|---|---|
docker-compose -f docker-compose.test.yml up --build |
Run unit tests |
docker-compose -f docker-compose.test.yml logs |
View test output |
docker-compose -f docker-compose.test.yml down |
Clean up test containers |
Coverage Commands
| Command | Description |
|---|---|
docker-compose -f docker-compose.coverage.yml up --build |
Generate coverage reports |
docker-compose -f docker-compose.coverage.yml logs |
View coverage generation logs |
docker-compose -f docker-compose.coverage.yml down |
Clean up coverage containers |
Accessing the Database
The SQLite database is mounted to the host machine and persists between container restarts:
# Access database from host
sqlite3 loan_app.db "SELECT * FROM customers;"
# Or access from within container
docker-compose exec jaxws-service sqlite3 /usr/local/tomcat/loan_app.db
Environment Configuration
The docker-compose.yml includes these configurations:
- Port Mapping:
8080:8080(host:container) - Memory Settings: Xms256m, Xmx512m
- Health Check: Automatic WSDL availability check every 30s
- Auto Restart: Container restarts unless manually stopped
- Volume Mounts:
./logs:/usr/local/tomcat/logs- Log persistence./loan_app.db:/usr/local/tomcat/loan_app.db- Database persistence
File Structure
jaxws-hello-world/
│
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── example/
│ │ │ ├── listener/
│ │ │ │ └── DatabaseInitializationListener.java # Application startup initialization
│ │ │ │
│ │ │ ├── model/ # Data models (POJOs)
│ │ │ │ ├── CustomerRegistrationRequest.java # Customer registration request
│ │ │ │ ├── LoanRequest.java # Loan application request
│ │ │ │ └── LoanResponse.java # Loan application response
│ │ │ │
│ │ │ ├── repository/ # Data access layer
│ │ │ │ ├── CustomerRepository.java # Customer DAO interface
│ │ │ │ ├── LoanHistoryRepository.java # Loan history DAO interface
│ │ │ │ └── impl/
│ │ │ │ ├── CustomerRepositoryImpl.java # Customer DAO implementation
│ │ │ │ └── LoanHistoryRepositoryImpl.java # Loan history DAO implementation
│ │ │ │
│ │ │ ├── service/ # Web service layer
│ │ │ │ ├── HelloWorldService.java # Hello World interface
│ │ │ │ ├── HelloWorldServiceImpl.java # Hello World implementation
│ │ │ │ ├── LoanApprovalService.java # Loan service (interface + impl)
│ │ │ │ └── CreditScoreService.java # Credit score evaluation
│ │ │ │
│ │ │ └── util/
│ │ │ └── DatabaseManager.java # Singleton DB connection manager
│ │ │
│ │ └── webapp/
│ │ └── WEB-INF/
│ │ ├── web.xml # Web application descriptor
│ │ └── sun-jaxws.xml # JAX-WS endpoint configuration
│ │
│ └── test/
│ └── java/
│ └── com/
│ └── example/
│ └── service/
│ └── HelloWorldServiceImplTest.java # Unit tests for Hello World service
│
├── scripts/ # Python test clients
│ ├── test_all_services.py # Comprehensive test suite
│ ├── test_hello_world.py # Hello World service tests
│ ├── test_register_customer.py # Customer registration tests
│ ├── simple_test.py # Quick smoke test
│ └── README.md # Test scripts documentation
│
├── logs/ # Tomcat logs (Docker volume)
├── target/ # Build output (generated)
│ ├── site/jacoco/ # Code coverage reports
│ │ ├── index.html # HTML coverage report
│ │ ├── jacoco.xml # XML coverage report
│ │ └── jacoco.csv # CSV coverage report
│ └── surefire-reports/ # Test execution reports
│
├── pom.xml # Maven project configuration
├── docker-compose.yml # Docker Compose orchestration
├── docker-compose.test.yml # Docker Compose for unit tests
├── docker-compose.coverage.yml # Docker Compose for coverage
├── Dockerfile # Multi-stage Docker build
├── Dockerfile.coverage.simple # Dockerfile for coverage
├── tomcat-users.xml # Tomcat manager users
├── loan_app.db # SQLite database (created on first run)
├── run-tests.bat # Windows test runner script
├── run-tests.sh # Linux/Mac test runner script
├── run-coverage.bat # Windows coverage script
├── run-coverage.sh # Linux/Mac coverage script
├── README.md # This file
├── DOCKER_COMPOSE_GUIDE.md # Docker Compose patterns reference
├── apidoc.md # Complete API documentation
├── requirements.md # Project requirements specification
├── .dockerignore # Docker build exclusions
└── .gitignore # Git exclusions
Architecture Layers
1. Listener Layer (listener/)
- Application lifecycle management
- Database schema initialization on startup
- Connection validation
2. Model Layer (model/)
- Plain Old Java Objects (POJOs)
- Request/Response DTOs for web services
- JAX-WS annotated data structures
3. Repository Layer (repository/)
- Data access objects (DAO pattern)
- Database CRUD operations
- SQL query execution with prepared statements
4. Service Layer (service/)
- JAX-WS web service endpoints
- Business logic implementation
- Credit score evaluation
- Loan approval decision engine
5. Utility Layer (util/)
- Singleton database connection manager
- Reusable helper classes
- Schema initialization and reset utilities
Database Structure
The application uses SQLite with the following schema:
Database File
- Location:
loan_app.db(project root) - Type: SQLite 3
- JDBC URL:
jdbc:sqlite:loan_app.db - Initialization: Automatic on application startup via
DatabaseInitializationListener
Schema
Table: customers
Stores registered customer information with blacklist status.
CREATE TABLE customers (
customer_name TEXT PRIMARY KEY,
is_blacklisted INTEGER DEFAULT 0,
registered_at TEXT
);
Column Details:
| Column | Type | Constraints | Description |
|---|---|---|---|
customer_name |
TEXT | PRIMARY KEY | Customer's unique name identifier |
is_blacklisted |
INTEGER | DEFAULT 0 | Blacklist flag (0 = not blacklisted, 1 = blacklisted) |
registered_at |
TEXT | NOT NULL | Registration timestamp (ISO 8601 format) |
Indexes:
- Primary key index on
customer_name(automatic)
Business Rules:
- Customer names must be unique
- Blacklisted customers cannot get loan approvals
- Registration timestamp is automatically set during registration
Table: loan_history
Stores all loan application records for audit and reporting.
CREATE TABLE loan_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
applicant_name TEXT,
requested_amount INTEGER,
approved INTEGER,
approved_rate REAL,
rejection_reason TEXT,
processed_at TEXT
);
Column Details:
| Column | Type | Constraints | Description |
|---|---|---|---|
id |
INTEGER | PRIMARY KEY AUTOINCREMENT | Unique record identifier |
applicant_name |
TEXT | Applicant's name (may not be registered customer) | |
requested_amount |
INTEGER | Loan amount requested in currency units | |
approved |
INTEGER | Approval status (0 = rejected, 1 = approved) | |
approved_rate |
REAL | Interest rate if approved (0.0 if rejected) | |
rejection_reason |
TEXT | NULL | Reason for rejection (NULL if approved) |
processed_at |
TEXT | NOT NULL | Processing timestamp (ISO 8601 format) |
Indexes:
- Primary key index on
id(automatic)
Business Rules:
- Every loan application is logged regardless of approval status
- Approved loans have
approved=1andrejection_reason=NULL - Rejected loans have
approved=0and populatedrejection_reason - Timestamp is automatically set during processing
Credit Score Tiers & Interest Rates
The loan approval logic uses the following credit score tiers:
| Credit Score Range | Approval Status | Interest Rate | Rejection Reason |
|---|---|---|---|
| >= 700 | ✅ Approved | 3.5% | - |
| 600 - 699 | ✅ Approved | 5.5% | - |
| 500 - 599 | ✅ Approved | 8.5% | - |
| < 500 | ❌ Rejected | - | "Credit score too low" |
| Blacklisted | ❌ Rejected | - | "Applicant is blacklisted" |
Database Operations
Manual Database Access
# View all customers
sqlite3 loan_app.db "SELECT * FROM customers;"
# View all loan applications
sqlite3 loan_app.db "SELECT * FROM loan_history;"
# View approved loans only
sqlite3 loan_app.db "SELECT * FROM loan_history WHERE approved = 1;"
# View rejected loans with reasons
sqlite3 loan_app.db "SELECT applicant_name, rejection_reason FROM loan_history WHERE approved = 0;"
# Count customers by blacklist status
sqlite3 loan_app.db "SELECT is_blacklisted, COUNT(*) FROM customers GROUP BY is_blacklisted;"
# View loan approval rate
sqlite3 loan_app.db "SELECT
COUNT(*) as total,
SUM(approved) as approved,
ROUND(CAST(SUM(approved) AS FLOAT) / COUNT(*) * 100, 2) as approval_rate
FROM loan_history;"
Database Reset (Testing)
The DatabaseManager class provides a reset method for testing:
DatabaseManager.getInstance().resetDatabase();
This will:
- Drop all tables
- Recreate the schema
- Clear all data
How to Run Tests
Prerequisites
Before running tests, ensure:
-
Service is Running
# Start the service docker-compose up -d # Wait 30-40 seconds, then verify curl http://localhost:8080/jaxws-hello-world/hello?wsdl -
Python Environment
- Python 3.6 or higher
- pip (Python package installer)
Step 1: Install Test Dependencies
Navigate to the scripts directory and install dependencies:
Windows:
cd scripts
pip install -r requirements.txt
Linux/Mac:
cd scripts
pip3 install -r requirements.txt
Required Python Package:
requests>=2.31.0- HTTP library for SOAP requests
Step 2: Choose Your Test Approach
Option 1: Comprehensive Test Suite (Recommended)
Run all tests in one go with the unified test script:
# Windows
python test_all_services.py
# Linux/Mac
python3 test_all_services.py
What it tests:
- ✅ Hello World Service (2 test cases)
- ✅ Customer Registration (4 test cases with validation)
- ✅ Loan Application Processing (14 test cases covering all scenarios)
- ✅ WSDL availability checks
- ✅ Error handling and edge cases
Expected Output:
================================================================================
JAX-WS UNIFIED TEST CLIENT
================================================================================
Testing all services and operations
Checking services availability...
Hello World Service: Service is accessible
Loan Approval Service: Service is accessible
================================================================================
AUTOMATED TEST SUITE
================================================================================
--------------------------------------------------------------------------------
1. Testing Hello World Service
--------------------------------------------------------------------------------
✓ PASS | getHelloWorld('John') - Should return greeting
Hello World, John!
✓ PASS | getHelloWorld('Alice') - Should return greeting
Hello World, Alice!
--------------------------------------------------------------------------------
2. Testing Customer Registration Service
--------------------------------------------------------------------------------
✓ PASS | registerNewCustomer('Alice Johnson', blacklisted=False)
Registration Successful
✓ PASS | registerNewCustomer('Bob Smith', blacklisted=False)
Registration Successful
✓ PASS | registerNewCustomer('Charlie Brown', blacklisted=True)
Registration Successful
✓ PASS | registerNewCustomer('Alice Johnson', blacklisted=False) - Duplicate
Error: Customer already exists
--------------------------------------------------------------------------------
3. Testing Loan Application Processing Service
--------------------------------------------------------------------------------
✓ PASS | processLoanApplication('Alice Johnson', $50000, score=750)
approved: True | rate: 3.5% | Loan approved with excellent rate
✓ PASS | processLoanApplication('Bob Smith', $30000, score=650)
approved: True | rate: 5.5% | Loan approved with standard rate
✓ PASS | processLoanApplication('New Customer', $25000, score=550)
approved: True | rate: 8.5% | Loan approved with high risk rate
✓ PASS | processLoanApplication('Poor Credit', $20000, score=450)
approved: False | Credit score too low
✓ PASS | processLoanApplication('Charlie Brown', $40000, score=700)
approved: False | Applicant is blacklisted
================================================================================
TEST SUMMARY
================================================================================
Total Tests: 20
Passed: 20
Failed: 0
Success Rate: 100.0%
Would you like to enter interactive mode? (y/n):
Interactive Mode:
After automated tests, you can enter interactive mode for manual testing:
Available commands:
hello <name> - Test Hello World service
register <name> - Register regular customer
blacklist <name> - Register blacklisted customer
loan <name> <amount> <credit_score> - Process loan application
quit - Exit
> hello TestUser
Response: Hello World, TestUser!
> register John Doe
Result: Registration Successful
> loan John Doe 50000 720
Result:
approved: True
approvedRate: 3.5
message: Loan approved with excellent rate
> quit
Option 2: Individual Test Scripts
Test specific services individually:
A. Hello World Service Test
# Windows
python test_hello_world.py
# Linux/Mac
python3 test_hello_world.py
Tests the basic Hello World SOAP service with predefined test cases and interactive mode.
B. Customer Registration Test
# Windows
python test_register_customer.py
# Linux/Mac
python3 test_register_customer.py
Tests customer registration including:
- Regular customer registration
- Blacklisted customer registration
- Duplicate registration handling
- Interactive registration mode
C. Simple Smoke Test
Quick test for CI/CD pipelines:
# Windows
python simple_test.py "Test Name"
# Linux/Mac
python3 simple_test.py "Test Name"
Returns exit code 0 on success, 1 on failure.
Option 3: Manual Testing with cURL
Test services directly using cURL commands:
Test Hello World Service
curl -X POST http://localhost:8080/jaxws-hello-world/hello \
-H "Content-Type: text/xml; charset=utf-8" \
-H "SOAPAction: " \
-d '<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ser="http://service.example.com/">
<soapenv:Header/>
<soapenv:Body>
<ser:getHelloWorld>
<arg0>TestUser</arg0>
</ser:getHelloWorld>
</soapenv:Body>
</soapenv:Envelope>'
Test Customer Registration
curl -X POST http://localhost:8080/jaxws-hello-world/loan \
-H "Content-Type: text/xml; charset=utf-8" \
-H "SOAPAction: " \
-d '<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ser="http://service.example.com/">
<soapenv:Header/>
<soapenv:Body>
<ser:registerNewCustomer>
<arg0>
<customerName>John Doe</customerName>
<blacklisted>false</blacklisted>
</arg0>
</ser:registerNewCustomer>
</soapenv:Body>
</soapenv:Envelope>'
Test Loan Application
curl -X POST http://localhost:8080/jaxws-hello-world/loan \
-H "Content-Type: text/xml; charset=utf-8" \
-H "SOAPAction: " \
-d '<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ser="http://service.example.com/">
<soapenv:Header/>
<soapenv:Body>
<ser:processLoanApplication>
<arg0>
<applicantName>John Doe</applicantName>
<requestedAmount>50000</requestedAmount>
<creditScore>720</creditScore>
</arg0>
</ser:processLoanApplication>
</soapenv:Body>
</soapenv:Envelope>'
Option 4: Testing with SoapUI
- Download and install SoapUI
- Create a new SOAP project
- Import WSDL URLs:
- Hello World:
http://localhost:8080/jaxws-hello-world/hello?wsdl - Loan Approval:
http://localhost:8080/jaxws-hello-world/loan?wsdl
- Hello World:
- SoapUI automatically generates request templates
- Fill in parameters and execute requests
Step 3: Verify Database Records
After running tests, verify data was persisted:
# View registered customers
sqlite3 loan_app.db "SELECT * FROM customers;"
# View loan application history
sqlite3 loan_app.db "SELECT * FROM loan_history;"
# View approved loans with rates
sqlite3 loan_app.db "
SELECT applicant_name, requested_amount, approved_rate
FROM loan_history
WHERE approved = 1;"
# View rejection reasons
sqlite3 loan_app.db "
SELECT applicant_name, rejection_reason
FROM loan_history
WHERE approved = 0;"
Troubleshooting Tests
Service Not Available
Error: Connection refused or Failed to establish connection
Solutions:
# Check if container is running
docker-compose ps
# Check container logs
docker-compose logs -f
# Restart the service
docker-compose restart
# Verify WSDL is accessible
curl http://localhost:8080/jaxws-hello-world/hello?wsdl
Python Command Not Found
Error: python: command not found
Solutions:
- Windows: Try
pyorpython3 - Linux/Mac: Use
python3instead ofpython - Check installation:
python --versionorpython3 --version
Dependencies Installation Failed
Error: Could not install packages
Solutions:
# Upgrade pip
python -m pip install --upgrade pip
# Use virtual environment (recommended)
python -m venv venv
# Windows
venv\Scripts\activate
# Linux/Mac
source venv/bin/activate
# Install dependencies in venv
pip install -r requirements.txt
Port Already in Use
Error: Port 8080 already in use
Solutions:
# Windows - Find process using port
netstat -ano | findstr :8080
# Linux/Mac - Find process using port
lsof -i :8080
# Kill the process or change docker-compose port mapping
Database Locked
Error: database is locked
Solutions:
# Stop all running tests
# Restart the service
docker-compose restart
# If persists, reset database
rm loan_app.db
docker-compose restart
Test Coverage
The comprehensive test suite covers:
| Category | Test Cases | Coverage |
|---|---|---|
| Hello World Service | 2 | Basic functionality, edge cases |
| Customer Registration | 4 | Regular, blacklisted, duplicates, validation |
| Loan Processing - Approvals | 7 | All credit score tiers (3.5%, 5.5%, 8.5%) |
| Loan Processing - Rejections | 5 | Low credit, blacklisted, validation errors |
| WSDL Availability | 2 | Both service endpoints |
| Total | 20 | Full workflow coverage |
API Documentation
For detailed API documentation including:
- Complete SOAP request/response examples
- All service operations and parameters
- Error handling and SOAP faults
- Business logic and validation rules
- Production testing considerations
See apidoc.md
For detailed test script documentation:
- Script usage and options
- Python client class examples
- Advanced testing scenarios
Unit Tests and Code Coverage
Overview
This project includes comprehensive unit testing with JUnit 5 and code coverage analysis with JaCoCo. All test results and coverage reports are automatically exported to the ./target directory for easy access.
Quick Start
Run Unit Tests
Windows:
run-tests.bat
Linux/Mac:
./run-tests.sh
Docker Compose:
docker-compose -f docker-compose.test.yml up --build
docker-compose -f docker-compose.test.yml down
Generate Code Coverage Reports
Windows:
run-coverage.bat
Linux/Mac:
./run-coverage.sh
Docker Compose:
docker-compose -f docker-compose.coverage.yml up --build
docker-compose -f docker-compose.coverage.yml down
Coverage Reports
All coverage reports are generated in ./target/site/jacoco/:
- HTML Report:
target/site/jacoco/index.html(open in browser) - XML Report:
target/site/jacoco/jacoco.xml(for CI/CD tools) - CSV Report:
target/site/jacoco/jacoco.csv(for spreadsheets)
Test Framework
The project uses:
- JUnit 5 (Jupiter) - Modern testing framework with annotations
- Mockito - Mocking framework for complex test scenarios
- JaCoCo - Code coverage analysis and reporting
- Maven Surefire - Test execution plugin
Current Test Coverage
| Service | Test Class | Test Cases | Status |
|---|---|---|---|
| Hello World Service | HelloWorldServiceImplTest | 10 | ✅ Passing |
Understanding Coverage Reports
Coverage Metrics:
- Line Coverage: Percentage of executable lines tested
- Branch Coverage: Percentage of decision points (if/else) tested
- Method Coverage: Percentage of methods invoked during tests
- Class Coverage: Percentage of classes with at least one test
Coverage Goals:
- Line Coverage: > 80%
- Branch Coverage: > 70%
- Method Coverage: > 85%
Running Tests with Maven (Local)
If you have Maven installed locally:
# Run tests only
mvn test
# Run tests with coverage
mvn clean test
# View coverage report
# Open target/site/jacoco/index.html in your browser
CI/CD Integration
GitHub Actions Example
- name: Run tests with coverage
run: mvn clean test
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
files: ./target/site/jacoco/jacoco.xml
Jenkins Example
stage('Test') {
steps {
sh 'mvn clean test'
jacoco(
execPattern: 'target/jacoco.exec',
classPattern: 'target/classes',
sourcePattern: 'src/main/java'
)
}
}
Writing New Tests
Create test classes in src/test/java/ following this pattern:
package com.example.service;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.DisplayName;
import static org.junit.jupiter.api.Assertions.*;
@DisplayName("Your Service Tests")
class YourServiceTest {
private YourService service;
@BeforeEach
void setUp() {
service = new YourServiceImpl();
}
@Test
@DisplayName("Should do something")
void testSomething() {
// Arrange
String input = "test";
String expected = "result";
// Act
String result = service.doSomething(input);
// Assert
assertEquals(expected, result);
}
}
Troubleshooting
Tests Not Running
# Ensure test files end with Test.java
# Verify directory: src/test/java/
# Check pom.xml includes maven-surefire-plugin
Coverage Reports Not Generated
# Ensure JaCoCo plugin is in pom.xml
mvn clean test
# Or use Docker
docker-compose -f docker-compose.coverage.yml up --build
Permission Issues (Linux/Mac)
chmod +x run-tests.sh
chmod +x run-coverage.sh
Service Endpoints
Once running, the following endpoints are available:
| Service | Endpoint | WSDL |
|---|---|---|
| Hello World | http://localhost:8080/jaxws-hello-world/hello | WSDL |
| Loan Approval | http://localhost:8080/jaxws-hello-world/loan | WSDL |
| Tomcat Manager | http://localhost:8080/manager | admin/admin123 |
Development
Building Locally (without Docker)
Prerequisites:
- Java JDK 8+
- Apache Maven 3.6+
- Apache Tomcat 9+
Build Commands:
# Clean and build
mvn clean package
# The WAR file will be created at:
# target/jaxws-hello-world.war
# Deploy to Tomcat webapps directory
cp target/jaxws-hello-world.war $CATALINA_HOME/webapps/
Project Dependencies
See pom.xml for complete dependency list:
javax.xml.ws:jaxws-api:2.3.1- JAX-WS APIcom.sun.xml.ws:jaxws-rt:2.3.3- JAX-WS Runtimejavax.servlet:javax.servlet-api:4.0.1- Servlet APIorg.xerial:sqlite-jdbc:3.43.0.0- SQLite JDBC Driver
Production Considerations
Security
⚠️ This is a demonstration project. For production use:
- Implement WS-Security for authentication
- Use HTTPS/TLS for all communications
- Add input sanitization beyond current validation
- Implement rate limiting
- Add audit logging for sensitive operations
- Store credentials in secure vault (not in
tomcat-users.xml)
Database
- Consider PostgreSQL or MySQL for production workloads
- Implement connection pooling (HikariCP, c3p0)
- Add database backup strategy
- Implement database migration tool (Flyway, Liquibase)
Performance
- The current
CreditScoreServicereturns a fixed value (700) - Integrate with real credit bureau APIs for production
- Add caching layer for credit scores
- Implement request/response logging
- Add monitoring and metrics (Prometheus, Grafana)
Troubleshooting
Container won't start
# Check container logs
docker-compose logs
# Check if port 8080 is already in use
netstat -ano | findstr :8080 # Windows
lsof -i :8080 # Linux/Mac
# Remove and rebuild
docker-compose down
docker-compose up -d --build
Database errors
# Check database file permissions
ls -la loan_app.db
# Reset database (will delete all data)
rm loan_app.db
docker-compose restart
WSDL not accessible
- Wait 30-40 seconds for Tomcat to fully start
- Check logs:
docker-compose logs -f - Verify container is running:
docker-compose ps
License
This is a demonstration project for educational purposes.
Support
For issues, questions, or contributions:
- Review the API Documentation
- Check the Requirements Specification
- Review test scripts in scripts/README.md