Eclipse MicroProfile Interview Questions and Answers

Master Eclipse MicroProfile with production-ready interview questions covering Config, REST Client, Fault Tolerance, Retry, Timeout, Circuit Breaker, Bulkhead, Health, OpenAPI, JWT authentication, telemetry, and cloud-native best practices.


Eclipse MicroProfile Interview Questions and Answers

Introduction

Eclipse MicroProfile provides specifications for developing cloud-native Java microservices.

Jakarta EE provides foundational enterprise capabilities such as:

  • Dependency injection
  • REST APIs
  • Persistence
  • Transactions
  • Validation
  • Security
  • Messaging

MicroProfile adds capabilities commonly needed when those applications run as distributed microservices.

These capabilities include:

  • Externalized configuration
  • Type-safe REST clients
  • Retries
  • Timeouts
  • Circuit breakers
  • Bulkheads
  • Fallback behavior
  • Health checks
  • JWT-based security
  • OpenAPI documentation
  • Telemetry and distributed observability

MicroProfile does not replace Jakarta EE.

It builds on selected Jakarta EE technologies, especially CDI and Jakarta REST, to provide a standardized microservice programming model.

A production-ready MicroProfile service must consider:

  • Partial failures
  • Network latency
  • Configuration management
  • Service health
  • Authentication and authorization
  • Distributed tracing
  • Metrics
  • Logging
  • Retry storms
  • Timeout budgets
  • Resource isolation
  • API documentation
  • Graceful degradation

This guide covers the 15 most frequently asked Eclipse MicroProfile interview questions with code examples, Mermaid diagrams, production scenarios, common mistakes, and senior-level recommendations.


Q1. What is Eclipse MicroProfile?

Answer

Eclipse MicroProfile is a collection of specifications for building cloud-native Java microservices.

It standardizes common microservice capabilities so applications can use portable APIs across compatible runtimes.

Core Capabilities

MicroProfile commonly provides specifications for:

  • Configuration
  • REST clients
  • Fault tolerance
  • Health checks
  • JWT authentication
  • OpenAPI documentation
  • Telemetry

High-Level Architecture

flowchart TD
    A[Client] --> B[MicroProfile Service]
    B --> C[Jakarta REST API]
    B --> D[MicroProfile Config]
    B --> E[MicroProfile Fault Tolerance]
    B --> F[MicroProfile Health]
    B --> G[MicroProfile JWT]
    B --> H[MicroProfile OpenAPI]
    B --> I[MicroProfile Telemetry]

    B --> J[Database]
    B --> K[Remote Service]
    B --> L[Message Broker]

Example Service

@Path("/orders")
@ApplicationScoped
@Produces(MediaType.APPLICATION_JSON)
public class OrderResource {

    private final OrderService orderService;

    @Inject
    public OrderResource(
        OrderService orderService
    ) {
        this.orderService = orderService;
    }

    @GET
    @Path("/{orderId}")
    public OrderResponse findOrder(
        @PathParam("orderId")
        Long orderId
    ) {
        return orderService.findOrder(
            orderId
        );
    }
}

The REST endpoint uses Jakarta REST.

MicroProfile can then add:

  • Configuration for remote URLs
  • REST clients for downstream calls
  • Retry and timeout behavior
  • Health checks
  • JWT security
  • OpenAPI documentation
  • Distributed tracing

Why Interviewers Ask This

Interviewers want to confirm that you understand MicroProfile as a set of standardized microservice specifications rather than one application server or framework.

Production Example

A claims service may use:

  • MicroProfile Config for environment-specific URLs
  • REST Client for member-service calls
  • Fault Tolerance for retries and circuit breaking
  • Health for Kubernetes probes
  • JWT for API security
  • OpenAPI for API documentation
  • Telemetry for distributed tracing

Common Mistake

Describing MicroProfile as a replacement for Jakarta EE.

Senior Follow-up

MicroProfile complements Jakarta EE by standardizing cloud-native capabilities that become especially important in distributed systems.


Q2. Why was MicroProfile introduced?

Answer

MicroProfile was introduced to standardize Java APIs for microservice requirements that were not fully covered by the traditional enterprise platform model.

Microservices introduce challenges such as:

  • Services failing independently
  • Network calls timing out
  • Configuration changing by environment
  • Services needing readiness checks
  • Distributed requests requiring tracing
  • APIs needing discoverable documentation
  • Tokens carrying caller identity
  • Downstream failures propagating through the system

Traditional Monolith

flowchart LR
    A[Web Layer] --> B[Business Layer]
    B --> C[Persistence Layer]
    C --> D[Single Database]

Many calls happen in the same process.

Distributed Microservices

flowchart TD
    A[API Gateway] --> B[Order Service]
    B --> C[Customer Service]
    B --> D[Inventory Service]
    B --> E[Payment Service]
    C --> F[Customer Database]
    D --> G[Inventory Database]
    E --> H[Payment Provider]

Every network call can:

  • Time out
  • Return an error
  • Become slow
  • Reject traffic
  • Return incompatible data
  • Fail during deployment

MicroProfile Response

flowchart TD
    A[Distributed-System Problem] --> B{Required Capability}

    B --> C[External Configuration]
    B --> D[REST Client]
    B --> E[Retry and Timeout]
    B --> F[Circuit Breaker]
    B --> G[Health Checks]
    B --> H[Telemetry]
    B --> I[JWT Security]

Common Mistake

Assuming microservices require only smaller deployment artifacts.

Senior Follow-up

Microservices require operational resilience, independent deployment, observability, secure communication, and failure isolation.


Q3. What is the difference between Jakarta EE and MicroProfile?

Answer

Jakarta EE provides standardized enterprise application APIs.

MicroProfile adds standardized cloud-native microservice capabilities.

Comparison

Jakarta EE MicroProfile
Enterprise Java platform Cloud-native Java specifications
Jakarta REST Type-safe REST Client
CDI Config injection
Jakarta Security JWT-based API authentication
Jakarta Transactions Fault-tolerance policies
Jakarta Persistence Health checks
Jakarta Messaging OpenAPI documentation
Bean Validation Telemetry integration

Combined Architecture

flowchart TD
    A[Application] --> B[Jakarta EE Foundation]
    A --> C[MicroProfile Capabilities]

    B --> D[CDI]
    B --> E[REST]
    B --> F[Persistence]
    B --> G[Transactions]
    B --> H[Validation]

    C --> I[Config]
    C --> J[REST Client]
    C --> K[Fault Tolerance]
    C --> L[Health]
    C --> M[JWT]
    C --> N[OpenAPI]
    C --> O[Telemetry]

Example

A service may use:

@Inject
private EntityManager entityManager;

from Jakarta Persistence and:

@Inject
@ConfigProperty(
    name = "inventory.timeout"
)
private Duration timeout;

from MicroProfile Config.

Common Mistake

Treating Jakarta EE and MicroProfile as competing frameworks.

Senior Follow-up

A MicroProfile runtime commonly provides selected Jakarta EE technologies and MicroProfile specifications as one integrated platform.


Q4. What is MicroProfile Config?

Answer

MicroProfile Config provides a standard way to obtain application configuration from multiple sources.

It presents configuration as one merged logical view.

Common sources include:

  • Application properties
  • Environment variables
  • JVM system properties
  • Container configuration
  • Kubernetes ConfigMaps
  • Kubernetes Secrets
  • Custom configuration sources

Configuration Flow

flowchart TD
    A[Default Properties] --> E[MicroProfile Config]
    B[Environment Variables] --> E
    C[System Properties] --> E
    D[Custom ConfigSource] --> E

    E --> F[Resolved Configuration Value]
    F --> G[Application Component]

Property File

inventory.service.url=
https://inventory.example.com

inventory.request.timeout=
2S

order.default.page-size=
20

Injection

import org.eclipse.microprofile.config.inject.ConfigProperty;

@ApplicationScoped
public class OrderConfiguration {

    @Inject
    @ConfigProperty(
        name = "order.default.page-size",
        defaultValue = "20"
    )
    private int defaultPageSize;

    public int defaultPageSize() {
        return defaultPageSize;
    }
}

Constructor Injection

@ApplicationScoped
public class InventoryConfiguration {

    private final URI serviceUrl;
    private final Duration timeout;

    @Inject
    public InventoryConfiguration(

        @ConfigProperty(
            name = "inventory.service.url"
        )
        URI serviceUrl,

        @ConfigProperty(
            name = "inventory.request.timeout",
            defaultValue = "2S"
        )
        Duration timeout
    ) {
        this.serviceUrl = serviceUrl;
        this.timeout = timeout;
    }

    public URI serviceUrl() {
        return serviceUrl;
    }

    public Duration timeout() {
        return timeout;
    }
}

Programmatic Access

Config config =
    ConfigProvider.getConfig();

String serviceUrl =
    config.getValue(
        "inventory.service.url",
        String.class
    );

Optional Property

Optional<String> region =
    config.getOptionalValue(
        "deployment.region",
        String.class
    );

Common Mistake

Hardcoding remote URLs or environment-specific values in application code.

Senior Follow-up

Configuration should be externalized, validated at startup, protected according to sensitivity, and observable without exposing secrets.


Q5. How does configuration precedence work?

Answer

MicroProfile Config combines values from multiple configuration sources.

When the same property exists in several sources, the value from the source with the higher ordinal wins.

Conceptual Precedence

flowchart TD
    A[Configuration Property Requested] --> B[Find All Matching Values]
    B --> C[Compare ConfigSource Ordinals]
    C --> D[Select Highest-Priority Source]
    D --> E[Convert Value to Required Type]
    E --> F[Inject or Return Value]

Example

Application default:

payment.timeout=5S

Environment override:

PAYMENT_TIMEOUT=2S

Runtime system property:

-Dpayment.timeout=1S

The effective value depends on source precedence and name mapping supported by the runtime.

Custom Config Source

public class DatabaseConfigSource
    implements ConfigSource {

    @Override
    public Map<String, String>
        getProperties() {

        return Map.of(
            "feature.claim-review.enabled",
            "true"
        );
    }

    @Override
    public String getValue(
        String propertyName
    ) {
        return getProperties()
            .get(propertyName);
    }

    @Override
    public String getName() {
        return "database-config-source";
    }

    @Override
    public int getOrdinal() {
        return 300;
    }
}

Best Practices

  • Provide safe application defaults.
  • Override values externally.
  • Document configuration ownership.
  • Validate mandatory properties during startup.
  • Avoid conflicting configuration sources.
  • Never print secret values to logs.
  • Keep environment-specific values outside the artifact.

Common Mistake

Assuming every environment-variable name maps identically without testing the runtime's mapping behavior.

Senior Follow-up

Configuration precedence must be deterministic and documented because unexpected overrides can create production incidents.


Q6. What is MicroProfile REST Client?

Answer

MicroProfile REST Client provides a type-safe interface for calling remote REST APIs.

Instead of manually constructing HTTP requests, developers define a Java interface using Jakarta REST annotations.

Client Interface

import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;

@Path("/inventory")
@RegisterRestClient(
    configKey = "inventory-api"
)
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public interface InventoryClient {

    @GET
    @Path("/products/{productCode}")
    InventoryResponse findInventory(

        @PathParam("productCode")
        String productCode
    );
}

Injection

import org.eclipse.microprofile.rest.client.inject.RestClient;

@ApplicationScoped
public class InventoryService {

    private final InventoryClient
        inventoryClient;

    @Inject
    public InventoryService(

        @RestClient
        InventoryClient inventoryClient
    ) {
        this.inventoryClient =
            inventoryClient;
    }

    public InventoryResponse findInventory(
        String productCode
    ) {
        return inventoryClient.findInventory(
            productCode
        );
    }
}

Configuration

inventory-api/mp-rest/url=
https://inventory.example.com

Call Flow

sequenceDiagram
    participant OrderService
    participant RestClient
    participant InventoryAPI
    OrderService->>RestClient: findInventory(productCode)
    RestClient->>InventoryAPI: GET /inventory/products/{code}
    InventoryAPI-->>RestClient: JSON response
    RestClient-->>OrderService: InventoryResponse

Benefits

  • Type-safe client contract
  • Reuse of Jakarta REST annotations
  • CDI integration
  • Configurable base URL
  • Filters and providers
  • Fault Tolerance integration
  • Easier testing

Common Mistake

Returning internal remote-service entities directly to the caller.

Senior Follow-up

The client interface should use stable integration DTOs and map them into the application's own domain or response models.


Q7. What is MicroProfile Fault Tolerance?

Answer

MicroProfile Fault Tolerance provides annotations for handling failures in distributed calls.

Common strategies include:

  • Timeout
  • Retry
  • Circuit breaker
  • Bulkhead
  • Fallback
  • Asynchronous execution

Example

import org.eclipse.microprofile.faulttolerance.Retry;
import org.eclipse.microprofile.faulttolerance.Timeout;

@ApplicationScoped
public class CustomerGateway {

    @Inject
    @RestClient
    private CustomerClient customerClient;

    @Retry(
        maxRetries = 2,
        delay = 200,
        jitter = 100
    )
    @Timeout(2000)
    public CustomerResponse findCustomer(
        Long customerId
    ) {
        return customerClient.findCustomer(
            customerId
        );
    }
}

Fault-Tolerance Pipeline

flowchart TD
    A[Service Invocation] --> B[Timeout Policy]
    B --> C[Retry Policy]
    C --> D[Circuit Breaker]
    D --> E[Bulkhead]
    E --> F[Remote Service]

    F --> G{Success?}
    G -->|Yes| H[Return Result]
    G -->|No| I[Fallback or Failure]

Important Principle

Fault-tolerance annotations are commonly applied through CDI interception.

The component must therefore be container-managed for the behavior to apply.

Common Mistake

Creating the annotated class manually with new.

CustomerGateway gateway =
    new CustomerGateway();

This can bypass CDI interception.

Senior Follow-up

Resilience policies must be designed as a coordinated system. Adding retries, timeouts, and circuit breakers independently can create unexpected interactions.


Q8. What are @Retry and @Timeout?

Answer

@Retry repeats an operation after certain failures.

@Timeout limits how long an invocation may take.

Retry Example

@Retry(
    maxRetries = 3,
    delay = 250,
    maxDuration = 3000,
    jitter = 100,
    retryOn = {
        ServiceUnavailableException.class,
        ProcessingException.class
    },
    abortOn = {
        InvalidCustomerException.class
    }
)
public CustomerResponse findCustomer(
    Long customerId
) {
    return customerClient.findCustomer(
        customerId
    );
}

Timeout Example

@Timeout(1500)
public PaymentResponse authorize(
    PaymentRequest request
) {
    return paymentClient.authorize(
        request
    );
}

Retry Flow

flowchart TD
    A[Attempt 1] --> B{Success?}
    B -->|Yes| C[Return Result]
    B -->|No| D{Retriable Failure?}
    D -->|No| E[Fail Immediately]
    D -->|Yes| F{Retries Remaining?}
    F -->|Yes| G[Wait with Delay and Jitter]
    G --> H[Next Attempt]
    H --> B
    F -->|No| E

Timeout Flow

sequenceDiagram
    participant Caller
    participant TimeoutPolicy
    participant RemoteService
    Caller->>TimeoutPolicy: Invoke operation
    TimeoutPolicy->>RemoteService: Start request
    alt Completes within timeout
        RemoteService-->>TimeoutPolicy: Result
        TimeoutPolicy-->>Caller: Result
    else Exceeds timeout
        TimeoutPolicy-->>Caller: Timeout failure
    end

When Retry Is Appropriate

  • Temporary connection error
  • Short dependency outage
  • Lock timeout
  • Service temporarily unavailable
  • Rate-limited operation with retry guidance

When Retry Is Dangerous

  • Non-idempotent payment creation
  • Invalid request data
  • Authentication failure
  • Permanent business rejection
  • Long-running overloaded dependency
  • Operation without duplicate protection

Jitter

Jitter adds randomness to retry delay.

Without jitter, many service instances may retry simultaneously.

flowchart TD
    A[Dependency Fails] --> B[100 Clients Retry at Same Time]
    B --> C[Retry Storm]
    C --> D[Dependency Remains Overloaded]

    E[Dependency Fails] --> F[Retries Use Jitter]
    F --> G[Retry Attempts Spread Over Time]
    G --> H[Improved Recovery Chance]

Common Mistake

Retrying every exception.

Senior Follow-up

Retry only transient, idempotent operations and ensure the total retry duration fits within the caller's end-to-end latency budget.


Q9. What is a Circuit Breaker?

Answer

A Circuit Breaker stops calls to a failing dependency when the recent failure rate exceeds a configured threshold.

Its common states are:

  • Closed
  • Open
  • Half-open

States

stateDiagram-v2
    [*] --> Closed

    Closed --> Open:
        Failure threshold reached

    Open --> HalfOpen:
        Delay expires

    HalfOpen --> Closed:
        Test calls succeed

    HalfOpen --> Open:
        Test call fails

Example

import org.eclipse.microprofile.faulttolerance.CircuitBreaker;

@CircuitBreaker(
    requestVolumeThreshold = 10,
    failureRatio = 0.5,
    delay = 5000,
    successThreshold = 3,
    failOn = {
        ServiceUnavailableException.class,
        ProcessingException.class
    },
    skipOn = {
        InvalidOrderException.class
    }
)
public InventoryResponse findInventory(
    String productCode
) {
    return inventoryClient.findInventory(
        productCode
    );
}

Closed State

Requests are sent normally.

flowchart LR
    A[Caller] --> B[Closed Circuit]
    B --> C[Remote Service]

Open State

Calls fail quickly without contacting the dependency.

flowchart LR
    A[Caller] --> B[Open Circuit]
    B --> C[Fail Fast]
    B -. No call .-> D[Failing Dependency]

Half-Open State

A limited number of test calls determine whether the dependency has recovered.

Benefits

  • Prevents repeated calls to a failing service
  • Reduces resource consumption
  • Supports faster failure
  • Gives the dependency recovery time
  • Prevents cascading failures

Common Mistake

Treating the circuit breaker as a replacement for monitoring or dependency repair.

Senior Follow-up

Circuit-breaker thresholds should be based on traffic volume, dependency behavior, recovery characteristics, and business tolerance.


Q10. What is a Bulkhead?

Answer

A Bulkhead isolates resources so one overloaded dependency or operation cannot consume all application capacity.

The concept comes from ships divided into watertight compartments.

If one compartment fails, the entire ship does not immediately fail.

Without Bulkhead

flowchart TD
    A[Shared Thread Pool] --> B[Payment Calls]
    A --> C[Inventory Calls]
    A --> D[Notification Calls]

    B --> E[Payment Service Becomes Slow]
    E --> F[All Threads Consumed]
    F --> G[Entire Application Becomes Unresponsive]

With Bulkhead

flowchart TD
    A[Application] --> B[Payment Bulkhead]
    A --> C[Inventory Bulkhead]
    A --> D[Notification Bulkhead]

    B --> E[Payment Service]
    C --> F[Inventory Service]
    D --> G[Notification Service]

    E --> H[Payment Capacity Exhausted]
    H --> I[Other Operations Continue]

Example

import org.eclipse.microprofile.faulttolerance.Bulkhead;

@Bulkhead(
    value = 20,
    waitingTaskQueue = 50
)
public PaymentResponse authorize(
    PaymentRequest request
) {
    return paymentClient.authorize(
        request
    );
}

Meaning

Maximum concurrent executions: 20
Waiting queue capacity: 50

When both capacities are full, additional calls fail quickly.

Benefits

  • Failure isolation
  • Capacity protection
  • Controlled concurrency
  • Reduced thread exhaustion
  • Better service availability

Common Mistake

Configuring a bulkhead larger than the downstream service or connection pool can support.

Senior Follow-up

Bulkhead capacity must align with HTTP pools, database pools, thread pools, rate limits, CPU, memory, and downstream capacity.


Q11. What is @Fallback?

Answer

@Fallback provides an alternative response when the original operation cannot complete successfully.

Method Fallback

import org.eclipse.microprofile.faulttolerance.Fallback;

@Retry(maxRetries = 2)
@Timeout(1500)
@CircuitBreaker(
    requestVolumeThreshold = 10,
    failureRatio = 0.5,
    delay = 5000
)
@Fallback(
    fallbackMethod =
        "fallbackInventory"
)
public InventoryResponse findInventory(
    String productCode
) {
    return inventoryClient.findInventory(
        productCode
    );
}

public InventoryResponse fallbackInventory(
    String productCode
) {
    return InventoryResponse
        .temporarilyUnavailable(
            productCode
        );
}

Flow

flowchart TD
    A[Invoke Remote Operation] --> B{Successful?}
    B -->|Yes| C[Return Actual Result]
    B -->|No| D[Retry or Circuit Policy]
    D --> E{Recovery Successful?}
    E -->|Yes| C
    E -->|No| F[Invoke Fallback]
    F --> G[Return Degraded Result]

Appropriate Fallbacks

  • Return cached reference data
  • Return an explicit temporary-unavailable state
  • Skip non-critical personalization
  • Return default recommendations
  • Queue work for later processing
  • Provide partial response with clear status

Dangerous Fallbacks

  • Return zero balance when account service fails
  • Report payment success without confirmation
  • Report inventory available without evidence
  • Hide permanent data corruption
  • Return stale security permissions

Common Mistake

Using a fallback that silently changes business meaning.

Senior Follow-up

Fallbacks must preserve correctness. Graceful degradation is valuable only when the degraded response is safe and clearly understood.


Q12. What is MicroProfile Health?

Answer

MicroProfile Health provides standardized health checks that container platforms can use to evaluate an application.

The primary categories are:

  • Liveness
  • Readiness
  • Startup

MicroProfile Health runtimes commonly expose corresponding endpoints for these categories.

Liveness

Liveness answers:

Is the process alive and capable of continuing?

A failed liveness check may cause the platform to restart the container.

Readiness

Readiness answers:

Is the application ready to receive traffic?

A failed readiness check should remove the instance from service routing without necessarily restarting it.

Startup

Startup answers:

Has the application completed startup successfully?

Health Architecture

flowchart TD
    A[Container Platform] --> B[Liveness Check]
    A --> C[Readiness Check]
    A --> D[Startup Check]

    B --> E{Alive?}
    E -->|No| F[Restart Container]
    E -->|Yes| G[Keep Running]

    C --> H{Ready?}
    H -->|No| I[Remove From Traffic]
    H -->|Yes| J[Route Requests]

    D --> K{Started?}
    K -->|No| L[Continue Startup Window]
    K -->|Yes| C

Liveness Check

import org.eclipse.microprofile.health.HealthCheck;
import org.eclipse.microprofile.health.HealthCheckResponse;
import org.eclipse.microprofile.health.Liveness;

@Liveness
@ApplicationScoped
public class ApplicationLivenessCheck
    implements HealthCheck {

    @Override
    public HealthCheckResponse call() {

        return HealthCheckResponse
            .up(
                "application-liveness"
            );
    }
}

Readiness Check

import org.eclipse.microprofile.health.Readiness;

@Readiness
@ApplicationScoped
public class DatabaseReadinessCheck
    implements HealthCheck {

    @Inject
    private DataSource dataSource;

    @Override
    public HealthCheckResponse call() {

        try (
            Connection connection =
                dataSource.getConnection()
        ) {
            return HealthCheckResponse
                .named(
                    "database-readiness"
                )
                .withData(
                    "database",
                    connection
                        .getMetaData()
                        .getDatabaseProductName()
                )
                .up()
                .build();

        } catch (SQLException exception) {

            return HealthCheckResponse
                .named(
                    "database-readiness"
                )
                .withData(
                    "reason",
                    "Database unavailable"
                )
                .down()
                .build();
        }
    }
}

Startup Check

import org.eclipse.microprofile.health.Startup;

@Startup
@ApplicationScoped
public class ApplicationStartupCheck
    implements HealthCheck {

    private volatile boolean initialized;

    @PostConstruct
    void initialize() {
        initialized = true;
    }

    @Override
    public HealthCheckResponse call() {

        return HealthCheckResponse
            .named(
                "application-startup"
            )
            .status(initialized)
            .build();
    }
}

Common Mistake

Calling every remote dependency in the liveness check.

This can create restart loops during a dependency outage.

Senior Follow-up

Liveness should primarily reflect whether the process is irrecoverably unhealthy. Readiness should reflect whether it can safely serve traffic.


Q13. What is MicroProfile OpenAPI?

Answer

MicroProfile OpenAPI provides a standard way to describe and expose REST API documentation using the OpenAPI model.

It supports:

  • Annotation-based documentation
  • Static OpenAPI documents
  • Programmatic model construction
  • Runtime-generated API descriptions

Resource Example

import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.media.Content;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.eclipse.microprofile.openapi.annotations.tags.Tag;

@Path("/orders")
@Tag(
    name = "Orders",
    description =
        "Order management operations"
)
public class OrderResource {

    @GET
    @Path("/{orderId}")
    @Operation(
        summary = "Find an order",
        description =
            "Returns an order using its identifier"
    )
    @APIResponse(
        responseCode = "200",
        description = "Order found",
        content = @Content(
            mediaType =
                MediaType.APPLICATION_JSON,
            schema = @Schema(
                implementation =
                    OrderResponse.class
            )
        )
    )
    @APIResponse(
        responseCode = "404",
        description = "Order not found"
    )
    public OrderResponse findOrder(

        @PathParam("orderId")
        Long orderId
    ) {
        return orderService.findOrder(
            orderId
        );
    }
}

Documentation Flow

flowchart TD
    A[REST Resource Annotations] --> B[OpenAPI Model]
    C[Static OpenAPI File] --> B
    D[Programmatic Model Filter] --> B

    B --> E[OpenAPI Document]
    E --> F[API Documentation UI]
    E --> G[Client Generation]
    E --> H[Contract Validation]

Static Document Example

openapi: 3.1.0
info:
  title: Order API
  version: "1.0"
paths:
  /orders/{orderId}:
    get:
      summary: Find an order
      parameters:
        - name: orderId
          in: path
          required: true
          schema:
            type: integer
            format: int64
      responses:
        "200":
          description: Order found
        "404":
          description: Order not found

Benefits

  • Discoverable API contract
  • Better consumer integration
  • Client-code generation
  • API review
  • Contract testing
  • Documentation consistency

Common Mistake

Treating generated OpenAPI documentation as automatically correct.

Senior Follow-up

The documented API must be tested against runtime behavior so status codes, schemas, validation constraints, and security requirements stay synchronized.


Q14. How do JWT and telemetry fit into MicroProfile?

Answer

MicroProfile JWT Authentication standardizes how REST services consume JWT-based security identities.

MicroProfile Telemetry supports distributed observability through tracing, metrics, and logs using the modern telemetry model.

JWT Security Flow

sequenceDiagram
    participant Client
    participant IdentityProvider
    participant MicroProfileAPI
    participant Resource
    Client->>IdentityProvider: Authenticate
    IdentityProvider-->>Client: Signed access token
    Client->>MicroProfileAPI: Bearer token
    MicroProfileAPI->>MicroProfileAPI: Validate token claims
    MicroProfileAPI->>Resource: Create caller identity
    Resource-->>Client: Authorized response

JWT Claim Injection

import org.eclipse.microprofile.jwt.JsonWebToken;

@Path("/profile")
@Authenticated
public class ProfileResource {

    @Inject
    private JsonWebToken jsonWebToken;

    @GET
    public ProfileResponse profile() {

        return new ProfileResponse(
            jsonWebToken.getSubject(),
            jsonWebToken.getGroups()
        );
    }
}

Role Protection

@GET
@Path("/admin")
@RolesAllowed("ADMIN")
public AdminResponse admin() {
    return adminService.loadDashboard();
}

Telemetry Flow

flowchart TD
    A[Incoming Request] --> B[Trace Span]
    B --> C[Service Method]
    C --> D[REST Client Span]
    D --> E[Remote Service]
    C --> F[Database Span]

    B --> G[Trace Exporter]
    C --> H[Metrics]
    C --> I[Structured Logs]

    G --> J[Observability Platform]
    H --> J
    I --> J

What Telemetry Helps Answer

  • Which service caused the delay?
  • How many requests failed?
  • Which downstream dependency timed out?
  • How much time was spent in the database?
  • Which trace corresponds to a production error?
  • Is latency increasing after deployment?

Correlation Example

traceId=4e91a2c
spanId=8a130d
orderId=ORD-1001

Common Mistake

Logging large JWTs or sensitive claims.

Senior Follow-up

Telemetry must be useful without exposing passwords, access tokens, personal data, or regulated information.


Q15. What are senior-level MicroProfile best practices?

Answer

Senior developers should treat MicroProfile annotations as operational policies rather than decorative configuration.

Best Practices

  • Externalize environment-specific configuration.
  • Validate required configuration at startup.
  • Do not expose secrets through logs or health endpoints.
  • Define type-safe REST client contracts.
  • Apply network timeouts to every downstream call.
  • Retry only transient and idempotent operations.
  • Add jitter to reduce retry storms.
  • Limit total retry duration.
  • Use circuit breakers to stop repeated failure.
  • Use bulkheads to isolate resource exhaustion.
  • Use fallbacks only when the degraded result is safe.
  • Separate liveness from readiness.
  • Keep health checks fast.
  • Protect health details where required.
  • Keep OpenAPI documentation synchronized with runtime behavior.
  • Validate JWT signature and claims.
  • Use short-lived access tokens.
  • Propagate trace context across services.
  • Measure latency, error rate, saturation, and traffic.
  • Test failure behavior under load.
  • Avoid stacking resilience policies without understanding order and timing.

Senior Design Flow

flowchart TD
    A[Remote Dependency] --> B[Define Timeout Budget]
    B --> C[Classify Failures]
    C --> D{Operation Idempotent?}

    D -->|Yes| E[Consider Bounded Retry]
    D -->|No| F[Do Not Retry Without Protection]

    E --> G[Add Jitter]
    F --> H[Fail Safely]

    G --> I[Configure Circuit Breaker]
    H --> I

    I --> J[Set Bulkhead Capacity]
    J --> K[Define Safe Fallback]
    K --> L[Add Metrics and Tracing]
    L --> M[Load and Failure Test]

Production Checklist

Externalized Configuration
        │
        ▼
Typed REST Client
        │
        ▼
Timeout Budget
        │
        ▼
Bounded Retry with Jitter
        │
        ▼
Circuit Breaker
        │
        ▼
Bulkhead Isolation
        │
        ▼
Safe Fallback
        │
        ▼
Health and Telemetry
        │
        ▼
Failure Testing

Senior Follow-up

Resilience improves reliability only when policies are coordinated with business semantics, capacity limits, and end-to-end latency objectives.


MicroProfile Platform Architecture

flowchart TD
    A[API Gateway] --> B[MicroProfile Order Service]

    B --> C[Jakarta REST]
    B --> D[MicroProfile Config]
    B --> E[MicroProfile JWT]
    B --> F[MicroProfile OpenAPI]
    B --> G[MicroProfile Health]
    B --> H[MicroProfile Telemetry]

    B --> I[Fault-Tolerant REST Client]
    I --> J[Inventory Service]
    I --> K[Payment Service]

    B --> L[Jakarta Persistence]
    L --> M[Order Database]

End-to-End Request Flow

sequenceDiagram
    participant Client
    participant Gateway
    participant OrderAPI
    participant InventoryClient
    participant InventoryAPI
    participant Telemetry
    Client->>Gateway: Bearer token and request
    Gateway->>OrderAPI: Forward request
    OrderAPI->>OrderAPI: Validate JWT
    OrderAPI->>Telemetry: Start trace
    OrderAPI->>InventoryClient: Check inventory
    InventoryClient->>InventoryAPI: HTTP request with timeout
    alt Inventory succeeds
        InventoryAPI-->>InventoryClient: Inventory result
        InventoryClient-->>OrderAPI: Result
        OrderAPI-->>Client: Success
    else Inventory fails temporarily
        InventoryClient->>InventoryAPI: Bounded retry
        InventoryAPI-->>InventoryClient: Failure
        InventoryClient-->>OrderAPI: Fallback or failure
        OrderAPI-->>Client: Degraded or error response
    end
    OrderAPI->>Telemetry: Complete trace and metrics

Configuration Example

# Inventory REST Client
inventory-api/mp-rest/url=
https://inventory.example.com

# Business configuration
order.default-page-size=20
order.maximum-page-size=100

# Feature configuration
features.order-recommendations.enabled=true

# Timeout configuration
inventory.timeout=2S
payment.timeout=3S

Typed Configuration Bean

@ApplicationScoped
public class OrderSettings {

    private final int defaultPageSize;
    private final int maximumPageSize;
    private final boolean recommendationsEnabled;

    @Inject
    public OrderSettings(

        @ConfigProperty(
            name =
                "order.default-page-size",
            defaultValue = "20"
        )
        int defaultPageSize,

        @ConfigProperty(
            name =
                "order.maximum-page-size",
            defaultValue = "100"
        )
        int maximumPageSize,

        @ConfigProperty(
            name =
                "features.order-recommendations.enabled",
            defaultValue = "false"
        )
        boolean recommendationsEnabled
    ) {

        if (
            defaultPageSize < 1
            || maximumPageSize
                < defaultPageSize
        ) {
            throw new IllegalStateException(
                "Invalid order pagination configuration"
            );
        }

        this.defaultPageSize =
            defaultPageSize;

        this.maximumPageSize =
            maximumPageSize;

        this.recommendationsEnabled =
            recommendationsEnabled;
    }
}

Configuration Source Precedence

flowchart TD
    A[Application Defaults] --> E[Configuration Resolution]
    B[External Properties] --> E
    C[Environment Variables] --> E
    D[System Properties] --> E

    E --> F[Highest-Priority Matching Value]
    F --> G[Type Conversion]
    G --> H[Injection Point]

Custom Configuration Converter

public final class MoneyConverter
    implements Converter<Money> {

    @Override
    public Money convert(
        String value
    ) {

        String[] parts =
            value.split(":");

        if (parts.length != 2) {
            throw new IllegalArgumentException(
                "Money must use CURRENCY:AMOUNT"
            );
        }

        return new Money(
            Currency.getInstance(
                parts[0]
            ),
            new BigDecimal(
                parts[1]
            )
        );
    }
}

Property:

order.minimum-total=USD:10.00

Injection:

@Inject
@ConfigProperty(
    name = "order.minimum-total"
)
private Money minimumTotal;

REST Client Error Mapping

A response exception mapper converts remote HTTP failures into application-specific exceptions.

import org.eclipse.microprofile.rest.client.ext.ResponseExceptionMapper;

@Provider
public class InventoryExceptionMapper
    implements ResponseExceptionMapper<
        RuntimeException
    > {

    @Override
    public RuntimeException toThrowable(
        Response response
    ) {

        return switch (
            response.getStatus()
        ) {
            case 404 ->
                new ProductNotFoundException();

            case 429 ->
                new InventoryRateLimitedException();

            case 503 ->
                new InventoryUnavailableException();

            default ->
                new InventoryClientException(
                    response.getStatus()
                );
        };
    }
}

Flow

flowchart TD
    A[REST Client Response] --> B{HTTP Status}
    B -->|2xx| C[Deserialize Success DTO]
    B -->|404| D[Product Not Found]
    B -->|429| E[Rate Limited]
    B -->|503| F[Temporary Unavailable]
    B -->|Other Error| G[Generic Client Exception]

REST Client Header Propagation

@RegisterRestClient(
    configKey = "inventory-api"
)
@RegisterClientHeaders(
    RequestHeaderFactory.class
)
public interface InventoryClient {
}
@ApplicationScoped
public class RequestHeaderFactory
    implements ClientHeadersFactory {

    @Override
    public MultivaluedMap<String, String>
        update(
            MultivaluedMap<
                String,
                String
            > incomingHeaders,

            MultivaluedMap<
                String,
                String
            > outgoingHeaders
        ) {

        MultivaluedMap<String, String>
            result =
                new MultivaluedHashMap<>();

        copyIfPresent(
            incomingHeaders,
            result,
            "Authorization"
        );

        copyIfPresent(
            incomingHeaders,
            result,
            "X-Correlation-ID"
        );

        return result;
    }
}

Security Warning

Do not automatically propagate every incoming header.

Use an allowlist.


Combined Fault-Tolerance Example

@ApplicationScoped
public class InventoryGateway {

    private final InventoryClient client;

    @Inject
    public InventoryGateway(

        @RestClient
        InventoryClient client
    ) {
        this.client = client;
    }

    @Timeout(1500)
    @Retry(
        maxRetries = 2,
        delay = 200,
        jitter = 100,
        retryOn = {
            ProcessingException.class,
            InventoryUnavailableException.class
        },
        abortOn = {
            ProductNotFoundException.class
        }
    )
    @CircuitBreaker(
        requestVolumeThreshold = 20,
        failureRatio = 0.5,
        delay = 5000,
        successThreshold = 3
    )
    @Bulkhead(
        value = 20,
        waitingTaskQueue = 40
    )
    @Fallback(
        fallbackMethod =
            "fallbackInventory"
    )
    public InventoryResponse findInventory(
        String productCode
    ) {
        return client.findInventory(
            productCode
        );
    }

    InventoryResponse fallbackInventory(
        String productCode
    ) {
        return InventoryResponse
            .temporarilyUnavailable(
                productCode
            );
    }
}

Resilience Policy Interaction

flowchart TD
    A[Caller] --> B[Bulkhead Admission]
    B --> C[Circuit Breaker Check]
    C --> D[Retry Policy]
    D --> E[Timeout Policy]
    E --> F[Remote Invocation]

    F --> G{Outcome}
    G -->|Success| H[Return]
    G -->|Transient Failure| D
    G -->|Final Failure| I[Fallback]

The exact interceptor order is governed by the specification and runtime behavior. Developers should test the effective behavior rather than assume an intuitive order.


Timeout Budget Design

Assume the API has a total response target of:

3 seconds

The service calls two downstream systems.

Gateway overhead:       200 ms
Database work:          400 ms
Inventory budget:       800 ms
Payment budget:       1,200 ms
Response serialization: 200 ms
Safety margin:          200 ms

Budget Flow

flowchart LR
    A[3000 ms Total] --> B[200 Gateway]
    B --> C[400 Database]
    C --> D[800 Inventory]
    D --> E[1200 Payment]
    E --> F[200 Serialization]
    F --> G[200 Safety Margin]

Common Mistake

Configuring each downstream timeout to the complete endpoint timeout.

That allows the total request to exceed its latency objective.


Retry Amplification

Assume:

  • Gateway retries twice.
  • Order service retries twice.
  • Inventory client retries twice.

Maximum downstream attempts can multiply.

3 × 3 × 3 = 27 attempts

Retry Amplification Diagram

flowchart TD
    A[One Client Request] --> B[Gateway Attempts 3 Times]
    B --> C[Order Service Attempts 3 Times]
    C --> D[Inventory Client Attempts 3 Times]
    D --> E[Up to 27 Inventory Calls]

Best Practices

  • Define retry ownership.
  • Avoid retries at every layer.
  • Use retry budgets.
  • Add jitter.
  • Respect rate-limit guidance.
  • Monitor retry count.
  • Stop when the request deadline is nearly exhausted.

Circuit Breaker Decision Matrix

Condition Suggested Behavior
Dependency healthy Circuit closed
Failure ratio exceeded Open circuit
Open delay elapsed Half-open test
Test calls succeed Close circuit
Test call fails Reopen circuit
Invalid client request Do not count as dependency failure
Authentication failure Usually do not retry
Temporary service failure May count as failure

Bulkhead Capacity Planning

Application request threads: 200
Inventory bulkhead:            30
Payment bulkhead:              20
Notification bulkhead:         10
Database connection pool:      40

Capacity Rule

Bulkhead capacity should not exceed the actual capacity of:

  • Remote connection pool
  • Database pool
  • Worker executor
  • Downstream rate limit
  • CPU
  • Memory

Safe and Unsafe Fallbacks

Operation Safer Fallback Dangerous Fallback
Recommendations Empty recommendation list Invented products
Profile image Default image Another user's image
Currency reference Recent cached value with timestamp Silent fabricated rate
Account balance Explicit unavailable status Zero balance
Payment authorization Fail closed Report success
Inventory Unknown availability Assume available
Audit authorization Deny access Allow access

Health Check Design Matrix

Check Liveness Readiness Startup
Application deadlock indicator Yes Possibly No
Required database available Usually no Yes Possibly
Optional analytics service No Usually no No
Configuration initialized No Yes Yes
Required model loaded No Yes Yes
Temporary downstream outage No Depends No
Internal unrecoverable corruption Yes Yes Possibly

Health Response Example

{
  "status": "UP",
  "checks": [
    {
      "name": "database-readiness",
      "status": "UP",
      "data": {
        "database": "PostgreSQL"
      }
    },
    {
      "name": "configuration-readiness",
      "status": "UP"
    }
  ]
}

Security Note

Do not expose:

  • Credentials
  • Connection strings
  • Internal hostnames
  • Stack traces
  • Sensitive dependency details

Kubernetes Probe Mapping

livenessProbe:
  httpGet:
    path: /health/live
    port: 8080
  initialDelaySeconds: 20
  periodSeconds: 10

readinessProbe:
  httpGet:
    path: /health/ready
    port: 8080
  initialDelaySeconds: 10
  periodSeconds: 5

startupProbe:
  httpGet:
    path: /health/started
    port: 8080
  failureThreshold: 30
  periodSeconds: 5

Exact endpoint prefixes can depend on runtime configuration.


OpenAPI Security Scheme Example

@OpenAPIDefinition(
    info = @Info(
        title = "Claims API",
        version = "1.0",
        description =
            "Claims processing operations"
    ),
    security = {
        @SecurityRequirement(
            name = "bearerAuth"
        )
    }
)
@SecurityScheme(
    securitySchemeName = "bearerAuth",
    type =
        SecuritySchemeType.HTTP,
    scheme = "bearer",
    bearerFormat = "JWT"
)
public class ClaimsApplication
    extends Application {
}

Protected operation:

@GET
@Path("/{claimId}")
@SecurityRequirement(
    name = "bearerAuth"
)
@RolesAllowed({
    "CLAIM_VIEWER",
    "CLAIM_ADJUSTER"
})
public ClaimResponse findClaim(
    @PathParam("claimId")
    Long claimId
) {
    return claimService.findClaim(
        claimId
    );
}

JWT Claims Example

@Path("/identity")
public class IdentityResource {

    @Inject
    private JsonWebToken token;

    @GET
    public IdentityResponse identity() {

        return new IdentityResponse(
            token.getSubject(),
            token.getIssuer(),
            token.getGroups(),
            token.getExpirationTime()
        );
    }
}

Important Claims

  • sub: caller identity
  • iss: token issuer
  • aud: intended audience
  • exp: expiration
  • iat: issue time
  • jti: token identifier
  • groups: role or group information

Distributed Trace Example

sequenceDiagram
    participant Gateway
    participant OrderService
    participant InventoryService
    participant PaymentService
    participant Database
    Gateway->>OrderService: Trace ID abc
    OrderService->>Database: Span order-db
    Database-->>OrderService: Result
    OrderService->>InventoryService: Trace ID abc
    InventoryService-->>OrderService: Inventory result
    OrderService->>PaymentService: Trace ID abc
    PaymentService-->>OrderService: Payment result
    OrderService-->>Gateway: Response

All spans can be viewed as one distributed trace.


Observability Signals

Traces

Explain how one request moved through the system.

Metrics

Show aggregated behavior over time.

Examples:

  • Request count
  • Error rate
  • Latency distribution
  • Retry count
  • Circuit-breaker state
  • Bulkhead rejection count
  • Queue depth
  • JVM memory
  • CPU

Logs

Provide event details.

Useful fields:

  • Timestamp
  • Severity
  • Service name
  • Trace ID
  • Span ID
  • Correlation ID
  • Error code
  • Business identifier where safe

Golden Signals

Monitor:

  • Latency
  • Traffic
  • Errors
  • Saturation

Diagram

flowchart TD
    A[Service Monitoring] --> B[Latency]
    A --> C[Traffic]
    A --> D[Errors]
    A --> E[Saturation]

    B --> F[Response Time]
    C --> G[Request Rate]
    D --> H[Failure Rate]
    E --> I[Threads Pools CPU Memory]

Production Microservice Architecture

flowchart TD
    A[Client] --> B[API Gateway]
    B --> C[JWT Validation]
    C --> D[Order Service]

    D --> E[MicroProfile Config]
    D --> F[Fault-Tolerant Inventory Client]
    D --> G[Fault-Tolerant Payment Client]
    D --> H[Order Database]

    F --> I[Inventory Service]
    G --> J[Payment Service]

    D --> K[Health Checks]
    D --> L[OpenAPI]
    D --> M[Telemetry]

    M --> N[Observability Platform]

Graceful Degradation

flowchart TD
    A[Order Page Request] --> B[Load Core Order Data]
    B --> C{Core Data Available?}

    C -->|No| D[Return Service Error]
    C -->|Yes| E[Load Recommendations]

    E --> F{Recommendation Service Available?}
    F -->|Yes| G[Return Order and Recommendations]
    F -->|No| H[Return Order Without Recommendations]

Core business data remains required.

Optional personalization can degrade safely.


Common MicroProfile Mistakes

  • Treating MicroProfile as a Jakarta EE replacement.
  • Hardcoding configuration.
  • Logging secrets.
  • Assuming all configuration refreshes automatically.
  • Calling REST services without timeouts.
  • Retrying non-idempotent operations.
  • Retrying invalid requests.
  • Retrying at every architecture layer.
  • Omitting retry jitter.
  • Using a fallback that changes business meaning.
  • Configuring an oversized bulkhead.
  • Counting every client error as a circuit failure.
  • Making liveness depend on every downstream system.
  • Running slow health checks.
  • Exposing internal details through health endpoints.
  • Allowing OpenAPI documentation to become stale.
  • Trusting JWT claims without validation.
  • Logging full access tokens.
  • Collecting telemetry without trace propagation.
  • Using annotations without load and failure testing.

Senior MicroProfile Checklist

Validate Configuration
        │
        ▼
Define Client Contract
        │
        ▼
Set Connection and Read Timeouts
        │
        ▼
Classify Retriable Failures
        │
        ▼
Use Bounded Retry and Jitter
        │
        ▼
Configure Circuit Breaker
        │
        ▼
Protect Capacity with Bulkhead
        │
        ▼
Define Correct Fallback
        │
        ▼
Add Health and Telemetry
        │
        ▼
Run Failure Tests

Interview Quick Revision

Concept Purpose
MicroProfile Cloud-native Java specifications
Config Externalized configuration
ConfigSource Supplies configuration values
REST Client Type-safe remote API client
Fault Tolerance Resilience policies
@Retry Repeat transiently failed operation
@Timeout Limit invocation duration
@CircuitBreaker Stop calls to failing dependency
@Bulkhead Isolate concurrency and capacity
@Fallback Provide safe degraded behavior
Health Runtime health reporting
Liveness Determines whether process should restart
Readiness Determines whether traffic should be routed
OpenAPI API contract documentation
Telemetry Traces, metrics, and logs

Key Takeaways

  • Eclipse MicroProfile standardizes cloud-native capabilities for Java microservices.
  • MicroProfile complements Jakarta EE rather than replacing it.
  • MicroProfile Config merges values from multiple configuration sources into one logical view.
  • Environment-specific configuration should remain outside application artifacts.
  • Mandatory configuration should be validated during startup.
  • MicroProfile REST Client provides type-safe remote API interfaces using Jakarta REST annotations.
  • Remote-service contracts should use dedicated integration DTOs.
  • Every remote call should have a bounded timeout.
  • Retries should apply only to transient failures and safe operations.
  • Retry delay and jitter help prevent retry storms.
  • Circuit breakers stop repeated calls to failing dependencies and support faster failure.
  • Bulkheads isolate resource exhaustion so one dependency does not consume all capacity.
  • Fallbacks must preserve business correctness and communicate degraded behavior clearly.
  • Liveness determines whether a process should remain running.
  • Readiness determines whether an instance should receive traffic.
  • Startup checks protect slow-starting applications from premature liveness failure.
  • Health checks should remain fast and should not expose sensitive infrastructure details.
  • MicroProfile OpenAPI creates a standardized, discoverable API description.
  • API documentation must be verified against actual runtime behavior.
  • MicroProfile JWT integrates token-based identities and groups with application security.
  • JWT signature, issuer, audience, expiration, and required claims must be validated.
  • MicroProfile Telemetry supports cross-service observability through traces, metrics, and logs.
  • Retry amplification, timeout budgets, resource pools, and downstream limits must be considered together.
  • Senior developers test resilience policies through latency injection, dependency failure, concurrency pressure, and production-scale load.