Polish endpoint

pull/10101/merge
Phillip Webb 7 years ago committed by Stephane Nicoll
parent 98455e30dc
commit f9e5b07eec

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

@ -22,7 +22,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
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.web.WebEndpointExtension;
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.type.AnnotatedTypeMetadata;
import org.springframework.core.type.MethodMetadata;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
@ -44,14 +45,9 @@ class OnEnabledEndpointCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
EndpointAttributes endpoint = getEndpointAttributes(context, metadata);
if (!StringUtils.hasText(endpoint.id)) {
throw new IllegalStateException("Endpoint id could not be determined");
}
EndpointEnablementProvider enablementProvider = new EndpointEnablementProvider(
context.getEnvironment());
EndpointEnablement endpointEnablement = enablementProvider.getEndpointEnablement(
endpoint.id, endpoint.enabled, endpoint.endpointType);
EndpointAttributes attributes = getEndpointAttributes(context, metadata);
EndpointEnablement endpointEnablement = attributes
.getEnablement(new EndpointEnablementProvider(context.getEnvironment()));
return new ConditionOutcome(endpointEnablement.isEnabled(),
ConditionMessage.forCondition(ConditionalOnEnabledEndpoint.class)
.because(endpointEnablement.getReason()));
@ -59,24 +55,27 @@ class OnEnabledEndpointCondition extends SpringBootCondition {
private EndpointAttributes getEndpointAttributes(ConditionContext context,
AnnotatedTypeMetadata metadata) {
if (metadata instanceof MethodMetadata
&& metadata.isAnnotated(Bean.class.getName())) {
MethodMetadata methodMetadata = (MethodMetadata) metadata;
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(
Assert.state(
metadata instanceof MethodMetadata
&& metadata.isAnnotated(Bean.class.getName()),
"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) {
@ -105,11 +104,10 @@ class OnEnabledEndpointCondition extends SpringBootCondition {
if (endpoint == null) {
return null;
}
// If both types are set, all techs are exposed
EndpointType endpointType = (endpoint.types().length == 1 ? endpoint.types()[0]
: null);
// If both types are set, all delivery technologies are exposed
EndpointDelivery[] delivery = endpoint.delivery();
return new EndpointAttributes(endpoint.id(), endpoint.enabledByDefault(),
endpointType);
(delivery.length == 1 ? delivery[0] : null));
}
private static class EndpointAttributes {
@ -118,12 +116,19 @@ class OnEnabledEndpointCondition extends SpringBootCondition {
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.enabled = enabled;
this.endpointType = endpointType;
this.delivery = delivery;
}
public EndpointEnablement getEnablement(EndpointEnablementProvider provider) {
return provider.getEndpointEnablement(this.id, this.enabled, this.delivery);
}
}

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

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

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

@ -16,9 +16,9 @@
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.util.StringUtils;
import org.springframework.util.Assert;
/**
* 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.
* @param endpointId the id of the endpoint
* @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
* {@link EndpointType}
* {@link EndpointDelivery}
*/
public EndpointEnablement getEndpointEnablement(String endpointId,
boolean enabledByDefault, EndpointType endpointType) {
if (!StringUtils.hasText(endpointId)) {
throw new IllegalArgumentException("Endpoint id must have a value");
boolean enabledByDefault, EndpointDelivery delivery) {
Assert.hasText(endpointId, "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")) {
throw new IllegalArgumentException("Endpoint id 'default' is a reserved "
+ "value and cannot be used by an endpoint");
result = findEnablement(getKey(endpointId, "enabled"));
if (result != null) {
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
// for the endpoint
if (!enabledByDefault) {
return defaultEndpointEnablement(endpointId, false, endpointType);
return getDefaultEndpointEnablement(endpointId, false, delivery);
}
return getGlobalEndpointEnablement(endpointId, enabledByDefault,
delivery);
}
private EndpointEnablement findEnablement(String endpointId,
EndpointDelivery delivery) {
if (delivery != null) {
return findEnablement(getKey(endpointId, delivery));
}
return findEnablementForAnyDeliveryTechnology(endpointId);
}
if (endpointType != null) {
String defaultTypeKey = createTechSpecificKey("default", endpointType);
EndpointEnablement globalTypeOutcome = getEnablementFor(defaultTypeKey);
if (globalTypeOutcome != null) {
return globalTypeOutcome;
private EndpointEnablement getGlobalEndpointEnablement(String endpointId,
boolean enabledByDefault, EndpointDelivery delivery) {
EndpointEnablement result = findGlobalEndpointEnablement(delivery);
if (result != null) {
return result;
}
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 (!endpointType.isEnabledByDefault()) {
return defaultEndpointEnablement("default", false, endpointType);
if (!delivery.isEnabledByDefault()) {
return getDefaultEndpointEnablement("default", false, delivery);
}
return null;
}
else {
// Check if there is a global tech required
EndpointEnablement anyTechGeneralOutcome = getAnyTechSpecificOutcomeFor(
"default");
if (anyTechGeneralOutcome != null) {
return anyTechGeneralOutcome;
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;
}
String defaultKey = createKey("default", "enabled");
EndpointEnablement globalOutCome = getEnablementFor(defaultKey);
if (globalOutCome != null) {
return globalOutCome;
}
return defaultEndpointEnablement(endpointId, enabledByDefault, endpointType);
private EndpointEnablement findEnablementForDeliveryTechnology(String endpointId,
EndpointDelivery delivery) {
String endpointTypeKey = getKey(endpointId, delivery);
EndpointEnablement endpointTypeSpecificOutcome = findEnablement(endpointTypeKey);
return endpointTypeSpecificOutcome;
}
private EndpointEnablement defaultEndpointEnablement(String endpointId,
boolean enabledByDefault, EndpointType endpointType) {
private EndpointEnablement getDefaultEndpointEnablement(String endpointId,
boolean enabledByDefault, EndpointDelivery delivery) {
return new EndpointEnablement(enabledByDefault, createDefaultEnablementMessage(
endpointId, enabledByDefault, endpointType));
endpointId, enabledByDefault, 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()));
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()));
}
sb.append(String.format("is %s by default",
message.append(String.format("is %s by default",
(enabledByDefault ? "enabled" : "disabled")));
return sb.toString();
}
private EndpointEnablement getAnyTechSpecificOutcomeFor(String endpointId) {
for (EndpointType endpointType : EndpointType.values()) {
String key = createTechSpecificKey(endpointId, endpointType);
EndpointEnablement outcome = getEnablementFor(key);
if (outcome != null && outcome.isEnabled()) {
return outcome;
}
}
return null;
return message.toString();
}
private String createTechSpecificKey(String endpointId, EndpointType endpointType) {
return createKey(endpointId, endpointType.name().toLowerCase() + ".enabled");
private String getKey(String endpointId, EndpointDelivery delivery) {
return getKey(endpointId, delivery.name().toLowerCase() + ".enabled");
}
private String createKey(String endpointId, String suffix) {
private String getKey(String endpointId, String suffix) {
return "endpoints." + endpointId + "." + suffix;
}
@ -166,7 +168,7 @@ public class EndpointEnablementProvider {
* @param key the key to check
* @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)) {
boolean match = this.environment.getProperty(key, Boolean.class, true);
return new EndpointEnablement(match, String.format("found property %s", key));

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

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

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

@ -19,7 +19,7 @@ package org.springframework.boot.actuate.autoconfigure.endpoint;
import org.junit.Test;
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.web.WebEndpointExtension;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
@ -297,8 +297,8 @@ public class ConditionalOnEnabledEndpointTests {
}
@Endpoint(id = "bar", types = { EndpointType.WEB,
EndpointType.JMX }, enabledByDefault = false)
@Endpoint(id = "bar", delivery = { EndpointDelivery.WEB,
EndpointDelivery.JMX }, enabledByDefault = false)
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 {
}

@ -20,7 +20,7 @@ import org.junit.Rule;
import org.junit.Test;
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.mock.env.MockEnvironment;
import org.springframework.util.ObjectUtils;
@ -41,7 +41,7 @@ public class EndpointEnablementProviderTests {
public void cannotDetermineEnablementWithEmptyEndpoint() {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Endpoint id must have a value");
determineEnablement(" ", true);
getEndpointEnablement(" ", true);
}
@Test
@ -49,289 +49,295 @@ public class EndpointEnablementProviderTests {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Endpoint id 'default' is a reserved value and cannot "
+ "be used by an endpoint");
determineEnablement("default", true);
getEndpointEnablement("default", true);
}
@Test
public void generalEnabledByDefault() {
validate(determineEnablement("foo", true), true,
"endpoint 'foo' is enabled by default");
EndpointEnablement enablement = getEndpointEnablement("foo", true);
validate(enablement, true, "endpoint 'foo' is enabled by default");
}
@Test
public void generalDisabledViaSpecificProperty() {
validate(determineEnablement("foo", true, "endpoints.foo.enabled=false"), false,
"found property endpoints.foo.enabled");
EndpointEnablement enablement = getEndpointEnablement("foo", true,
"endpoints.foo.enabled=false");
validate(enablement, false, "found property endpoints.foo.enabled");
}
@Test
public void generalDisabledViaGeneralProperty() {
validate(determineEnablement("foo", true, "endpoints.default.enabled=false"), false,
"found property endpoints.default.enabled");
EndpointEnablement enablement = getEndpointEnablement("foo", true,
"endpoints.default.enabled=false");
validate(enablement, false, "found property endpoints.default.enabled");
}
@Test
public void generalEnabledOverrideViaSpecificProperty() {
validate(
determineEnablement("foo", true, "endpoints.default.enabled=false",
"endpoints.foo.enabled=true"),
true, "found property endpoints.foo.enabled");
EndpointEnablement enablement = getEndpointEnablement("foo", true,
"endpoints.default.enabled=false", "endpoints.foo.enabled=true");
validate(enablement, true, "found property endpoints.foo.enabled");
}
@Test
public void generalEnabledOverrideViaSpecificWebProperty() {
validate(
determineEnablement("foo", true, "endpoints.foo.enabled=false",
"endpoints.foo.web.enabled=true"),
true, "found property endpoints.foo.web.enabled");
EndpointEnablement enablement = getEndpointEnablement("foo", true,
"endpoints.foo.enabled=false", "endpoints.foo.web.enabled=true");
validate(enablement, true, "found property endpoints.foo.web.enabled");
}
@Test
public void generalEnabledOverrideViaSpecificJmxProperty() {
validate(
determineEnablement("foo", true, "endpoints.foo.enabled=false",
"endpoints.foo.jmx.enabled=true"),
true, "found property endpoints.foo.jmx.enabled");
EndpointEnablement enablement = getEndpointEnablement("foo", true,
"endpoints.foo.enabled=false", "endpoints.foo.jmx.enabled=true");
validate(enablement, true, "found property endpoints.foo.jmx.enabled");
}
@Test
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"),
true, "found property endpoints.foo.jmx.enabled");
}
@Test
public void generalEnabledOverrideViaGeneralWebProperty() {
validate(
determineEnablement("foo", true, "endpoints.default.enabled=false",
"endpoints.default.web.enabled=true"),
true, "found property endpoints.default.web.enabled");
EndpointEnablement enablement = getEndpointEnablement("foo", true,
"endpoints.default.enabled=false", "endpoints.default.web.enabled=true");
validate(enablement, true, "found property endpoints.default.web.enabled");
}
@Test
public void generalEnabledOverrideViaGeneralJmxProperty() {
validate(
determineEnablement("foo", true, "endpoints.default.enabled=false",
"endpoints.default.jmx.enabled=true"),
true, "found property endpoints.default.jmx.enabled");
EndpointEnablement enablement = getEndpointEnablement("foo", true,
"endpoints.default.enabled=false", "endpoints.default.jmx.enabled=true");
validate(enablement, true, "found property endpoints.default.jmx.enabled");
}
@Test
public void generalEnabledOverrideViaGeneralAnyProperty() {
validate(determineEnablement("foo", true, "endpoints.default.enabled=false",
"endpoints.default.web.enabled=false", "endpoints.default.jmx.enabled=true"),
true, "found property endpoints.default.jmx.enabled");
EndpointEnablement enablement = getEndpointEnablement("foo", true,
"endpoints.default.enabled=false", "endpoints.default.web.enabled=false",
"endpoints.default.jmx.enabled=true");
validate(enablement, true, "found property endpoints.default.jmx.enabled");
}
@Test
public void generalDisabledEvenWithEnabledGeneralProperties() {
validate(
determineEnablement("foo", true, "endpoints.default.enabled=true",
"endpoints.default.web.enabled=true",
"endpoints.default.jmx.enabled=true", "endpoints.foo.enabled=false"),
false, "found property endpoints.foo.enabled");
EndpointEnablement enablement = getEndpointEnablement("foo", true,
"endpoints.default.enabled=true", "endpoints.default.web.enabled=true",
"endpoints.default.jmx.enabled=true", "endpoints.foo.enabled=false");
validate(enablement, false, "found property endpoints.foo.enabled");
}
@Test
public void generalDisabledByDefaultWithAnnotationFlag() {
validate(determineEnablement("bar", false), false,
"endpoint 'bar' is disabled by default");
EndpointEnablement enablement = getEndpointEnablement("bar", false);
validate(enablement, false, "endpoint 'bar' is disabled by default");
}
@Test
public void generalDisabledByDefaultWithAnnotationFlagEvenWithGeneralProperty() {
validate(determineEnablement("bar", false, "endpoints.default.enabled=true"), false,
"endpoint 'bar' is disabled by default");
EndpointEnablement enablement = getEndpointEnablement("bar", false,
"endpoints.default.enabled=true");
validate(enablement, false, "endpoint 'bar' is disabled by default");
}
@Test
public void generalDisabledByDefaultWithAnnotationFlagEvenWithGeneralWebProperty() {
validate(determineEnablement("bar", false, "endpoints.default.web.enabled=true"),
false, "endpoint 'bar' is disabled by default");
EndpointEnablement enablement = getEndpointEnablement("bar", false,
"endpoints.default.web.enabled=true");
validate(enablement, false, "endpoint 'bar' is disabled by default");
}
@Test
public void generalDisabledByDefaultWithAnnotationFlagEvenWithGeneralJmxProperty() {
validate(determineEnablement("bar", false, "endpoints.default.jmx.enabled=true"),
false, "endpoint 'bar' is disabled by default");
EndpointEnablement enablement = getEndpointEnablement("bar", false,
"endpoints.default.jmx.enabled=true");
validate(enablement, false, "endpoint 'bar' is disabled by default");
}
@Test
public void generalEnabledOverrideWithAndAnnotationFlagAndSpecificProperty() {
validate(determineEnablement("bar", false, "endpoints.bar.enabled=true"), true,
"found property endpoints.bar.enabled");
EndpointEnablement enablement = getEndpointEnablement("bar", false,
"endpoints.bar.enabled=true");
validate(enablement, true, "found property endpoints.bar.enabled");
}
@Test
public void generalEnabledOverrideWithAndAnnotationFlagAndSpecificWebProperty() {
validate(determineEnablement("bar", false, "endpoints.bar.web.enabled=true"),
true, "found property endpoints.bar.web.enabled");
EndpointEnablement enablement = getEndpointEnablement("bar", false,
"endpoints.bar.web.enabled=true");
validate(enablement, true, "found property endpoints.bar.web.enabled");
}
@Test
public void generalEnabledOverrideWithAndAnnotationFlagAndSpecificJmxProperty() {
validate(determineEnablement("bar", false, "endpoints.bar.jmx.enabled=true"),
true, "found property endpoints.bar.jmx.enabled");
EndpointEnablement enablement = getEndpointEnablement("bar", false,
"endpoints.bar.jmx.enabled=true");
validate(enablement, true, "found property endpoints.bar.jmx.enabled");
}
@Test
public void generalEnabledOverrideWithAndAnnotationFlagAndAnyProperty() {
validate(
determineEnablement("bar", false, "endpoints.bar.web.enabled=false",
"endpoints.bar.jmx.enabled=true"),
true, "found property endpoints.bar.jmx.enabled");
EndpointEnablement enablement = getEndpointEnablement("bar", false,
"endpoints.bar.web.enabled=false", "endpoints.bar.jmx.enabled=true");
validate(enablement, true, "found property endpoints.bar.jmx.enabled");
}
@Test
public void specificEnabledByDefault() {
validate(determineEnablement("foo", true, EndpointType.JMX), true,
"endpoint 'foo' (jmx) is enabled by default");
EndpointEnablement enablement = getEndpointEnablement("foo", true,
EndpointDelivery.JMX);
validate(enablement, true, "endpoint 'foo' (jmx) is enabled by default");
}
@Test
public void specificDisabledViaEndpointProperty() {
validate(
determineEnablement("foo", true, EndpointType.WEB,
"endpoints.foo.enabled=false"),
false, "found property endpoints.foo.enabled");
EndpointEnablement enablement = getEndpointEnablement("foo", true,
EndpointDelivery.WEB, "endpoints.foo.enabled=false");
validate(enablement, false, "found property endpoints.foo.enabled");
}
@Test
public void specificDisabledViaTechProperty() {
validate(
determineEnablement("foo", true, EndpointType.WEB,
"endpoints.foo.web.enabled=false"),
false, "found property endpoints.foo.web.enabled");
EndpointEnablement enablement = getEndpointEnablement("foo", true,
EndpointDelivery.WEB, "endpoints.foo.web.enabled=false");
validate(enablement, false, "found property endpoints.foo.web.enabled");
}
@Test
public void specificNotDisabledViaUnrelatedTechProperty() {
validate(
determineEnablement("foo", true, EndpointType.JMX,
"endpoints.foo.web.enabled=false"),
true, "endpoint 'foo' (jmx) is enabled by default");
EndpointEnablement enablement = getEndpointEnablement("foo", true,
EndpointDelivery.JMX, "endpoints.foo.web.enabled=false");
validate(enablement, true, "endpoint 'foo' (jmx) is enabled by default");
}
@Test
public void specificDisabledViaGeneralProperty() {
validate(
determineEnablement("foo", true, EndpointType.JMX,
"endpoints.default.enabled=false"),
false, "found property endpoints.default.enabled");
EndpointEnablement enablement = getEndpointEnablement("foo", true,
EndpointDelivery.JMX, "endpoints.default.enabled=false");
validate(enablement, false, "found property endpoints.default.enabled");
}
@Test
public void specificEnabledOverrideViaEndpointProperty() {
validate(
determineEnablement("foo", true, EndpointType.WEB,
"endpoints.default.enabled=false", "endpoints.foo.enabled=true"),
true, "found property endpoints.foo.enabled");
EndpointEnablement enablement = getEndpointEnablement("foo", true,
EndpointDelivery.WEB, "endpoints.default.enabled=false",
"endpoints.foo.enabled=true");
validate(enablement, true, "found property endpoints.foo.enabled");
}
@Test
public void specificEnabledOverrideViaTechProperty() {
validate(
determineEnablement("foo", true, EndpointType.WEB,
"endpoints.foo.enabled=false", "endpoints.foo.web.enabled=true"),
true, "found property endpoints.foo.web.enabled");
EndpointEnablement enablement = getEndpointEnablement("foo", true,
EndpointDelivery.WEB, "endpoints.foo.enabled=false",
"endpoints.foo.web.enabled=true");
validate(enablement, true, "found property endpoints.foo.web.enabled");
}
@Test
public void specificEnabledOverrideHasNotEffectWithUnrelatedTechProperty() {
validate(
determineEnablement("foo", true, EndpointType.WEB,
"endpoints.foo.enabled=false", "endpoints.foo.jmx.enabled=true"),
false, "found property endpoints.foo.enabled");
EndpointEnablement enablement = getEndpointEnablement("foo", true,
EndpointDelivery.WEB, "endpoints.foo.enabled=false",
"endpoints.foo.jmx.enabled=true");
validate(enablement, false, "found property endpoints.foo.enabled");
}
@Test
public void specificEnabledOverrideViaGeneralWebProperty() {
validate(
determineEnablement("foo", true, EndpointType.WEB,
"endpoints.default.enabled=false", "endpoints.default.web.enabled=true"),
true, "found property endpoints.default.web.enabled");
EndpointEnablement enablement = getEndpointEnablement("foo", true,
EndpointDelivery.WEB, "endpoints.default.enabled=false",
"endpoints.default.web.enabled=true");
validate(enablement, true, "found property endpoints.default.web.enabled");
}
@Test
public void specificEnabledOverrideHasNoEffectWithUnrelatedTechProperty() {
validate(
determineEnablement("foo", true, EndpointType.JMX,
getEndpointEnablement("foo", true, EndpointDelivery.JMX,
"endpoints.default.enabled=false", "endpoints.default.web.enabled=true"),
false, "found property endpoints.default.enabled");
}
@Test
public void specificDisabledWithEndpointPropertyEvenWithEnabledGeneralProperties() {
validate(
determineEnablement("foo", true, EndpointType.WEB,
"endpoints.default.enabled=true", "endpoints.default.web.enabled=true",
"endpoints.default.jmx.enabled=true", "endpoints.foo.enabled=false"),
false, "found property endpoints.foo.enabled");
EndpointEnablement enablement = getEndpointEnablement("foo", true,
EndpointDelivery.WEB, "endpoints.default.enabled=true",
"endpoints.default.web.enabled=true", "endpoints.default.jmx.enabled=true",
"endpoints.foo.enabled=false");
validate(enablement, false, "found property endpoints.foo.enabled");
}
@Test
public void specificDisabledWithTechPropertyEvenWithEnabledGeneralProperties() {
validate(
determineEnablement("foo", true, EndpointType.WEB,
"endpoints.default.enabled=true", "endpoints.default.web.enabled=true",
"endpoints.default.jmx.enabled=true", "endpoints.foo.enabled=true",
"endpoints.foo.web.enabled=false"),
false, "found property endpoints.foo.web.enabled");
EndpointEnablement enablement = getEndpointEnablement("foo", true,
EndpointDelivery.WEB, "endpoints.default.enabled=true",
"endpoints.default.web.enabled=true", "endpoints.default.jmx.enabled=true",
"endpoints.foo.enabled=true", "endpoints.foo.web.enabled=false");
validate(enablement, false, "found property endpoints.foo.web.enabled");
}
@Test
public void specificDisabledByDefaultWithAnnotationFlag() {
validate(determineEnablement("bar", false, EndpointType.WEB), false,
"endpoint 'bar' (web) is disabled by default");
EndpointEnablement enablement = getEndpointEnablement("bar", false,
EndpointDelivery.WEB);
validate(enablement, false, "endpoint 'bar' (web) is disabled by default");
}
@Test
public void specificDisabledByDefaultWithAnnotationFlagEvenWithGeneralProperty() {
validate(
determineEnablement("bar", false, EndpointType.WEB,
"endpoints.default.enabled=true"),
false, "endpoint 'bar' (web) is disabled by default");
EndpointEnablement enablement = getEndpointEnablement("bar", false,
EndpointDelivery.WEB, "endpoints.default.enabled=true");
validate(enablement, false, "endpoint 'bar' (web) is disabled by default");
}
@Test
public void specificDisabledByDefaultWithAnnotationFlagEvenWithGeneralWebProperty() {
validate(
determineEnablement("bar", false, EndpointType.WEB,
"endpoints.default.web.enabled=true"),
false, "endpoint 'bar' (web) is disabled by default");
EndpointEnablement enablement = getEndpointEnablement("bar", false,
EndpointDelivery.WEB, "endpoints.default.web.enabled=true");
validate(enablement, false, "endpoint 'bar' (web) is disabled by default");
}
@Test
public void specificDisabledByDefaultWithAnnotationFlagEvenWithGeneralJmxProperty() {
validate(
determineEnablement("bar", false, EndpointType.WEB,
"endpoints.default.jmx.enabled=true"),
false, "endpoint 'bar' (web) is disabled by default");
EndpointEnablement enablement = getEndpointEnablement("bar", false,
EndpointDelivery.WEB, "endpoints.default.jmx.enabled=true");
validate(enablement, false, "endpoint 'bar' (web) is disabled by default");
}
@Test
public void specificEnabledOverrideWithAndAnnotationFlagAndEndpointProperty() {
validate(
determineEnablement("bar", false, EndpointType.WEB,
"endpoints.bar.enabled=true"),
true, "found property endpoints.bar.enabled");
EndpointEnablement enablement = getEndpointEnablement("bar", false,
EndpointDelivery.WEB, "endpoints.bar.enabled=true");
validate(enablement, true, "found property endpoints.bar.enabled");
}
@Test
public void specificEnabledOverrideWithAndAnnotationFlagAndTechProperty() {
validate(
determineEnablement("bar", false, EndpointType.WEB,
"endpoints.bar.web.enabled=true"),
true, "found property endpoints.bar.web.enabled");
EndpointEnablement enablement = getEndpointEnablement("bar", false,
EndpointDelivery.WEB, "endpoints.bar.web.enabled=true");
validate(enablement, true, "found property endpoints.bar.web.enabled");
}
@Test
public void specificEnabledOverrideWithAndAnnotationFlagHasNoEffectWithUnrelatedTechProperty() {
validate(
determineEnablement("bar", false, EndpointType.WEB,
"endpoints.bar.jmx.enabled=true"),
false, "endpoint 'bar' (web) is disabled by default");
EndpointEnablement enablement = getEndpointEnablement("bar", false,
EndpointDelivery.WEB, "endpoints.bar.jmx.enabled=true");
validate(enablement, 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,
@ -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);
}
}

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

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

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

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

@ -17,12 +17,12 @@
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
* @since 2.0.0
*/
public enum EndpointType {
public enum EndpointDelivery {
/**
* Expose the endpoint as a JMX MBean.
@ -36,11 +36,12 @@ public enum EndpointType {
private final boolean enabledByDefault;
EndpointType(boolean enabledByDefault) {
EndpointDelivery(boolean enabledByDefault) {
this.enabledByDefault = enabledByDefault;
}
public boolean isEnabledByDefault() {
return this.enabledByDefault;
}
}

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

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

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

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

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

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

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

@ -21,11 +21,11 @@ import java.util.Map;
import javax.management.MBeanInfo;
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
* names to an {@link EndpointOperation}.
* names to an {@link Operation}.
*
* @author Stephane Nicoll
* @since 2.0.0

@ -31,7 +31,7 @@ import javax.management.modelmbean.ModelMBeanNotificationInfo;
import javax.management.modelmbean.ModelMBeanOperationInfo;
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}.
@ -61,7 +61,6 @@ class EndpointMBeanInfoAssembler {
.toArray(new ModelMBeanOperationInfo[] {});
Map<String, JmxEndpointOperation> operationsInfo = new LinkedHashMap<>();
operationsMapping.forEach((name, t) -> operationsInfo.put(name, t.operation));
MBeanInfo info = new ModelMBeanInfoSupport(EndpointMBean.class.getName(),
getDescription(endpointInfo), new ModelMBeanAttributeInfo[0],
new ModelMBeanConstructorInfo[0], operationsMBeanInfo,
@ -100,11 +99,11 @@ class EndpointMBeanInfoAssembler {
.toArray(new MBeanParameterInfo[parameterInfos.size()])));
}
private int mapOperationType(EndpointOperationType type) {
if (type == EndpointOperationType.READ) {
private int mapOperationType(OperationType type) {
if (type == OperationType.READ) {
return MBeanOperationInfo.INFO;
}
if (type == EndpointOperationType.WRITE) {
if (type == OperationType.WRITE) {
return MBeanOperationInfo.ACTION;
}
return MBeanOperationInfo.UNKNOWN;

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

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

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

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

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

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

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

@ -16,9 +16,9 @@
package org.springframework.boot.endpoint.web;
import org.springframework.boot.endpoint.EndpointOperation;
import org.springframework.boot.endpoint.EndpointOperationType;
import org.springframework.boot.endpoint.Operation;
import org.springframework.boot.endpoint.OperationInvoker;
import org.springframework.boot.endpoint.OperationType;
/**
* An operation on a web endpoint.
@ -26,7 +26,7 @@ import org.springframework.boot.endpoint.OperationInvoker;
* @author Andy Wilkinson
* @since 2.0.0
*/
public class WebEndpointOperation extends EndpointOperation {
public class WebEndpointOperation extends Operation {
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 id the id of the operation, unique within its endpoint operation
*/
public WebEndpointOperation(EndpointOperationType type,
OperationInvoker operationInvoker, boolean blocking,
OperationRequestPredicate requestPredicate, String id) {
public WebEndpointOperation(OperationType type, OperationInvoker operationInvoker,
boolean blocking, OperationRequestPredicate requestPredicate, String id) {
super(type, operationInvoker, blocking);
this.requestPredicate = requestPredicate;
this.id = id;

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

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

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

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

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

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

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

@ -24,7 +24,7 @@ import org.assertj.core.api.Condition;
import org.junit.Test;
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;
@ -74,7 +74,7 @@ public class EndpointLinksResolverTests {
}
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,
Collections.emptyList(), Collections.emptyList()),
id);

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

Loading…
Cancel
Save