|
11 | 11 | import io.microprofile.tutorial.store.payment.service.PaymentService; |
12 | 12 | import jakarta.enterprise.context.RequestScoped; |
13 | 13 | import jakarta.inject.Inject; |
| 14 | +import jakarta.ws.rs.GET; |
14 | 15 | import jakarta.ws.rs.POST; |
15 | 16 | import jakarta.ws.rs.Path; |
| 17 | +import jakarta.ws.rs.PathParam; |
16 | 18 | import jakarta.ws.rs.Produces; |
17 | 19 | import jakarta.ws.rs.QueryParam; |
18 | 20 | import jakarta.ws.rs.Consumes; |
|
22 | 24 | import java.math.BigDecimal; |
23 | 25 | import java.util.concurrent.CompletionStage; |
24 | 26 | import java.util.UUID; |
| 27 | +import java.util.logging.Logger; |
25 | 28 |
|
26 | 29 | @RequestScoped |
27 | 30 | @Path("/") |
28 | 31 | public class PaymentResource { |
| 32 | + |
| 33 | + private static final Logger logger = Logger.getLogger(PaymentResource.class.getName()); |
29 | 34 |
|
30 | 35 | @Inject |
31 | 36 | @ConfigProperty(name = "payment.gateway.endpoint") |
@@ -131,4 +136,72 @@ public Response verifyPaymentWithTelemetry(PaymentDetails paymentDetails) |
131 | 136 | } |
132 | 137 | } |
133 | 138 |
|
| 139 | + @GET |
| 140 | + @Path("/health/gateway") |
| 141 | + @Produces(MediaType.APPLICATION_JSON) |
| 142 | + @Operation(summary = "Check gateway health", description = "Check payment gateway health with circuit breaker protection") |
| 143 | + @APIResponses(value = { |
| 144 | + @APIResponse(responseCode = "200", description = "Gateway is healthy"), |
| 145 | + @APIResponse(responseCode = "503", description = "Gateway is unavailable") |
| 146 | + }) |
| 147 | + public Response checkGatewayHealth() { |
| 148 | + try { |
| 149 | + boolean healthy = paymentService.checkGatewayHealth(); |
| 150 | + |
| 151 | + if (healthy) { |
| 152 | + return Response.ok() |
| 153 | + .entity("{\"status\":\"healthy\",\"message\":\"Payment gateway is operational\"}") |
| 154 | + .build(); |
| 155 | + } |
| 156 | + |
| 157 | + return Response.status(Response.Status.SERVICE_UNAVAILABLE) |
| 158 | + .entity("{\"status\":\"unhealthy\",\"message\":\"Payment gateway is not responding\"}") |
| 159 | + .build(); |
| 160 | + } catch (Exception e) { |
| 161 | + logger.warning("Gateway health check failed: " + e.getMessage()); |
| 162 | + return Response.status(Response.Status.SERVICE_UNAVAILABLE) |
| 163 | + .entity("{\"status\":\"circuit_open\",\"message\":\"Circuit breaker is open - gateway appears to be down\"}") |
| 164 | + .build(); |
| 165 | + } |
| 166 | + } |
| 167 | + |
| 168 | + @POST |
| 169 | + @Path("/notify/{paymentId}") |
| 170 | + @Produces(MediaType.APPLICATION_JSON) |
| 171 | + @Operation(summary = "Send payment notification", description = "Send asynchronous payment notification with bulkhead protection") |
| 172 | + @APIResponses(value = { |
| 173 | + @APIResponse(responseCode = "200", description = "Notification sent or queued"), |
| 174 | + @APIResponse(responseCode = "503", description = "Bulkhead rejected request") |
| 175 | + }) |
| 176 | + public CompletionStage<Response> sendNotification( |
| 177 | + @PathParam("paymentId") String paymentId, |
| 178 | + @QueryParam("recipient") String recipient |
| 179 | + ) { |
| 180 | + |
| 181 | + if (recipient == null || recipient.isEmpty()) { |
| 182 | + recipient = "default@example.com"; |
| 183 | + } |
| 184 | + |
| 185 | + return paymentService.sendPaymentNotification(paymentId, recipient) |
| 186 | + .thenApply(result -> { |
| 187 | + logger.info("Notification result: " + result); |
| 188 | + return Response.ok() |
| 189 | + .entity("{\"status\":\"success\",\"message\":\"" + result + "\"}") |
| 190 | + .build(); |
| 191 | + }) |
| 192 | + .exceptionally(ex -> { |
| 193 | + logger.warning("Notification failed: " + ex.getMessage()); |
| 194 | + |
| 195 | + if (ex.getMessage() != null && ex.getMessage().contains("BulkheadException")) { |
| 196 | + return Response.status(Response.Status.SERVICE_UNAVAILABLE) |
| 197 | + .entity("{\"status\":\"rejected\",\"message\":\"Too many concurrent notifications - please try again later\"}") |
| 198 | + .build(); |
| 199 | + } |
| 200 | + |
| 201 | + return Response.status(Response.Status.INTERNAL_SERVER_ERROR) |
| 202 | + .entity("{\"status\":\"error\",\"message\":\"Notification processing failed\"}") |
| 203 | + .build(); |
| 204 | + }); |
| 205 | + } |
| 206 | + |
134 | 207 | } |
0 commit comments