πŸ“¦ Modules (JPMS)

Master Java's modular architecture through JPMS design patterns, dependency management strategies, and enterprise-scale application organization


🎯 Learning Objectives

By the end of this lesson, you will:


πŸ—οΈ Module System Architecture Overview

<aside> πŸ“¦

JPMS Philosophy

JPMS treats applications like LEGO construction sets - each module is a well-defined building block with clear interfaces (what it provides) and dependencies (what it needs). This creates reliable, maintainable, and secure software architectures.

</aside>

Pre-Module vs Module Era Comparison

Java Module Evolution - From Classpath Chaos to Modular Order
═══════════════════════════════════════════════════════════════════════════════

    😰 PRE-JAVA 9 (CLASSPATH)           πŸ—οΈ JAVA 9+ (MODULE SYSTEM)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ πŸ“ All JARs on classpath            β”‚   β”‚ πŸ“¦ Explicit module dependencies    β”‚
β”‚ ❌ No encapsulation                 β”‚   β”‚ βœ… Strong encapsulation            β”‚
β”‚ ❌ JAR hell & conflicts             β”‚   β”‚ βœ… Reliable configuration          β”‚
β”‚ ❌ Runtime discovery of missing     β”‚   β”‚ βœ… Compile-time dependency check   β”‚
β”‚    dependencies                     β”‚   β”‚                                     β”‚
β”‚ ❌ All public classes accessible    β”‚   β”‚ βœ… Only exported packages visible  β”‚
β”‚ ❌ Monolithic runtime              β”‚   β”‚ βœ… Custom runtime images          β”‚
β”‚                                     β”‚   β”‚                                     β”‚
β”‚ java -cp app.jar:lib1.jar:lib2.jar β”‚   β”‚ java --module-path mods            β”‚
β”‚      MyApplication                  β”‚   β”‚      -m myapp/com.example.Main     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

    🧱 MODULE ARCHITECTURE LAYERS
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚                     APPLICATION LAYER                                   β”‚
    β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
    β”‚  β”‚   UI Module β”‚  β”‚ Service     β”‚  β”‚ Data Access β”‚  β”‚ Utility     β”‚   β”‚
    β”‚  β”‚             β”‚  β”‚ Module      β”‚  β”‚ Module      β”‚  β”‚ Module      β”‚   β”‚
    β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
    β”‚         β”‚                β”‚                β”‚                β”‚           β”‚
    β”‚         β–Ό                β–Ό                β–Ό                β–Ό           β”‚
    β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
    β”‚  β”‚                     API LAYER                                   β”‚   β”‚
    β”‚  β”‚    (Exported Interfaces & Public APIs)                         β”‚   β”‚
    β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
    β”‚         β”‚                                                               β”‚
    β”‚         β–Ό                                                               β”‚
    β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
    β”‚  β”‚                  PLATFORM LAYER                                 β”‚   β”‚
    β”‚  β”‚  java.base β”‚ java.logging β”‚ java.sql β”‚ [java.net](<http://java.net>).http β”‚         β”‚   β”‚
    β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

    πŸ”’ ENCAPSULATION LEVELS
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚ VISIBILITY SCOPE           β”‚ ACCESSIBLE FROM           β”‚ USE CASE       β”‚
    │────────────────────────────│──────────────────────────│────────────────│
    β”‚ public + exported          β”‚ Other modules            β”‚ Public API     β”‚
    β”‚ public + not exported      β”‚ Same module only         β”‚ Internal API   β”‚
    β”‚ package-private            β”‚ Same package only        β”‚ Implementation β”‚
    β”‚ private                    β”‚ Same class only          β”‚ Internal logic β”‚
    β”‚ opened (reflection)        β”‚ Frameworks via reflectionβ”‚ Framework integβ”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

🧩 Module Declaration Mastery

Module-Info Anatomy

<aside> 🎯

Module Declaration Strategy

Think of module-info.java as a contract document - it declares what your module provides (exports), what it needs (requires), and how it collaborates with other modules (services).

</aside>

🏒 Enterprise E-Commerce Platform

// ========================================================================================
// πŸͺ E-COMMERCE PLATFORM MODULE STRUCTURE
// ========================================================================================

// πŸ“¦ Module 1: Core API Module
// File: ecommerce.api/src/[module-info.java](<http://module-info.java>)
module ecommerce.api {
    // Export public APIs to all consumers
    exports com.ecommerce.api.product;
    exports com.ecommerce.api.customer;
    exports com.ecommerce.api.order;
    exports com.ecommerce.api.payment;
    
    // Qualified exports - only to specific modules
    exports com.ecommerce.api.internal to 
        ecommerce.service,
        [ecommerce.data](<http://ecommerce.data>);
    
    // Service interfaces that can be implemented by providers
    uses com.ecommerce.api.payment.PaymentProcessor;
    uses com.ecommerce.api.notification.NotificationService;
}

// 🎯 Core API Interfaces
// File: ecommerce.api/src/com/ecommerce/api/product/[Product.java](<http://Product.java>)
package com.ecommerce.api.product;

import java.math.BigDecimal;
import java.util.List;

public interface Product {
    String getId();
    String getName();
    String getDescription();
    BigDecimal getPrice();
    String getCategory();
    List<String> getImages();
    boolean isAvailable();
}

// File: ecommerce.api/src/com/ecommerce/api/product/[ProductService.java](<http://ProductService.java>)
package com.ecommerce.api.product;

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

public interface ProductService {
    List<Product> findAll();
    List<Product> findByCategory(String category);
    Optional<Product> findById(String id);
    List<Product> search(String query);
    Product save(Product product);
    void deleteById(String id);
}

// File: ecommerce.api/src/com/ecommerce/api/customer/[Customer.java](<http://Customer.java>)
package com.ecommerce.api.customer;

import java.time.LocalDateTime;
import java.util.List;

public interface Customer {
    String getId();
    String getEmail();
    String getFirstName();
    String getLastName();
    List<Address> getAddresses();
    LocalDateTime getRegistrationDate();
    CustomerStatus getStatus();
}

public enum CustomerStatus {
    ACTIVE, INACTIVE, SUSPENDED, VIP
}

// File: ecommerce.api/src/com/ecommerce/api/customer/[Address.java](<http://Address.java>)
package com.ecommerce.api.customer;

public interface Address {
    String getStreet();
    String getCity();
    String getState();
    String getZipCode();
    String getCountry();
    AddressType getType();
}

public enum AddressType {
    BILLING, SHIPPING, BOTH
}

// File: ecommerce.api/src/com/ecommerce/api/order/[Order.java](<http://Order.java>)
package com.ecommerce.api.order;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;

public interface Order {
    String getId();
    String getCustomerId();
    List<OrderItem> getItems();
    BigDecimal getTotalAmount();
    OrderStatus getStatus();
    LocalDateTime getCreatedDate();
    LocalDateTime getUpdatedDate();
    String getShippingAddress();
    String getBillingAddress();
}

public enum OrderStatus {
    PENDING, CONFIRMED, PROCESSING, SHIPPED, DELIVERED, CANCELLED, REFUNDED
}

// File: ecommerce.api/src/com/ecommerce/api/order/[OrderItem.java](<http://OrderItem.java>)
package com.ecommerce.api.order;

import java.math.BigDecimal;

public interface OrderItem {
    String getProductId();
    int getQuantity();
    BigDecimal getUnitPrice();
    BigDecimal getTotalPrice();
}

// File: ecommerce.api/src/com/ecommerce/api/payment/[PaymentProcessor.java](<http://PaymentProcessor.java>)
package com.ecommerce.api.payment;

import java.math.BigDecimal;

public interface PaymentProcessor {
    String getProviderName();
    PaymentResult processPayment(PaymentRequest request);
    PaymentResult refundPayment(String transactionId, BigDecimal amount);
    boolean supports(PaymentMethod method);
}

public class PaymentRequest {
    private final String customerId;
    private final BigDecimal amount;
    private final PaymentMethod method;
    private final String currency;
    
    // Constructor, getters, builder pattern
    public PaymentRequest(String customerId, BigDecimal amount, 
                         PaymentMethod method, String currency) {
        this.customerId = customerId;
        this.amount = amount;
        this.method = method;
        this.currency = currency;
    }
    
    // Getters
    public String getCustomerId() { return customerId; }
    public BigDecimal getAmount() { return amount; }
    public PaymentMethod getMethod() { return method; }
    public String getCurrency() { return currency; }
}

public class PaymentResult {
    private final boolean success;
    private final String transactionId;
    private final String message;
    private final PaymentStatus status;
    
    public PaymentResult(boolean success, String transactionId, 
                        String message, PaymentStatus status) {
        this.success = success;
        this.transactionId = transactionId;
        this.message = message;
        this.status = status;
    }
    
    // Getters
    public boolean isSuccess() { return success; }
    public String getTransactionId() { return transactionId; }
    public String getMessage() { return message; }
    public PaymentStatus getStatus() { return status; }
}

public enum PaymentMethod {
    CREDIT_CARD, DEBIT_CARD, PAYPAL, BANK_TRANSFER, CRYPTO
}

public enum PaymentStatus {
    PENDING, APPROVED, DECLINED, ERROR, REFUNDED
}

// ========================================================================================
// πŸ“¦ Module 2: Service Implementation Module
// ========================================================================================

// File: ecommerce.service/src/[module-info.java](<http://module-info.java>)
module ecommerce.service {
    // Require API module
    requires ecommerce.api;
    
    // Require platform modules
    requires java.logging;
    requires transitive java.sql;  // Transitive - consumers get java.sql too
    
    // Export service implementations
    exports com.ecommerce.service.impl to ecommerce.web;
    
    // Open packages for dependency injection frameworks
    opens com.ecommerce.service.impl to spring.core, spring.context;
    
    // Provide service implementations
    provides com.ecommerce.api.product.ProductService 
        with com.ecommerce.service.impl.ProductServiceImpl;
}

// πŸ›οΈ Service Implementation
// File: ecommerce.service/src/com/ecommerce/service/impl/[ProductServiceImpl.java](<http://ProductServiceImpl.java>)
package com.ecommerce.service.impl;

import com.ecommerce.api.product.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
import [java.util.stream](<http://java.util.stream>).Collectors;

public class ProductServiceImpl implements ProductService {
    private static final Logger logger = Logger.getLogger(ProductServiceImpl.class.getName());
    
    // In-memory storage for demo (in real app, this would be injected)
    private final Map<String, Product> products = new ConcurrentHashMap<>();
    
    public ProductServiceImpl() {
        initializeProducts();
        [logger.info](<http://logger.info>)("ProductServiceImpl initialized with " + products.size() + " products");
    }
    
    @Override
    public List<Product> findAll() {
        [logger.info](<http://logger.info>)("Finding all products");
        return new ArrayList<>(products.values());
    }
    
    @Override
    public List<Product> findByCategory(String category) {
        [logger.info](<http://logger.info>)("Finding products by category: " + category);
        return products.values().stream()
                      .filter(product -> product.getCategory().equalsIgnoreCase(category))
                      .collect(Collectors.toList());
    }
    
    @Override
    public Optional<Product> findById(String id) {
        [logger.info](<http://logger.info>)("Finding product by ID: " + id);
        return Optional.ofNullable(products.get(id));
    }
    
    @Override
    public List<Product> search(String query) {
        [logger.info](<http://logger.info>)("Searching products with query: " + query);
        String lowerQuery = query.toLowerCase();
        return products.values().stream()
                      .filter(product -> 
                          product.getName().toLowerCase().contains(lowerQuery) ||
                          product.getDescription().toLowerCase().contains(lowerQuery))
                      .collect(Collectors.toList());
    }
    
    @Override
    public Product save(Product product) {
        [logger.info](<http://logger.info>)("Saving product: " + product.getId());
        products.put(product.getId(), product);
        return product;
    }
    
    @Override
    public void deleteById(String id) {
        [logger.info](<http://logger.info>)("Deleting product: " + id);
        products.remove(id);
    }
    
    private void initializeProducts() {
        // Sample products for demonstration
        List<ProductImpl> sampleProducts = Arrays.asList(
            new ProductImpl("1", "Laptop Pro", "High-performance laptop", 
                           new java.math.BigDecimal("1299.99"), "Electronics", 
                           Arrays.asList("laptop1.jpg", "laptop2.jpg"), true),
            new ProductImpl("2", "Wireless Headphones", "Noise-cancelling headphones", 
                           new java.math.BigDecimal("249.99"), "Electronics", 
                           Arrays.asList("headphones1.jpg"), true),
            new ProductImpl("3", "Running Shoes", "Comfortable running shoes", 
                           new java.math.BigDecimal("89.99"), "Sports", 
                           Arrays.asList("shoes1.jpg", "shoes2.jpg", "shoes3.jpg"), true),
            new ProductImpl("4", "Coffee Maker", "Automatic coffee maker", 
                           new java.math.BigDecimal("79.99"), "Home", 
                           Arrays.asList("coffee1.jpg"), true),
            new ProductImpl("5", "Programming Book", "Advanced Java Programming", 
                           new java.math.BigDecimal("49.99"), "Books", 
                           Arrays.asList("book1.jpg"), true)
        );
        
        sampleProducts.forEach(product -> products.put(product.getId(), product));
    }
    
    // Internal implementation class
    private static class ProductImpl implements Product {
        private final String id;
        private final String name;
        private final String description;
        private final java.math.BigDecimal price;
        private final String category;
        private final List<String> images;
        private final boolean available;
        
        public ProductImpl(String id, String name, String description, 
                          java.math.BigDecimal price, String category, 
                          List<String> images, boolean available) {
            [this.id](<http://this.id>) = id;
            [this.name](<http://this.name>) = name;
            this.description = description;
            this.price = price;
            this.category = category;
            this.images = new ArrayList<>(images);
            this.available = available;
        }
        
        // Implement all Product interface methods
        @Override public String getId() { return id; }
        @Override public String getName() { return name; }
        @Override public String getDescription() { return description; }
        @Override public java.math.BigDecimal getPrice() { return price; }
        @Override public String getCategory() { return category; }
        @Override public List<String> getImages() { return new ArrayList<>(images); }
        @Override public boolean isAvailable() { return available; }
        
        @Override
        public String toString() {
            return String.format("Product[id=%s, name=%s, category=%s, price=$%.2f]", 
                               id, name, category, price);
        }
    }
}

// ========================================================================================
// πŸ“¦ Module 3: Payment Provider Module  
// ========================================================================================

// File: ecommerce.payment.stripe/src/[module-info.java](<http://module-info.java>)
module ecommerce.payment.stripe {
    requires ecommerce.api;
    requires [java.net](<http://java.net>).http;
    requires java.logging;
    
    // Provide payment processor implementation
    provides com.ecommerce.api.payment.PaymentProcessor 
        with com.ecommerce.payment.stripe.StripePaymentProcessor;
}

// πŸ’³ Stripe Payment Implementation
// File: ecommerce.payment.stripe/src/com/ecommerce/payment/stripe/[StripePaymentProcessor.java](<http://StripePaymentProcessor.java>)
package com.ecommerce.payment.stripe;

import com.ecommerce.api.payment.*;
import java.math.BigDecimal;
import java.util.logging.Logger;
import java.util.UUID;

public class StripePaymentProcessor implements PaymentProcessor {
    private static final Logger logger = Logger.getLogger(StripePaymentProcessor.class.getName());
    
    @Override
    public String getProviderName() {
        return "Stripe Payment Processor";
    }
    
    @Override
    public PaymentResult processPayment(PaymentRequest request) {
        [logger.info](<http://logger.info>)("Processing Stripe payment for customer: " + request.getCustomerId() + 
                   " amount: " + request.getAmount());
        
        try {
            // Simulate Stripe API call
            Thread.sleep(500); // Simulate network delay
            
            // Simulate different outcomes based on amount
            if (request.getAmount().compareTo(new BigDecimal("10000")) > 0) {
                // Large amounts might be declined
                return new PaymentResult(false, null, "Amount too large", PaymentStatus.DECLINED);
            }
            
            if (request.getAmount().compareTo([BigDecimal.ZERO](<http://BigDecimal.ZERO>)) <= 0) {
                return new PaymentResult(false, null, "Invalid amount", PaymentStatus.ERROR);
            }
            
            // Successful payment
            String transactionId = "stripe_" + UUID.randomUUID().toString().substring(0, 8);
            return new PaymentResult(true, transactionId, "Payment successful", PaymentStatus.APPROVED);
            
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return new PaymentResult(false, null, "Payment interrupted", PaymentStatus.ERROR);
        } catch (Exception e) {
            logger.severe("Stripe payment processing error: " + e.getMessage());
            return new PaymentResult(false, null, "Processing error: " + e.getMessage(), PaymentStatus.ERROR);
        }
    }
    
    @Override
    public PaymentResult refundPayment(String transactionId, BigDecimal amount) {
        [logger.info](<http://logger.info>)("Processing Stripe refund for transaction: " + transactionId + 
                   " amount: " + amount);
        
        try {
            Thread.sleep(300); // Simulate API call
            
            if (transactionId == null || !transactionId.startsWith("stripe_")) {
                return new PaymentResult(false, null, "Invalid transaction ID", PaymentStatus.ERROR);
            }
            
            String refundId = "refund_" + UUID.randomUUID().toString().substring(0, 8);
            return new PaymentResult(true, refundId, "Refund successful", PaymentStatus.REFUNDED);
            
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return new PaymentResult(false, null, "Refund interrupted", PaymentStatus.ERROR);
        } catch (Exception e) {
            logger.severe("Stripe refund processing error: " + e.getMessage());
            return new PaymentResult(false, null, "Refund error: " + e.getMessage(), PaymentStatus.ERROR);
        }
    }
    
    @Override
    public boolean supports(PaymentMethod method) {
        // Stripe supports most payment methods except crypto
        return method != PaymentMethod.CRYPTO;
    }
}

// ========================================================================================
// πŸ“¦ Module 4: Alternative Payment Provider
// ========================================================================================

// File: ecommerce.payment.paypal/src/[module-info.java](<http://module-info.java>)
module ecommerce.payment.paypal {
    requires ecommerce.api;
    requires java.logging;
    
    provides com.ecommerce.api.payment.PaymentProcessor 
        with com.ecommerce.payment.paypal.PayPalPaymentProcessor;
}

// πŸ’° PayPal Payment Implementation  
// File: ecommerce.payment.paypal/src/com/ecommerce/payment/paypal/[PayPalPaymentProcessor.java](<http://PayPalPaymentProcessor.java>)
package com.ecommerce.payment.paypal;

import com.ecommerce.api.payment.*;
import java.math.BigDecimal;
import java.util.logging.Logger;
import java.util.UUID;

public class PayPalPaymentProcessor implements PaymentProcessor {
    private static final Logger logger = Logger.getLogger(PayPalPaymentProcessor.class.getName());
    
    @Override
    public String getProviderName() {
        return "PayPal Payment Processor";
    }
    
    @Override
    public PaymentResult processPayment(PaymentRequest request) {
        [logger.info](<http://logger.info>)("Processing PayPal payment for customer: " + request.getCustomerId() + 
                   " amount: " + request.getAmount());
        
        try {
            Thread.sleep(800); // PayPal might be slower
            
            // PayPal has different business rules
            if (request.getMethod() != PaymentMethod.PAYPAL && 
                request.getMethod() != [PaymentMethod.BANK](<http://PaymentMethod.BANK>)_TRANSFER) {
                return new PaymentResult(false, null, "PayPal doesn't support this method", 
                                       PaymentStatus.DECLINED);
            }
            
            if (request.getAmount().compareTo(new BigDecimal("5000")) > 0) {
                return new PaymentResult(false, null, "Amount exceeds PayPal limit", 
                                       PaymentStatus.DECLINED);
            }
            
            String transactionId = "paypal_" + UUID.randomUUID().toString().substring(0, 10);
            return new PaymentResult(true, transactionId, "PayPal payment successful", 
                                   PaymentStatus.APPROVED);
            
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return new PaymentResult(false, null, "Payment interrupted", PaymentStatus.ERROR);
        } catch (Exception e) {
            logger.severe("PayPal payment processing error: " + e.getMessage());
            return new PaymentResult(false, null, "Processing error: " + e.getMessage(), 
                                   PaymentStatus.ERROR);
        }
    }
    
    @Override
    public PaymentResult refundPayment(String transactionId, BigDecimal amount) {
        [logger.info](<http://logger.info>)("Processing PayPal refund for transaction: " + transactionId);
        
        try {
            Thread.sleep(600);
            
            if (transactionId == null || !transactionId.startsWith("paypal_")) {
                return new PaymentResult(false, null, "Invalid PayPal transaction ID", 
                                       PaymentStatus.ERROR);
            }
            
            String refundId = "paypal_refund_" + UUID.randomUUID().toString().substring(0, 8);
            return new PaymentResult(true, refundId, "PayPal refund successful", 
                                   PaymentStatus.REFUNDED);
            
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return new PaymentResult(false, null, "Refund interrupted", PaymentStatus.ERROR);
        } catch (Exception e) {
            logger.severe("PayPal refund processing error: " + e.getMessage());
            return new PaymentResult(false, null, "Refund error: " + e.getMessage(), 
                                   PaymentStatus.ERROR);
        }
    }
    
    @Override
    public boolean supports(PaymentMethod method) {
        return method == PaymentMethod.PAYPAL || method == [PaymentMethod.BANK](<http://PaymentMethod.BANK>)_TRANSFER;
    }
}

// ========================================================================================
// πŸ“¦ Module 5: Main Application Module
// ========================================================================================

// File: [ecommerce.app/src/module-info.java](<http://ecommerce.app/src/module-info.java>)
module [ecommerce.app](<http://ecommerce.app>) {
    // Require all necessary modules
    requires ecommerce.api;
    requires ecommerce.service;
    
    // Platform modules
    requires java.logging;
    requires java.base; // implicit, but shown for clarity
    
    // Payment processors are loaded dynamically via ServiceLoader
    uses com.ecommerce.api.payment.PaymentProcessor;
    
    // Main class is in this module
    exports [com.ecommerce.app](<http://com.ecommerce.app>) to java.base; // Allow JVM to access main method
}

// πŸš€ Main Application
// File: [ecommerce.app/src/com/ecommerce/app/ECommerceApplication.java](<http://ecommerce.app/src/com/ecommerce/app/ECommerceApplication.java>)
package [com.ecommerce.app](<http://com.ecommerce.app>);

import com.ecommerce.api.product.*;
import com.ecommerce.api.payment.*;
import java.math.BigDecimal;
import java.util.List;
import java.util.ServiceLoader;
import java.util.logging.Logger;
import [java.util.stream](<http://java.util.stream>).Collectors;

public class ECommerceApplication {
    private static final Logger logger = Logger.getLogger(ECommerceApplication.class.getName());
    
    public static void main(String[] args) {
        [logger.info](<http://logger.info>)("πŸš€ Starting E-Commerce Platform");
        
        ECommerceApplication app = new ECommerceApplication();
        
        // Demonstrate modular architecture
        app.demonstrateProductService();
        app.demonstratePaymentProcessors();
        app.simulateOrderProcessing();
        
        [logger.info](<http://logger.info>)("βœ… E-Commerce Platform demonstration completed");
    }
    
    private void demonstrateProductService() {
        System.out.println("\\nπŸ›οΈ Product Service Demonstration");
        System.out.println("=================================");
        
        // Load ProductService implementation via ServiceLoader
        ServiceLoader<ProductService> loader = ServiceLoader.load(ProductService.class);
        ProductService productService = loader.findFirst()
                .orElseThrow(() -> new RuntimeException("No ProductService implementation found"));
        
        System.out.println("πŸ“¦ Product service loaded: " + productService.getClass().getName());
        
        // Find all products
        List<Product> allProducts = productService.findAll();
        System.out.println("\\nπŸ“‹ All Products (" + allProducts.size() + "):");
        allProducts.forEach(product -> 
            System.out.println("  β€’ " + product.getName() + " - $" + product.getPrice()));
        
        // Search by category
        List<Product> electronics = productService.findByCategory("Electronics");
        System.out.println("\\nπŸ”Œ Electronics (" + electronics.size() + "):");
        electronics.forEach(product -> 
            System.out.println("  β€’ " + product.getName() + " - $" + product.getPrice()));
        
        // Search products
        List<Product> searchResults = [productService.search](<http://productService.search>)("java");
        System.out.println("\\nπŸ” Search results for 'java' (" + searchResults.size() + "):");
        searchResults.forEach(product -> 
            System.out.println("  β€’ " + product.getName() + " - $" + product.getPrice()));
    }
    
    private void demonstratePaymentProcessors() {
        System.out.println("\\nπŸ’³ Payment Processor Demonstration");
        System.out.println("===================================");
        
        // Load all available payment processors
        ServiceLoader<PaymentProcessor> loader = ServiceLoader.load(PaymentProcessor.class);
        List<PaymentProcessor> processors = [loader.stream](<http://loader.stream>)()
                .map(ServiceLoader.Provider::get)
                .collect(Collectors.toList());
        
        System.out.println("πŸ’° Available Payment Processors (" + processors.size() + "):");
        processors.forEach(processor -> 
            System.out.println("  β€’ " + processor.getProviderName()));
        
        // Test each payment processor
        PaymentRequest testRequest = new PaymentRequest(
            "customer123",
            new BigDecimal("99.99"),
            [PaymentMethod.CREDIT](<http://PaymentMethod.CREDIT>)_CARD,
            "USD"
        );
        
        System.out.println("\\nπŸ§ͺ Testing payment processing:");
        for (PaymentProcessor processor : processors) {
            System.out.println("\\n  Testing " + processor.getProviderName() + ":");
            
            // Check if processor supports the payment method
            boolean supports = processor.supports(testRequest.getMethod());
            System.out.println("    Supports Credit Card: " + supports);
            
            if (supports) {
                PaymentResult result = processor.processPayment(testRequest);
                System.out.println("    Result: " + (result.isSuccess() ? "SUCCESS" : "FAILED"));
                System.out.println("    Message: " + result.getMessage());
                if (result.isSuccess()) {
                    System.out.println("    Transaction ID: " + result.getTransactionId());
                }
            }
        }
    }
    
    private void simulateOrderProcessing() {
        System.out.println("\\nπŸ›’ Order Processing Simulation");
        System.out.println("===============================");
        
        // Load services
        ProductService productService = ServiceLoader.load(ProductService.class)
                .findFirst()
                .orElseThrow(() -> new RuntimeException("ProductService not found"));
        
        List<PaymentProcessor> paymentProcessors = ServiceLoader.load(PaymentProcessor.class)
                .stream()
                .map(ServiceLoader.Provider::get)
                .collect(Collectors.toList());
        
        // Simulate an order
        System.out.println("πŸ“ Creating sample order...");
        
        // Find some products
        List<Product> cart = List.of(
            productService.findById("1").orElse(null),
            productService.findById("3").orElse(null)
        ).stream()
         .filter(p -> p != null)
         .collect(Collectors.toList());
        
        if (cart.isEmpty()) {
            System.out.println("❌ No products found for order");
            return;
        }
        
        // Calculate total
        BigDecimal total = [cart.stream](<http://cart.stream>)()
                .map(Product::getPrice)
                .reduce([BigDecimal.ZERO](<http://BigDecimal.ZERO>), BigDecimal::add);
        
        System.out.println("πŸ›οΈ Order contents:");
        cart.forEach(product -> 
            System.out.println("  β€’ " + product.getName() + " - $" + product.getPrice()));
        System.out.println("πŸ’° Total: $" + total);
        
        // Try different payment methods
        PaymentMethod[] methods = {[PaymentMethod.CREDIT](<http://PaymentMethod.CREDIT>)_CARD, PaymentMethod.PAYPAL};
        
        for (PaymentMethod method : methods) {
            System.out.println("\\nπŸ’³ Attempting payment with " + method + ":");
            
            // Find a suitable payment processor
            PaymentProcessor processor = [paymentProcessors.stream](<http://paymentProcessors.stream>)()
                    .filter(p -> p.supports(method))
                    .findFirst()
                    .orElse(null);
            
            if (processor != null) {
                PaymentRequest request = new PaymentRequest("customer123", total, method, "USD");
                PaymentResult result = processor.processPayment(request);
                
                System.out.println("  Processor: " + processor.getProviderName());
                System.out.println("  Result: " + (result.isSuccess() ? "βœ… SUCCESS" : "❌ FAILED"));
                System.out.println("  Message: " + result.getMessage());
                
                if (result.isSuccess()) {
                    System.out.println("  Transaction ID: " + result.getTransactionId());
                    System.out.println("  πŸŽ‰ Order completed successfully!");
                    break; // Order successful, stop trying other methods
                }
            } else {
                System.out.println("  ❌ No processor available for " + method);
            }
        }
    }
}