Skip to content

Commit 2ad03d1

Browse files
committed
SOPE Project Phase 3, 4, 5 completed - doecorataor, observer and exception handling
1 parent cc6bf12 commit 2ad03d1

13 files changed

Lines changed: 302 additions & 12 deletions

File tree

Projects/SOPE/src/main/java/com/gautam/SOPE/SopeApplication.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
import org.springframework.boot.SpringApplication;
44
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
import org.springframework.scheduling.annotation.EnableAsync;
56

67
@SpringBootApplication
8+
@EnableAsync
79
public class SopeApplication {
810

911
public static void main(String[] args) {
Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.gautam.SOPE.orderengine.controller;
22

3-
import com.gautam.SOPE.orderengine.model.Order;
3+
import com.gautam.SOPE.orderengine.pricing.*;
4+
import com.gautam.SOPE.orderengine.service.OrderService;
45
import com.gautam.SOPE.orderengine.service.PaymentService;
56
import org.springframework.web.bind.annotation.*;
67

@@ -11,29 +12,62 @@
1112
public class OrderController {
1213

1314
private final PaymentService paymentService;
15+
private final OrderService orderService;
1416

15-
public OrderController(PaymentService paymentService) {
17+
public OrderController(PaymentService paymentService, OrderService orderService) {
1618
this.paymentService = paymentService;
19+
this.orderService = orderService;
1720
}
1821

22+
// ==========================================
23+
// Phase 1: Strategy Pattern (Payment)
24+
// ==========================================
1925
@PostMapping("/checkout")
2026
public String checkout(@RequestParam String type, @RequestParam BigDecimal amount) {
2127
paymentService.checkout(type, amount);
28+
return "Payment processed via " + type;
29+
}
2230

23-
// Simulation
24-
Order order = new Order();
25-
order.printStatus(); // Output: Order is currently in [NEW] state...
31+
// ==========================================
32+
// Phase 2 & 4: State & Observer (Lifecycle)
33+
// ==========================================
34+
@PostMapping("/create")
35+
public String createOrder() {
36+
Long id = orderService.createOrder();
37+
// The Service now handles the Event Publishing internally!
38+
return "Order created with ID: " + id + " [Status: NewState]. Check logs for Async Email event.";
39+
}
2640

27-
order.nextState(); // Output: Order payment processed...
28-
order.printStatus(); // Output: Order is currently in [PAID] state...
41+
@PostMapping("/{id}/next")
42+
public String nextState(@PathVariable Long id) {
43+
String newState = orderService.advanceOrderStatus(id);
44+
return "Order " + id + " moved to: " + newState;
45+
}
2946

30-
order.nextState(); // Output: Order has been shipped!
31-
order.printStatus(); // Output: Order is currently in [SHIPPED] state.
47+
@GetMapping("/{id}")
48+
public String getOrder(@PathVariable Long id) {
49+
return "Order " + id + " is currently in: " + orderService.getStatus(id);
50+
}
3251

33-
order.nextState(); // Output: Order is already shipped...
52+
// ==========================================
53+
// Phase 3: Decorator Pattern (Pricing)
54+
// ==========================================
55+
@GetMapping("/price")
56+
public BigDecimal calculatePrice(@RequestParam BigDecimal cost,
57+
@RequestParam(defaultValue = "false") boolean tax,
58+
@RequestParam(defaultValue = "false") boolean pack) {
3459

35-
return "Payment processed via " + type;
36-
}
60+
// 1. Start with Base Price
61+
PriceComponent finalPrice = new BasePrice(cost);
3762

63+
// 2. Wrap Dynamically based on flags
64+
if (tax) {
65+
finalPrice = new TaxDecorator(finalPrice);
66+
}
67+
if (pack) {
68+
finalPrice = new PackagingDecorator(finalPrice);
69+
}
3870

71+
return finalPrice.getCost();
72+
}
3973
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.gautam.SOPE.orderengine.event;
2+
3+
import com.gautam.SOPE.orderengine.model.Order;
4+
import org.springframework.context.ApplicationEvent;
5+
6+
public class OrderPlacedEvent extends ApplicationEvent {
7+
8+
private final Order order;
9+
10+
public OrderPlacedEvent(Object source, Order order) {
11+
super(source);
12+
this.order = order;
13+
}
14+
15+
public Order getOrder() {
16+
return order;
17+
}
18+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.gautam.SOPE.orderengine.exception;
2+
3+
import org.springframework.http.HttpStatus;
4+
import org.springframework.http.ResponseEntity;
5+
import org.springframework.web.bind.annotation.ControllerAdvice;
6+
import org.springframework.web.bind.annotation.ExceptionHandler;
7+
8+
import java.time.LocalDateTime;
9+
import java.util.HashMap;
10+
import java.util.Map;
11+
12+
@ControllerAdvice
13+
public class GlobalExceptionHandler {
14+
15+
// 1. Handle our specific Custom Exception
16+
@ExceptionHandler(OrderNotFoundException.class)
17+
public ResponseEntity<Map<String, Object>> handleOrderNotFound(OrderNotFoundException ex) {
18+
Map<String, Object> errorResponse = new HashMap<>();
19+
errorResponse.put("timestamp", LocalDateTime.now());
20+
errorResponse.put("status", HttpStatus.NOT_FOUND.value());
21+
errorResponse.put("error", "Order Not Found");
22+
errorResponse.put("message", ex.getMessage());
23+
24+
return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND);
25+
}
26+
27+
// 2. Handle generic "Catch All" (e.g., NullPointer, IllegalArgument)
28+
@ExceptionHandler(Exception.class)
29+
public ResponseEntity<Map<String, Object>> handleGlobalException(Exception ex) {
30+
Map<String, Object> errorResponse = new HashMap<>();
31+
errorResponse.put("timestamp", LocalDateTime.now());
32+
errorResponse.put("status", HttpStatus.INTERNAL_SERVER_ERROR.value());
33+
errorResponse.put("error", "Internal Server Error");
34+
errorResponse.put("message", ex.getMessage());
35+
36+
return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
37+
}
38+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.gautam.SOPE.orderengine.exception;
2+
3+
4+
public class OrderNotFoundException extends RuntimeException {
5+
public OrderNotFoundException(String message) {
6+
super(message);
7+
}
8+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.gautam.SOPE.orderengine.listener;
2+
3+
import com.gautam.SOPE.orderengine.event.OrderPlacedEvent;
4+
import org.springframework.context.event.EventListener;
5+
import org.springframework.scheduling.annotation.Async;
6+
import org.springframework.stereotype.Component;
7+
8+
@Component
9+
public class OrderEventListener {
10+
11+
// Listener 1: Email Service
12+
@Async // This runs in a separate thread!
13+
@EventListener
14+
public void handleEmailNotification(OrderPlacedEvent event) {
15+
try {
16+
// Simulate a slow network call (2 seconds)
17+
System.out.println("2. Email Listener: Sending confirmation email...");
18+
Thread.sleep(2000);
19+
System.out.println("3. Email Listener: Email sent for Order.");
20+
} catch (InterruptedException e) {
21+
e.printStackTrace();
22+
}
23+
}
24+
25+
// Listener 2: Inventory Service
26+
@Async // Runs in parallel with Email
27+
@EventListener
28+
public void handleInventoryUpdate(OrderPlacedEvent event) {
29+
System.out.println("2. Inventory Listener: Deducting stock...");
30+
}
31+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.gautam.SOPE.orderengine.pricing;
2+
3+
import java.math.BigDecimal;
4+
5+
public class BasePrice implements PriceComponent {
6+
7+
private final BigDecimal originalPrice;
8+
9+
public BasePrice(BigDecimal originalPrice) {
10+
this.originalPrice = originalPrice;
11+
}
12+
13+
@Override
14+
public BigDecimal getCost() {
15+
return originalPrice;
16+
}
17+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.gautam.SOPE.orderengine.pricing;
2+
3+
import java.math.BigDecimal;
4+
5+
public class PackagingDecorator extends PriceDecorator {
6+
7+
public PackagingDecorator(PriceComponent price) {
8+
super(price);
9+
}
10+
11+
@Override
12+
public BigDecimal getCost() {
13+
return super.getCost().add(new BigDecimal("5.00"));
14+
}
15+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.gautam.SOPE.orderengine.pricing;
2+
3+
import java.math.BigDecimal;
4+
5+
public interface PriceComponent {
6+
BigDecimal getCost();
7+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.gautam.SOPE.orderengine.pricing;
2+
3+
4+
import java.math.BigDecimal;
5+
6+
public abstract class PriceDecorator implements PriceComponent {
7+
8+
protected final PriceComponent wrappedPrice;
9+
10+
public PriceDecorator(PriceComponent price) {
11+
this.wrappedPrice = price;
12+
}
13+
14+
@Override
15+
public BigDecimal getCost() {
16+
return wrappedPrice.getCost();
17+
}
18+
}

0 commit comments

Comments
 (0)