Skip to content

Latest commit

 

History

History
1111 lines (848 loc) · 30.7 KB

File metadata and controls

1111 lines (848 loc) · 30.7 KB

MicroProfile E-Commerce Store

Overview

This project demonstrates a microservices-based e-commerce application built with Jakarta EE and MicroProfile, running on Open Liberty runtime. The application is composed of multiple independent services that work together to provide a complete e-commerce solution.

Services

The application is split into the following microservices:

Service Description

User Service

Manages user accounts, authentication, and profile information

Inventory Service

Tracks product inventory and stock levels

Order Service

Manages customer orders, order items, and order status

Catalog Service

Provides product information, categories, and search capabilities

Shopping Cart Service

Manages user shopping cart items and temporary product storage

Shipment Service

Handles shipping orders, tracking, and delivery status updates

Payment Service

Processes payments and manages payment methods and transactions. Demonstrates MicroProfile Rest Client 4.0 features including CDI injection, ResponseExceptionMapper, custom filters, and programmatic client creation.

Technology Stack

  • Jakarta EE 10.0: For enterprise Java standardization

  • MicroProfile 7.x: For cloud-native APIs

  • MicroProfile Rest Client 4.0: Type-safe REST service consumption

  • Open Liberty: Lightweight, flexible runtime for Java microservices

  • Maven: For project management and builds

Quick Start

Prerequisites

  • JDK 21 or later

  • Maven 3.9 or later

  • Docker (optional for containerized deployment)

Running the Application

  1. Clone the repository:

    git clone https://github.com/microprofile/microprofile-tutorial.git
    cd code/chapter11
  2. Start each microservice individually:

User Service

cd user
mvn liberty:run

The service will be available at http://localhost:6050/user

Inventory Service

cd inventory
mvn liberty:run

The service will be available at http://localhost:7050/inventory

Order Service

cd order
mvn liberty:run

The service will be available at http://localhost:8050/order

Catalog Service

cd catalog
mvn liberty:run

The service will be available at http://localhost:9050/catalog

Building the Application

To build all services:

mvn clean package

Docker Deployment

You can also run all services together using Docker Compose:

# Make the script executable (if needed)
chmod +x run-all-services.sh

# Run the script to build and start all services
./run-all-services.sh

Or manually:

# Build all projects first
cd user && mvn clean package && cd ..
cd inventory && mvn clean package && cd ..
cd order && mvn clean package && cd ..
cd catalog && mvn clean package && cd ..

# Start all services
docker-compose up -d

This will start all services in Docker containers with the following endpoints:

API Documentation

Each microservice provides its own OpenAPI documentation, available at:

MicroProfile Rest Client 4.0 Features

This application demonstrates the latest MicroProfile Rest Client 4.0 features:

New baseUri(String) Convenience Method

The Inventory Service showcases the new baseUri(String) method that eliminates the need for URI.create():

// MicroProfile Rest Client 4.0 - Simplified approach
ProductServiceClient client = RestClientBuilder.newBuilder()
    .baseUri("http://localhost:5050/catalog/api")  // Direct String parameter
    .connectTimeout(5, TimeUnit.SECONDS)
    .readTimeout(10, TimeUnit.SECONDS)
    .build(ProductServiceClient.class);

This replaces the older pattern:

// Old approach (Rest Client 3.x)
URI catalogServiceUri = URI.create("http://localhost:5050/catalog/api");
ProductServiceClient client = RestClientBuilder.newBuilder()
    .baseUri(catalogServiceUri)  // Required URI object
    .build(ProductServiceClient.class);

CDI Integration with @RestClient

The @RestClient qualifier is mandatory for CDI injection:

@Inject
@RestClient  // Required qualifier
private ProductServiceClient productClient;

AutoCloseable Support

REST client interfaces extend AutoCloseable for proper resource management:

@RegisterRestClient(configKey = "product-service")
@Path("/products")
public interface ProductServiceClient extends AutoCloseable {
    // Client methods
}

See the InventoryService class for complete working examples of these features.

Testing the Services

User Service

# Get all users
curl -X GET http://localhost:6050/user/api/users

# Create a new user
curl -X POST http://localhost:6050/user/api/users \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Jane Doe",
    "email": "jane@example.com",
    "passwordHash": "password123",
    "address": "123 Main St",
    "phoneNumber": "555-123-4567"
  }'

# Get a user by ID
curl -X GET http://localhost:6050/user/api/users/1

# Update a user
curl -X PUT http://localhost:6050/user/api/users/1 \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Jane Smith",
    "email": "jane@example.com",
    "passwordHash": "password123",
    "address": "456 Oak Ave",
    "phoneNumber": "555-123-4567"
  }'

# Delete a user
curl -X DELETE http://localhost:6050/user/api/users/1

Inventory Service

# Get all inventory items
curl -X GET http://localhost:7050/inventory/api/inventories

# Create a new inventory item
curl -X POST http://localhost:7050/inventory/api/inventories \
  -H "Content-Type: application/json" \
  -d '{
    "productId": 101,
    "quantity": 25
  }'

# Get inventory by ID
curl -X GET http://localhost:7050/inventory/api/inventories/1

# Get inventory by product ID
curl -X GET http://localhost:7050/inventory/api/inventories/product/101

# Update inventory
curl -X PUT http://localhost:7050/inventory/api/inventories/1 \
  -H "Content-Type: application/json" \
  -d '{
    "productId": 101,
    "quantity": 50
  }'

# Update product quantity
curl -X PATCH http://localhost:7050/inventory/api/inventories/product/101/quantity/75

# Delete inventory
curl -X DELETE http://localhost:7050/inventory/api/inventories/1

Order Service

# Get all orders
curl -X GET http://localhost:8050/order/api/orders

# Create a new order
curl -X POST http://localhost:8050/order/api/orders \
  -H "Content-Type: application/json" \
  -d '{
    "userId": 1,
    "totalPrice": 149.98,
    "status": "CREATED",
    "orderItems": [
      {
        "productId": 101,
        "quantity": 2,
        "priceAtOrder": 49.99
      },
      {
        "productId": 102,
        "quantity": 1,
        "priceAtOrder": 50.00
      }
    ]
  }'

# Get order by ID
curl -X GET http://localhost:8050/order/api/orders/1

# Update order status
curl -X PATCH http://localhost:8050/order/api/orders/1/status/PAID

# Get items for an order
curl -X GET http://localhost:8050/order/api/orders/1/items

# Delete order
curl -X DELETE http://localhost:8050/order/api/orders/1

Catalog Service

# Get all products
curl -X GET http://localhost:9050/catalog/api/products

# Get a product by ID
curl -X GET http://localhost:9050/catalog/api/products/1

# Search products
curl -X GET "http://localhost:9050/catalog/api/products/search?keyword=laptop"

Verifying MicroProfile Rest Client Examples

The Payment Service demonstrates comprehensive MicroProfile Rest Client 4.0 features. Follow these steps to verify each example.

Prerequisites

Before testing the examples, ensure both services are running:

# Terminal 1 - Start the Catalog Service (provides product data)
cd catalog
mvn liberty:run

# Terminal 2 - Start the Payment Service (consumes catalog service)
cd payment
mvn liberty:run

The services will be available at:

Example 1: Basic REST Client with @RegisterRestClient

This example demonstrates defining a MicroProfile Rest Client interface with @RegisterRestClient.

What to Verify:

  • ProductClient interface is annotated with @RegisterRestClient(configKey = "catalog-service")

  • The client uses Jakarta REST annotations (@GET, @Path, @PathParam)

  • Configuration is externalized in microprofile-config.properties

Test the Client:

# Get all products via REST client
curl http://localhost:9050/payment/catalog/products

# Expected Response: JSON array of products
[
  {
    "id": 1,
    "name": "Laptop",
    "price": 999.99
  },
  ...
]

Files to Review:

  • src/main/java/io/microprofile/tutorial/store/payment/client/ProductClient.java

  • src/main/resources/META-INF/microprofile-config.properties

Example 2: MicroProfile Config Integration

This example shows externalized configuration for REST clients using MicroProfile Config.

What to Verify:

  • Configuration properties follow the pattern: <configKey>/mp-rest/<property>

  • Base URL, timeouts, and scope are configured

  • Properties can be overridden via environment variables

Configuration Properties:

# Base URL
catalog-service/mp-rest/url=http://localhost:5050/catalog/api

# Timeouts (in milliseconds)
catalog-service/mp-rest/connectTimeout=3000
catalog-service/mp-rest/readTimeout=5000

# CDI Scope
catalog-service/mp-rest/scope=jakarta.enterprise.context.ApplicationScoped

# Follow redirects
catalog-service/mp-rest/followRedirects=true

Test Configuration:

# Verify the client respects timeout settings
# (This will timeout if catalog service is slow)
time curl http://localhost:9050/payment/catalog/products

Files to Review:

  • src/main/resources/META-INF/microprofile-config.properties

Example 3: CDI Injection with @Inject and @RestClient

This example demonstrates dependency injection of REST clients using CDI.

What to Verify:

  • ProductClient is injected with @Inject and @RestClient qualifiers

  • The @RestClient qualifier is mandatory in MicroProfile Rest Client 4.0

  • Service layer (ProductCatalogService) uses the injected client

  • @ApplicationScoped bean lifecycle management

Test CDI Injection:

# Test service that uses injected REST client
curl http://localhost:9050/payment/catalog/products

# Get specific product (uses getProductById method)
curl http://localhost:9050/payment/catalog/products/1

# Expected Response:
{
  "id": 1,
  "name": "Laptop",
  "price": 999.99
}

Files to Review:

  • src/main/java/io/microprofile/tutorial/store/payment/service/ProductCatalogService.java

  • src/main/webapp/WEB-INF/beans.xml (CDI configuration)

Example 4: Parameter Annotations (@PathParam, @QueryParam)

This example shows different parameter types in REST client methods.

What to Verify:

  • @PathParam for path variables (e.g., /products/{id})

  • @QueryParam for query string parameters (e.g., ?price=99.99)

  • Parameters are automatically encoded and passed to remote service

Test Path Parameters:

# Test @PathParam - Get product by ID
curl http://localhost:9050/payment/catalog/products/1

# Test with different IDs
curl http://localhost:9050/payment/catalog/products/2
curl http://localhost:9050/payment/catalog/products/3

Test Query Parameters:

# Test @QueryParam - Validate product price
curl "http://localhost:9050/payment/catalog/products/1/validate-price?price=999.99"

# Expected Response (if price matches):
{
  "productId": 1,
  "expectedPrice": 999.99,
  "valid": true,
  "message": "Price matches"
}

# Test with wrong price
curl "http://localhost:9050/payment/catalog/products/1/validate-price?price=500.00"

# Expected Response (if price doesn't match):
{
  "productId": 1,
  "expectedPrice": 500.0,
  "valid": false,
  "message": "Price mismatch detected"
}

Files to Review:

  • src/main/java/io/microprofile/tutorial/store/payment/client/ProductClient.java (method signatures)

  • src/main/java/io/microprofile/tutorial/store/payment/resource/ProductCatalogResource.java (endpoint implementation)

Example 5: ResponseExceptionMapper - Custom Error Handling

This example demonstrates mapping HTTP error responses to custom exceptions.

What to Verify:

  • ProductServiceResponseExceptionMapper implements ResponseExceptionMapper<Throwable>

  • HTTP 404 responses map to ProductNotFoundException (checked exception)

  • HTTP 503 responses map to ServiceUnavailableException (unchecked exception)

  • Error messages are extracted from JSON response bodies

  • @Priority annotation controls mapper ordering

Test Exception Mapping:

# Test successful request (no exception)
curl http://localhost:9050/payment/catalog/products/1/detailed

# Expected Response:
{
  "productId": 1,
  "product": {
    "id": 1,
    "name": "Laptop",
    "price": 999.99
  },
  "note": "Product retrieved successfully - no exceptions thrown"
}

# Test 404 error (ProductNotFoundException)
curl http://localhost:9050/payment/catalog/products/999999/detailed

# Expected Response:
{
  "productId": 999999,
  "error": "Product not found",
  "note": "ProductNotFoundException was caught and handled by the service layer"
}

# Test product availability (handles 404 gracefully)
curl http://localhost:9050/payment/catalog/products/999999/availability

# Expected Response:
{
  "productId": 999999,
  "available": false,
  "message": "Product is not available"
}

Verify Exception Types:

  1. Checked Exception (ProductNotFoundException):

    • Must be declared in method signature: throws ProductNotFoundException

    • Only thrown if method declares it

    • Handled gracefully in service layer

  2. Unchecked Exception (ServiceUnavailableException):

    • Extends RuntimeException

    • Always thrown regardless of method signature

    • Used for service availability errors

Test Service Unavailable Scenario:

# Stop the catalog service to simulate 503 error
# Then try to access products
curl http://localhost:9050/payment/catalog/products/1/detailed

# Expected Response (if catalog service is down):
{
  "productId": 1,
  "error": "Catalog service unavailable",
  "statusCode": 503,
  "message": "...",
  "note": "ServiceUnavailableException (unchecked) was thrown by ResponseExceptionMapper"
}

Files to Review:

  • src/main/java/io/microprofile/tutorial/store/payment/client/ProductServiceResponseExceptionMapper.java

  • src/main/java/io/microprofile/tutorial/store/payment/exception/ProductNotFoundException.java

  • src/main/java/io/microprofile/tutorial/store/payment/exception/ServiceUnavailableException.java

  • src/main/java/io/microprofile/tutorial/store/payment/client/ProductClient.java (see @RegisterProvider)

Verifying beans.xml Configuration

The beans.xml file is required for CDI bean discovery and REST client registration.

What to Verify:

  • File location: src/main/webapp/WEB-INF/beans.xml

  • Bean discovery mode: bean-discovery-mode="all"

  • Version: Jakarta EE 10 (beans 4.0)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://jakarta.ee/xml/ns/jakartaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
                           https://jakarta.ee/xml/ns/jakartaee/beans_4_0.xsd"
       version="4.0"
       bean-discovery-mode="all">
</beans>

Files to Review:

  • src/main/webapp/WEB-INF/beans.xml

Additional Testing Scenarios

Test Complete Product Validation Flow

# 1. Check if product is available
curl http://localhost:9050/payment/catalog/products/1/availability

# 2. Validate product price before payment
curl "http://localhost:9050/payment/catalog/products/1/validate-price?price=999.99"

# 3. Get product details for payment processing
curl http://localhost:9050/payment/catalog/products/1

Test Error Scenarios

# Invalid product ID (404)
curl http://localhost:9050/payment/catalog/products/999999

# Missing query parameter (400)
curl http://localhost:9050/payment/catalog/products/1/validate-price

# Invalid price value (400)
curl "http://localhost:9050/payment/catalog/products/1/validate-price?price=-10"

Viewing OpenAPI Documentation

Access the OpenAPI documentation to see all available endpoints:

# Payment Service OpenAPI documentation
open http://localhost:9050/openapi/ui/

# Or view the OpenAPI JSON spec
curl http://localhost:9050/openapi

Debugging Tips

Enable Detailed Logging:

Edit src/main/liberty/config/server.xml and add:

<logging traceSpecification="*=info:org.eclipse.microprofile.rest.client=all"/>

Check Server Logs:

# View real-time logs
tail -f target/liberty/wlp/usr/servers/mpServer/logs/messages.log

# Search for REST client activity
grep "ProductClient" target/liberty/wlp/usr/servers/mpServer/logs/messages.log

Verify Configuration Loading:

# Check if properties are loaded correctly
# Look for log messages about MicroProfile Config
grep "mp-rest" target/liberty/wlp/usr/servers/mpServer/logs/messages.log

Example 6: Custom Filters and Interceptors

This example demonstrates implementing custom filters for cross-cutting concerns like authentication, logging, and distributed tracing.

What to Verify:

  • ClientRequestFilter implementations for request interception

  • ClientResponseFilter implementations for response logging

  • Filter registration using @RegisterProvider

  • Filter execution order using @Priority

  • Cross-cutting concerns separated from business logic

Filters Implemented:

  1. BearerTokenFilter (Priority 1000 - AUTHENTICATION)

    • Adds Authorization header with Bearer token

    • Reads token from MicroProfile Config

    • Runs first due to authentication priority

  2. CorrelationIdFilter (Priority 100)

    • Adds X-Correlation-ID for distributed tracing

    • Generates or propagates correlation IDs

    • Adds unique X-Request-ID for each request

  3. RequestLoggingFilter (Priority 300)

    • Logs complete outgoing request details

    • Masks sensitive headers (Authorization, API keys)

    • Runs after authentication and correlation filters

  4. ResponseLoggingFilter (Priority 300)

    • Logs complete incoming response details

    • Includes status codes, headers, timing

    • Correlates responses with requests

Test Basic Filter Functionality:

# Get all products with filters (watch server logs)
curl http://localhost:9050/payment/catalog/filtered/products

# Expected in logs:
# 1. "Bearer token authentication added to request" (if token configured)
# 2. "Generated new Correlation ID: <uuid>"
# 3. "=== Outgoing REST Client Request ==="
# 4. "Method: GET"
# 5. "URI: http://localhost:5050/catalog/api/products"
# 6. "=== Incoming REST Client Response ==="
# 7. "Status: 200 OK"

Test Filter Execution with Specific Product:

# Get specific product - observe filter chain execution
curl http://localhost:9050/payment/catalog/filtered/products/1

# Expected Response:
{
  "product": {
    "id": 1,
    "name": "Laptop",
    "price": 999.99
  },
  "method": "REST Client with Custom Filters",
  "filterChain": "BearerToken → CorrelationId → RequestLogging → [HTTP] → ResponseLogging",
  "note": "Check server logs for X-Correlation-ID and detailed request/response traces"
}

Test Filters with Error Responses:

# Test 404 error - filters still execute and log the error
curl http://localhost:9050/payment/catalog/filtered/products/999999

# Expected in logs:
# - RequestLoggingFilter logs the outgoing request
# - ResponseLoggingFilter logs: "Status: 404 Not Found"
# - ResponseLoggingFilter logs: "Result: CLIENT ERROR (404)"
# - ProductNotFoundException is thrown after logging

# Check product availability (handles 404 gracefully)
curl http://localhost:9050/payment/catalog/filtered/products/999999/available

# Expected Response:
{
  "productId": 999999,
  "available": false,
  "method": "REST Client with Custom Filters",
  "note": "Product not found - ResponseLoggingFilter logged 404 before returning false"
}

Compare Filtered vs Non-Filtered Clients:

# Side-by-side comparison endpoint
curl http://localhost:9050/payment/catalog/compare/1

# This endpoint calls BOTH clients:
# 1. Non-filtered client (minimal logging)
# 2. Filtered client (comprehensive logging)

# Compare the server logs to see the difference in observability

Expected Log Output (Filtered Client):

INFO: Getting product with custom filters: 1
INFO: Bearer token authentication added to request
INFO: Generated new Correlation ID: a1b2c3d4-e5f6-7890-abcd-ef1234567890
INFO: === Outgoing REST Client Request ===
INFO: Method: GET
INFO: URI: http://localhost:5050/catalog/api/products/1
INFO: Request Headers:
INFO:   Authorization: [REDACTED]
INFO:   X-Correlation-ID: a1b2c3d4-e5f6-7890-abcd-ef1234567890
INFO:   X-Request-ID: b2c3d4e5-f6a7-8901-bcde-f12345678901
INFO: ===================================
INFO: === Incoming REST Client Response ===
INFO: Request: GET http://localhost:5050/catalog/api/products/1
INFO: Status: 200 OK
INFO: Content-Type: application/json
INFO:   X-Correlation-ID: a1b2c3d4-e5f6-7890-abcd-ef1234567890
INFO: Result: SUCCESS
INFO: ====================================
INFO: Successfully retrieved product with filters: Laptop

Configure Bearer Token (Optional):

Edit src/main/resources/META-INF/microprofile-config.properties:

# Uncomment and set your Bearer token
catalog-service.bearer.token=your-test-token-here

Restart the server and test again - you’ll see the Authorization header added.

Verify Filter Priority:

Filter execution order is determined by @Priority values (lower numbers execute first):

Request Flow:
1. BearerTokenFilter (Priority 1000 - AUTHENTICATION)
2. CorrelationIdFilter (Priority 100)
3. RequestLoggingFilter (Priority 300)
4. --- HTTP REQUEST SENT ---
5. --- HTTP RESPONSE RECEIVED ---
6. ResponseLoggingFilter (Priority 300)

Files to Review:

  • [BearerTokenFilter.java](src/main/java/io/microprofile/tutorial/store/payment/filter/BearerTokenFilter.java) - Authentication filter

  • [CorrelationIdFilter.java](src/main/java/io/microprofile/tutorial/store/payment/filter/CorrelationIdFilter.java) - Distributed tracing

  • [RequestLoggingFilter.java](src/main/java/io/microprofile/tutorial/store/payment/filter/RequestLoggingFilter.java) - Request logging

  • [ResponseLoggingFilter.java](src/main/java/io/microprofile/tutorial/store/payment/filter/ResponseLoggingFilter.java) - Response logging

  • [ProductClientWithFilters.java](src/main/java/io/microprofile/tutorial/store/payment/client/ProductClientWithFilters.java) - Client with filters registered

Benefits of Using Filters:

# Test the comparison endpoint to see benefits
curl http://localhost:9050/payment/catalog/compare/1

# Response shows benefits:
{
  "comparison": {
    "noFilters": "Minimal logging - only business logic logs",
    "withFilters": "Comprehensive logging - authentication, tracing, request/response details"
  },
  "filterBenefits": [
    "Authentication without changing business code",
    "Distributed tracing with correlation IDs",
    "Complete request/response visibility",
    "Easier debugging and monitoring",
    "Cross-cutting concerns separated from business logic"
  ]
}

Common Use Cases for Filters:

  • Authentication: Inject API keys, Bearer tokens, Basic Auth credentials

  • Tracing: Propagate correlation IDs, request IDs, span IDs across services

  • Logging: Debug requests/responses without modifying business logic

  • Headers: Add custom headers, propagate context from incoming requests

  • Monitoring: Collect metrics, measure latency, track error rates

  • Security: Sign requests, validate responses, enforce policies

Security Best Practices:

  1. Never log sensitive data (passwords, tokens, PII) in production

  2. Use [REDACTED] masks for sensitive headers in logs

  3. Store tokens in configuration, not hardcoded

  4. Use environment-specific configuration for different deployments

  5. Implement proper error handling in filters

Example 7: RestClientBuilder for Programmatic Client Creation

This example demonstrates creating REST clients programmatically without CDI injection.

What to Verify:

  • Creating clients with RestClientBuilder.newBuilder()

  • Using baseUri(String) method (MicroProfile Rest Client 4.0)

  • Try-with-resources pattern with AutoCloseable

  • Dynamic configuration from MicroProfile Config

  • Environment-specific client configuration

Test Basic RestClientBuilder:

# Check product availability with programmatic client
curl http://localhost:9050/payment/catalog/builder/products/1/check

# Expected Response:
{
  "productId": 1,
  "available": true,
  "method": "RestClientBuilder (programmatic)",
  "note": "Product found using programmatically created client"
}

Test Dynamic Configuration:

# Get product with configuration loaded from microprofile-config.properties
curl http://localhost:9050/payment/catalog/builder/products/1/dynamic

# Expected Response:
{
  "product": {
    "id": 1,
    "name": "Laptop",
    "price": 999.99
  },
  "method": "RestClientBuilder with dynamic config",
  "note": "Configuration loaded from microprofile-config.properties at runtime"
}

Test Environment-Specific Configuration:

# Get product for development environment
curl http://localhost:9050/payment/catalog/builder/products/1/env/dev

# Get product for staging environment (will fail if service not available)
curl http://localhost:9050/payment/catalog/builder/products/1/env/staging

# Get product for production environment (will fail if service not available)
curl http://localhost:9050/payment/catalog/builder/products/1/env/prod

# Expected Response (dev):
{
  "product": {
    "id": 1,
    "name": "Laptop",
    "price": 999.99
  },
  "environment": "dev",
  "method": "RestClientBuilder with environment-specific config",
  "note": "Client configured for dev environment"
}

Test Batch Processing:

# Check multiple products in batch
curl -X POST http://localhost:9050/payment/catalog/builder/products/batch-check \
  -H "Content-Type: application/json" \
  -d '{"productIds": [1, 2, 3, 999, 1000]}'

# Expected Response:
{
  "totalChecked": 5,
  "availableCount": 3,
  "unavailableCount": 2,
  "method": "RestClientBuilder (batch processing)",
  "note": "Each product checked with a separate programmatically created client instance"
}

When to Use RestClientBuilder:

  • CDI is unavailable or not desired

  • Client configuration must be determined dynamically at runtime

  • Creating multiple clients with different configurations

  • Utility methods or batch jobs

  • Testing environments where CDI injection is complex

Files to Review:

  • [ProductClientBuilderService.java](src/main/java/io/microprofile/tutorial/store/payment/service/ProductClientBuilderService.java) - Programmatic client creation examples

  • [ProductClient.java](src/main/java/io/microprofile/tutorial/store/payment/client/ProductClient.java) - Note extends AutoCloseable

Learning Objective

After completing these examples, you should understand:

  • How to define a REST client interface with @RegisterRestClient

  • How to configure REST clients using MicroProfile Config properties

  • How to inject REST clients with @Inject and @RestClient in CDI beans

  • How to use parameter annotations (@PathParam, @QueryParam, @HeaderParam)

  • How to implement ResponseExceptionMapper for custom error handling

  • The difference between checked and unchecked exceptions in exception mappers

  • How to register exception mappers with @RegisterProvider

  • How to extract error messages from JSON response bodies

  • How CDI bean discovery works with beans.xml

  • How to test REST clients in a microservices environment

  • How to implement custom ClientRequestFilter for request interception

  • How to implement custom ClientResponseFilter for response logging

  • How to use @Priority to control filter execution order

  • How to register filters using @RegisterProvider annotation

  • How to separate cross-cutting concerns (auth, logging, tracing) from business logic

  • How to create REST clients programmatically using RestClientBuilder

  • How to use the baseUri(String) method in MicroProfile Rest Client 4.0

  • When to use RestClientBuilder vs CDI injection

  • How to use try-with-resources with AutoCloseable REST clients

  • How to dynamically configure REST clients at runtime

Troubleshooting Common Issues

Issue: "Unsatisfied dependencies for type ProductClient"

Solution: Ensure beans.xml exists in src/main/webapp/WEB-INF/ with bean-discovery-mode="all"

Issue: "Connection refused" errors

Solution: Verify the catalog service is running on port 5050:

curl http://localhost:5050/catalog/api/products

Issue: "Read timeout" errors

Solution: Increase timeout values in microprofile-config.properties:

catalog-service/mp-rest/readTimeout=10000

Issue: Exceptions not being mapped correctly

Solution: Verify @RegisterProvider is present on the client interface and the mapper implements ResponseExceptionMapper<Throwable>

Project Structure

liberty-rest-app/
├── user/               # User management service
├── inventory/          # Inventory management service
├── order/              # Order management service
└── catalog/            # Product catalog service

Each service follows a similar internal structure:

service/
├── src/
│   ├── main/
│   │   ├── java/      # Java source code
│   │   ├── liberty/   # Liberty server configuration
│   │   └── webapp/    # Web resources
│   └── test/          # Test code
└── pom.xml            # Maven configuration

Key MicroProfile Features Demonstrated

  • Config: Externalized configuration

  • Fault Tolerance: Circuit breakers, retries, fallbacks

  • Health Checks: Application health monitoring

  • Metrics: Performance monitoring

  • OpenAPI: API documentation

  • Rest Client: Type-safe REST clients