From 2fe315b7dfdffb7092a0106115e71d5fa81ecb11 Mon Sep 17 00:00:00 2001 From: Ken Yasue Date: Fri, 5 Dec 2025 10:22:10 +0100 Subject: [PATCH] initial implementation of loan approval service --- .claude/settings.local.json | 7 +- README.md | 5 + pom.xml | 7 + .../DatabaseInitializationListener.java | 47 +++++ .../model/CustomerRegistrationRequest.java | 42 ++++ .../java/com/example/model/LoanRequest.java | 53 +++++ .../java/com/example/model/LoanResponse.java | 64 ++++++ .../repository/CustomerRepository.java | 22 ++ .../repository/LoanHistoryRepository.java | 18 ++ .../impl/CustomerRepositoryImpl.java | 102 +++++++++ .../impl/LoanHistoryRepositoryImpl.java | 48 +++++ .../example/service/CreditScoreService.java | 15 ++ .../example/service/LoanApprovalService.java | 193 ++++++++++++++++++ .../com/example/util/DatabaseManager.java | 137 +++++++++++++ src/main/webapp/WEB-INF/sun-jaxws.xml | 5 + src/main/webapp/WEB-INF/web.xml | 21 +- 16 files changed, 784 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/example/listener/DatabaseInitializationListener.java create mode 100644 src/main/java/com/example/model/CustomerRegistrationRequest.java create mode 100644 src/main/java/com/example/model/LoanRequest.java create mode 100644 src/main/java/com/example/model/LoanResponse.java create mode 100644 src/main/java/com/example/repository/CustomerRepository.java create mode 100644 src/main/java/com/example/repository/LoanHistoryRepository.java create mode 100644 src/main/java/com/example/repository/impl/CustomerRepositoryImpl.java create mode 100644 src/main/java/com/example/repository/impl/LoanHistoryRepositoryImpl.java create mode 100644 src/main/java/com/example/service/CreditScoreService.java create mode 100644 src/main/java/com/example/service/LoanApprovalService.java create mode 100644 src/main/java/com/example/util/DatabaseManager.java diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 4bfc467..4f28e31 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -12,7 +12,12 @@ "Bash(pip install:*)", "Bash(python:*)", "Bash(git init:*)", - "Bash(git add:*)" + "Bash(git add:*)", + "Bash(git commit -m \"$(cat <<''EOF''\nInitial commit: JAX-WS Hello World Service\n\n- Complete JAX-WS Hello World implementation\n- Docker and Docker Compose support for easy deployment\n- Python test scripts for service validation\n- Comprehensive README with setup instructions for Windows and Linux\n- Maven configuration with JAX-WS dependencies\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude \nEOF\n)\")", + "Bash(git remote add:*)", + "Bash(git push:*)", + "Bash(git commit:*)", + "Bash(docker-compose ps:*)" ], "deny": [], "ask": [] diff --git a/README.md b/README.md index 98f7522..cdc5ae4 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,11 @@ The easiest way to test this application is using Docker. No need to install Jav docker-compose up -d ``` +**1. Start the application ( when you changed code ):** +```bash +docker-compose up -d --build +``` + **2. Wait for the application to start (about 30-40 seconds), then access:** - **WSDL**: http://localhost:8080/jaxws-hello-world/hello?wsdl - **Tomcat Manager**: http://localhost:8080/manager (username: `admin`, password: `admin123`) diff --git a/pom.xml b/pom.xml index 4fcfe37..1a578d9 100644 --- a/pom.xml +++ b/pom.xml @@ -41,6 +41,13 @@ 4.0.1 provided + + + + org.xerial + sqlite-jdbc + 3.43.0.0 + diff --git a/src/main/java/com/example/listener/DatabaseInitializationListener.java b/src/main/java/com/example/listener/DatabaseInitializationListener.java new file mode 100644 index 0000000..878fabc --- /dev/null +++ b/src/main/java/com/example/listener/DatabaseInitializationListener.java @@ -0,0 +1,47 @@ +package com.example.listener; + +import com.example.util.DatabaseManager; + +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; + +/** + * DatabaseInitializationListener - Initializes database schema when application starts + */ +public class DatabaseInitializationListener implements ServletContextListener { + + @Override + public void contextInitialized(ServletContextEvent sce) { + System.out.println("==========================================="); + System.out.println("Starting Database Initialization..."); + System.out.println("==========================================="); + + try { + DatabaseManager dbManager = DatabaseManager.getInstance(); + + // Test connection + if (dbManager.testConnection()) { + System.out.println("Database connection test: PASSED"); + } else { + System.err.println("Database connection test: FAILED"); + return; + } + + // Initialize schema (create tables if they don't exist) + dbManager.initializeSchema(); + + System.out.println("Database initialization completed successfully"); + System.out.println("==========================================="); + + } catch (Exception e) { + System.err.println("FATAL: Database initialization failed!"); + e.printStackTrace(); + throw new RuntimeException("Failed to initialize database", e); + } + } + + @Override + public void contextDestroyed(ServletContextEvent sce) { + System.out.println("Application shutdown - Database cleanup completed"); + } +} diff --git a/src/main/java/com/example/model/CustomerRegistrationRequest.java b/src/main/java/com/example/model/CustomerRegistrationRequest.java new file mode 100644 index 0000000..42d5990 --- /dev/null +++ b/src/main/java/com/example/model/CustomerRegistrationRequest.java @@ -0,0 +1,42 @@ +package com.example.model; + +/** + * CustomerRegistrationRequest - Input model for customer registration + */ +public class CustomerRegistrationRequest { + + private String customerName; + private boolean blacklisted; + + public CustomerRegistrationRequest() { + } + + public CustomerRegistrationRequest(String customerName, boolean blacklisted) { + this.customerName = customerName; + this.blacklisted = blacklisted; + } + + public String getCustomerName() { + return customerName; + } + + public void setCustomerName(String customerName) { + this.customerName = customerName; + } + + public boolean isBlacklisted() { + return blacklisted; + } + + public void setBlacklisted(boolean blacklisted) { + this.blacklisted = blacklisted; + } + + @Override + public String toString() { + return "CustomerRegistrationRequest{" + + "customerName='" + customerName + '\'' + + ", blacklisted=" + blacklisted + + '}'; + } +} diff --git a/src/main/java/com/example/model/LoanRequest.java b/src/main/java/com/example/model/LoanRequest.java new file mode 100644 index 0000000..4e99c9c --- /dev/null +++ b/src/main/java/com/example/model/LoanRequest.java @@ -0,0 +1,53 @@ +package com.example.model; + +/** + * LoanRequest - Input model for loan application processing + */ +public class LoanRequest { + + private String applicantName; + private int requestedAmount; + private int creditScore; + + public LoanRequest() { + } + + public LoanRequest(String applicantName, int requestedAmount, int creditScore) { + this.applicantName = applicantName; + this.requestedAmount = requestedAmount; + this.creditScore = creditScore; + } + + public String getApplicantName() { + return applicantName; + } + + public void setApplicantName(String applicantName) { + this.applicantName = applicantName; + } + + public int getRequestedAmount() { + return requestedAmount; + } + + public void setRequestedAmount(int requestedAmount) { + this.requestedAmount = requestedAmount; + } + + public int getCreditScore() { + return creditScore; + } + + public void setCreditScore(int creditScore) { + this.creditScore = creditScore; + } + + @Override + public String toString() { + return "LoanRequest{" + + "applicantName='" + applicantName + '\'' + + ", requestedAmount=" + requestedAmount + + ", creditScore=" + creditScore + + '}'; + } +} diff --git a/src/main/java/com/example/model/LoanResponse.java b/src/main/java/com/example/model/LoanResponse.java new file mode 100644 index 0000000..994c122 --- /dev/null +++ b/src/main/java/com/example/model/LoanResponse.java @@ -0,0 +1,64 @@ +package com.example.model; + +/** + * LoanResponse - Output model for loan application result + */ +public class LoanResponse { + + private boolean approved; + private double approvedRate; + private String rejectionReason; + private String message; + + public LoanResponse() { + } + + public LoanResponse(boolean approved, double approvedRate, String rejectionReason, String message) { + this.approved = approved; + this.approvedRate = approvedRate; + this.rejectionReason = rejectionReason; + this.message = message; + } + + public boolean isApproved() { + return approved; + } + + public void setApproved(boolean approved) { + this.approved = approved; + } + + public double getApprovedRate() { + return approvedRate; + } + + public void setApprovedRate(double approvedRate) { + this.approvedRate = approvedRate; + } + + public String getRejectionReason() { + return rejectionReason; + } + + public void setRejectionReason(String rejectionReason) { + this.rejectionReason = rejectionReason; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + @Override + public String toString() { + return "LoanResponse{" + + "approved=" + approved + + ", approvedRate=" + approvedRate + + ", rejectionReason='" + rejectionReason + '\'' + + ", message='" + message + '\'' + + '}'; + } +} diff --git a/src/main/java/com/example/repository/CustomerRepository.java b/src/main/java/com/example/repository/CustomerRepository.java new file mode 100644 index 0000000..952103a --- /dev/null +++ b/src/main/java/com/example/repository/CustomerRepository.java @@ -0,0 +1,22 @@ +package com.example.repository; + +/** + * CustomerRepository - Interface for customer data access + */ +public interface CustomerRepository { + + /** + * Checks if the customer is on the blacklist + * @param name Customer name + * @return true if blacklisted, false otherwise + */ + boolean isBlacklisted(String name); + + /** + * Registers a new customer into the database + * @param name Customer name + * @param isBlacklisted Blacklist status + * @return true if registered successfully, false if customer already exists + */ + boolean registerCustomer(String name, boolean isBlacklisted); +} diff --git a/src/main/java/com/example/repository/LoanHistoryRepository.java b/src/main/java/com/example/repository/LoanHistoryRepository.java new file mode 100644 index 0000000..d334181 --- /dev/null +++ b/src/main/java/com/example/repository/LoanHistoryRepository.java @@ -0,0 +1,18 @@ +package com.example.repository; + +/** + * LoanHistoryRepository - Interface for loan history data access + */ +public interface LoanHistoryRepository { + + /** + * Saves loan application history to the database + * @param applicantName Name of the applicant + * @param requestedAmount Requested loan amount + * @param approved Whether the loan was approved + * @param approvedRate Interest rate if approved + * @param rejectionReason Reason for rejection if declined + */ + void saveHistory(String applicantName, int requestedAmount, boolean approved, + double approvedRate, String rejectionReason); +} diff --git a/src/main/java/com/example/repository/impl/CustomerRepositoryImpl.java b/src/main/java/com/example/repository/impl/CustomerRepositoryImpl.java new file mode 100644 index 0000000..cba08a0 --- /dev/null +++ b/src/main/java/com/example/repository/impl/CustomerRepositoryImpl.java @@ -0,0 +1,102 @@ +package com.example.repository.impl; + +import com.example.repository.CustomerRepository; +import com.example.util.DatabaseManager; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +/** + * CustomerRepositoryImpl - SQLite implementation of CustomerRepository + */ +public class CustomerRepositoryImpl implements CustomerRepository { + + private final DatabaseManager dbManager; + private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + public CustomerRepositoryImpl() { + this.dbManager = DatabaseManager.getInstance(); + } + + @Override + public boolean isBlacklisted(String name) { + String query = "SELECT is_blacklisted FROM customers WHERE customer_name = ?"; + + try (Connection conn = dbManager.getConnection(); + PreparedStatement pstmt = conn.prepareStatement(query)) { + + pstmt.setString(1, name); + + try (ResultSet rs = pstmt.executeQuery()) { + if (rs.next()) { + int blacklisted = rs.getInt("is_blacklisted"); + return blacklisted == 1; + } + } + + // Customer not found, not blacklisted by default + return false; + + } catch (SQLException e) { + throw new RuntimeException("Error checking blacklist status for: " + name, e); + } + } + + @Override + public boolean registerCustomer(String name, boolean isBlacklisted) { + if (name == null || name.trim().isEmpty()) { + throw new IllegalArgumentException("Customer name cannot be null or empty"); + } + + // Check if customer already exists + if (customerExists(name)) { + return false; + } + + String insert = "INSERT INTO customers (customer_name, is_blacklisted, registered_at) VALUES (?, ?, ?)"; + + try (Connection conn = dbManager.getConnection(); + PreparedStatement pstmt = conn.prepareStatement(insert)) { + + pstmt.setString(1, name); + pstmt.setInt(2, isBlacklisted ? 1 : 0); + pstmt.setString(3, LocalDateTime.now().format(DATE_FORMATTER)); + + int rowsAffected = pstmt.executeUpdate(); + return rowsAffected > 0; + + } catch (SQLException e) { + throw new RuntimeException("Error registering customer: " + name, e); + } + } + + /** + * Helper method to check if customer already exists + * @param name Customer name + * @return true if exists, false otherwise + */ + private boolean customerExists(String name) { + String query = "SELECT COUNT(*) as count FROM customers WHERE customer_name = ?"; + + try (Connection conn = dbManager.getConnection(); + PreparedStatement pstmt = conn.prepareStatement(query)) { + + pstmt.setString(1, name); + + try (ResultSet rs = pstmt.executeQuery()) { + if (rs.next()) { + return rs.getInt("count") > 0; + } + } + + return false; + + } catch (SQLException e) { + throw new RuntimeException("Error checking customer existence: " + name, e); + } + } +} diff --git a/src/main/java/com/example/repository/impl/LoanHistoryRepositoryImpl.java b/src/main/java/com/example/repository/impl/LoanHistoryRepositoryImpl.java new file mode 100644 index 0000000..f53d67a --- /dev/null +++ b/src/main/java/com/example/repository/impl/LoanHistoryRepositoryImpl.java @@ -0,0 +1,48 @@ +package com.example.repository.impl; + +import com.example.repository.LoanHistoryRepository; +import com.example.util.DatabaseManager; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +/** + * LoanHistoryRepositoryImpl - SQLite implementation of LoanHistoryRepository + */ +public class LoanHistoryRepositoryImpl implements LoanHistoryRepository { + + private final DatabaseManager dbManager; + private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + public LoanHistoryRepositoryImpl() { + this.dbManager = DatabaseManager.getInstance(); + } + + @Override + public void saveHistory(String applicantName, int requestedAmount, boolean approved, + double approvedRate, String rejectionReason) { + + String insert = "INSERT INTO loan_history " + + "(applicant_name, requested_amount, approved, approved_rate, rejection_reason, processed_at) " + + "VALUES (?, ?, ?, ?, ?, ?)"; + + try (Connection conn = dbManager.getConnection(); + PreparedStatement pstmt = conn.prepareStatement(insert)) { + + pstmt.setString(1, applicantName); + pstmt.setInt(2, requestedAmount); + pstmt.setInt(3, approved ? 1 : 0); + pstmt.setDouble(4, approvedRate); + pstmt.setString(5, rejectionReason); + pstmt.setString(6, LocalDateTime.now().format(DATE_FORMATTER)); + + pstmt.executeUpdate(); + + } catch (SQLException e) { + throw new RuntimeException("Error saving loan history for: " + applicantName, e); + } + } +} diff --git a/src/main/java/com/example/service/CreditScoreService.java b/src/main/java/com/example/service/CreditScoreService.java new file mode 100644 index 0000000..e5362b7 --- /dev/null +++ b/src/main/java/com/example/service/CreditScoreService.java @@ -0,0 +1,15 @@ +package com.example.service; + +/** + * CreditScoreService - Interface for external credit score checking + * This interface is designed to be mocked in unit tests + */ +public interface CreditScoreService { + + /** + * Get credit score for an applicant + * @param applicantName Name of the applicant + * @return Credit score (0-850) + */ + int getCreditScore(String applicantName); +} diff --git a/src/main/java/com/example/service/LoanApprovalService.java b/src/main/java/com/example/service/LoanApprovalService.java new file mode 100644 index 0000000..7b78916 --- /dev/null +++ b/src/main/java/com/example/service/LoanApprovalService.java @@ -0,0 +1,193 @@ +package com.example.service; + +import com.example.model.CustomerRegistrationRequest; +import com.example.model.LoanRequest; +import com.example.model.LoanResponse; +import com.example.repository.CustomerRepository; +import com.example.repository.LoanHistoryRepository; +import com.example.repository.impl.CustomerRepositoryImpl; +import com.example.repository.impl.LoanHistoryRepositoryImpl; + +import javax.jws.WebMethod; +import javax.jws.WebService; +import javax.xml.ws.soap.SOAPFaultException; +import javax.xml.soap.SOAPFactory; +import javax.xml.soap.SOAPFault; + +/** + * LoanApprovalService - Main JAX-WS service for loan processing and customer management + */ +@WebService( + serviceName = "LoanApprovalService", + portName = "LoanApprovalPort" +) +public class LoanApprovalService { + + private CustomerRepository customerRepository; + private LoanHistoryRepository loanHistoryRepository; + private CreditScoreService creditScoreService; + + /** + * Default constructor - initializes with real implementations + */ + public LoanApprovalService() { + this.customerRepository = new CustomerRepositoryImpl(); + this.loanHistoryRepository = new LoanHistoryRepositoryImpl(); + // Default credit score service (can be overridden for testing) + this.creditScoreService = new CreditScoreService() { + @Override + public int getCreditScore(String applicantName) { + // Simple mock implementation - returns a fixed score + // In production, this would call an external API + return 700; + } + }; + } + + /** + * Setter for CustomerRepository (for dependency injection in tests) + */ + @WebMethod(exclude = true) + public void setCustomerRepository(CustomerRepository customerRepository) { + this.customerRepository = customerRepository; + } + + /** + * Setter for LoanHistoryRepository (for dependency injection in tests) + */ + @WebMethod(exclude = true) + public void setLoanHistoryRepository(LoanHistoryRepository loanHistoryRepository) { + this.loanHistoryRepository = loanHistoryRepository; + } + + /** + * Setter for CreditScoreService (for dependency injection in tests) + */ + @WebMethod(exclude = true) + public void setCreditScoreService(CreditScoreService creditScoreService) { + this.creditScoreService = creditScoreService; + } + + /** + * Register a new customer + * @param request Customer registration request + * @return Success or error message + */ + @WebMethod + public String registerNewCustomer(CustomerRegistrationRequest request) { + // Input validation + if (request == null || request.getCustomerName() == null || + request.getCustomerName().trim().isEmpty()) { + throw createSOAPFault("Customer name cannot be null or empty"); + } + + // Register customer + boolean registered = customerRepository.registerCustomer( + request.getCustomerName(), + request.isBlacklisted() + ); + + if (registered) { + return "Registration Successful"; + } else { + return "Error: Customer already exists"; + } + } + + /** + * Process loan application + * @param request Loan application request + * @return Loan decision response + */ + @WebMethod + public LoanResponse processLoanApplication(LoanRequest request) { + // Input validation + if (request == null || request.getApplicantName() == null || + request.getApplicantName().trim().isEmpty()) { + throw createSOAPFault("Applicant name cannot be null or empty"); + } + + if (request.getRequestedAmount() <= 0) { + throw createSOAPFault("Requested amount must be greater than 0"); + } + + // Check blacklist + if (customerRepository.isBlacklisted(request.getApplicantName())) { + LoanResponse response = new LoanResponse( + false, + 0.0, + "Applicant is blacklisted", + "Loan application rejected" + ); + + // Save history + loanHistoryRepository.saveHistory( + request.getApplicantName(), + request.getRequestedAmount(), + false, + 0.0, + "Applicant is blacklisted" + ); + + return response; + } + + // Get credit score + int creditScore = creditScoreService.getCreditScore(request.getApplicantName()); + + // Loan approval logic + boolean approved; + double approvedRate; + String rejectionReason = null; + String message; + + if (creditScore >= 700) { + // Good credit - approved with low rate + approved = true; + approvedRate = 3.5; + message = "Loan approved with excellent rate"; + } else if (creditScore >= 600) { + // Fair credit - approved with standard rate + approved = true; + approvedRate = 5.5; + message = "Loan approved with standard rate"; + } else if (creditScore >= 500) { + // Poor credit - approved with high rate + approved = true; + approvedRate = 8.5; + message = "Loan approved with high risk rate"; + } else { + // Very poor credit - rejected + approved = false; + approvedRate = 0.0; + rejectionReason = "Credit score too low"; + message = "Loan application rejected"; + } + + // Save history + loanHistoryRepository.saveHistory( + request.getApplicantName(), + request.getRequestedAmount(), + approved, + approvedRate, + rejectionReason + ); + + // Create response + return new LoanResponse(approved, approvedRate, rejectionReason, message); + } + + /** + * Helper method to create SOAP fault + */ + private SOAPFaultException createSOAPFault(String message) { + try { + SOAPFactory soapFactory = SOAPFactory.newInstance(); + SOAPFault soapFault = soapFactory.createFault(); + soapFault.setFaultString(message); + return new SOAPFaultException(soapFault); + } catch (Exception e) { + throw new RuntimeException("Error creating SOAP fault", e); + } + } +} diff --git a/src/main/java/com/example/util/DatabaseManager.java b/src/main/java/com/example/util/DatabaseManager.java new file mode 100644 index 0000000..5a6dd7c --- /dev/null +++ b/src/main/java/com/example/util/DatabaseManager.java @@ -0,0 +1,137 @@ +package com.example.util; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; + +/** + * DatabaseManager - Utility class for managing SQLite database connections and schema initialization. + * This class provides methods to: + * - Get database connections + * - Initialize database schema (create tables) + * - Reset database (drop and recreate tables) + */ +public class DatabaseManager { + + private static final String DB_URL = "jdbc:sqlite:loan_app.db"; + private static DatabaseManager instance; + + // Private constructor for singleton pattern + private DatabaseManager() { + try { + // Load SQLite JDBC driver + Class.forName("org.sqlite.JDBC"); + } catch (ClassNotFoundException e) { + throw new RuntimeException("Failed to load SQLite JDBC driver", e); + } + } + + /** + * Get singleton instance of DatabaseManager + * @return DatabaseManager instance + */ + public static synchronized DatabaseManager getInstance() { + if (instance == null) { + instance = new DatabaseManager(); + } + return instance; + } + + /** + * Get a new database connection + * @return Connection object + * @throws SQLException if connection fails + */ + public Connection getConnection() throws SQLException { + return DriverManager.getConnection(DB_URL); + } + + /** + * Initialize database schema - create tables if they don't exist + */ + public void initializeSchema() { + String createCustomersTable = + "CREATE TABLE IF NOT EXISTS customers (" + + " customer_name TEXT PRIMARY KEY," + + " is_blacklisted INTEGER DEFAULT 0," + + " registered_at TEXT" + + ")"; + + String createLoanHistoryTable = + "CREATE TABLE IF NOT EXISTS loan_history (" + + " id INTEGER PRIMARY KEY AUTOINCREMENT," + + " applicant_name TEXT," + + " requested_amount INTEGER," + + " approved INTEGER," + + " approved_rate REAL," + + " rejection_reason TEXT," + + " processed_at TEXT" + + ")"; + + try (Connection conn = getConnection(); + Statement stmt = conn.createStatement()) { + + // Create tables + stmt.execute(createCustomersTable); + stmt.execute(createLoanHistoryTable); + + System.out.println("Database schema initialized successfully"); + + } catch (SQLException e) { + throw new RuntimeException("Failed to initialize database schema", e); + } + } + + /** + * Reset database - drop all tables and recreate them + * Useful for testing scenarios + */ + public void resetDatabase() { + String dropCustomers = "DROP TABLE IF EXISTS customers"; + String dropLoanHistory = "DROP TABLE IF EXISTS loan_history"; + + try (Connection conn = getConnection(); + Statement stmt = conn.createStatement()) { + + // Drop tables + stmt.execute(dropCustomers); + stmt.execute(dropLoanHistory); + + System.out.println("Database tables dropped"); + + // Recreate tables + initializeSchema(); + + } catch (SQLException e) { + throw new RuntimeException("Failed to reset database", e); + } + } + + /** + * Test database connection + * @return true if connection is successful + */ + public boolean testConnection() { + try (Connection conn = getConnection()) { + return conn != null && !conn.isClosed(); + } catch (SQLException e) { + System.err.println("Database connection test failed: " + e.getMessage()); + return false; + } + } + + /** + * Close connection safely + * @param conn Connection to close + */ + public static void closeConnection(Connection conn) { + if (conn != null) { + try { + conn.close(); + } catch (SQLException e) { + System.err.println("Error closing connection: " + e.getMessage()); + } + } + } +} diff --git a/src/main/webapp/WEB-INF/sun-jaxws.xml b/src/main/webapp/WEB-INF/sun-jaxws.xml index 16f9355..5316b21 100644 --- a/src/main/webapp/WEB-INF/sun-jaxws.xml +++ b/src/main/webapp/WEB-INF/sun-jaxws.xml @@ -6,4 +6,9 @@ implementation="com.example.service.HelloWorldServiceImpl" url-pattern="/hello"/> + + diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml index b083056..6b4ee42 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -5,12 +5,19 @@ http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> - JAX-WS Hello World Service + JAX-WS Loan Approval Service + + + com.example.listener.DatabaseInitializationListener + + + com.sun.xml.ws.transport.http.servlet.WSServletContextListener + HelloWorldService com.sun.xml.ws.transport.http.servlet.WSServlet @@ -22,4 +29,16 @@ /hello + + + LoanApprovalService + com.sun.xml.ws.transport.http.servlet.WSServlet + 2 + + + + LoanApprovalService + /loan + +