or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

conditional-annotations.mdcore-infrastructure.mdendpoint-filtering.mdendpoint-properties.mdindex.mdjmx-endpoints.mdmanagement-endpoints.mdmanagement-server.mdweb-endpoints.md
tile.json

core-infrastructure.mddocs/

Core Endpoint Infrastructure

Core infrastructure for endpoint discovery, filtering, parameter mapping, caching, and access control in Spring Boot Actuator.

Imports

import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.PropertiesEndpointAccessResolver;
import org.springframework.boot.actuate.endpoint.EndpointAccessResolver;
import org.springframework.boot.actuate.endpoint.Access;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.invoke.ParameterValueMapper;
import org.springframework.boot.actuate.endpoint.invoker.cache.CachingOperationInvokerAdvisor;

Quick Reference

Essential Classes

ClassPackagePurpose
EndpointAutoConfigurationo.s.b.actuate.autoconfigure.endpointCore endpoint infrastructure beans
PropertiesEndpointAccessResolvero.s.b.actuate.autoconfigure.endpointProperty-based access control
ParameterValueMappero.s.b.actuate.endpoint.invokeParameter type conversion
CachingOperationInvokerAdvisoro.s.b.actuate.endpoint.invoker.cacheOperation caching
EndpointIdo.s.b.actuate.endpointEndpoint identifier

Access Levels (Quick Lookup)

enum Access {
    NONE,           // Endpoint disabled
    READ_ONLY,      // Allows: READ only
    UNRESTRICTED    // Allows: READ, WRITE, DELETE
}

Note: Enum values are shown in logical order (least permissive to most permissive) for clarity. In source code, they are declared in the same order.

API Reference

EndpointAutoConfiguration

@AutoConfiguration
public final class EndpointAutoConfiguration {

    // Parameter conversion for endpoint operations
    @Bean
    @ConditionalOnMissingBean
    ParameterValueMapper endpointOperationParameterMapper(
        @EndpointConverter ObjectProvider<Converter<?, ?>> converters,
        @EndpointConverter ObjectProvider<GenericConverter> genericConverters
    );

    // Caching advisor for expensive operations
    @Bean
    @ConditionalOnMissingBean
    CachingOperationInvokerAdvisor endpointCachingOperationInvokerAdvisor(Environment environment);

    // Access resolver from properties
    @Bean
    @ConditionalOnMissingBean(EndpointAccessResolver.class)
    PropertiesEndpointAccessResolver propertiesEndpointAccessResolver(Environment environment);
}

PropertiesEndpointAccessResolver

/**
 * Resolves endpoint access levels from properties
 * Resolution order:
 * 1. management.endpoint.<id>.access (specific)
 * 2. management.endpoints.access.default (fallback)
 * 3. management.endpoints.access.max-permitted (cap)
 *
 * Implementation Details:
 * - Caches resolved access levels per endpoint in a ConcurrentHashMap
 * - Initializes default access level and max-permitted access at construction time
 * - Supports deprecated 'enabled' and 'enabled-by-default' boolean properties
 * - Validates that 'enabled' and 'access' properties are mutually exclusive
 */
public class PropertiesEndpointAccessResolver implements EndpointAccessResolver {

    /**
     * Creates resolver from property resolver (typically Spring Environment)
     *
     * Initialization Process:
     * 1. Determines default access level from management.endpoints.access.default
     *    (or deprecated management.endpoints.enabled-by-default)
     * 2. Reads max-permitted access from management.endpoints.access.max-permitted
     *    with default of UNRESTRICTED
     * 3. Initializes internal ConcurrentHashMap cache (Map<EndpointId, Access>)
     *    for thread-safe caching of resolved access levels
     *
     * @param properties Property resolver for reading configuration
     */
    public PropertiesEndpointAccessResolver(PropertyResolver properties);

    /**
     * Resolves access level for an endpoint using property configuration
     *
     * Resolution Logic:
     * 1. Check cache for previously resolved access
     * 2. Read management.endpoint.<id>.access (or deprecated enabled)
     * 3. If not set, use management.endpoints.access.default (or deprecated enabled-by-default)
     * 4. Apply management.endpoints.access.max-permitted cap
     * 5. Cache and return result
     *
     * Property Precedence:
     * - management.endpoint.<id>.access takes precedence over enabled
     * - management.endpoints.access.default takes precedence over enabled-by-default
     * - Validates that 'enabled' and 'access' are not both set (mutually exclusive)
     *
     * Deprecated Property Mapping:
     * - enabled=true → UNRESTRICTED
     * - enabled=false → NONE
     * - enabled-by-default=true → UNRESTRICTED
     * - enabled-by-default=false → NONE
     *
     * @param endpointId The endpoint identifier
     * @param defaultAccess The default access level if no properties are set
     * @return The resolved access level, capped by max-permitted if configured
     */
    @Override
    public Access accessFor(EndpointId endpointId, Access defaultAccess);
}

EndpointIdTimeToLivePropertyFunction

⚠️ INTERNAL IMPLEMENTATION - NOT FOR PUBLIC USE

This class is package-private and is an internal implementation detail of the caching infrastructure. It is not part of the public API and should not be used directly by application code. It is documented here for completeness and to aid understanding of the caching mechanism.

/**
 * Function that resolves cache time-to-live from properties for a given endpoint
 * Reads management.endpoint.<id>.cache.time-to-live property
 * Package-private internal implementation class - NOT FOR PUBLIC USE
 * @since 2.0.0
 */
class EndpointIdTimeToLivePropertyFunction implements Function<EndpointId, @Nullable Long> {

    /**
     * Creates function that reads TTL from environment properties
     * @param environment Spring environment for property resolution
     */
    EndpointIdTimeToLivePropertyFunction(Environment environment);

    /**
     * Gets the cache time-to-live in milliseconds for an endpoint
     * Reads from management.endpoint.<id>.cache.time-to-live
     * @param endpointId The endpoint identifier
     * @return Time-to-live in milliseconds, or null if not configured
     */
    @Override
    public @Nullable Long apply(EndpointId endpointId);
}

Usage Example:

EndpointIdTimeToLivePropertyFunction ttlFunction =
    new EndpointIdTimeToLivePropertyFunction(environment);

Long healthTtl = ttlFunction.apply(EndpointId.of("health"));
// Returns value from management.endpoint.health.cache.time-to-live

Access Control Logic

┌─────────────────────────────────────┐
│ 1. Check per-endpoint access        │
│    management.endpoint.<id>.access  │
└──────────────┬──────────────────────┘
               ↓ (if not set)
┌─────────────────────────────────────┐
│ 2. Check default access             │
│    management.endpoints.access.     │
│    default                          │
└──────────────┬──────────────────────┘
               ↓
┌─────────────────────────────────────┐
│ 3. Apply max-permitted cap          │
│    management.endpoints.access.     │
│    max-permitted                    │
└─────────────────────────────────────┘

Configuration Properties

# Per-endpoint access
management.endpoint.health.access=UNRESTRICTED
management.endpoint.metrics.access=READ_ONLY
management.endpoint.shutdown.access=NONE

# Default access
management.endpoints.access.default=READ_ONLY

# Maximum permitted (caps all endpoints)
management.endpoints.access.max-permitted=READ_ONLY

Property Resolution Examples

ScenarioPer-EndpointDefaultMax-PermittedResult
Explicit UNRESTRICTEDUNRESTRICTEDREAD_ONLYUNRESTRICTEDUNRESTRICTED
Capped by maxUNRESTRICTEDREAD_ONLYREAD_ONLYREAD_ONLY
Use default(not set)READ_ONLYUNRESTRICTEDREAD_ONLY
DisabledNONEUNRESTRICTEDUNRESTRICTEDNONE

Operation Caching

Configuration

# Cache TTL per endpoint (milliseconds)
management.endpoint.health.cache.time-to-live=10000
management.endpoint.beans.cache.time-to-live=30000

# Disable caching
management.endpoint.metrics.cache.time-to-live=0

When to Cache

EndpointCache?Typical TTLReason
healthOptional10sChanges infrequently
metricsNo0Real-time data
beansYes30-60sStatic after startup
conditionsYes60s+Never changes
envYes30sChanges rarely
mappingsYes60s+Static after startup

Parameter Value Mapping

public interface ParameterValueMapper {
    /**
     * Maps string parameter to required type
     * Supports: primitives, enums, common types
     * @throws ParameterMappingException if conversion fails
     */
    Object mapParameterValue(OperationParameter parameter, @Nullable Object value)
        throws ParameterMappingException;
}

Supported Type Conversions

FromToExample
StringInteger"123"123
StringBoolean"true"true
StringEnum"READ"OperationType.READ
StringDuration"10s"Duration.ofSeconds(10)
StringCustomVia registered converters

Customization Examples

Custom Access Resolver

@Component
public class CustomEndpointAccessResolver implements EndpointAccessResolver {

    @Override
    public Access accessFor(EndpointId endpointId, Access defaultAccess) {
        // Custom logic
        if (endpointId.toString().equals("shutdown")) {
            return Access.NONE; // Never allow shutdown
        }
        if (isProductionEnvironment()) {
            return Access.READ_ONLY; // Production: read-only
        }
        return defaultAccess;
    }

    private boolean isProductionEnvironment() {
        return "production".equals(System.getenv("ENV"));
    }
}

Custom Parameter Converter

@Component
@EndpointConverter
public class CustomTypeConverter implements Converter<String, CustomType> {

    @Override
    public CustomType convert(String source) {
        return CustomType.parse(source);
    }
}

Common Issues

Issue: Endpoint Not Accessible (Access Denied)

Cause: Access level set to NONE or READ_ONLY for write operation

Solution:

management.endpoint.<id>.access=UNRESTRICTED

Issue: Parameter Conversion Fails

Symptom: ParameterMappingException when invoking endpoint

Solution:

  • Ensure parameter types are supported
  • Register custom converter if needed:
@Component
@EndpointConverter
public class MyConverter implements Converter<String, MyType> {
    @Override
    public MyType convert(String source) {
        return MyType.valueOf(source);
    }
}

Issue: Caching Not Working

Cause: Cache TTL not set or set to 0

Solution:

management.endpoint.<id>.cache.time-to-live=10000

Types Reference

/**
 * Represents an endpoint identifier
 * Immutable, normalized representation of an endpoint ID
 */
public final class EndpointId {
    /**
     * Creates an endpoint ID from a string value
     * @param value The endpoint ID value (e.g., "health", "custom-endpoint")
     * @return The endpoint ID
     * @throws IllegalArgumentException if value is invalid
     */
    public static EndpointId of(String value);

    /**
     * Creates an endpoint ID from a property value
     * Normalizes the value (e.g., converts kebab-case/snake_case to lowercase)
     * @param value The property value
     * @return The endpoint ID
     */
    public static EndpointId fromPropertyValue(String value);

    /**
     * Returns the string representation of this endpoint ID
     * @return The endpoint ID as a string
     */
    @Override
    public String toString();

    /**
     * Returns the lower-case string representation of this endpoint ID
     * @return The endpoint ID as a lower-case string
     * @since 2.0.6
     */
    public String toLowerCaseString();

    /**
     * Factory method that respects legacy name migration property
     * @param environment Spring environment
     * @param value The endpoint ID value
     * @return The endpoint ID
     * @since 2.2.0
     */
    public static EndpointId of(Environment environment, String value);

    /**
     * Compares this endpoint ID to another object
     * @param obj The object to compare
     * @return true if equal
     */
    @Override
    public boolean equals(Object obj);

    /**
     * Returns the hash code for this endpoint ID
     * @return The hash code
     */
    @Override
    public int hashCode();
}

/**
 * Endpoint access levels
 * Controls what operations can be performed on an endpoint
 * @since 3.4.0
 */
public enum Access {
    /**
     * UNRESTRICTED - Full access to all operations
     * Allows READ, WRITE, and DELETE operations
     * Use for: Public endpoints like health, info
     */
    UNRESTRICTED,

    /**
     * READ_ONLY - Read operations only
     * Allows READ operations, blocks WRITE and DELETE
     * Use for: Endpoints that should be monitored but not modified (beans, env, metrics)
     */
    READ_ONLY,

    /**
     * NONE - Endpoint is disabled
     * Blocks all operations, endpoint will not be exposed
     * Use for: Disabling specific endpoints (shutdown, threaddump in production)
     */
    NONE;

    /**
     * Caps this access level to the maximum permitted level
     * Returns the more restrictive of this access and the maximum permitted
     * @param maxPermitted The maximum permitted access level
     * @return The capped access level
     * @since 3.4.0
     */
    public Access cap(Access maxPermitted);
}

/**
 * Operation types for endpoints
 */
public enum OperationType {
    READ,    // Query operation (safe, idempotent) - maps to HTTP GET
    WRITE,   // Mutating operation - maps to HTTP POST/PUT
    DELETE   // Deletion operation - maps to HTTP DELETE
}

/**
 * Resolves endpoint access levels from configuration
 */
public interface EndpointAccessResolver {
    /**
     * Determines the access level for an endpoint
     * @param endpointId The endpoint identifier
     * @param defaultAccess The default access level
     * @return The resolved access level
     */
    Access accessFor(EndpointId endpointId, Access defaultAccess);
}

/**
 * Applies caching behavior to endpoint operations
 */
public interface CachingOperationInvokerAdvisor {
    /**
     * Applies caching to an operation invoker
     * @param endpointId Endpoint ID
     * @param operationType Operation type (READ, WRITE, DELETE)
     * @param parameters Operation parameters
     * @param invoker Original invoker
     * @return Wrapped invoker with caching
     */
    OperationInvoker apply(
        EndpointId endpointId,
        OperationType operationType,
        OperationParameters parameters,
        OperationInvoker invoker
    );
}

/**
 * Invokes an endpoint operation
 */
@FunctionalInterface
public interface OperationInvoker {
    /**
     * Invokes the operation
     * @param context The invocation context
     * @return The operation result
     */
    Object invoke(InvocationContext context);
}

/**
 * Context for operation invocation
 * @since 2.0.0
 */
public class InvocationContext {
    /**
     * Creates a new context for an operation invocation
     * @param securityContext The security context
     * @param arguments The arguments available to the operation
     * @param argumentResolvers Optional resolvers for additional arguments
     */
    public InvocationContext(SecurityContext securityContext, Map<String, Object> arguments,
            OperationArgumentResolver... argumentResolvers);

    /**
     * Gets the operation arguments
     * @return Map of argument names to values
     */
    public Map<String, Object> getArguments();

    /**
     * Resolves an argument with the given argument type
     * @param <T> Type of the argument
     * @param argumentType Type of the argument
     * @return Resolved argument of the required type or null
     * @since 2.5.0
     */
    public <T> T resolveArgument(Class<T> argumentType);

    /**
     * Returns whether the context is capable of resolving an argument of the given type
     * @param type Argument type
     * @return true if resolution is possible, otherwise false
     * @since 2.5.0
     */
    public boolean canResolve(Class<?> type);
}

/**
 * Exception thrown when parameter mapping fails
 * @since 2.0.0
 */
public final class ParameterMappingException extends InvalidEndpointRequestException {
    /**
     * Creates a new parameter mapping exception
     * @param parameter The parameter being mapped
     * @param value The value being mapped
     * @param cause The underlying cause
     */
    public ParameterMappingException(OperationParameter parameter, @Nullable Object value, Throwable cause);

    /**
     * Gets the parameter being mapped
     * @return The parameter
     */
    public OperationParameter getParameter();

    /**
     * Gets the value being mapped
     * @return The value
     */
    public @Nullable Object getValue();
}

/**
 * Abstract base class for endpoint-related Spring conditions
 * Provides property-based evaluation for endpoint enablement
 * NOTE: OnAvailableEndpointCondition extends SpringBootCondition directly, not this class
 * @since 2.0.0
 */
public abstract class OnEndpointElementCondition extends SpringBootCondition {

    /**
     * Gets the outcome of the condition
     * @param context Condition evaluation context
     * @param metadata Annotation metadata
     * @return Condition outcome indicating match or no-match
     */
    @Override
    public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata);
}

Integration Points

With Other Auto-Configurations

EndpointAutoConfiguration
        ↓ (provides beans to)
┌───────────────────────────────┐
│  Individual Endpoint          │
│  Auto-Configurations          │
│  (e.g., HealthEndpoint        │
│  AutoConfiguration)           │
└───────────────────────────────┘
        ↓ (registers endpoints for)
┌───────────────────────────────┐
│  Exposure Auto-Configurations │
│  (Web, JMX)                   │
└───────────────────────────────┘

With Spring Boot

  • Auto-Configuration: Runs early, before endpoint auto-configurations
  • Conditional Evaluation: @ConditionalOnMissingBean allows customization
  • Environment: Reads from merged Spring Environment
  • Order: @AutoConfigureBefore ensures proper ordering

Best Practices

  1. Access Control: Use READ_ONLY as default, grant UNRESTRICTED explicitly
  2. Caching: Cache static endpoints (beans, conditions, mappings)
  3. Parameter Types: Use standard types when possible
  4. Custom Resolvers: Override entire EndpointAccessResolver for complex logic
  5. Testing: Test access resolution with different property combinations

Performance Notes

  • Access Resolution: Cached per-request, negligible overhead
  • Parameter Mapping: Conversion cached per operation invocation
  • Caching: Reduces load for expensive operations (beans, conditions)
  • Thread Safety: All infrastructure components are thread-safe

Related Documentation

Within This Tile

  • index.md - Complete overview, decision matrices, common patterns
    • See "Core Endpoint Infrastructure" section for high-level overview
    • See "Architecture Integration Flow" for how this fits into the system
  • management-endpoints.md - Individual endpoint implementations that use this infrastructure
    • Each endpoint relies on ParameterValueMapper, CachingOperationInvokerAdvisor, and EndpointAccessResolver
  • endpoint-filtering.md - Include/exclude filtering and operation filters
    • Uses Access enum defined here for access control decisions
    • OperationFilter integrates with access resolution
  • web-endpoints.md - HTTP exposure layer
    • WebEndpointAutoConfiguration depends on EndpointAutoConfiguration beans
    • PathMapper works with endpoint discovery from this layer
  • endpoint-properties.md - Endpoint-specific configuration
    • PropertiesEndpointAccessResolver reads properties to determine access levels
  • conditional-annotations.md - Conditional registration
    • @ConditionalOnAvailableEndpoint uses EndpointAccessResolver to check availability

Quick Navigation

From Core Infrastructure, you can:

  • Configure access control → See index.md for property examples
  • Understand caching → See index.md for caching strategies
  • Implement custom resolver → See endpoint-filtering.md for filter examples
  • Debug access issues → See index.md for common problems