Polish endpoint

This commit is contained in:
Phillip Webb 2017-08-28 18:17:29 -07:00 committed by Stephane Nicoll
parent 98455e30dc
commit f9e5b07eec
42 changed files with 634 additions and 648 deletions

View File

@ -23,7 +23,7 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import org.springframework.boot.endpoint.Endpoint; import org.springframework.boot.endpoint.Endpoint;
import org.springframework.boot.endpoint.EndpointType; import org.springframework.boot.endpoint.EndpointDelivery;
import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Conditional;
/** /**
@ -33,8 +33,8 @@ import org.springframework.context.annotation.Conditional;
* <p> * <p>
* If no specific {@code endpoints.<id>.*} or {@code endpoints.default.*} properties are * If no specific {@code endpoints.<id>.*} or {@code endpoints.default.*} properties are
* defined, the condition matches the {@code enabledByDefault} value regardless of the * defined, the condition matches the {@code enabledByDefault} value regardless of the
* specific {@link EndpointType}, if any. If any property are set, they are evaluated with * specific {@link EndpointDelivery}, if any. If any property are set, they are evaluated
* a sensible order of precedence. * with a sensible order of precedence.
* <p> * <p>
* For instance if {@code endpoints.default.enabled} is {@code false} but * For instance if {@code endpoints.default.enabled} is {@code false} but
* {@code endpoints.<id>.enabled} is {@code true}, the condition will match. * {@code endpoints.<id>.enabled} is {@code true}, the condition will match.

View File

@ -22,7 +22,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition; import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.endpoint.Endpoint; import org.springframework.boot.endpoint.Endpoint;
import org.springframework.boot.endpoint.EndpointType; import org.springframework.boot.endpoint.EndpointDelivery;
import org.springframework.boot.endpoint.jmx.JmxEndpointExtension; import org.springframework.boot.endpoint.jmx.JmxEndpointExtension;
import org.springframework.boot.endpoint.web.WebEndpointExtension; import org.springframework.boot.endpoint.web.WebEndpointExtension;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@ -30,6 +30,7 @@ import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.core.type.MethodMetadata; import org.springframework.core.type.MethodMetadata;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -44,14 +45,9 @@ class OnEnabledEndpointCondition extends SpringBootCondition {
@Override @Override
public ConditionOutcome getMatchOutcome(ConditionContext context, public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) { AnnotatedTypeMetadata metadata) {
EndpointAttributes endpoint = getEndpointAttributes(context, metadata); EndpointAttributes attributes = getEndpointAttributes(context, metadata);
if (!StringUtils.hasText(endpoint.id)) { EndpointEnablement endpointEnablement = attributes
throw new IllegalStateException("Endpoint id could not be determined"); .getEnablement(new EndpointEnablementProvider(context.getEnvironment()));
}
EndpointEnablementProvider enablementProvider = new EndpointEnablementProvider(
context.getEnvironment());
EndpointEnablement endpointEnablement = enablementProvider.getEndpointEnablement(
endpoint.id, endpoint.enabled, endpoint.endpointType);
return new ConditionOutcome(endpointEnablement.isEnabled(), return new ConditionOutcome(endpointEnablement.isEnabled(),
ConditionMessage.forCondition(ConditionalOnEnabledEndpoint.class) ConditionMessage.forCondition(ConditionalOnEnabledEndpoint.class)
.because(endpointEnablement.getReason())); .because(endpointEnablement.getReason()));
@ -59,24 +55,27 @@ class OnEnabledEndpointCondition extends SpringBootCondition {
private EndpointAttributes getEndpointAttributes(ConditionContext context, private EndpointAttributes getEndpointAttributes(ConditionContext context,
AnnotatedTypeMetadata metadata) { AnnotatedTypeMetadata metadata) {
if (metadata instanceof MethodMetadata Assert.state(
&& metadata.isAnnotated(Bean.class.getName())) { metadata instanceof MethodMetadata
MethodMetadata methodMetadata = (MethodMetadata) metadata; && metadata.isAnnotated(Bean.class.getName()),
try {
// We should be safe to load at this point since we are in the
// REGISTER_BEAN phase
Class<?> returnType = ClassUtils.forName(
methodMetadata.getReturnTypeName(), context.getClassLoader());
return extractEndpointAttributes(returnType);
}
catch (Throwable ex) {
throw new IllegalStateException("Failed to extract endpoint id for "
+ methodMetadata.getDeclaringClassName() + "."
+ methodMetadata.getMethodName(), ex);
}
}
throw new IllegalStateException(
"OnEnabledEndpointCondition may only be used on @Bean methods"); "OnEnabledEndpointCondition may only be used on @Bean methods");
return getEndpointAttributes(context, (MethodMetadata) metadata);
}
private EndpointAttributes getEndpointAttributes(ConditionContext context,
MethodMetadata methodMetadata) {
try {
// We should be safe to load at this point since we are in the
// REGISTER_BEAN phase
Class<?> returnType = ClassUtils.forName(methodMetadata.getReturnTypeName(),
context.getClassLoader());
return extractEndpointAttributes(returnType);
}
catch (Throwable ex) {
throw new IllegalStateException("Failed to extract endpoint id for "
+ methodMetadata.getDeclaringClassName() + "."
+ methodMetadata.getMethodName(), ex);
}
} }
protected EndpointAttributes extractEndpointAttributes(Class<?> type) { protected EndpointAttributes extractEndpointAttributes(Class<?> type) {
@ -105,11 +104,10 @@ class OnEnabledEndpointCondition extends SpringBootCondition {
if (endpoint == null) { if (endpoint == null) {
return null; return null;
} }
// If both types are set, all techs are exposed // If both types are set, all delivery technologies are exposed
EndpointType endpointType = (endpoint.types().length == 1 ? endpoint.types()[0] EndpointDelivery[] delivery = endpoint.delivery();
: null);
return new EndpointAttributes(endpoint.id(), endpoint.enabledByDefault(), return new EndpointAttributes(endpoint.id(), endpoint.enabledByDefault(),
endpointType); (delivery.length == 1 ? delivery[0] : null));
} }
private static class EndpointAttributes { private static class EndpointAttributes {
@ -118,12 +116,19 @@ class OnEnabledEndpointCondition extends SpringBootCondition {
private final boolean enabled; private final boolean enabled;
private final EndpointType endpointType; private final EndpointDelivery delivery;
EndpointAttributes(String id, boolean enabled, EndpointType endpointType) { EndpointAttributes(String id, boolean enabled, EndpointDelivery delivery) {
if (!StringUtils.hasText(id)) {
throw new IllegalStateException("Endpoint id could not be determined");
}
this.id = id; this.id = id;
this.enabled = enabled; this.enabled = enabled;
this.endpointType = endpointType; this.delivery = delivery;
}
public EndpointEnablement getEnablement(EndpointEnablementProvider provider) {
return provider.getEndpointEnablement(this.id, this.enabled, this.delivery);
} }
} }

View File

@ -34,7 +34,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicat
import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration; import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.endpoint.ConversionServiceOperationParameterMapper; import org.springframework.boot.endpoint.ConversionServiceOperationParameterMapper;
import org.springframework.boot.endpoint.EndpointType; import org.springframework.boot.endpoint.EndpointDelivery;
import org.springframework.boot.endpoint.OperationParameterMapper; import org.springframework.boot.endpoint.OperationParameterMapper;
import org.springframework.boot.endpoint.jmx.EndpointMBeanRegistrar; import org.springframework.boot.endpoint.jmx.EndpointMBeanRegistrar;
import org.springframework.boot.endpoint.jmx.JmxAnnotationEndpointDiscoverer; import org.springframework.boot.endpoint.jmx.JmxAnnotationEndpointDiscoverer;
@ -102,10 +102,10 @@ public class EndpointInfrastructureAutoConfiguration {
ObjectProvider<ObjectMapper> objectMapper) { ObjectProvider<ObjectMapper> objectMapper) {
EndpointProvider<JmxEndpointOperation> endpointProvider = new EndpointProvider<>( EndpointProvider<JmxEndpointOperation> endpointProvider = new EndpointProvider<>(
this.applicationContext.getEnvironment(), endpointDiscoverer, this.applicationContext.getEnvironment(), endpointDiscoverer,
EndpointType.JMX); EndpointDelivery.JMX);
EndpointMBeanRegistrar endpointMBeanRegistrar = new EndpointMBeanRegistrar( EndpointMBeanRegistrar endpointMBeanRegistrar = new EndpointMBeanRegistrar(
mBeanServer, new DefaultEndpointObjectNameFactory(properties, mBeanServer, new DefaultEndpointObjectNameFactory(properties, mBeanServer,
mBeanServer, ObjectUtils.getIdentityHexString(this.applicationContext))); ObjectUtils.getIdentityHexString(this.applicationContext)));
return new JmxEndpointExporter(endpointProvider, endpointMBeanRegistrar, return new JmxEndpointExporter(endpointProvider, endpointMBeanRegistrar,
objectMapper.getIfAvailable(ObjectMapper::new)); objectMapper.getIfAvailable(ObjectMapper::new));
} }
@ -127,7 +127,7 @@ public class EndpointInfrastructureAutoConfiguration {
return new EndpointProvider<>(this.applicationContext.getEnvironment(), return new EndpointProvider<>(this.applicationContext.getEnvironment(),
webEndpointDiscoverer(operationParameterMapper, webEndpointDiscoverer(operationParameterMapper,
cachingConfigurationFactory), cachingConfigurationFactory),
EndpointType.WEB); EndpointDelivery.WEB);
} }
private WebAnnotationEndpointDiscoverer webEndpointDiscoverer( private WebAnnotationEndpointDiscoverer webEndpointDiscoverer(

View File

@ -20,10 +20,10 @@ import java.util.Collection;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.springframework.boot.actuate.autoconfigure.endpoint.support.EndpointEnablementProvider; import org.springframework.boot.actuate.autoconfigure.endpoint.support.EndpointEnablementProvider;
import org.springframework.boot.endpoint.EndpointDelivery;
import org.springframework.boot.endpoint.EndpointDiscoverer; import org.springframework.boot.endpoint.EndpointDiscoverer;
import org.springframework.boot.endpoint.EndpointInfo; import org.springframework.boot.endpoint.EndpointInfo;
import org.springframework.boot.endpoint.EndpointOperation; import org.springframework.boot.endpoint.Operation;
import org.springframework.boot.endpoint.EndpointType;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
/** /**
@ -34,25 +34,25 @@ import org.springframework.core.env.Environment;
* @author Stephane Nicoll * @author Stephane Nicoll
* @since 2.0.0 * @since 2.0.0
*/ */
public final class EndpointProvider<T extends EndpointOperation> { public final class EndpointProvider<T extends Operation> {
private final EndpointDiscoverer<T> discoverer; private final EndpointDiscoverer<T> discoverer;
private final EndpointEnablementProvider endpointEnablementProvider; private final EndpointEnablementProvider endpointEnablementProvider;
private final EndpointType endpointType; private final EndpointDelivery delivery;
/** /**
* Creates a new instance. * Creates a new instance.
* @param environment the environment to use to check the endpoints that are enabled * @param environment the environment to use to check the endpoints that are enabled
* @param discoverer the discoverer to get the initial set of endpoints * @param discoverer the discoverer to get the initial set of endpoints
* @param endpointType the type of endpoint to handle * @param delivery the delivery technology for the endpoint
*/ */
public EndpointProvider(Environment environment, EndpointDiscoverer<T> discoverer, public EndpointProvider(Environment environment, EndpointDiscoverer<T> discoverer,
EndpointType endpointType) { EndpointDelivery delivery) {
this.discoverer = discoverer; this.discoverer = discoverer;
this.endpointEnablementProvider = new EndpointEnablementProvider(environment); this.endpointEnablementProvider = new EndpointEnablementProvider(environment);
this.endpointType = endpointType; this.delivery = delivery;
} }
public Collection<EndpointInfo<T>> getEndpoints() { public Collection<EndpointInfo<T>> getEndpoints() {
@ -62,7 +62,7 @@ public final class EndpointProvider<T extends EndpointOperation> {
private boolean isEnabled(EndpointInfo<?> endpoint) { private boolean isEnabled(EndpointInfo<?> endpoint) {
return this.endpointEnablementProvider.getEndpointEnablement(endpoint.getId(), return this.endpointEnablementProvider.getEndpointEnablement(endpoint.getId(),
endpoint.isEnabledByDefault(), this.endpointType).isEnabled(); endpoint.isEnabledByDefault(), this.delivery).isEnabled();
} }
} }

View File

@ -25,6 +25,7 @@ package org.springframework.boot.actuate.autoconfigure.endpoint.support;
public final class EndpointEnablement { public final class EndpointEnablement {
private final boolean enabled; private final boolean enabled;
private final String reason; private final String reason;
/** /**

View File

@ -16,9 +16,9 @@
package org.springframework.boot.actuate.autoconfigure.endpoint.support; package org.springframework.boot.actuate.autoconfigure.endpoint.support;
import org.springframework.boot.endpoint.EndpointType; import org.springframework.boot.endpoint.EndpointDelivery;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils; import org.springframework.util.Assert;
/** /**
* Determines an endpoint's enablement based on the current {@link Environment}. * Determines an endpoint's enablement based on the current {@link Environment}.
@ -54,109 +54,111 @@ public class EndpointEnablementProvider {
* Return the {@link EndpointEnablement} of an endpoint for a specific tech exposure. * Return the {@link EndpointEnablement} of an endpoint for a specific tech exposure.
* @param endpointId the id of the endpoint * @param endpointId the id of the endpoint
* @param enabledByDefault whether the endpoint is enabled by default or not * @param enabledByDefault whether the endpoint is enabled by default or not
* @param endpointType the requested {@link EndpointType} * @param delivery the requested {@link EndpointDelivery}
* @return the {@link EndpointEnablement} of that endpoint for the specified * @return the {@link EndpointEnablement} of that endpoint for the specified
* {@link EndpointType} * {@link EndpointDelivery}
*/ */
public EndpointEnablement getEndpointEnablement(String endpointId, public EndpointEnablement getEndpointEnablement(String endpointId,
boolean enabledByDefault, EndpointType endpointType) { boolean enabledByDefault, EndpointDelivery delivery) {
if (!StringUtils.hasText(endpointId)) { Assert.hasText(endpointId, "Endpoint id must have a value");
throw new IllegalArgumentException("Endpoint id must have a value"); Assert.isTrue(!endpointId.equals("default"), "Endpoint id 'default' is a reserved "
+ "value and cannot be used by an endpoint");
EndpointEnablement result = findEnablement(endpointId, delivery);
if (result != null) {
return result;
} }
if (endpointId.equals("default")) { result = findEnablement(getKey(endpointId, "enabled"));
throw new IllegalArgumentException("Endpoint id 'default' is a reserved " if (result != null) {
+ "value and cannot be used by an endpoint"); return result;
} }
if (endpointType != null) {
String endpointTypeKey = createTechSpecificKey(endpointId, endpointType);
EndpointEnablement endpointTypeSpecificOutcome = getEnablementFor(
endpointTypeKey);
if (endpointTypeSpecificOutcome != null) {
return endpointTypeSpecificOutcome;
}
}
else {
// If any tech specific is on at this point we should enable the endpoint
EndpointEnablement anyTechSpecificOutcome = getAnyTechSpecificOutcomeFor(
endpointId);
if (anyTechSpecificOutcome != null) {
return anyTechSpecificOutcome;
}
}
String endpointKey = createKey(endpointId, "enabled");
EndpointEnablement endpointSpecificOutcome = getEnablementFor(endpointKey);
if (endpointSpecificOutcome != null) {
return endpointSpecificOutcome;
}
// All endpoints specific attributes have been looked at. Checking default value // All endpoints specific attributes have been looked at. Checking default value
// for the endpoint // for the endpoint
if (!enabledByDefault) { if (!enabledByDefault) {
return defaultEndpointEnablement(endpointId, false, endpointType); return getDefaultEndpointEnablement(endpointId, false, delivery);
} }
return getGlobalEndpointEnablement(endpointId, enabledByDefault,
if (endpointType != null) { delivery);
String defaultTypeKey = createTechSpecificKey("default", endpointType);
EndpointEnablement globalTypeOutcome = getEnablementFor(defaultTypeKey);
if (globalTypeOutcome != null) {
return globalTypeOutcome;
}
if (!endpointType.isEnabledByDefault()) {
return defaultEndpointEnablement("default", false, endpointType);
}
}
else {
// Check if there is a global tech required
EndpointEnablement anyTechGeneralOutcome = getAnyTechSpecificOutcomeFor(
"default");
if (anyTechGeneralOutcome != null) {
return anyTechGeneralOutcome;
}
}
String defaultKey = createKey("default", "enabled");
EndpointEnablement globalOutCome = getEnablementFor(defaultKey);
if (globalOutCome != null) {
return globalOutCome;
}
return defaultEndpointEnablement(endpointId, enabledByDefault, endpointType);
} }
private EndpointEnablement defaultEndpointEnablement(String endpointId, private EndpointEnablement findEnablement(String endpointId,
boolean enabledByDefault, EndpointType endpointType) { EndpointDelivery delivery) {
return new EndpointEnablement(enabledByDefault, createDefaultEnablementMessage( if (delivery != null) {
endpointId, enabledByDefault, endpointType)); return findEnablement(getKey(endpointId, delivery));
}
private String createDefaultEnablementMessage(String endpointId,
boolean enabledByDefault, EndpointType endpointType) {
StringBuilder sb = new StringBuilder();
sb.append(String.format("endpoint '%s' ", endpointId));
if (endpointType != null) {
sb.append(String.format("(%s) ", endpointType.name().toLowerCase()));
} }
sb.append(String.format("is %s by default", return findEnablementForAnyDeliveryTechnology(endpointId);
(enabledByDefault ? "enabled" : "disabled")));
return sb.toString();
} }
private EndpointEnablement getAnyTechSpecificOutcomeFor(String endpointId) { private EndpointEnablement getGlobalEndpointEnablement(String endpointId,
for (EndpointType endpointType : EndpointType.values()) { boolean enabledByDefault, EndpointDelivery delivery) {
String key = createTechSpecificKey(endpointId, endpointType); EndpointEnablement result = findGlobalEndpointEnablement(delivery);
EndpointEnablement outcome = getEnablementFor(key); if (result != null) {
if (outcome != null && outcome.isEnabled()) { return result;
return outcome; }
result = findEnablement(getKey("default", "enabled"));
if (result != null) {
return result;
}
return getDefaultEndpointEnablement(endpointId, enabledByDefault,
delivery);
}
private EndpointEnablement findGlobalEndpointEnablement(
EndpointDelivery delivery) {
if (delivery != null) {
EndpointEnablement result = findEnablement(getKey("default", delivery));
if (result != null) {
return result;
}
if (!delivery.isEnabledByDefault()) {
return getDefaultEndpointEnablement("default", false, delivery);
}
return null;
}
return findEnablementForAnyDeliveryTechnology("default");
}
private EndpointEnablement findEnablementForAnyDeliveryTechnology(String endpointId) {
for (EndpointDelivery candidate : EndpointDelivery.values()) {
EndpointEnablement result = findEnablementForDeliveryTechnology(endpointId,
candidate);
if (result != null && result.isEnabled()) {
return result;
} }
} }
return null; return null;
} }
private String createTechSpecificKey(String endpointId, EndpointType endpointType) { private EndpointEnablement findEnablementForDeliveryTechnology(String endpointId,
return createKey(endpointId, endpointType.name().toLowerCase() + ".enabled"); EndpointDelivery delivery) {
String endpointTypeKey = getKey(endpointId, delivery);
EndpointEnablement endpointTypeSpecificOutcome = findEnablement(endpointTypeKey);
return endpointTypeSpecificOutcome;
} }
private String createKey(String endpointId, String suffix) { private EndpointEnablement getDefaultEndpointEnablement(String endpointId,
boolean enabledByDefault, EndpointDelivery delivery) {
return new EndpointEnablement(enabledByDefault, createDefaultEnablementMessage(
endpointId, enabledByDefault, delivery));
}
private String createDefaultEnablementMessage(String endpointId,
boolean enabledByDefault, EndpointDelivery delivery) {
StringBuilder message = new StringBuilder();
message.append(String.format("endpoint '%s' ", endpointId));
if (delivery != null) {
message.append(
String.format("(%s) ", delivery.name().toLowerCase()));
}
message.append(String.format("is %s by default",
(enabledByDefault ? "enabled" : "disabled")));
return message.toString();
}
private String getKey(String endpointId, EndpointDelivery delivery) {
return getKey(endpointId, delivery.name().toLowerCase() + ".enabled");
}
private String getKey(String endpointId, String suffix) {
return "endpoints." + endpointId + "." + suffix; return "endpoints." + endpointId + "." + suffix;
} }
@ -166,7 +168,7 @@ public class EndpointEnablementProvider {
* @param key the key to check * @param key the key to check
* @return the outcome or {@code null} if the key is no set * @return the outcome or {@code null} if the key is no set
*/ */
private EndpointEnablement getEnablementFor(String key) { private EndpointEnablement findEnablement(String key) {
if (this.environment.containsProperty(key)) { if (this.environment.containsProperty(key)) {
boolean match = this.environment.getProperty(key, Boolean.class, true); boolean match = this.environment.getProperty(key, Boolean.class, true);
return new EndpointEnablement(match, String.format("found property %s", key)); return new EndpointEnablement(match, String.format("found property %s", key));

View File

@ -111,7 +111,7 @@ class CloudFoundryWebEndpointServletHandlerMapping extends AbstractWebEndpointSe
@Override @Override
protected void registerMappingForOperation(WebEndpointOperation operation) { protected void registerMappingForOperation(WebEndpointOperation operation) {
registerMapping(createRequestMappingInfo(operation), registerMapping(createRequestMappingInfo(operation),
new OperationHandler(operation.getOperationInvoker(), operation.getId(), this.securityInterceptor), this.handle); new OperationHandler(operation.getInvoker(), operation.getId(), this.securityInterceptor), this.handle);
} }
/** /**

View File

@ -36,7 +36,7 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.endpoint.Endpoint; import org.springframework.boot.endpoint.Endpoint;
import org.springframework.boot.endpoint.EndpointType; import org.springframework.boot.endpoint.EndpointDelivery;
import org.springframework.boot.endpoint.ReadOperation; import org.springframework.boot.endpoint.ReadOperation;
import org.springframework.boot.endpoint.web.WebEndpointResponse; import org.springframework.boot.endpoint.web.WebEndpointResponse;
import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.FileSystemResource;
@ -55,7 +55,7 @@ import org.springframework.util.ReflectionUtils;
* @since 2.0.0 * @since 2.0.0
*/ */
@ConfigurationProperties(prefix = "endpoints.heapdump") @ConfigurationProperties(prefix = "endpoints.heapdump")
@Endpoint(id = "heapdump", types = EndpointType.WEB) @Endpoint(id = "heapdump", delivery = EndpointDelivery.WEB)
public class HeapDumpWebEndpoint { public class HeapDumpWebEndpoint {
private final long timeout; private final long timeout;

View File

@ -23,7 +23,7 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.endpoint.Endpoint; import org.springframework.boot.endpoint.Endpoint;
import org.springframework.boot.endpoint.EndpointType; import org.springframework.boot.endpoint.EndpointDelivery;
import org.springframework.boot.endpoint.ReadOperation; import org.springframework.boot.endpoint.ReadOperation;
import org.springframework.boot.logging.LogFile; import org.springframework.boot.logging.LogFile;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
@ -39,7 +39,7 @@ import org.springframework.core.io.Resource;
* @since 2.0.0 * @since 2.0.0
*/ */
@ConfigurationProperties(prefix = "endpoints.logfile") @ConfigurationProperties(prefix = "endpoints.logfile")
@Endpoint(id = "logfile", types = EndpointType.WEB) @Endpoint(id = "logfile", delivery = EndpointDelivery.WEB)
public class LogFileWebEndpoint { public class LogFileWebEndpoint {
private static final Log logger = LogFactory.getLog(LogFileWebEndpoint.class); private static final Log logger = LogFactory.getLog(LogFileWebEndpoint.class);

View File

@ -19,7 +19,7 @@ package org.springframework.boot.actuate.autoconfigure.endpoint;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.endpoint.Endpoint; import org.springframework.boot.endpoint.Endpoint;
import org.springframework.boot.endpoint.EndpointType; import org.springframework.boot.endpoint.EndpointDelivery;
import org.springframework.boot.endpoint.jmx.JmxEndpointExtension; import org.springframework.boot.endpoint.jmx.JmxEndpointExtension;
import org.springframework.boot.endpoint.web.WebEndpointExtension; import org.springframework.boot.endpoint.web.WebEndpointExtension;
import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.test.context.runner.ApplicationContextRunner;
@ -297,8 +297,8 @@ public class ConditionalOnEnabledEndpointTests {
} }
@Endpoint(id = "bar", types = { EndpointType.WEB, @Endpoint(id = "bar", delivery = { EndpointDelivery.WEB,
EndpointType.JMX }, enabledByDefault = false) EndpointDelivery.JMX }, enabledByDefault = false)
static class BarEndpoint { static class BarEndpoint {
} }
@ -314,7 +314,7 @@ public class ConditionalOnEnabledEndpointTests {
} }
@Endpoint(id = "onlyweb", types = EndpointType.WEB) @Endpoint(id = "onlyweb", delivery = EndpointDelivery.WEB)
static class OnlyWebEndpoint { static class OnlyWebEndpoint {
} }

View File

@ -20,7 +20,7 @@ import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.ExpectedException; import org.junit.rules.ExpectedException;
import org.springframework.boot.endpoint.EndpointType; import org.springframework.boot.endpoint.EndpointDelivery;
import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.mock.env.MockEnvironment; import org.springframework.mock.env.MockEnvironment;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
@ -41,7 +41,7 @@ public class EndpointEnablementProviderTests {
public void cannotDetermineEnablementWithEmptyEndpoint() { public void cannotDetermineEnablementWithEmptyEndpoint() {
this.thrown.expect(IllegalArgumentException.class); this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Endpoint id must have a value"); this.thrown.expectMessage("Endpoint id must have a value");
determineEnablement(" ", true); getEndpointEnablement(" ", true);
} }
@Test @Test
@ -49,289 +49,295 @@ public class EndpointEnablementProviderTests {
this.thrown.expect(IllegalArgumentException.class); this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Endpoint id 'default' is a reserved value and cannot " this.thrown.expectMessage("Endpoint id 'default' is a reserved value and cannot "
+ "be used by an endpoint"); + "be used by an endpoint");
determineEnablement("default", true); getEndpointEnablement("default", true);
} }
@Test @Test
public void generalEnabledByDefault() { public void generalEnabledByDefault() {
validate(determineEnablement("foo", true), true, EndpointEnablement enablement = getEndpointEnablement("foo", true);
"endpoint 'foo' is enabled by default"); validate(enablement, true, "endpoint 'foo' is enabled by default");
} }
@Test @Test
public void generalDisabledViaSpecificProperty() { public void generalDisabledViaSpecificProperty() {
validate(determineEnablement("foo", true, "endpoints.foo.enabled=false"), false, EndpointEnablement enablement = getEndpointEnablement("foo", true,
"found property endpoints.foo.enabled"); "endpoints.foo.enabled=false");
validate(enablement, false, "found property endpoints.foo.enabled");
} }
@Test @Test
public void generalDisabledViaGeneralProperty() { public void generalDisabledViaGeneralProperty() {
validate(determineEnablement("foo", true, "endpoints.default.enabled=false"), false, EndpointEnablement enablement = getEndpointEnablement("foo", true,
"found property endpoints.default.enabled"); "endpoints.default.enabled=false");
validate(enablement, false, "found property endpoints.default.enabled");
} }
@Test @Test
public void generalEnabledOverrideViaSpecificProperty() { public void generalEnabledOverrideViaSpecificProperty() {
validate( EndpointEnablement enablement = getEndpointEnablement("foo", true,
determineEnablement("foo", true, "endpoints.default.enabled=false", "endpoints.default.enabled=false", "endpoints.foo.enabled=true");
"endpoints.foo.enabled=true"), validate(enablement, true, "found property endpoints.foo.enabled");
true, "found property endpoints.foo.enabled");
} }
@Test @Test
public void generalEnabledOverrideViaSpecificWebProperty() { public void generalEnabledOverrideViaSpecificWebProperty() {
validate( EndpointEnablement enablement = getEndpointEnablement("foo", true,
determineEnablement("foo", true, "endpoints.foo.enabled=false", "endpoints.foo.enabled=false", "endpoints.foo.web.enabled=true");
"endpoints.foo.web.enabled=true"), validate(enablement, true, "found property endpoints.foo.web.enabled");
true, "found property endpoints.foo.web.enabled");
} }
@Test @Test
public void generalEnabledOverrideViaSpecificJmxProperty() { public void generalEnabledOverrideViaSpecificJmxProperty() {
validate( EndpointEnablement enablement = getEndpointEnablement("foo", true,
determineEnablement("foo", true, "endpoints.foo.enabled=false", "endpoints.foo.enabled=false", "endpoints.foo.jmx.enabled=true");
"endpoints.foo.jmx.enabled=true"), validate(enablement, true, "found property endpoints.foo.jmx.enabled");
true, "found property endpoints.foo.jmx.enabled");
} }
@Test @Test
public void generalEnabledOverrideViaSpecificAnyProperty() { public void generalEnabledOverrideViaSpecificAnyProperty() {
validate(determineEnablement("foo", true, "endpoints.foo.enabled=false", validate(getEndpointEnablement("foo", true, "endpoints.foo.enabled=false",
"endpoints.foo.web.enabled=false", "endpoints.foo.jmx.enabled=true"), "endpoints.foo.web.enabled=false", "endpoints.foo.jmx.enabled=true"),
true, "found property endpoints.foo.jmx.enabled"); true, "found property endpoints.foo.jmx.enabled");
} }
@Test @Test
public void generalEnabledOverrideViaGeneralWebProperty() { public void generalEnabledOverrideViaGeneralWebProperty() {
validate( EndpointEnablement enablement = getEndpointEnablement("foo", true,
determineEnablement("foo", true, "endpoints.default.enabled=false", "endpoints.default.enabled=false", "endpoints.default.web.enabled=true");
"endpoints.default.web.enabled=true"), validate(enablement, true, "found property endpoints.default.web.enabled");
true, "found property endpoints.default.web.enabled");
} }
@Test @Test
public void generalEnabledOverrideViaGeneralJmxProperty() { public void generalEnabledOverrideViaGeneralJmxProperty() {
validate( EndpointEnablement enablement = getEndpointEnablement("foo", true,
determineEnablement("foo", true, "endpoints.default.enabled=false", "endpoints.default.enabled=false", "endpoints.default.jmx.enabled=true");
"endpoints.default.jmx.enabled=true"), validate(enablement, true, "found property endpoints.default.jmx.enabled");
true, "found property endpoints.default.jmx.enabled");
} }
@Test @Test
public void generalEnabledOverrideViaGeneralAnyProperty() { public void generalEnabledOverrideViaGeneralAnyProperty() {
validate(determineEnablement("foo", true, "endpoints.default.enabled=false", EndpointEnablement enablement = getEndpointEnablement("foo", true,
"endpoints.default.web.enabled=false", "endpoints.default.jmx.enabled=true"), "endpoints.default.enabled=false", "endpoints.default.web.enabled=false",
true, "found property endpoints.default.jmx.enabled"); "endpoints.default.jmx.enabled=true");
validate(enablement, true, "found property endpoints.default.jmx.enabled");
} }
@Test @Test
public void generalDisabledEvenWithEnabledGeneralProperties() { public void generalDisabledEvenWithEnabledGeneralProperties() {
validate( EndpointEnablement enablement = getEndpointEnablement("foo", true,
determineEnablement("foo", true, "endpoints.default.enabled=true", "endpoints.default.enabled=true", "endpoints.default.web.enabled=true",
"endpoints.default.web.enabled=true", "endpoints.default.jmx.enabled=true", "endpoints.foo.enabled=false");
"endpoints.default.jmx.enabled=true", "endpoints.foo.enabled=false"), validate(enablement, false, "found property endpoints.foo.enabled");
false, "found property endpoints.foo.enabled");
} }
@Test @Test
public void generalDisabledByDefaultWithAnnotationFlag() { public void generalDisabledByDefaultWithAnnotationFlag() {
validate(determineEnablement("bar", false), false, EndpointEnablement enablement = getEndpointEnablement("bar", false);
"endpoint 'bar' is disabled by default"); validate(enablement, false, "endpoint 'bar' is disabled by default");
} }
@Test @Test
public void generalDisabledByDefaultWithAnnotationFlagEvenWithGeneralProperty() { public void generalDisabledByDefaultWithAnnotationFlagEvenWithGeneralProperty() {
validate(determineEnablement("bar", false, "endpoints.default.enabled=true"), false, EndpointEnablement enablement = getEndpointEnablement("bar", false,
"endpoint 'bar' is disabled by default"); "endpoints.default.enabled=true");
validate(enablement, false, "endpoint 'bar' is disabled by default");
} }
@Test @Test
public void generalDisabledByDefaultWithAnnotationFlagEvenWithGeneralWebProperty() { public void generalDisabledByDefaultWithAnnotationFlagEvenWithGeneralWebProperty() {
validate(determineEnablement("bar", false, "endpoints.default.web.enabled=true"), EndpointEnablement enablement = getEndpointEnablement("bar", false,
false, "endpoint 'bar' is disabled by default"); "endpoints.default.web.enabled=true");
validate(enablement, false, "endpoint 'bar' is disabled by default");
} }
@Test @Test
public void generalDisabledByDefaultWithAnnotationFlagEvenWithGeneralJmxProperty() { public void generalDisabledByDefaultWithAnnotationFlagEvenWithGeneralJmxProperty() {
validate(determineEnablement("bar", false, "endpoints.default.jmx.enabled=true"), EndpointEnablement enablement = getEndpointEnablement("bar", false,
false, "endpoint 'bar' is disabled by default"); "endpoints.default.jmx.enabled=true");
validate(enablement, false, "endpoint 'bar' is disabled by default");
} }
@Test @Test
public void generalEnabledOverrideWithAndAnnotationFlagAndSpecificProperty() { public void generalEnabledOverrideWithAndAnnotationFlagAndSpecificProperty() {
validate(determineEnablement("bar", false, "endpoints.bar.enabled=true"), true, EndpointEnablement enablement = getEndpointEnablement("bar", false,
"found property endpoints.bar.enabled"); "endpoints.bar.enabled=true");
validate(enablement, true, "found property endpoints.bar.enabled");
} }
@Test @Test
public void generalEnabledOverrideWithAndAnnotationFlagAndSpecificWebProperty() { public void generalEnabledOverrideWithAndAnnotationFlagAndSpecificWebProperty() {
validate(determineEnablement("bar", false, "endpoints.bar.web.enabled=true"), EndpointEnablement enablement = getEndpointEnablement("bar", false,
true, "found property endpoints.bar.web.enabled"); "endpoints.bar.web.enabled=true");
validate(enablement, true, "found property endpoints.bar.web.enabled");
} }
@Test @Test
public void generalEnabledOverrideWithAndAnnotationFlagAndSpecificJmxProperty() { public void generalEnabledOverrideWithAndAnnotationFlagAndSpecificJmxProperty() {
validate(determineEnablement("bar", false, "endpoints.bar.jmx.enabled=true"), EndpointEnablement enablement = getEndpointEnablement("bar", false,
true, "found property endpoints.bar.jmx.enabled"); "endpoints.bar.jmx.enabled=true");
validate(enablement, true, "found property endpoints.bar.jmx.enabled");
} }
@Test @Test
public void generalEnabledOverrideWithAndAnnotationFlagAndAnyProperty() { public void generalEnabledOverrideWithAndAnnotationFlagAndAnyProperty() {
validate( EndpointEnablement enablement = getEndpointEnablement("bar", false,
determineEnablement("bar", false, "endpoints.bar.web.enabled=false", "endpoints.bar.web.enabled=false", "endpoints.bar.jmx.enabled=true");
"endpoints.bar.jmx.enabled=true"), validate(enablement, true, "found property endpoints.bar.jmx.enabled");
true, "found property endpoints.bar.jmx.enabled");
} }
@Test @Test
public void specificEnabledByDefault() { public void specificEnabledByDefault() {
validate(determineEnablement("foo", true, EndpointType.JMX), true, EndpointEnablement enablement = getEndpointEnablement("foo", true,
"endpoint 'foo' (jmx) is enabled by default"); EndpointDelivery.JMX);
validate(enablement, true, "endpoint 'foo' (jmx) is enabled by default");
} }
@Test @Test
public void specificDisabledViaEndpointProperty() { public void specificDisabledViaEndpointProperty() {
validate( EndpointEnablement enablement = getEndpointEnablement("foo", true,
determineEnablement("foo", true, EndpointType.WEB, EndpointDelivery.WEB, "endpoints.foo.enabled=false");
"endpoints.foo.enabled=false"), validate(enablement, false, "found property endpoints.foo.enabled");
false, "found property endpoints.foo.enabled");
} }
@Test @Test
public void specificDisabledViaTechProperty() { public void specificDisabledViaTechProperty() {
validate( EndpointEnablement enablement = getEndpointEnablement("foo", true,
determineEnablement("foo", true, EndpointType.WEB, EndpointDelivery.WEB, "endpoints.foo.web.enabled=false");
"endpoints.foo.web.enabled=false"), validate(enablement, false, "found property endpoints.foo.web.enabled");
false, "found property endpoints.foo.web.enabled");
} }
@Test @Test
public void specificNotDisabledViaUnrelatedTechProperty() { public void specificNotDisabledViaUnrelatedTechProperty() {
validate( EndpointEnablement enablement = getEndpointEnablement("foo", true,
determineEnablement("foo", true, EndpointType.JMX, EndpointDelivery.JMX, "endpoints.foo.web.enabled=false");
"endpoints.foo.web.enabled=false"), validate(enablement, true, "endpoint 'foo' (jmx) is enabled by default");
true, "endpoint 'foo' (jmx) is enabled by default");
} }
@Test @Test
public void specificDisabledViaGeneralProperty() { public void specificDisabledViaGeneralProperty() {
validate( EndpointEnablement enablement = getEndpointEnablement("foo", true,
determineEnablement("foo", true, EndpointType.JMX, EndpointDelivery.JMX, "endpoints.default.enabled=false");
"endpoints.default.enabled=false"), validate(enablement, false, "found property endpoints.default.enabled");
false, "found property endpoints.default.enabled");
} }
@Test @Test
public void specificEnabledOverrideViaEndpointProperty() { public void specificEnabledOverrideViaEndpointProperty() {
validate( EndpointEnablement enablement = getEndpointEnablement("foo", true,
determineEnablement("foo", true, EndpointType.WEB, EndpointDelivery.WEB, "endpoints.default.enabled=false",
"endpoints.default.enabled=false", "endpoints.foo.enabled=true"), "endpoints.foo.enabled=true");
true, "found property endpoints.foo.enabled"); validate(enablement, true, "found property endpoints.foo.enabled");
} }
@Test @Test
public void specificEnabledOverrideViaTechProperty() { public void specificEnabledOverrideViaTechProperty() {
validate( EndpointEnablement enablement = getEndpointEnablement("foo", true,
determineEnablement("foo", true, EndpointType.WEB, EndpointDelivery.WEB, "endpoints.foo.enabled=false",
"endpoints.foo.enabled=false", "endpoints.foo.web.enabled=true"), "endpoints.foo.web.enabled=true");
true, "found property endpoints.foo.web.enabled"); validate(enablement, true, "found property endpoints.foo.web.enabled");
} }
@Test @Test
public void specificEnabledOverrideHasNotEffectWithUnrelatedTechProperty() { public void specificEnabledOverrideHasNotEffectWithUnrelatedTechProperty() {
validate( EndpointEnablement enablement = getEndpointEnablement("foo", true,
determineEnablement("foo", true, EndpointType.WEB, EndpointDelivery.WEB, "endpoints.foo.enabled=false",
"endpoints.foo.enabled=false", "endpoints.foo.jmx.enabled=true"), "endpoints.foo.jmx.enabled=true");
false, "found property endpoints.foo.enabled"); validate(enablement, false, "found property endpoints.foo.enabled");
} }
@Test @Test
public void specificEnabledOverrideViaGeneralWebProperty() { public void specificEnabledOverrideViaGeneralWebProperty() {
validate( EndpointEnablement enablement = getEndpointEnablement("foo", true,
determineEnablement("foo", true, EndpointType.WEB, EndpointDelivery.WEB, "endpoints.default.enabled=false",
"endpoints.default.enabled=false", "endpoints.default.web.enabled=true"), "endpoints.default.web.enabled=true");
true, "found property endpoints.default.web.enabled"); validate(enablement, true, "found property endpoints.default.web.enabled");
} }
@Test @Test
public void specificEnabledOverrideHasNoEffectWithUnrelatedTechProperty() { public void specificEnabledOverrideHasNoEffectWithUnrelatedTechProperty() {
validate( validate(
determineEnablement("foo", true, EndpointType.JMX, getEndpointEnablement("foo", true, EndpointDelivery.JMX,
"endpoints.default.enabled=false", "endpoints.default.web.enabled=true"), "endpoints.default.enabled=false", "endpoints.default.web.enabled=true"),
false, "found property endpoints.default.enabled"); false, "found property endpoints.default.enabled");
} }
@Test @Test
public void specificDisabledWithEndpointPropertyEvenWithEnabledGeneralProperties() { public void specificDisabledWithEndpointPropertyEvenWithEnabledGeneralProperties() {
validate( EndpointEnablement enablement = getEndpointEnablement("foo", true,
determineEnablement("foo", true, EndpointType.WEB, EndpointDelivery.WEB, "endpoints.default.enabled=true",
"endpoints.default.enabled=true", "endpoints.default.web.enabled=true", "endpoints.default.web.enabled=true", "endpoints.default.jmx.enabled=true",
"endpoints.default.jmx.enabled=true", "endpoints.foo.enabled=false"), "endpoints.foo.enabled=false");
false, "found property endpoints.foo.enabled"); validate(enablement, false, "found property endpoints.foo.enabled");
} }
@Test @Test
public void specificDisabledWithTechPropertyEvenWithEnabledGeneralProperties() { public void specificDisabledWithTechPropertyEvenWithEnabledGeneralProperties() {
validate( EndpointEnablement enablement = getEndpointEnablement("foo", true,
determineEnablement("foo", true, EndpointType.WEB, EndpointDelivery.WEB, "endpoints.default.enabled=true",
"endpoints.default.enabled=true", "endpoints.default.web.enabled=true", "endpoints.default.web.enabled=true", "endpoints.default.jmx.enabled=true",
"endpoints.default.jmx.enabled=true", "endpoints.foo.enabled=true", "endpoints.foo.enabled=true", "endpoints.foo.web.enabled=false");
"endpoints.foo.web.enabled=false"), validate(enablement, false, "found property endpoints.foo.web.enabled");
false, "found property endpoints.foo.web.enabled");
} }
@Test @Test
public void specificDisabledByDefaultWithAnnotationFlag() { public void specificDisabledByDefaultWithAnnotationFlag() {
validate(determineEnablement("bar", false, EndpointType.WEB), false, EndpointEnablement enablement = getEndpointEnablement("bar", false,
"endpoint 'bar' (web) is disabled by default"); EndpointDelivery.WEB);
validate(enablement, false, "endpoint 'bar' (web) is disabled by default");
} }
@Test @Test
public void specificDisabledByDefaultWithAnnotationFlagEvenWithGeneralProperty() { public void specificDisabledByDefaultWithAnnotationFlagEvenWithGeneralProperty() {
validate( EndpointEnablement enablement = getEndpointEnablement("bar", false,
determineEnablement("bar", false, EndpointType.WEB, EndpointDelivery.WEB, "endpoints.default.enabled=true");
"endpoints.default.enabled=true"), validate(enablement, false, "endpoint 'bar' (web) is disabled by default");
false, "endpoint 'bar' (web) is disabled by default");
} }
@Test @Test
public void specificDisabledByDefaultWithAnnotationFlagEvenWithGeneralWebProperty() { public void specificDisabledByDefaultWithAnnotationFlagEvenWithGeneralWebProperty() {
validate( EndpointEnablement enablement = getEndpointEnablement("bar", false,
determineEnablement("bar", false, EndpointType.WEB, EndpointDelivery.WEB, "endpoints.default.web.enabled=true");
"endpoints.default.web.enabled=true"), validate(enablement, false, "endpoint 'bar' (web) is disabled by default");
false, "endpoint 'bar' (web) is disabled by default");
} }
@Test @Test
public void specificDisabledByDefaultWithAnnotationFlagEvenWithGeneralJmxProperty() { public void specificDisabledByDefaultWithAnnotationFlagEvenWithGeneralJmxProperty() {
validate( EndpointEnablement enablement = getEndpointEnablement("bar", false,
determineEnablement("bar", false, EndpointType.WEB, EndpointDelivery.WEB, "endpoints.default.jmx.enabled=true");
"endpoints.default.jmx.enabled=true"), validate(enablement, false, "endpoint 'bar' (web) is disabled by default");
false, "endpoint 'bar' (web) is disabled by default");
} }
@Test @Test
public void specificEnabledOverrideWithAndAnnotationFlagAndEndpointProperty() { public void specificEnabledOverrideWithAndAnnotationFlagAndEndpointProperty() {
validate( EndpointEnablement enablement = getEndpointEnablement("bar", false,
determineEnablement("bar", false, EndpointType.WEB, EndpointDelivery.WEB, "endpoints.bar.enabled=true");
"endpoints.bar.enabled=true"), validate(enablement, true, "found property endpoints.bar.enabled");
true, "found property endpoints.bar.enabled");
} }
@Test @Test
public void specificEnabledOverrideWithAndAnnotationFlagAndTechProperty() { public void specificEnabledOverrideWithAndAnnotationFlagAndTechProperty() {
validate( EndpointEnablement enablement = getEndpointEnablement("bar", false,
determineEnablement("bar", false, EndpointType.WEB, EndpointDelivery.WEB, "endpoints.bar.web.enabled=true");
"endpoints.bar.web.enabled=true"), validate(enablement, true, "found property endpoints.bar.web.enabled");
true, "found property endpoints.bar.web.enabled");
} }
@Test @Test
public void specificEnabledOverrideWithAndAnnotationFlagHasNoEffectWithUnrelatedTechProperty() { public void specificEnabledOverrideWithAndAnnotationFlagHasNoEffectWithUnrelatedTechProperty() {
validate( EndpointEnablement enablement = getEndpointEnablement("bar", false,
determineEnablement("bar", false, EndpointType.WEB, EndpointDelivery.WEB, "endpoints.bar.jmx.enabled=true");
"endpoints.bar.jmx.enabled=true"), validate(enablement, false, "endpoint 'bar' (web) is disabled by default");
false, "endpoint 'bar' (web) is disabled by default"); }
private EndpointEnablement getEndpointEnablement(String id, boolean enabledByDefault,
String... environment) {
return getEndpointEnablement(id, enabledByDefault, null, environment);
}
private EndpointEnablement getEndpointEnablement(String id, boolean enabledByDefault,
EndpointDelivery delivery, String... environment) {
MockEnvironment env = new MockEnvironment();
TestPropertyValues.of(environment).applyTo(env);
EndpointEnablementProvider provider = new EndpointEnablementProvider(env);
return provider.getEndpointEnablement(id, enabledByDefault, delivery);
} }
private void validate(EndpointEnablement enablement, boolean enabled, private void validate(EndpointEnablement enablement, boolean enabled,
@ -343,17 +349,4 @@ public class EndpointEnablementProviderTests {
} }
} }
private EndpointEnablement determineEnablement(String id, boolean enabledByDefault,
String... environment) {
return determineEnablement(id, enabledByDefault, null, environment);
}
private EndpointEnablement determineEnablement(String id, boolean enabledByDefault,
EndpointType type, String... environment) {
MockEnvironment env = new MockEnvironment();
TestPropertyValues.of(environment).applyTo(env);
EndpointEnablementProvider provider = new EndpointEnablementProvider(env);
return provider.getEndpointEnablement(id, enabledByDefault, type);
}
} }

View File

@ -22,7 +22,7 @@ import java.util.Map;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.endpoint.EndpointInfo; import org.springframework.boot.endpoint.EndpointInfo;
import org.springframework.boot.endpoint.EndpointOperationType; import org.springframework.boot.endpoint.OperationType;
import org.springframework.boot.endpoint.web.OperationRequestPredicate; import org.springframework.boot.endpoint.web.OperationRequestPredicate;
import org.springframework.boot.endpoint.web.WebEndpointHttpMethod; import org.springframework.boot.endpoint.web.WebEndpointHttpMethod;
import org.springframework.boot.endpoint.web.WebEndpointOperation; import org.springframework.boot.endpoint.web.WebEndpointOperation;
@ -136,7 +136,7 @@ public class RequestMappingEndpointTests {
WebEndpointHttpMethod.GET, Collections.singletonList("application/json"), WebEndpointHttpMethod.GET, Collections.singletonList("application/json"),
Collections.singletonList("application/json")); Collections.singletonList("application/json"));
WebEndpointOperation operation = new WebEndpointOperation( WebEndpointOperation operation = new WebEndpointOperation(
EndpointOperationType.READ, (arguments) -> "Invoked", true, OperationType.READ, (arguments) -> "Invoked", true,
requestPredicate, "test"); requestPredicate, "test");
WebEndpointServletHandlerMapping mapping = new WebEndpointServletHandlerMapping( WebEndpointServletHandlerMapping mapping = new WebEndpointServletHandlerMapping(
"application", Collections.singleton(new EndpointInfo<>("test", true, "application", Collections.singleton(new EndpointInfo<>("test", true,

View File

@ -19,10 +19,10 @@ package org.springframework.boot.endpoint;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Consumer; import java.util.function.Consumer;
@ -32,6 +32,7 @@ import org.springframework.context.ApplicationContext;
import org.springframework.core.MethodIntrospector; import org.springframework.core.MethodIntrospector;
import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.util.Assert;
import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
@ -45,7 +46,7 @@ import org.springframework.util.ObjectUtils;
* @author Stephane Nicoll * @author Stephane Nicoll
* @since 2.0.0 * @since 2.0.0
*/ */
public abstract class AnnotationEndpointDiscoverer<T extends EndpointOperation, K> public abstract class AnnotationEndpointDiscoverer<T extends Operation, K>
implements EndpointDiscoverer<T> { implements EndpointDiscoverer<T> {
private final ApplicationContext applicationContext; private final ApplicationContext applicationContext;
@ -69,15 +70,15 @@ public abstract class AnnotationEndpointDiscoverer<T extends EndpointOperation,
/** /**
* Perform endpoint discovery, including discovery and merging of extensions. * Perform endpoint discovery, including discovery and merging of extensions.
* @param extensionType the annotation type of the extension * @param extensionType the annotation type of the extension
* @param endpointType the {@link EndpointType} that should be considered * @param delivery the {@link EndpointDelivery} that should be considered
* @return the list of {@link EndpointInfo EndpointInfos} that describes the * @return the list of {@link EndpointInfo EndpointInfos} that describes the
* discovered endpoints matching the specified {@link EndpointType} * discovered endpoints matching the specified {@link EndpointDelivery}
*/ */
protected Collection<EndpointInfoDescriptor<T, K>> discoverEndpointsWithExtension( protected Collection<EndpointInfoDescriptor<T, K>> discoverEndpoints(
Class<? extends Annotation> extensionType, EndpointType endpointType) { Class<? extends Annotation> extensionType, EndpointDelivery delivery) {
Map<Class<?>, EndpointInfo<T>> endpoints = discoverGenericEndpoints(endpointType); Map<Class<?>, EndpointInfo<T>> endpoints = discoverEndpoints(delivery);
Map<Class<?>, EndpointExtensionInfo<T>> extensions = discoverExtensions(endpoints, Map<Class<?>, EndpointExtensionInfo<T>> extensions = discoverExtensions(endpoints,
extensionType, endpointType); extensionType, delivery);
Collection<EndpointInfoDescriptor<T, K>> result = new ArrayList<>(); Collection<EndpointInfoDescriptor<T, K>> result = new ArrayList<>();
endpoints.forEach((endpointClass, endpointInfo) -> { endpoints.forEach((endpointClass, endpointInfo) -> {
EndpointExtensionInfo<T> extension = extensions.remove(endpointClass); EndpointExtensionInfo<T> extension = extensions.remove(endpointClass);
@ -86,169 +87,156 @@ public abstract class AnnotationEndpointDiscoverer<T extends EndpointOperation,
return result; return result;
} }
private EndpointInfoDescriptor<T, K> createDescriptor(Class<?> endpointType, private Map<Class<?>, EndpointInfo<T>> discoverEndpoints(EndpointDelivery delivery) {
EndpointInfo<T> endpoint, EndpointExtensionInfo<T> extension) { String[] beanNames = this.applicationContext
Map<OperationKey<K>, List<T>> endpointOperations = indexOperations( .getBeanNamesForAnnotation(Endpoint.class);
endpoint.getId(), endpointType, endpoint.getOperations()); Map<Class<?>, EndpointInfo<T>> endpoints = new LinkedHashMap<>();
Map<String, EndpointInfo<T>> endpointsById = new LinkedHashMap<>();
for (String beanName : beanNames) {
Class<?> beanType = this.applicationContext.getType(beanName);
AnnotationAttributes attributes = AnnotatedElementUtils
.findMergedAnnotationAttributes(beanType, Endpoint.class, true, true);
if (isDeliveredOver(attributes, delivery)) {
EndpointInfo<T> info = createEndpointInfo(beanName, beanType, attributes);
EndpointInfo<T> previous = endpointsById.putIfAbsent(info.getId(), info);
Assert.state(previous == null, () -> "Found two endpoints with the id '"
+ info.getId() + "': " + info + " and " + previous);
endpoints.put(beanType, info);
}
}
return endpoints;
}
private EndpointInfo<T> createEndpointInfo(String beanName, Class<?> beanType,
AnnotationAttributes attributes) {
String id = attributes.getString("id");
boolean enabledByDefault = attributes.getBoolean("enabledByDefault");
Map<Method, T> operations = discoverOperations(id, beanName, beanType);
return new EndpointInfo<>(id, enabledByDefault, operations.values());
}
private Map<Class<?>, EndpointExtensionInfo<T>> discoverExtensions(
Map<Class<?>, EndpointInfo<T>> endpoints,
Class<? extends Annotation> extensionType, EndpointDelivery delivery) {
if (extensionType == null) {
return Collections.emptyMap();
}
String[] beanNames = this.applicationContext
.getBeanNamesForAnnotation(extensionType);
Map<Class<?>, EndpointExtensionInfo<T>> extensions = new HashMap<>();
for (String beanName : beanNames) {
Class<?> beanType = this.applicationContext.getType(beanName);
Class<?> endpointType = getEndpointType(extensionType, beanType);
AnnotationAttributes endpointAttributes = AnnotatedElementUtils
.getMergedAnnotationAttributes(endpointType, Endpoint.class);
Assert.state(isDeliveredOver(endpointAttributes, delivery),
"Invalid extension " + beanType.getName() + "': endpoint '"
+ endpointType.getName()
+ "' does not support such extension");
EndpointInfo<T> info = getEndpointInfo(endpoints, beanType, endpointType);
Map<Method, T> operations = discoverOperations(info.getId(), beanName,
beanType);
EndpointExtensionInfo<T> extension = new EndpointExtensionInfo<>(beanType,
operations.values());
EndpointExtensionInfo<T> previous = extensions.putIfAbsent(endpointType,
extension);
Assert.state(previous == null,
() -> "Found two extensions for the same endpoint '"
+ endpointType.getName() + "': "
+ extension.getExtensionType().getName() + " and "
+ previous.getExtensionType().getName());
}
return extensions;
}
private EndpointInfo<T> getEndpointInfo(Map<Class<?>, EndpointInfo<T>> endpoints,
Class<?> beanType, Class<?> endpointClass) {
EndpointInfo<T> endpoint = endpoints.get(endpointClass);
Assert.state(endpoint != null, "Invalid extension '" + beanType.getName()
+ "': no endpoint found with type '" + endpointClass.getName() + "'");
return endpoint;
}
private Class<?> getEndpointType(Class<? extends Annotation> extensionType,
Class<?> beanType) {
AnnotationAttributes attributes = AnnotatedElementUtils
.getMergedAnnotationAttributes(beanType, extensionType);
return (Class<?>) attributes.get("endpoint");
}
private EndpointInfoDescriptor<T, K> createDescriptor(Class<?> type,
EndpointInfo<T> info, EndpointExtensionInfo<T> extension) {
Map<OperationKey<K>, List<T>> operations = indexOperations(info.getId(), type,
info.getOperations());
if (extension != null) { if (extension != null) {
endpointOperations.putAll(indexOperations(endpoint.getId(), operations.putAll(indexOperations(info.getId(), extension.getExtensionType(),
extension.getEndpointExtensionType(), extension.getOperations())); extension.getOperations()));
return new EndpointInfoDescriptor<>(mergeEndpoint(endpoint, extension), return new EndpointInfoDescriptor<>(mergeEndpoint(info, extension),
endpointOperations); operations);
}
else {
return new EndpointInfoDescriptor<>(endpoint, endpointOperations);
} }
return new EndpointInfoDescriptor<>(info, operations);
} }
private EndpointInfo<T> mergeEndpoint(EndpointInfo<T> endpoint, private EndpointInfo<T> mergeEndpoint(EndpointInfo<T> endpoint,
EndpointExtensionInfo<T> extension) { EndpointExtensionInfo<T> extension) {
Map<K, T> operations = new HashMap<>(); Map<K, T> operations = new HashMap<>();
Consumer<T> operationConsumer = (operation) -> operations Consumer<T> consumer = (operation) -> operations
.put(this.operationKeyFactory.apply(operation), operation); .put(this.operationKeyFactory.apply(operation), operation);
endpoint.getOperations().forEach(operationConsumer); endpoint.getOperations().forEach(consumer);
extension.getOperations().forEach(operationConsumer); extension.getOperations().forEach(consumer);
return new EndpointInfo<>(endpoint.getId(), endpoint.isEnabledByDefault(), return new EndpointInfo<>(endpoint.getId(), endpoint.isEnabledByDefault(),
operations.values()); operations.values());
} }
private Map<OperationKey<K>, List<T>> indexOperations(String endpointId, private Map<OperationKey<K>, List<T>> indexOperations(String endpointId,
Class<?> target, Collection<T> operations) { Class<?> target, Collection<T> operations) {
LinkedMultiValueMap<OperationKey<K>, T> operationByKey = new LinkedMultiValueMap<>(); LinkedMultiValueMap<OperationKey<K>, T> result = new LinkedMultiValueMap<>();
operations operations.forEach((operation) -> {
.forEach( K key = this.operationKeyFactory.apply(operation);
(operation) -> operationByKey.add( result.add(new OperationKey<>(endpointId, target, key), operation);
new OperationKey<>(endpointId, target, });
this.operationKeyFactory.apply(operation)), return result;
operation));
return operationByKey;
} }
private Map<Class<?>, EndpointInfo<T>> discoverGenericEndpoints( private boolean isDeliveredOver(AnnotationAttributes attributes,
EndpointType endpointType) { EndpointDelivery delivery) {
String[] endpointBeanNames = this.applicationContext if (delivery == null) {
.getBeanNamesForAnnotation(Endpoint.class);
Map<String, EndpointInfo<T>> endpointsById = new HashMap<>();
Map<Class<?>, EndpointInfo<T>> endpointsByClass = new HashMap<>();
for (String beanName : endpointBeanNames) {
Class<?> beanType = this.applicationContext.getType(beanName);
AnnotationAttributes endpointAttributes = AnnotatedElementUtils
.findMergedAnnotationAttributes(beanType, Endpoint.class, true, true);
String endpointId = endpointAttributes.getString("id");
if (matchEndpointType(endpointAttributes, endpointType)) {
EndpointInfo<T> endpointInfo = createEndpointInfo(endpointsById, beanName,
beanType, endpointAttributes, endpointId);
endpointsByClass.put(beanType, endpointInfo);
}
}
return endpointsByClass;
}
private EndpointInfo<T> createEndpointInfo(Map<String, EndpointInfo<T>> endpointsById,
String beanName, Class<?> beanType, AnnotationAttributes endpointAttributes,
String endpointId) {
Map<Method, T> operationMethods = discoverOperations(endpointId, beanName,
beanType);
EndpointInfo<T> endpointInfo = new EndpointInfo<>(endpointId,
endpointAttributes.getBoolean("enabledByDefault"),
operationMethods.values());
EndpointInfo<T> previous = endpointsById.putIfAbsent(endpointInfo.getId(),
endpointInfo);
if (previous != null) {
throw new IllegalStateException("Found two endpoints with the id '"
+ endpointInfo.getId() + "': " + endpointInfo + " and " + previous);
}
return endpointInfo;
}
private Map<Class<?>, EndpointExtensionInfo<T>> discoverExtensions(
Map<Class<?>, EndpointInfo<T>> endpoints,
Class<? extends Annotation> extensionType, EndpointType endpointType) {
if (extensionType == null) {
return Collections.emptyMap();
}
String[] extensionBeanNames = this.applicationContext
.getBeanNamesForAnnotation(extensionType);
Map<Class<?>, EndpointExtensionInfo<T>> extensionsByEndpoint = new HashMap<>();
for (String beanName : extensionBeanNames) {
Class<?> beanType = this.applicationContext.getType(beanName);
AnnotationAttributes extensionAttributes = AnnotatedElementUtils
.getMergedAnnotationAttributes(beanType, extensionType);
Class<?> endpointClass = (Class<?>) extensionAttributes.get("endpoint");
AnnotationAttributes endpointAttributes = AnnotatedElementUtils
.getMergedAnnotationAttributes(endpointClass, Endpoint.class);
if (!matchEndpointType(endpointAttributes, endpointType)) {
throw new IllegalStateException(String.format(
"Invalid extension %s': "
+ "endpoint '%s' does not support such extension",
beanType.getName(), endpointClass.getName()));
}
EndpointInfo<T> endpoint = endpoints.get(endpointClass);
if (endpoint == null) {
throw new IllegalStateException(String.format(
"Invalid extension '%s': no endpoint found with type '%s'",
beanType.getName(), endpointClass.getName()));
}
Map<Method, T> operationMethods = discoverOperations(endpoint.getId(),
beanName, beanType);
EndpointExtensionInfo<T> extension = new EndpointExtensionInfo<>(beanType,
operationMethods.values());
EndpointExtensionInfo<T> previous = extensionsByEndpoint
.putIfAbsent(endpointClass, extension);
if (previous != null) {
throw new IllegalStateException(
"Found two extensions for the same endpoint '"
+ endpointClass.getName() + "': "
+ extension.getEndpointExtensionType().getName() + " and "
+ previous.getEndpointExtensionType().getName());
}
}
return extensionsByEndpoint;
}
private boolean matchEndpointType(AnnotationAttributes endpointAttributes,
EndpointType endpointType) {
if (endpointType == null) {
return true; return true;
} }
Object types = endpointAttributes.get("types"); EndpointDelivery[] supported = (EndpointDelivery[]) attributes.get("delivery");
if (ObjectUtils.isEmpty(types)) { return ObjectUtils.isEmpty(supported)
return true; || ObjectUtils.containsElement(supported, delivery);
}
return Arrays.stream((EndpointType[]) types)
.anyMatch((t) -> t.equals(endpointType));
} }
private Map<Method, T> discoverOperations(String endpointId, String beanName, private Map<Method, T> discoverOperations(String id, String name, Class<?> type) {
Class<?> beanType) { return MethodIntrospector.selectMethods(type,
return MethodIntrospector.selectMethods(beanType,
(MethodIntrospector.MetadataLookup<T>) ( (MethodIntrospector.MetadataLookup<T>) (
method) -> createOperationIfPossible(endpointId, beanName, method) -> createOperationIfPossible(id, name, method));
method));
} }
private T createOperationIfPossible(String endpointId, String beanName, private T createOperationIfPossible(String endpointId, String beanName,
Method method) { Method method) {
T readOperation = createReadOperationIfPossible(endpointId, beanName, method); T readOperation = createReadOperationIfPossible(endpointId, beanName, method);
return readOperation != null ? readOperation return (readOperation != null ? readOperation
: createWriteOperationIfPossible(endpointId, beanName, method); : createWriteOperationIfPossible(endpointId, beanName, method));
} }
private T createReadOperationIfPossible(String endpointId, String beanName, private T createReadOperationIfPossible(String endpointId, String beanName,
Method method) { Method method) {
return createOperationIfPossible(endpointId, beanName, method, return createOperationIfPossible(endpointId, beanName, method,
ReadOperation.class, EndpointOperationType.READ); ReadOperation.class, OperationType.READ);
} }
private T createWriteOperationIfPossible(String endpointId, String beanName, private T createWriteOperationIfPossible(String endpointId, String beanName,
Method method) { Method method) {
return createOperationIfPossible(endpointId, beanName, method, return createOperationIfPossible(endpointId, beanName, method,
WriteOperation.class, EndpointOperationType.WRITE); WriteOperation.class, OperationType.WRITE);
} }
private T createOperationIfPossible(String endpointId, String beanName, Method method, private T createOperationIfPossible(String endpointId, String beanName, Method method,
Class<? extends Annotation> operationAnnotation, Class<? extends Annotation> operationAnnotation,
EndpointOperationType operationType) { OperationType operationType) {
AnnotationAttributes operationAttributes = AnnotatedElementUtils AnnotationAttributes operationAttributes = AnnotatedElementUtils
.getMergedAnnotationAttributes(method, operationAnnotation); .getMergedAnnotationAttributes(method, operationAnnotation);
if (operationAttributes == null) { if (operationAttributes == null) {
@ -262,9 +250,9 @@ public abstract class AnnotationEndpointDiscoverer<T extends EndpointOperation,
} }
private long determineTimeToLive(CachingConfiguration cachingConfiguration, private long determineTimeToLive(CachingConfiguration cachingConfiguration,
EndpointOperationType operationType, Method method) { OperationType operationType, Method method) {
if (cachingConfiguration != null && cachingConfiguration.getTimeToLive() > 0 if (cachingConfiguration != null && cachingConfiguration.getTimeToLive() > 0
&& operationType == EndpointOperationType.READ && operationType == OperationType.READ
&& method.getParameters().length == 0) { && method.getParameters().length == 0) {
return cachingConfiguration.getTimeToLive(); return cachingConfiguration.getTimeToLive();
} }
@ -272,13 +260,13 @@ public abstract class AnnotationEndpointDiscoverer<T extends EndpointOperation,
} }
/** /**
* An {@code EndpointOperationFactory} creates an {@link EndpointOperation} for an * An {@code EndpointOperationFactory} creates an {@link Operation} for an operation
* operation on an endpoint. * on an endpoint.
* *
* @param <T> the {@link EndpointOperation} type * @param <T> the {@link Operation} type
*/ */
@FunctionalInterface @FunctionalInterface
protected interface EndpointOperationFactory<T extends EndpointOperation> { protected interface EndpointOperationFactory<T extends Operation> {
/** /**
* Creates an {@code EndpointOperation} for an operation on an endpoint. * Creates an {@code EndpointOperation} for an operation on an endpoint.
@ -291,8 +279,8 @@ public abstract class AnnotationEndpointDiscoverer<T extends EndpointOperation,
* @return the operation info that describes the operation * @return the operation info that describes the operation
*/ */
T createOperation(String endpointId, AnnotationAttributes operationAttributes, T createOperation(String endpointId, AnnotationAttributes operationAttributes,
Object target, Method operationMethod, Object target, Method operationMethod, OperationType operationType,
EndpointOperationType operationType, long timeToLive); long timeToLive);
} }
@ -300,20 +288,19 @@ public abstract class AnnotationEndpointDiscoverer<T extends EndpointOperation,
* Describes a tech-specific extension of an endpoint. * Describes a tech-specific extension of an endpoint.
* @param <T> the type of the operation * @param <T> the type of the operation
*/ */
private static final class EndpointExtensionInfo<T extends EndpointOperation> { private static final class EndpointExtensionInfo<T extends Operation> {
private final Class<?> endpointExtensionType; private final Class<?> extensionType;
private final Collection<T> operations; private final Collection<T> operations;
private EndpointExtensionInfo(Class<?> endpointExtensionType, private EndpointExtensionInfo(Class<?> extensionType, Collection<T> operations) {
Collection<T> operations) { this.extensionType = extensionType;
this.endpointExtensionType = endpointExtensionType;
this.operations = operations; this.operations = operations;
} }
private Class<?> getEndpointExtensionType() { private Class<?> getExtensionType() {
return this.endpointExtensionType; return this.extensionType;
} }
private Collection<T> getOperations() { private Collection<T> getOperations() {
@ -328,7 +315,7 @@ public abstract class AnnotationEndpointDiscoverer<T extends EndpointOperation,
* @param <T> the type of the operation * @param <T> the type of the operation
* @param <K> the type of the operation key * @param <K> the type of the operation key
*/ */
protected static class EndpointInfoDescriptor<T extends EndpointOperation, K> { protected static class EndpointInfoDescriptor<T extends Operation, K> {
private final EndpointInfo<T> endpointInfo; private final EndpointInfo<T> endpointInfo;
@ -377,22 +364,18 @@ public abstract class AnnotationEndpointDiscoverer<T extends EndpointOperation,
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) { if (o == this) {
return true; return true;
} }
if (o == null || getClass() != o.getClass()) { if (o == null || getClass() != o.getClass()) {
return false; return false;
} }
OperationKey<?> other = (OperationKey<?>) o;
OperationKey<?> that = (OperationKey<?>) o; Boolean result = true;
result = result && this.endpointId.equals(other.endpointId);
if (!this.endpointId.equals(that.endpointId)) { result = result && this.endpointType.equals(other.endpointType);
return false; result = result && this.key.equals(other.key);
} return result;
if (!this.endpointType.equals(that.endpointType)) {
return false;
}
return this.key.equals(that.key);
} }
@Override @Override

View File

@ -17,8 +17,8 @@
package org.springframework.boot.endpoint; package org.springframework.boot.endpoint;
/** /**
* {@link EndpointPathResolver} implementation that does not support * {@link EndpointPathResolver} implementation that does not support resolving endpoint
* resolving endpoint paths. * paths.
* *
* @author Madhura Bhave * @author Madhura Bhave
* @since 2.0.0 * @since 2.0.0
@ -31,4 +31,3 @@ public class DefaultEndpointPathResolver implements EndpointPathResolver {
} }
} }

View File

@ -41,11 +41,11 @@ public @interface Endpoint {
String id(); String id();
/** /**
* Defines the endpoint {@link EndpointType types} that should be exposed. By default, * Defines the {@link EndpointDelivery delivery technologies} over which the
* all types are exposed. * endpoint should be delivered over. By default, all technologies are supported.
* @return the endpoint types to expose * @return the supported endpoint delivery technologies
*/ */
EndpointType[] types() default {}; EndpointDelivery[] delivery() default {};
/** /**
* Whether or not the endpoint is enabled by default. * Whether or not the endpoint is enabled by default.

View File

@ -17,12 +17,12 @@
package org.springframework.boot.endpoint; package org.springframework.boot.endpoint;
/** /**
* An enumeration of the available {@link Endpoint} types. * An enumeration of the available {@link Endpoint} delivery technologies.
* *
* @author Stephane Nicoll * @author Stephane Nicoll
* @since 2.0.0 * @since 2.0.0
*/ */
public enum EndpointType { public enum EndpointDelivery {
/** /**
* Expose the endpoint as a JMX MBean. * Expose the endpoint as a JMX MBean.
@ -36,11 +36,12 @@ public enum EndpointType {
private final boolean enabledByDefault; private final boolean enabledByDefault;
EndpointType(boolean enabledByDefault) { EndpointDelivery(boolean enabledByDefault) {
this.enabledByDefault = enabledByDefault; this.enabledByDefault = enabledByDefault;
} }
public boolean isEnabledByDefault() { public boolean isEnabledByDefault() {
return this.enabledByDefault; return this.enabledByDefault;
} }
} }

View File

@ -27,7 +27,7 @@ import java.util.Collection;
* @since 2.0.0 * @since 2.0.0
*/ */
@FunctionalInterface @FunctionalInterface
public interface EndpointDiscoverer<T extends EndpointOperation> { public interface EndpointDiscoverer<T extends Operation> {
/** /**
* Perform endpoint discovery. * Perform endpoint discovery.

View File

@ -25,7 +25,7 @@ import java.util.Collection;
* @author Andy Wilkinson * @author Andy Wilkinson
* @since 2.0.0 * @since 2.0.0
*/ */
public class EndpointInfo<T extends EndpointOperation> { public class EndpointInfo<T extends Operation> {
private final String id; private final String id;

View File

@ -22,11 +22,11 @@ package org.springframework.boot.endpoint;
* @author Andy Wilkinson * @author Andy Wilkinson
* @since 2.0.0 * @since 2.0.0
*/ */
public class EndpointOperation { public class Operation {
private final EndpointOperationType type; private final OperationType type;
private final OperationInvoker operationInvoker; private final OperationInvoker invoker;
private final boolean blocking; private final boolean blocking;
@ -37,18 +37,18 @@ public class EndpointOperation {
* @param operationInvoker used to perform the operation * @param operationInvoker used to perform the operation
* @param blocking whether or not this is a blocking operation * @param blocking whether or not this is a blocking operation
*/ */
public EndpointOperation(EndpointOperationType type, public Operation(OperationType type,
OperationInvoker operationInvoker, boolean blocking) { OperationInvoker operationInvoker, boolean blocking) {
this.type = type; this.type = type;
this.operationInvoker = operationInvoker; this.invoker = operationInvoker;
this.blocking = blocking; this.blocking = blocking;
} }
/** /**
* Returns the {@link EndpointOperationType type} of the operation. * Returns the {@link OperationType type} of the operation.
* @return the type * @return the type
*/ */
public EndpointOperationType getType() { public OperationType getType() {
return this.type; return this.type;
} }
@ -57,8 +57,8 @@ public class EndpointOperation {
* operation. * operation.
* @return the operation invoker * @return the operation invoker
*/ */
public OperationInvoker getOperationInvoker() { public OperationInvoker getInvoker() {
return this.operationInvoker; return this.invoker;
} }
/** /**

View File

@ -22,7 +22,7 @@ package org.springframework.boot.endpoint;
* @author Andy Wilkinson * @author Andy Wilkinson
* @since 2.0.0 * @since 2.0.0
*/ */
public enum EndpointOperationType { public enum OperationType {
/** /**
* A read operation. * A read operation.

View File

@ -47,7 +47,6 @@ public class ParameterMappingException extends RuntimeException {
/** /**
* Returns the input that was to be mapped. * Returns the input that was to be mapped.
*
* @return the input * @return the input
*/ */
public Object getInput() { public Object getInput() {
@ -56,7 +55,6 @@ public class ParameterMappingException extends RuntimeException {
/** /**
* Returns the type to be mapped to. * Returns the type to be mapped to.
*
* @return the type * @return the type
*/ */
public Class<?> getType() { public Class<?> getType() {

View File

@ -42,7 +42,6 @@ public class ReflectiveOperationInvoker implements OperationInvoker {
* Creates a new {code ReflectiveOperationInvoker} that will invoke the given * Creates a new {code ReflectiveOperationInvoker} that will invoke the given
* {@code method} on the given {@code target}. The given {@code parameterMapper} will * {@code method} on the given {@code target}. The given {@code parameterMapper} will
* be used to map parameters to the required types. * be used to map parameters to the required types.
*
* @param parameterMapper the parameter mapper * @param parameterMapper the parameter mapper
* @param target the target of the reflective call * @param target the target of the reflective call
* @param method the method to call * @param method the method to call

View File

@ -74,16 +74,12 @@ public class EndpointMBean implements DynamicMBean {
@Override @Override
public Object invoke(String actionName, Object[] params, String[] signature) public Object invoke(String actionName, Object[] params, String[] signature)
throws MBeanException, ReflectionException { throws MBeanException, ReflectionException {
JmxEndpointOperation operationInfo = this.endpointInfo.getOperations() JmxEndpointOperation operation = this.endpointInfo.getOperations()
.get(actionName); .get(actionName);
if (operationInfo != null) { if (operation != null) {
Map<String, Object> arguments = new HashMap<>(); Map<String, Object> arguments = getArguments(params,
List<JmxEndpointOperationParameterInfo> parameters = operationInfo operation.getParameters());
.getParameters(); Object result = operation.getInvoker().invoke(arguments);
for (int i = 0; i < params.length; i++) {
arguments.put(parameters.get(i).getName(), params[i]);
}
Object result = operationInfo.getOperationInvoker().invoke(arguments);
if (REACTOR_PRESENT) { if (REACTOR_PRESENT) {
result = ReactiveHandler.handle(result); result = ReactiveHandler.handle(result);
} }
@ -94,6 +90,15 @@ public class EndpointMBean implements DynamicMBean {
this.endpointInfo.getEndpointId(), actionName))); this.endpointInfo.getEndpointId(), actionName)));
} }
private Map<String, Object> getArguments(Object[] params,
List<JmxEndpointOperationParameterInfo> parameters) {
Map<String, Object> arguments = new HashMap<>();
for (int i = 0; i < params.length; i++) {
arguments.put(parameters.get(i).getName(), params[i]);
}
return arguments;
}
@Override @Override
public Object getAttribute(String attribute) public Object getAttribute(String attribute)
throws AttributeNotFoundException, MBeanException, ReflectionException { throws AttributeNotFoundException, MBeanException, ReflectionException {

View File

@ -21,11 +21,11 @@ import java.util.Map;
import javax.management.MBeanInfo; import javax.management.MBeanInfo;
import org.springframework.boot.endpoint.EndpointInfo; import org.springframework.boot.endpoint.EndpointInfo;
import org.springframework.boot.endpoint.EndpointOperation; import org.springframework.boot.endpoint.Operation;
/** /**
* The {@link MBeanInfo} for a particular {@link EndpointInfo endpoint}. Maps operation * The {@link MBeanInfo} for a particular {@link EndpointInfo endpoint}. Maps operation
* names to an {@link EndpointOperation}. * names to an {@link Operation}.
* *
* @author Stephane Nicoll * @author Stephane Nicoll
* @since 2.0.0 * @since 2.0.0

View File

@ -31,7 +31,7 @@ import javax.management.modelmbean.ModelMBeanNotificationInfo;
import javax.management.modelmbean.ModelMBeanOperationInfo; import javax.management.modelmbean.ModelMBeanOperationInfo;
import org.springframework.boot.endpoint.EndpointInfo; import org.springframework.boot.endpoint.EndpointInfo;
import org.springframework.boot.endpoint.EndpointOperationType; import org.springframework.boot.endpoint.OperationType;
/** /**
* Gathers the management operations of a particular {@link EndpointInfo endpoint}. * Gathers the management operations of a particular {@link EndpointInfo endpoint}.
@ -61,7 +61,6 @@ class EndpointMBeanInfoAssembler {
.toArray(new ModelMBeanOperationInfo[] {}); .toArray(new ModelMBeanOperationInfo[] {});
Map<String, JmxEndpointOperation> operationsInfo = new LinkedHashMap<>(); Map<String, JmxEndpointOperation> operationsInfo = new LinkedHashMap<>();
operationsMapping.forEach((name, t) -> operationsInfo.put(name, t.operation)); operationsMapping.forEach((name, t) -> operationsInfo.put(name, t.operation));
MBeanInfo info = new ModelMBeanInfoSupport(EndpointMBean.class.getName(), MBeanInfo info = new ModelMBeanInfoSupport(EndpointMBean.class.getName(),
getDescription(endpointInfo), new ModelMBeanAttributeInfo[0], getDescription(endpointInfo), new ModelMBeanAttributeInfo[0],
new ModelMBeanConstructorInfo[0], operationsMBeanInfo, new ModelMBeanConstructorInfo[0], operationsMBeanInfo,
@ -100,11 +99,11 @@ class EndpointMBeanInfoAssembler {
.toArray(new MBeanParameterInfo[parameterInfos.size()]))); .toArray(new MBeanParameterInfo[parameterInfos.size()])));
} }
private int mapOperationType(EndpointOperationType type) { private int mapOperationType(OperationType type) {
if (type == EndpointOperationType.READ) { if (type == OperationType.READ) {
return MBeanOperationInfo.INFO; return MBeanOperationInfo.INFO;
} }
if (type == EndpointOperationType.WRITE) { if (type == OperationType.WRITE) {
return MBeanOperationInfo.ACTION; return MBeanOperationInfo.ACTION;
} }
return MBeanOperationInfo.UNKNOWN; return MBeanOperationInfo.UNKNOWN;

View File

@ -68,24 +68,21 @@ public class EndpointMBeanRegistrar {
Assert.notNull(endpoint, "Endpoint must not be null"); Assert.notNull(endpoint, "Endpoint must not be null");
try { try {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug(String.format( logger.debug("Registering endpoint with id '" + endpoint.getEndpointId()
"Registering endpoint with id '%s' to " + "the JMX domain", + "' to the JMX domain");
endpoint.getEndpointId()));
} }
ObjectName objectName = this.objectNameFactory.generate(endpoint); ObjectName objectName = this.objectNameFactory.generate(endpoint);
this.mBeanServer.registerMBean(endpoint, objectName); this.mBeanServer.registerMBean(endpoint, objectName);
return objectName; return objectName;
} }
catch (MalformedObjectNameException ex) { catch (MalformedObjectNameException ex) {
throw new IllegalStateException( throw new IllegalStateException("Invalid ObjectName for endpoint with id '"
String.format("Invalid ObjectName for " + "endpoint with id '%s'", + endpoint.getEndpointId() + "'", ex);
endpoint.getEndpointId()),
ex);
} }
catch (Exception ex) { catch (Exception ex) {
throw new MBeanExportException( throw new MBeanExportException(
String.format("Failed to register MBean for endpoint with id '%s'", "Failed to register MBean for endpoint with id '"
endpoint.getEndpointId()), + endpoint.getEndpointId() + "'",
ex); ex);
} }
} }
@ -99,8 +96,8 @@ public class EndpointMBeanRegistrar {
public boolean unregisterEndpointMbean(ObjectName objectName) { public boolean unregisterEndpointMbean(ObjectName objectName) {
try { try {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug(String.format("Unregister endpoint with ObjectName '%s' " logger.debug("Unregister endpoint with ObjectName '" + objectName + "' "
+ "from the JMX domain", objectName)); + "from the JMX domain");
} }
this.mBeanServer.unregisterMBean(objectName); this.mBeanServer.unregisterMBean(objectName);
return true; return true;
@ -110,8 +107,7 @@ public class EndpointMBeanRegistrar {
} }
catch (MBeanRegistrationException ex) { catch (MBeanRegistrationException ex) {
throw new JmxException( throw new JmxException(
String.format("Failed to unregister MBean with" + "ObjectName '%s'", "Failed to unregister MBean with ObjectName '" + objectName + "'",
objectName),
ex); ex);
} }
} }

View File

@ -20,6 +20,7 @@ import java.lang.reflect.Method;
import java.lang.reflect.Parameter; import java.lang.reflect.Parameter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.function.Function; import java.util.function.Function;
@ -30,11 +31,11 @@ import org.springframework.boot.endpoint.AnnotationEndpointDiscoverer;
import org.springframework.boot.endpoint.CachingConfiguration; import org.springframework.boot.endpoint.CachingConfiguration;
import org.springframework.boot.endpoint.CachingOperationInvoker; import org.springframework.boot.endpoint.CachingOperationInvoker;
import org.springframework.boot.endpoint.Endpoint; import org.springframework.boot.endpoint.Endpoint;
import org.springframework.boot.endpoint.EndpointDelivery;
import org.springframework.boot.endpoint.EndpointInfo; import org.springframework.boot.endpoint.EndpointInfo;
import org.springframework.boot.endpoint.EndpointOperationType;
import org.springframework.boot.endpoint.EndpointType;
import org.springframework.boot.endpoint.OperationInvoker; import org.springframework.boot.endpoint.OperationInvoker;
import org.springframework.boot.endpoint.OperationParameterMapper; import org.springframework.boot.endpoint.OperationParameterMapper;
import org.springframework.boot.endpoint.OperationType;
import org.springframework.boot.endpoint.ReflectiveOperationInvoker; import org.springframework.boot.endpoint.ReflectiveOperationInvoker;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.annotation.AnnotationAttributes;
@ -60,7 +61,6 @@ public class JmxAnnotationEndpointDiscoverer
* Creates a new {@link JmxAnnotationEndpointDiscoverer} that will discover * Creates a new {@link JmxAnnotationEndpointDiscoverer} that will discover
* {@link Endpoint endpoints} and {@link JmxEndpointExtension jmx extensions} using * {@link Endpoint endpoints} and {@link JmxEndpointExtension jmx extensions} using
* the given {@link ApplicationContext}. * the given {@link ApplicationContext}.
*
* @param applicationContext the application context * @param applicationContext the application context
* @param parameterMapper the {@link OperationParameterMapper} used to convert * @param parameterMapper the {@link OperationParameterMapper} used to convert
* arguments when an operation is invoked * arguments when an operation is invoked
@ -75,8 +75,8 @@ public class JmxAnnotationEndpointDiscoverer
@Override @Override
public Collection<EndpointInfo<JmxEndpointOperation>> discoverEndpoints() { public Collection<EndpointInfo<JmxEndpointOperation>> discoverEndpoints() {
Collection<EndpointInfoDescriptor<JmxEndpointOperation, String>> endpointDescriptors = discoverEndpointsWithExtension( Collection<EndpointInfoDescriptor<JmxEndpointOperation, String>> endpointDescriptors = discoverEndpoints(
JmxEndpointExtension.class, EndpointType.JMX); JmxEndpointExtension.class, EndpointDelivery.JMX);
verifyThatOperationsHaveDistinctName(endpointDescriptors); verifyThatOperationsHaveDistinctName(endpointDescriptors);
return endpointDescriptors.stream().map(EndpointInfoDescriptor::getEndpointInfo) return endpointDescriptors.stream().map(EndpointInfoDescriptor::getEndpointInfo)
.collect(Collectors.toList()); .collect(Collectors.toList());
@ -113,7 +113,7 @@ public class JmxAnnotationEndpointDiscoverer
@Override @Override
public JmxEndpointOperation createOperation(String endpointId, public JmxEndpointOperation createOperation(String endpointId,
AnnotationAttributes operationAttributes, Object target, Method method, AnnotationAttributes operationAttributes, Object target, Method method,
EndpointOperationType type, long timeToLive) { OperationType type, long timeToLive) {
String operationName = method.getName(); String operationName = method.getName();
Class<?> outputType = mapParameterType(method.getReturnType()); Class<?> outputType = mapParameterType(method.getReturnType());
String description = getDescription(method, String description = getDescription(method,
@ -139,29 +139,40 @@ public class JmxAnnotationEndpointDiscoverer
} }
private List<JmxEndpointOperationParameterInfo> getParameters(Method method) { private List<JmxEndpointOperationParameterInfo> getParameters(Method method) {
List<JmxEndpointOperationParameterInfo> parameters = new ArrayList<>();
Parameter[] methodParameters = method.getParameters(); Parameter[] methodParameters = method.getParameters();
if (methodParameters.length == 0) { if (methodParameters.length == 0) {
return parameters; return Collections.emptyList();
} }
ManagedOperationParameter[] managedOperationParameters = jmxAttributeSource ManagedOperationParameter[] managedOperationParameters = jmxAttributeSource
.getManagedOperationParameters(method); .getManagedOperationParameters(method);
if (managedOperationParameters.length > 0) { if (managedOperationParameters.length == 0) {
for (int i = 0; i < managedOperationParameters.length; i++) { return getParametersInfo(methodParameters);
ManagedOperationParameter mBeanParameter = managedOperationParameters[i];
Parameter methodParameter = methodParameters[i];
parameters.add(new JmxEndpointOperationParameterInfo(
mBeanParameter.getName(),
mapParameterType(methodParameter.getType()),
mBeanParameter.getDescription()));
}
} }
else { return getParametersInfo(methodParameters, managedOperationParameters);
for (Parameter parameter : methodParameters) { }
parameters.add(
new JmxEndpointOperationParameterInfo(parameter.getName(), private List<JmxEndpointOperationParameterInfo> getParametersInfo(
mapParameterType(parameter.getType()), null)); Parameter[] methodParameters) {
} List<JmxEndpointOperationParameterInfo> parameters = new ArrayList<>();
for (Parameter methodParameter : methodParameters) {
String name = methodParameter.getName();
Class<?> type = mapParameterType(methodParameter.getType());
parameters.add(new JmxEndpointOperationParameterInfo(name, type, null));
}
return parameters;
}
private List<JmxEndpointOperationParameterInfo> getParametersInfo(
Parameter[] methodParameters,
ManagedOperationParameter[] managedOperationParameters) {
List<JmxEndpointOperationParameterInfo> parameters = new ArrayList<>();
for (int i = 0; i < managedOperationParameters.length; i++) {
ManagedOperationParameter managedOperationParameter = managedOperationParameters[i];
Parameter methodParameter = methodParameters[i];
parameters.add(new JmxEndpointOperationParameterInfo(
managedOperationParameter.getName(),
mapParameterType(methodParameter.getType()),
managedOperationParameter.getDescription()));
} }
return parameters; return parameters;
} }

View File

@ -51,11 +51,13 @@ public class JmxEndpointMBeanFactory {
*/ */
public Collection<EndpointMBean> createMBeans( public Collection<EndpointMBean> createMBeans(
Collection<EndpointInfo<JmxEndpointOperation>> endpoints) { Collection<EndpointInfo<JmxEndpointOperation>> endpoints) {
return endpoints.stream().map((endpointInfo) -> { return endpoints.stream().map(this::createMBean).collect(Collectors.toList());
EndpointMBeanInfo endpointMBeanInfo = this.assembler }
.createEndpointMBeanInfo(endpointInfo);
return new EndpointMBean(this.resultMapper::mapResponse, endpointMBeanInfo); private EndpointMBean createMBean(EndpointInfo<JmxEndpointOperation> endpointInfo) {
}).collect(Collectors.toList()); EndpointMBeanInfo endpointMBeanInfo = this.assembler
.createEndpointMBeanInfo(endpointInfo);
return new EndpointMBean(this.resultMapper::mapResponse, endpointMBeanInfo);
} }
} }

View File

@ -19,9 +19,9 @@ package org.springframework.boot.endpoint.jmx;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import org.springframework.boot.endpoint.EndpointOperation; import org.springframework.boot.endpoint.Operation;
import org.springframework.boot.endpoint.EndpointOperationType;
import org.springframework.boot.endpoint.OperationInvoker; import org.springframework.boot.endpoint.OperationInvoker;
import org.springframework.boot.endpoint.OperationType;
/** /**
* An operation on a JMX endpoint. * An operation on a JMX endpoint.
@ -30,7 +30,7 @@ import org.springframework.boot.endpoint.OperationInvoker;
* @author Andy Wilkinson * @author Andy Wilkinson
* @since 2.0.0 * @since 2.0.0
*/ */
public class JmxEndpointOperation extends EndpointOperation { public class JmxEndpointOperation extends Operation {
private final String operationName; private final String operationName;
@ -50,7 +50,7 @@ public class JmxEndpointOperation extends EndpointOperation {
* @param description the description of the operation * @param description the description of the operation
* @param parameters the parameters of the operation * @param parameters the parameters of the operation
*/ */
public JmxEndpointOperation(EndpointOperationType type, OperationInvoker invoker, public JmxEndpointOperation(OperationType type, OperationInvoker invoker,
String operationName, Class<?> outputType, String description, String operationName, Class<?> outputType, String description,
List<JmxEndpointOperationParameterInfo> parameters) { List<JmxEndpointOperationParameterInfo> parameters) {
super(type, invoker, true); super(type, invoker, true);

View File

@ -33,7 +33,6 @@ public class EndpointLinksResolver {
/** /**
* Resolves links to the operations of the given {code webEndpoints} based on a * Resolves links to the operations of the given {code webEndpoints} based on a
* request with the given {@code requestUrl}. * request with the given {@code requestUrl}.
*
* @param webEndpoints the web endpoints * @param webEndpoints the web endpoints
* @param requestUrl the url of the request for the endpoint links * @param requestUrl the url of the request for the endpoint links
* @return the links * @return the links

View File

@ -111,26 +111,16 @@ public class OperationRequestPredicate {
if (this == obj) { if (this == obj) {
return true; return true;
} }
if (obj == null) { if (obj == null || getClass() != obj.getClass()) {
return false;
}
if (getClass() != obj.getClass()) {
return false; return false;
} }
OperationRequestPredicate other = (OperationRequestPredicate) obj; OperationRequestPredicate other = (OperationRequestPredicate) obj;
if (!this.consumes.equals(other.consumes)) { boolean result = true;
return false; result = result && this.consumes.equals(other.consumes);
} result = result && this.httpMethod == other.httpMethod;
if (this.httpMethod != other.httpMethod) { result = result && this.canonicalPath.equals(other.canonicalPath);
return false; result = result && this.produces.equals(other.produces);
} return result;
if (!this.canonicalPath.equals(other.canonicalPath)) {
return false;
}
if (!this.produces.equals(other.produces)) {
return false;
}
return true;
} }
} }

View File

@ -31,11 +31,11 @@ import org.springframework.boot.endpoint.AnnotationEndpointDiscoverer;
import org.springframework.boot.endpoint.CachingConfiguration; import org.springframework.boot.endpoint.CachingConfiguration;
import org.springframework.boot.endpoint.CachingOperationInvoker; import org.springframework.boot.endpoint.CachingOperationInvoker;
import org.springframework.boot.endpoint.Endpoint; import org.springframework.boot.endpoint.Endpoint;
import org.springframework.boot.endpoint.EndpointDelivery;
import org.springframework.boot.endpoint.EndpointInfo; import org.springframework.boot.endpoint.EndpointInfo;
import org.springframework.boot.endpoint.EndpointOperationType;
import org.springframework.boot.endpoint.EndpointType;
import org.springframework.boot.endpoint.OperationInvoker; import org.springframework.boot.endpoint.OperationInvoker;
import org.springframework.boot.endpoint.OperationParameterMapper; import org.springframework.boot.endpoint.OperationParameterMapper;
import org.springframework.boot.endpoint.OperationType;
import org.springframework.boot.endpoint.ReflectiveOperationInvoker; import org.springframework.boot.endpoint.ReflectiveOperationInvoker;
import org.springframework.boot.endpoint.Selector; import org.springframework.boot.endpoint.Selector;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
@ -79,8 +79,8 @@ public class WebAnnotationEndpointDiscoverer extends
@Override @Override
public Collection<EndpointInfo<WebEndpointOperation>> discoverEndpoints() { public Collection<EndpointInfo<WebEndpointOperation>> discoverEndpoints() {
Collection<EndpointInfoDescriptor<WebEndpointOperation, OperationRequestPredicate>> endpoints = discoverEndpointsWithExtension( Collection<EndpointInfoDescriptor<WebEndpointOperation, OperationRequestPredicate>> endpoints = discoverEndpoints(
WebEndpointExtension.class, EndpointType.WEB); WebEndpointExtension.class, EndpointDelivery.WEB);
verifyThatOperationsHaveDistinctPredicates(endpoints); verifyThatOperationsHaveDistinctPredicates(endpoints);
return endpoints.stream().map(EndpointInfoDescriptor::getEndpointInfo) return endpoints.stream().map(EndpointInfoDescriptor::getEndpointInfo)
.collect(Collectors.toList()); .collect(Collectors.toList());
@ -129,7 +129,7 @@ public class WebAnnotationEndpointDiscoverer extends
@Override @Override
public WebEndpointOperation createOperation(String endpointId, public WebEndpointOperation createOperation(String endpointId,
AnnotationAttributes operationAttributes, Object target, Method method, AnnotationAttributes operationAttributes, Object target, Method method,
EndpointOperationType type, long timeToLive) { OperationType type, long timeToLive) {
WebEndpointHttpMethod httpMethod = determineHttpMethod(type); WebEndpointHttpMethod httpMethod = determineHttpMethod(type);
OperationRequestPredicate requestPredicate = new OperationRequestPredicate( OperationRequestPredicate requestPredicate = new OperationRequestPredicate(
determinePath(endpointId, method), httpMethod, determinePath(endpointId, method), httpMethod,
@ -201,9 +201,8 @@ public class WebAnnotationEndpointDiscoverer extends
(parameter) -> parameter.getAnnotation(Selector.class) == null); (parameter) -> parameter.getAnnotation(Selector.class) == null);
} }
private WebEndpointHttpMethod determineHttpMethod( private WebEndpointHttpMethod determineHttpMethod(OperationType operationType) {
EndpointOperationType operationType) { if (operationType == OperationType.WRITE) {
if (operationType == EndpointOperationType.WRITE) {
return WebEndpointHttpMethod.POST; return WebEndpointHttpMethod.POST;
} }
return WebEndpointHttpMethod.GET; return WebEndpointHttpMethod.GET;

View File

@ -16,9 +16,9 @@
package org.springframework.boot.endpoint.web; package org.springframework.boot.endpoint.web;
import org.springframework.boot.endpoint.EndpointOperation; import org.springframework.boot.endpoint.Operation;
import org.springframework.boot.endpoint.EndpointOperationType;
import org.springframework.boot.endpoint.OperationInvoker; import org.springframework.boot.endpoint.OperationInvoker;
import org.springframework.boot.endpoint.OperationType;
/** /**
* An operation on a web endpoint. * An operation on a web endpoint.
@ -26,7 +26,7 @@ import org.springframework.boot.endpoint.OperationInvoker;
* @author Andy Wilkinson * @author Andy Wilkinson
* @since 2.0.0 * @since 2.0.0
*/ */
public class WebEndpointOperation extends EndpointOperation { public class WebEndpointOperation extends Operation {
private final OperationRequestPredicate requestPredicate; private final OperationRequestPredicate requestPredicate;
@ -42,9 +42,8 @@ public class WebEndpointOperation extends EndpointOperation {
* @param requestPredicate the predicate for requests that can be handled by the * @param requestPredicate the predicate for requests that can be handled by the
* @param id the id of the operation, unique within its endpoint operation * @param id the id of the operation, unique within its endpoint operation
*/ */
public WebEndpointOperation(EndpointOperationType type, public WebEndpointOperation(OperationType type, OperationInvoker operationInvoker,
OperationInvoker operationInvoker, boolean blocking, boolean blocking, OperationRequestPredicate requestPredicate, String id) {
OperationRequestPredicate requestPredicate, String id) {
super(type, operationInvoker, blocking); super(type, operationInvoker, blocking);
this.requestPredicate = requestPredicate; this.requestPredicate = requestPredicate;
this.id = id; this.id = id;

View File

@ -80,7 +80,7 @@ public class JerseyEndpointResourceFactory {
resourceBuilder.addMethod(requestPredicate.getHttpMethod().name()) resourceBuilder.addMethod(requestPredicate.getHttpMethod().name())
.consumes(toStringArray(requestPredicate.getConsumes())) .consumes(toStringArray(requestPredicate.getConsumes()))
.produces(toStringArray(requestPredicate.getProduces())) .produces(toStringArray(requestPredicate.getProduces()))
.handledBy(new EndpointInvokingInflector(operation.getOperationInvoker(), .handledBy(new EndpointInvokingInflector(operation.getInvoker(),
!requestPredicate.getConsumes().isEmpty())); !requestPredicate.getConsumes().isEmpty()));
return resourceBuilder.build(); return resourceBuilder.build();
} }
@ -110,28 +110,32 @@ public class JerseyEndpointResourceFactory {
this.readBody = readBody; this.readBody = readBody;
} }
@SuppressWarnings("unchecked")
@Override @Override
public Response apply(ContainerRequestContext data) { public Response apply(ContainerRequestContext data) {
Map<String, Object> arguments = new HashMap<>(); Map<String, Object> arguments = new HashMap<>();
if (this.readBody) { if (this.readBody) {
Map<String, Object> body = ((ContainerRequest) data) arguments.putAll(extractBodyArguments(data));
.readEntity(Map.class);
if (body != null) {
arguments.putAll(body);
}
} }
arguments.putAll(extractPathParameters(data)); arguments.putAll(extractPathParameters(data));
arguments.putAll(extractQueryParameters(data)); arguments.putAll(extractQueryParameters(data));
try { try {
return convertToJaxRsResponse(this.operationInvoker.invoke(arguments), Object response = this.operationInvoker.invoke(arguments);
data.getRequest().getMethod()); return convertToJaxRsResponse(response, data.getRequest().getMethod());
} }
catch (ParameterMappingException ex) { catch (ParameterMappingException ex) {
return Response.status(Status.BAD_REQUEST).build(); return Response.status(Status.BAD_REQUEST).build();
} }
} }
@SuppressWarnings("unchecked")
private Map<String, Object> extractBodyArguments(ContainerRequestContext data) {
Map<?, ?> entity = ((ContainerRequest) data).readEntity(Map.class);
if (entity == null) {
return Collections.emptyMap();
}
return (Map<String, Object>) entity;
}
private Map<String, Object> extractPathParameters( private Map<String, Object> extractPathParameters(
ContainerRequestContext requestContext) { ContainerRequestContext requestContext) {
return extract(requestContext.getUriInfo().getPathParameters()); return extract(requestContext.getUriInfo().getPathParameters());
@ -155,8 +159,9 @@ public class JerseyEndpointResourceFactory {
private Response convertToJaxRsResponse(Object response, String httpMethod) { private Response convertToJaxRsResponse(Object response, String httpMethod) {
if (response == null) { if (response == null) {
return Response.status(HttpMethod.GET.equals(httpMethod) boolean isGet = HttpMethod.GET.equals(httpMethod);
? Status.NOT_FOUND : Status.NO_CONTENT).build(); Status status = (isGet ? Status.NOT_FOUND : Status.NO_CONTENT);
return Response.status(status).build();
} }
try { try {
if (!(response instanceof WebEndpointResponse)) { if (!(response instanceof WebEndpointResponse)) {

View File

@ -47,8 +47,8 @@ import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMappi
* @author Madhura Bhave * @author Madhura Bhave
* @since 2.0.0 * @since 2.0.0
*/ */
public abstract class AbstractWebEndpointServletHandlerMapping extends RequestMappingInfoHandlerMapping public abstract class AbstractWebEndpointServletHandlerMapping
implements InitializingBean { extends RequestMappingInfoHandlerMapping implements InitializingBean {
private final String endpointPath; private final String endpointPath;
@ -96,9 +96,12 @@ public abstract class AbstractWebEndpointServletHandlerMapping extends RequestMa
this.webEndpoints.stream() this.webEndpoints.stream()
.flatMap((webEndpoint) -> webEndpoint.getOperations().stream()) .flatMap((webEndpoint) -> webEndpoint.getOperations().stream())
.forEach(this::registerMappingForOperation); .forEach(this::registerMappingForOperation);
registerMapping(new RequestMappingInfo(patternsRequestConditionForPattern(""), PatternsRequestCondition patterns = patternsRequestConditionForPattern("");
new RequestMethodsRequestCondition(RequestMethod.GET), null, null, null, RequestMethodsRequestCondition methods = new RequestMethodsRequestCondition(
null, null), this, getLinks()); RequestMethod.GET);
RequestMappingInfo mapping = new RequestMappingInfo(patterns, methods, null, null,
null, null, null);
registerMapping(mapping, this, getLinks());
} }
@Override @Override
@ -114,23 +117,22 @@ public abstract class AbstractWebEndpointServletHandlerMapping extends RequestMa
protected RequestMappingInfo createRequestMappingInfo( protected RequestMappingInfo createRequestMappingInfo(
WebEndpointOperation operationInfo) { WebEndpointOperation operationInfo) {
OperationRequestPredicate requestPredicate = operationInfo.getRequestPredicate(); OperationRequestPredicate requestPredicate = operationInfo.getRequestPredicate();
return new RequestMappingInfo(null, PatternsRequestCondition patterns = patternsRequestConditionForPattern(
patternsRequestConditionForPattern(requestPredicate.getPath()), requestPredicate.getPath());
new RequestMethodsRequestCondition( RequestMethodsRequestCondition methods = new RequestMethodsRequestCondition(
RequestMethod.valueOf(requestPredicate.getHttpMethod().name())), RequestMethod.valueOf(requestPredicate.getHttpMethod().name()));
null, null, ConsumesRequestCondition consumes = new ConsumesRequestCondition(
new ConsumesRequestCondition( toStringArray(requestPredicate.getConsumes()));
toStringArray(requestPredicate.getConsumes())), ProducesRequestCondition produces = new ProducesRequestCondition(
new ProducesRequestCondition( toStringArray(requestPredicate.getProduces()));
toStringArray(requestPredicate.getProduces())), return new RequestMappingInfo(null, patterns, methods, null, null, consumes,
null); produces, null);
} }
private PatternsRequestCondition patternsRequestConditionForPattern(String path) { private PatternsRequestCondition patternsRequestConditionForPattern(String path) {
return new PatternsRequestCondition( String[] patterns = new String[] {
new String[] { this.endpointPath this.endpointPath + (StringUtils.hasText(path) ? "/" + path : "") };
+ (StringUtils.hasText(path) ? "/" + path : "") }, return new PatternsRequestCondition(patterns, null, null, false, false);
null, null, false, false);
} }
private String[] toStringArray(Collection<String> collection) { private String[] toStringArray(Collection<String> collection) {
@ -172,5 +174,4 @@ public abstract class AbstractWebEndpointServletHandlerMapping extends RequestMa
} }
} }

View File

@ -49,7 +49,8 @@ import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMappi
* @author Andy Wilkinson * @author Andy Wilkinson
* @since 2.0.0 * @since 2.0.0
*/ */
public class WebEndpointServletHandlerMapping extends AbstractWebEndpointServletHandlerMapping { public class WebEndpointServletHandlerMapping
extends AbstractWebEndpointServletHandlerMapping {
private final Method handle = ReflectionUtils.findMethod(OperationHandler.class, private final Method handle = ReflectionUtils.findMethod(OperationHandler.class,
"handle", HttpServletRequest.class, Map.class); "handle", HttpServletRequest.class, Map.class);
@ -84,11 +85,13 @@ public class WebEndpointServletHandlerMapping extends AbstractWebEndpointServlet
setOrder(-100); setOrder(-100);
} }
@Override
protected void registerMappingForOperation(WebEndpointOperation operation) { protected void registerMappingForOperation(WebEndpointOperation operation) {
registerMapping(createRequestMappingInfo(operation), registerMapping(createRequestMappingInfo(operation),
new OperationHandler(operation.getOperationInvoker()), this.handle); new OperationHandler(operation.getInvoker()), this.handle);
} }
@Override
protected Method getLinks() { protected Method getLinks() {
return this.links; return this.links;
} }

View File

@ -24,8 +24,8 @@ import java.util.Map;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.endpoint.EndpointInfo; import org.springframework.boot.endpoint.EndpointInfo;
import org.springframework.boot.endpoint.EndpointOperationType;
import org.springframework.boot.endpoint.OperationInvoker; import org.springframework.boot.endpoint.OperationInvoker;
import org.springframework.boot.endpoint.OperationType;
import org.springframework.boot.endpoint.ParameterMappingException; import org.springframework.boot.endpoint.ParameterMappingException;
import org.springframework.boot.endpoint.web.EndpointLinksResolver; import org.springframework.boot.endpoint.web.EndpointLinksResolver;
import org.springframework.boot.endpoint.web.Link; import org.springframework.boot.endpoint.web.Link;
@ -126,29 +126,28 @@ public class WebEndpointReactiveHandlerMapping extends RequestMappingInfoHandler
} }
private void registerMappingForOperation(WebEndpointOperation operation) { private void registerMappingForOperation(WebEndpointOperation operation) {
EndpointOperationType operationType = operation.getType(); OperationType operationType = operation.getType();
registerMapping(createRequestMappingInfo(operation), registerMapping(createRequestMappingInfo(operation),
operationType == EndpointOperationType.WRITE operationType == OperationType.WRITE
? new WriteOperationHandler(operation.getOperationInvoker()) ? new WriteOperationHandler(operation.getInvoker())
: new ReadOperationHandler(operation.getOperationInvoker()), : new ReadOperationHandler(operation.getInvoker()),
operationType == EndpointOperationType.WRITE ? this.handleWrite operationType == OperationType.WRITE ? this.handleWrite
: this.handleRead); : this.handleRead);
} }
private RequestMappingInfo createRequestMappingInfo( private RequestMappingInfo createRequestMappingInfo(
WebEndpointOperation operationInfo) { WebEndpointOperation operationInfo) {
OperationRequestPredicate requestPredicate = operationInfo.getRequestPredicate(); OperationRequestPredicate requestPredicate = operationInfo.getRequestPredicate();
return new RequestMappingInfo(null, PatternsRequestCondition patterns = new PatternsRequestCondition(pathPatternParser
new PatternsRequestCondition(pathPatternParser .parse(this.endpointPath + "/" + requestPredicate.getPath()));
.parse(this.endpointPath + "/" + requestPredicate.getPath())), RequestMethodsRequestCondition methods = new RequestMethodsRequestCondition(
new RequestMethodsRequestCondition( RequestMethod.valueOf(requestPredicate.getHttpMethod().name()));
RequestMethod.valueOf(requestPredicate.getHttpMethod().name())), ConsumesRequestCondition consumes = new ConsumesRequestCondition(
null, null, toStringArray(requestPredicate.getConsumes()));
new ConsumesRequestCondition( ProducesRequestCondition produces = new ProducesRequestCondition(
toStringArray(requestPredicate.getConsumes())), toStringArray(requestPredicate.getProduces()));
new ProducesRequestCondition( return new RequestMappingInfo(null, patterns, methods, null, null, consumes,
toStringArray(requestPredicate.getProduces())), produces, null);
null);
} }
private String[] toStringArray(Collection<String> collection) { private String[] toStringArray(Collection<String> collection) {

View File

@ -116,7 +116,7 @@ public class AnnotationEndpointDiscovererTests {
endpoints.get("test")); endpoints.get("test"));
assertThat(operations).hasSize(3); assertThat(operations).hasSize(3);
operations.values() operations.values()
.forEach(operation -> assertThat(operation.getOperationInvoker()) .forEach(operation -> assertThat(operation.getInvoker())
.isNotInstanceOf(CachingOperationInvoker.class)); .isNotInstanceOf(CachingOperationInvoker.class));
}); });
} }
@ -135,7 +135,7 @@ public class AnnotationEndpointDiscovererTests {
endpoints.get("test")); endpoints.get("test"));
assertThat(operations).hasSize(3); assertThat(operations).hasSize(3);
operations.values() operations.values()
.forEach(operation -> assertThat(operation.getOperationInvoker()) .forEach(operation -> assertThat(operation.getInvoker())
.isNotInstanceOf(CachingOperationInvoker.class)); .isNotInstanceOf(CachingOperationInvoker.class));
}); });
} }
@ -154,16 +154,16 @@ public class AnnotationEndpointDiscovererTests {
endpoints.get("test")); endpoints.get("test"));
OperationInvoker getAllOperationInvoker = operations OperationInvoker getAllOperationInvoker = operations
.get(ReflectionUtils.findMethod(TestEndpoint.class, "getAll")) .get(ReflectionUtils.findMethod(TestEndpoint.class, "getAll"))
.getOperationInvoker(); .getInvoker();
assertThat(getAllOperationInvoker) assertThat(getAllOperationInvoker)
.isInstanceOf(CachingOperationInvoker.class); .isInstanceOf(CachingOperationInvoker.class);
assertThat(((CachingOperationInvoker) getAllOperationInvoker).getTimeToLive()) assertThat(((CachingOperationInvoker) getAllOperationInvoker).getTimeToLive())
.isEqualTo(500); .isEqualTo(500);
assertThat(operations.get(ReflectionUtils.findMethod(TestEndpoint.class, assertThat(operations.get(ReflectionUtils.findMethod(TestEndpoint.class,
"getOne", String.class)).getOperationInvoker()) "getOne", String.class)).getInvoker())
.isNotInstanceOf(CachingOperationInvoker.class); .isNotInstanceOf(CachingOperationInvoker.class);
assertThat(operations.get(ReflectionUtils.findMethod(TestEndpoint.class, assertThat(operations.get(ReflectionUtils.findMethod(TestEndpoint.class,
"update", String.class, String.class)).getOperationInvoker()) "update", String.class, String.class)).getInvoker())
.isNotInstanceOf(CachingOperationInvoker.class); .isNotInstanceOf(CachingOperationInvoker.class);
}); });
} }
@ -186,7 +186,7 @@ public class AnnotationEndpointDiscovererTests {
EndpointInfo<TestEndpointOperation> endpoint) { EndpointInfo<TestEndpointOperation> endpoint) {
Map<Method, TestEndpointOperation> operationByMethod = new HashMap<>(); Map<Method, TestEndpointOperation> operationByMethod = new HashMap<>();
endpoint.getOperations().forEach((operation) -> { endpoint.getOperations().forEach((operation) -> {
EndpointOperation existing = operationByMethod Operation existing = operationByMethod
.put(operation.getOperationMethod(), operation); .put(operation.getOperationMethod(), operation);
if (existing != null) { if (existing != null) {
throw new AssertionError(String.format( throw new AssertionError(String.format(
@ -281,11 +281,11 @@ public class AnnotationEndpointDiscovererTests {
} }
} }
private static final class TestEndpointOperation extends EndpointOperation { private static final class TestEndpointOperation extends Operation {
private final Method operationMethod; private final Method operationMethod;
private TestEndpointOperation(EndpointOperationType type, private TestEndpointOperation(OperationType type,
OperationInvoker operationInvoker, Method operationMethod) { OperationInvoker operationInvoker, Method operationMethod) {
super(type, operationInvoker, true); super(type, operationInvoker, true);
this.operationMethod = operationMethod; this.operationMethod = operationMethod;
@ -313,7 +313,7 @@ public class AnnotationEndpointDiscovererTests {
@Override @Override
public Collection<EndpointInfo<TestEndpointOperation>> discoverEndpoints() { public Collection<EndpointInfo<TestEndpointOperation>> discoverEndpoints() {
return discoverEndpointsWithExtension(null, null).stream() return discoverEndpoints(null, null).stream()
.map(EndpointInfoDescriptor::getEndpointInfo) .map(EndpointInfoDescriptor::getEndpointInfo)
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@ -324,7 +324,7 @@ public class AnnotationEndpointDiscovererTests {
@Override @Override
public TestEndpointOperation createOperation(String endpointId, public TestEndpointOperation createOperation(String endpointId,
AnnotationAttributes operationAttributes, Object target, AnnotationAttributes operationAttributes, Object target,
Method operationMethod, EndpointOperationType operationType, Method operationMethod, OperationType operationType,
long timeToLive) { long timeToLive) {
return new TestEndpointOperation(operationType, return new TestEndpointOperation(operationType,
createOperationInvoker(timeToLive), operationMethod); createOperationInvoker(timeToLive), operationMethod);

View File

@ -26,8 +26,8 @@ import javax.management.MBeanParameterInfo;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.endpoint.EndpointInfo; import org.springframework.boot.endpoint.EndpointInfo;
import org.springframework.boot.endpoint.EndpointOperationType;
import org.springframework.boot.endpoint.OperationInvoker; import org.springframework.boot.endpoint.OperationInvoker;
import org.springframework.boot.endpoint.OperationType;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry; import static org.assertj.core.api.Assertions.entry;
@ -44,9 +44,9 @@ public class EndpointMBeanInfoAssemblerTests {
@Test @Test
public void exposeSimpleReadOperation() { public void exposeSimpleReadOperation() {
JmxEndpointOperation operation = new JmxEndpointOperation( JmxEndpointOperation operation = new JmxEndpointOperation(OperationType.READ,
EndpointOperationType.READ, new DummyOperationInvoker(), "getAll", new DummyOperationInvoker(), "getAll", Object.class, "Test operation",
Object.class, "Test operation", Collections.emptyList()); Collections.emptyList());
EndpointInfo<JmxEndpointOperation> endpoint = new EndpointInfo<>("test", true, EndpointInfo<JmxEndpointOperation> endpoint = new EndpointInfo<>("test", true,
Collections.singletonList(operation)); Collections.singletonList(operation));
EndpointMBeanInfo endpointMBeanInfo = this.mBeanInfoAssembler EndpointMBeanInfo endpointMBeanInfo = this.mBeanInfoAssembler
@ -73,9 +73,8 @@ public class EndpointMBeanInfoAssemblerTests {
@Test @Test
public void exposeSimpleWriteOperation() { public void exposeSimpleWriteOperation() {
JmxEndpointOperation operation = new JmxEndpointOperation( JmxEndpointOperation operation = new JmxEndpointOperation(OperationType.WRITE,
EndpointOperationType.WRITE, new DummyOperationInvoker(), "update", new DummyOperationInvoker(), "update", Object.class, "Update operation",
Object.class, "Update operation",
Collections.singletonList(new JmxEndpointOperationParameterInfo("test", Collections.singletonList(new JmxEndpointOperationParameterInfo("test",
String.class, "Test argument"))); String.class, "Test argument")));
EndpointInfo<JmxEndpointOperation> endpoint = new EndpointInfo<>("another", true, EndpointInfo<JmxEndpointOperation> endpoint = new EndpointInfo<>("another", true,

View File

@ -31,8 +31,8 @@ import org.springframework.boot.endpoint.CachingConfiguration;
import org.springframework.boot.endpoint.CachingOperationInvoker; import org.springframework.boot.endpoint.CachingOperationInvoker;
import org.springframework.boot.endpoint.ConversionServiceOperationParameterMapper; import org.springframework.boot.endpoint.ConversionServiceOperationParameterMapper;
import org.springframework.boot.endpoint.Endpoint; import org.springframework.boot.endpoint.Endpoint;
import org.springframework.boot.endpoint.EndpointDelivery;
import org.springframework.boot.endpoint.EndpointInfo; import org.springframework.boot.endpoint.EndpointInfo;
import org.springframework.boot.endpoint.EndpointType;
import org.springframework.boot.endpoint.ReadOperation; import org.springframework.boot.endpoint.ReadOperation;
import org.springframework.boot.endpoint.ReflectiveOperationInvoker; import org.springframework.boot.endpoint.ReflectiveOperationInvoker;
import org.springframework.boot.endpoint.WriteOperation; import org.springframework.boot.endpoint.WriteOperation;
@ -77,7 +77,7 @@ public class JmxAnnotationEndpointDiscovererTests {
.isEqualTo("Invoke getAll for endpoint test"); .isEqualTo("Invoke getAll for endpoint test");
assertThat(getAll.getOutputType()).isEqualTo(Object.class); assertThat(getAll.getOutputType()).isEqualTo(Object.class);
assertThat(getAll.getParameters()).isEmpty(); assertThat(getAll.getParameters()).isEmpty();
assertThat(getAll.getOperationInvoker()) assertThat(getAll.getInvoker())
.isInstanceOf(ReflectiveOperationInvoker.class); .isInstanceOf(ReflectiveOperationInvoker.class);
JmxEndpointOperation getSomething = operationByName.get("getSomething"); JmxEndpointOperation getSomething = operationByName.get("getSomething");
assertThat(getSomething.getDescription()) assertThat(getSomething.getDescription())
@ -155,10 +155,9 @@ public class JmxAnnotationEndpointDiscovererTests {
assertThat(operationByName).containsOnlyKeys("getAll", "getSomething", assertThat(operationByName).containsOnlyKeys("getAll", "getSomething",
"update"); "update");
JmxEndpointOperation getAll = operationByName.get("getAll"); JmxEndpointOperation getAll = operationByName.get("getAll");
assertThat(getAll.getOperationInvoker()) assertThat(getAll.getInvoker()).isInstanceOf(CachingOperationInvoker.class);
.isInstanceOf(CachingOperationInvoker.class); assertThat(((CachingOperationInvoker) getAll.getInvoker()).getTimeToLive())
assertThat(((CachingOperationInvoker) getAll.getOperationInvoker()) .isEqualTo(500);
.getTimeToLive()).isEqualTo(500);
}); });
} }
@ -174,16 +173,15 @@ public class JmxAnnotationEndpointDiscovererTests {
assertThat(operationByName).containsOnlyKeys("getAll", "getSomething", assertThat(operationByName).containsOnlyKeys("getAll", "getSomething",
"update", "getAnother"); "update", "getAnother");
JmxEndpointOperation getAll = operationByName.get("getAll"); JmxEndpointOperation getAll = operationByName.get("getAll");
assertThat(getAll.getOperationInvoker()) assertThat(getAll.getInvoker())
.isInstanceOf(CachingOperationInvoker.class); .isInstanceOf(CachingOperationInvoker.class);
assertThat(((CachingOperationInvoker) getAll.getOperationInvoker()) assertThat(((CachingOperationInvoker) getAll.getInvoker())
.getTimeToLive()).isEqualTo(500); .getTimeToLive()).isEqualTo(500);
JmxEndpointOperation getAnother = operationByName.get("getAnother"); JmxEndpointOperation getAnother = operationByName.get("getAnother");
assertThat(getAnother.getOperationInvoker()) assertThat(getAnother.getInvoker())
.isInstanceOf(CachingOperationInvoker.class); .isInstanceOf(CachingOperationInvoker.class);
assertThat( assertThat(((CachingOperationInvoker) getAnother.getInvoker())
((CachingOperationInvoker) getAnother.getOperationInvoker()) .getTimeToLive()).isEqualTo(500);
.getTimeToLive()).isEqualTo(500);
}); });
} }
@ -333,7 +331,7 @@ public class JmxAnnotationEndpointDiscovererTests {
} }
@Endpoint(id = "jmx", types = EndpointType.JMX) @Endpoint(id = "jmx", delivery = EndpointDelivery.JMX)
private static class TestJmxEndpoint { private static class TestJmxEndpoint {
@ReadOperation @ReadOperation
@ -412,7 +410,7 @@ public class JmxAnnotationEndpointDiscovererTests {
} }
@Endpoint(id = "nonjmx", types = EndpointType.WEB) @Endpoint(id = "nonjmx", delivery = EndpointDelivery.WEB)
private static class NonJmxEndpoint { private static class NonJmxEndpoint {
@ReadOperation @ReadOperation

View File

@ -24,7 +24,7 @@ import org.assertj.core.api.Condition;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.endpoint.EndpointInfo; import org.springframework.boot.endpoint.EndpointInfo;
import org.springframework.boot.endpoint.EndpointOperationType; import org.springframework.boot.endpoint.OperationType;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -74,7 +74,7 @@ public class EndpointLinksResolverTests {
} }
private WebEndpointOperation operationWithPath(String path, String id) { private WebEndpointOperation operationWithPath(String path, String id) {
return new WebEndpointOperation(EndpointOperationType.READ, null, false, return new WebEndpointOperation(OperationType.READ, null, false,
new OperationRequestPredicate(path, WebEndpointHttpMethod.GET, new OperationRequestPredicate(path, WebEndpointHttpMethod.GET,
Collections.emptyList(), Collections.emptyList()), Collections.emptyList(), Collections.emptyList()),
id); id);

View File

@ -37,8 +37,8 @@ import org.springframework.boot.endpoint.CachingConfiguration;
import org.springframework.boot.endpoint.CachingOperationInvoker; import org.springframework.boot.endpoint.CachingOperationInvoker;
import org.springframework.boot.endpoint.ConversionServiceOperationParameterMapper; import org.springframework.boot.endpoint.ConversionServiceOperationParameterMapper;
import org.springframework.boot.endpoint.Endpoint; import org.springframework.boot.endpoint.Endpoint;
import org.springframework.boot.endpoint.EndpointDelivery;
import org.springframework.boot.endpoint.EndpointInfo; import org.springframework.boot.endpoint.EndpointInfo;
import org.springframework.boot.endpoint.EndpointType;
import org.springframework.boot.endpoint.OperationInvoker; import org.springframework.boot.endpoint.OperationInvoker;
import org.springframework.boot.endpoint.ReadOperation; import org.springframework.boot.endpoint.ReadOperation;
import org.springframework.boot.endpoint.Selector; import org.springframework.boot.endpoint.Selector;
@ -194,7 +194,7 @@ public class WebAnnotationEndpointDiscovererTests {
EndpointInfo<WebEndpointOperation> endpoint = endpoints.get("test"); EndpointInfo<WebEndpointOperation> endpoint = endpoints.get("test");
assertThat(endpoint.getOperations()).hasSize(1); assertThat(endpoint.getOperations()).hasSize(1);
OperationInvoker operationInvoker = endpoint.getOperations() OperationInvoker operationInvoker = endpoint.getOperations()
.iterator().next().getOperationInvoker(); .iterator().next().getInvoker();
assertThat(operationInvoker) assertThat(operationInvoker)
.isInstanceOf(CachingOperationInvoker.class); .isInstanceOf(CachingOperationInvoker.class);
assertThat( assertThat(
@ -375,7 +375,7 @@ public class WebAnnotationEndpointDiscovererTests {
} }
@Endpoint(id = "nonweb", types = EndpointType.JMX) @Endpoint(id = "nonweb", delivery = EndpointDelivery.JMX)
static class NonWebEndpoint { static class NonWebEndpoint {
@ReadOperation @ReadOperation