Overhaul actuator endpoint code
Refactor several areas of the actuator endpoint code in order to make future extensions easier. The primary goal is to introduce the concept of an `ExposableEndpoint` that has technology specific subclasses and can carry additional data for filters to use. Many other changes have been made along the way including: * A new EndpointSupplier interface that allows cleaner separation of supplying vs discovering endpoints. This allows cleaner class names and allows for better auto-configuration since a user can choose to provide their own supplier entirely. * A `DiscoveredEndpoint` interface that allows the `EndpointFilter` to be greatly simplified. A filter now doesn't need to know about discovery concerns unless absolutely necessary. * Improved naming and package structure. Many technology specific concerns are now grouped in a better way. Related concerns are co-located and concepts from one area no longer leakage into another. * Simplified `HandlerMapping` implementations. Many common concerns have been pulled up helping to create simpler subclasses. * Simplified JMX adapters. Many of the intermediary `Info` classes have been removed. The `DiscoveredJmxOperation` is now responsible for mapping methods to operations. * A specific @`HealthEndpointCloudFoundryExtension` for Cloud Foundry. The extension logic used to create a "full" health endpoint extension has been made explicit. Fixes gh-11428 Fixes gh-11581
This commit is contained in:
@@ -1,126 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.endpoint;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link NamePatternFilter}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @author Dylian Bego
|
||||
*/
|
||||
public class NamePatternFilterTests {
|
||||
|
||||
@Test
|
||||
public void nonRegex() {
|
||||
MockNamePatternFilter filter = new MockNamePatternFilter();
|
||||
assertThat(filter.getResults("not.a.regex")).containsEntry("not.a.regex",
|
||||
"not.a.regex");
|
||||
assertThat(filter.isGetNamesCalled()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonRegexThatContainsRegexPart() {
|
||||
MockNamePatternFilter filter = new MockNamePatternFilter();
|
||||
assertThat(filter.getResults("*")).containsEntry("*", "*");
|
||||
assertThat(filter.isGetNamesCalled()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void regexRepetitionZeroOrMore() {
|
||||
MockNamePatternFilter filter = new MockNamePatternFilter();
|
||||
Map<String, Object> results = filter.getResults("fo.*");
|
||||
assertThat(results.get("foo")).isEqualTo("foo");
|
||||
assertThat(results.get("fool")).isEqualTo("fool");
|
||||
assertThat(filter.isGetNamesCalled()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void regexRepetitionOneOrMore() {
|
||||
MockNamePatternFilter filter = new MockNamePatternFilter();
|
||||
Map<String, Object> results = filter.getResults("fo.+");
|
||||
assertThat(results.get("foo")).isEqualTo("foo");
|
||||
assertThat(results.get("fool")).isEqualTo("fool");
|
||||
assertThat(filter.isGetNamesCalled()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void regexEndAnchor() {
|
||||
MockNamePatternFilter filter = new MockNamePatternFilter();
|
||||
Map<String, Object> results = filter.getResults("foo$");
|
||||
assertThat(results.get("foo")).isEqualTo("foo");
|
||||
assertThat(results.get("fool")).isNull();
|
||||
assertThat(filter.isGetNamesCalled()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void regexStartAnchor() {
|
||||
MockNamePatternFilter filter = new MockNamePatternFilter();
|
||||
Map<String, Object> results = filter.getResults("^foo");
|
||||
assertThat(results.get("foo")).isEqualTo("foo");
|
||||
assertThat(results.get("fool")).isNull();
|
||||
assertThat(filter.isGetNamesCalled()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void regexCharacterClass() {
|
||||
MockNamePatternFilter filter = new MockNamePatternFilter();
|
||||
Map<String, Object> results = filter.getResults("fo[a-z]l");
|
||||
assertThat(results.get("foo")).isNull();
|
||||
assertThat(results.get("fool")).isEqualTo("fool");
|
||||
assertThat(filter.isGetNamesCalled()).isTrue();
|
||||
}
|
||||
|
||||
private static class MockNamePatternFilter extends NamePatternFilter<Object> {
|
||||
|
||||
MockNamePatternFilter() {
|
||||
super(null);
|
||||
}
|
||||
|
||||
private boolean getNamesCalled;
|
||||
|
||||
@Override
|
||||
protected Object getOptionalValue(Object source, String name) {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object getValue(Object source, String name) {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void getNames(Object source, NameCallback callback) {
|
||||
this.getNamesCalled = true;
|
||||
callback.addName("foo");
|
||||
callback.addName("fool");
|
||||
callback.addName("fume");
|
||||
}
|
||||
|
||||
public boolean isGetNamesCalled() {
|
||||
return this.getNamesCalled;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,497 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.endpoint.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.EndpointDiscoverer;
|
||||
import org.springframework.boot.actuate.endpoint.EndpointFilter;
|
||||
import org.springframework.boot.actuate.endpoint.EndpointInfo;
|
||||
import org.springframework.boot.actuate.endpoint.Operation;
|
||||
import org.springframework.boot.actuate.endpoint.OperationInvoker;
|
||||
import org.springframework.boot.actuate.endpoint.cache.CachingOperationInvoker;
|
||||
import org.springframework.boot.actuate.endpoint.cache.CachingOperationInvokerAdvisor;
|
||||
import org.springframework.boot.actuate.endpoint.convert.ConversionServiceParameterMapper;
|
||||
import org.springframework.boot.actuate.endpoint.reflect.OperationMethodInfo;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link AnnotationEndpointDiscoverer}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class AnnotationEndpointDiscovererTests {
|
||||
|
||||
@Rule
|
||||
public final ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void discoverWorksWhenThereAreNoEndpoints() {
|
||||
load(EmptyConfiguration.class,
|
||||
(context) -> assertThat(new TestAnnotationEndpointDiscoverer(context)
|
||||
.discoverEndpoints().isEmpty()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void endpointIsDiscovered() {
|
||||
load(TestEndpointConfiguration.class, hasTestEndpoint());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void endpointInParentContextIsDiscovered() {
|
||||
AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext(
|
||||
TestEndpointConfiguration.class);
|
||||
loadWithParent(parent, EmptyConfiguration.class, hasTestEndpoint());
|
||||
}
|
||||
|
||||
private Consumer<AnnotationConfigApplicationContext> hasTestEndpoint() {
|
||||
return (context) -> {
|
||||
Map<String, EndpointInfo<TestEndpointOperation>> endpoints = mapEndpoints(
|
||||
new TestAnnotationEndpointDiscoverer(context).discoverEndpoints());
|
||||
assertThat(endpoints).containsOnlyKeys("test");
|
||||
Map<Method, TestEndpointOperation> operations = mapOperations(
|
||||
endpoints.get("test"));
|
||||
assertThat(operations).hasSize(4);
|
||||
assertThat(operations).containsKeys(
|
||||
ReflectionUtils.findMethod(TestEndpoint.class, "getAll"),
|
||||
ReflectionUtils.findMethod(TestEndpoint.class, "getOne",
|
||||
String.class),
|
||||
ReflectionUtils.findMethod(TestEndpoint.class, "update", String.class,
|
||||
String.class),
|
||||
ReflectionUtils.findMethod(TestEndpoint.class, "deleteOne",
|
||||
String.class));
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void subclassedEndpointIsDiscovered() {
|
||||
load(TestEndpointSubclassConfiguration.class, (context) -> {
|
||||
Map<String, EndpointInfo<TestEndpointOperation>> endpoints = mapEndpoints(
|
||||
new TestAnnotationEndpointDiscoverer(context).discoverEndpoints());
|
||||
assertThat(endpoints).containsOnlyKeys("test");
|
||||
Map<Method, TestEndpointOperation> operations = mapOperations(
|
||||
endpoints.get("test"));
|
||||
assertThat(operations).hasSize(5);
|
||||
assertThat(operations).containsKeys(
|
||||
ReflectionUtils.findMethod(TestEndpoint.class, "getAll"),
|
||||
ReflectionUtils.findMethod(TestEndpoint.class, "getOne",
|
||||
String.class),
|
||||
ReflectionUtils.findMethod(TestEndpoint.class, "update", String.class,
|
||||
String.class),
|
||||
ReflectionUtils.findMethod(TestEndpoint.class, "deleteOne",
|
||||
String.class),
|
||||
ReflectionUtils.findMethod(TestEndpointSubclass.class,
|
||||
"updateWithMoreArguments", String.class, String.class,
|
||||
String.class));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void discoveryFailsWhenTwoEndpointsHaveTheSameId() {
|
||||
load(ClashingEndpointConfiguration.class, (context) -> {
|
||||
this.thrown.expect(IllegalStateException.class);
|
||||
this.thrown.expectMessage("Found two endpoints with the id 'test': ");
|
||||
new TestAnnotationEndpointDiscoverer(context).discoverEndpoints();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void endpointMainReadOperationIsNotCachedWithTtlSetToZero() {
|
||||
Function<String, Long> timeToLive = (endpointId) -> 0L;
|
||||
load(TestEndpointConfiguration.class, (context) -> {
|
||||
Map<String, EndpointInfo<TestEndpointOperation>> endpoints = mapEndpoints(
|
||||
new TestAnnotationEndpointDiscoverer(context, timeToLive)
|
||||
.discoverEndpoints());
|
||||
assertThat(endpoints).containsOnlyKeys("test");
|
||||
Map<Method, TestEndpointOperation> operations = mapOperations(
|
||||
endpoints.get("test"));
|
||||
assertThat(operations).hasSize(4);
|
||||
operations.values().forEach(operation -> assertThat(operation.getInvoker())
|
||||
.isNotInstanceOf(CachingOperationInvoker.class));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void endpointMainReadOperationIsNotCachedWithNonMatchingId() {
|
||||
Function<String, Long> timeToLive = (id) -> (id.equals("foo") ? 500L : 0L);
|
||||
load(TestEndpointConfiguration.class, (context) -> {
|
||||
Map<String, EndpointInfo<TestEndpointOperation>> endpoints = mapEndpoints(
|
||||
new TestAnnotationEndpointDiscoverer(context, timeToLive)
|
||||
.discoverEndpoints());
|
||||
assertThat(endpoints).containsOnlyKeys("test");
|
||||
Map<Method, TestEndpointOperation> operations = mapOperations(
|
||||
endpoints.get("test"));
|
||||
assertThat(operations).hasSize(4);
|
||||
operations.values().forEach(operation -> assertThat(operation.getInvoker())
|
||||
.isNotInstanceOf(CachingOperationInvoker.class));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void endpointMainReadOperationIsCachedWithMatchingId() {
|
||||
Function<String, Long> timeToLive = (id) -> (id.equals("test") ? 500L : 0L);
|
||||
load(TestEndpointConfiguration.class, (context) -> {
|
||||
Map<String, EndpointInfo<TestEndpointOperation>> endpoints = mapEndpoints(
|
||||
new TestAnnotationEndpointDiscoverer(context, timeToLive)
|
||||
.discoverEndpoints());
|
||||
assertThat(endpoints).containsOnlyKeys("test");
|
||||
Map<Method, TestEndpointOperation> operations = mapOperations(
|
||||
endpoints.get("test"));
|
||||
OperationInvoker getAllOperationInvoker = operations
|
||||
.get(ReflectionUtils.findMethod(TestEndpoint.class, "getAll"))
|
||||
.getInvoker();
|
||||
assertThat(getAllOperationInvoker)
|
||||
.isInstanceOf(CachingOperationInvoker.class);
|
||||
assertThat(((CachingOperationInvoker) getAllOperationInvoker).getTimeToLive())
|
||||
.isEqualTo(500);
|
||||
assertThat(operations.get(ReflectionUtils.findMethod(TestEndpoint.class,
|
||||
"getOne", String.class)).getInvoker())
|
||||
.isNotInstanceOf(CachingOperationInvoker.class);
|
||||
assertThat(operations.get(ReflectionUtils.findMethod(TestEndpoint.class,
|
||||
"update", String.class, String.class)).getInvoker())
|
||||
.isNotInstanceOf(CachingOperationInvoker.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void specializedEndpointsAreFilteredFromRegular() {
|
||||
load(TestEndpointsConfiguration.class, (context) -> {
|
||||
Map<String, EndpointInfo<TestEndpointOperation>> endpoints = mapEndpoints(
|
||||
new TestAnnotationEndpointDiscoverer(context).discoverEndpoints());
|
||||
assertThat(endpoints).containsOnlyKeys("test");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void specializedEndpointsAreNotFilteredFromSpecialized() {
|
||||
load(TestEndpointsConfiguration.class, (context) -> {
|
||||
Map<String, EndpointInfo<SpecializedTestEndpointOperation>> endpoints = mapEndpoints(
|
||||
new SpecializedTestAnnotationEndpointDiscoverer(context)
|
||||
.discoverEndpoints());
|
||||
assertThat(endpoints).containsOnlyKeys("test", "specialized");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extensionsAreApplied() {
|
||||
load(TestEndpointsConfiguration.class, (context) -> {
|
||||
Map<String, EndpointInfo<SpecializedTestEndpointOperation>> endpoints = mapEndpoints(
|
||||
new SpecializedTestAnnotationEndpointDiscoverer(context)
|
||||
.discoverEndpoints());
|
||||
Map<Method, TestEndpointOperation> operations = mapOperations(
|
||||
endpoints.get("specialized"));
|
||||
assertThat(operations).containsKeys(
|
||||
ReflectionUtils.findMethod(SpecializedExtension.class, "getSpecial"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filtersAreApplied() {
|
||||
load(TestEndpointsConfiguration.class, (context) -> {
|
||||
EndpointFilter<SpecializedTestEndpointOperation> filter = (info,
|
||||
discoverer) -> !(info.getId().equals("specialized"));
|
||||
Map<String, EndpointInfo<SpecializedTestEndpointOperation>> endpoints = mapEndpoints(
|
||||
new SpecializedTestAnnotationEndpointDiscoverer(context,
|
||||
Collections.singleton(filter)).discoverEndpoints());
|
||||
assertThat(endpoints).containsOnlyKeys("test");
|
||||
});
|
||||
}
|
||||
|
||||
private <T extends Operation> Map<String, EndpointInfo<T>> mapEndpoints(
|
||||
Collection<EndpointInfo<T>> endpoints) {
|
||||
Map<String, EndpointInfo<T>> endpointById = new LinkedHashMap<>();
|
||||
endpoints.forEach((endpoint) -> {
|
||||
EndpointInfo<T> existing = endpointById.put(endpoint.getId(), endpoint);
|
||||
if (existing != null) {
|
||||
throw new AssertionError(String.format(
|
||||
"Found endpoints with duplicate id '%s'", endpoint.getId()));
|
||||
}
|
||||
});
|
||||
return endpointById;
|
||||
}
|
||||
|
||||
private Map<Method, TestEndpointOperation> mapOperations(
|
||||
EndpointInfo<? extends TestEndpointOperation> endpoint) {
|
||||
Map<Method, TestEndpointOperation> operationByMethod = new HashMap<>();
|
||||
endpoint.getOperations().forEach((operation) -> {
|
||||
Method method = operation.getMethodInfo().getMethod();
|
||||
Operation existing = operationByMethod.put(method, operation);
|
||||
if (existing != null) {
|
||||
throw new AssertionError(String.format(
|
||||
"Found endpoint with duplicate operation method '%s'", method));
|
||||
}
|
||||
});
|
||||
return operationByMethod;
|
||||
}
|
||||
|
||||
private void load(Class<?> configuration,
|
||||
Consumer<AnnotationConfigApplicationContext> consumer) {
|
||||
doLoad(null, configuration, consumer);
|
||||
}
|
||||
|
||||
private void loadWithParent(ApplicationContext parent, Class<?> configuration,
|
||||
Consumer<AnnotationConfigApplicationContext> consumer) {
|
||||
doLoad(parent, configuration, consumer);
|
||||
}
|
||||
|
||||
private void doLoad(ApplicationContext parent, Class<?> configuration,
|
||||
Consumer<AnnotationConfigApplicationContext> consumer) {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
if (parent != null) {
|
||||
context.setParent(parent);
|
||||
}
|
||||
context.register(configuration);
|
||||
context.refresh();
|
||||
try {
|
||||
consumer.accept(context);
|
||||
}
|
||||
finally {
|
||||
context.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class EmptyConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@Endpoint(id = "test")
|
||||
static class TestEndpoint {
|
||||
|
||||
@ReadOperation
|
||||
public Object getAll() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ReadOperation
|
||||
public Object getOne(@Selector String id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@WriteOperation
|
||||
public void update(String foo, String bar) {
|
||||
|
||||
}
|
||||
|
||||
@DeleteOperation
|
||||
public void deleteOne(@Selector String id) {
|
||||
|
||||
}
|
||||
|
||||
public void someOtherMethod() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SpecializedEndpoint(id = "specialized")
|
||||
static class SpecializedTestEndpoint {
|
||||
|
||||
@ReadOperation
|
||||
public Object getAll() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class TestEndpointSubclass extends TestEndpoint {
|
||||
|
||||
@WriteOperation
|
||||
public void updateWithMoreArguments(String foo, String bar, String baz) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class TestEndpointConfiguration {
|
||||
|
||||
@Bean
|
||||
public TestEndpoint testEndpoint() {
|
||||
return new TestEndpoint();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class TestEndpointSubclassConfiguration {
|
||||
|
||||
@Bean
|
||||
public TestEndpointSubclass testEndpointSubclass() {
|
||||
return new TestEndpointSubclass();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Import({ TestEndpoint.class, SpecializedTestEndpoint.class,
|
||||
SpecializedExtension.class })
|
||||
static class TestEndpointsConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ClashingEndpointConfiguration {
|
||||
|
||||
@Bean
|
||||
public TestEndpoint testEndpointTwo() {
|
||||
return new TestEndpoint();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TestEndpoint testEndpointOne() {
|
||||
return new TestEndpoint();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Endpoint
|
||||
@FilteredEndpoint(SpecializedEndpointFilter.class)
|
||||
public @interface SpecializedEndpoint {
|
||||
|
||||
@AliasFor(annotation = Endpoint.class)
|
||||
String id();
|
||||
|
||||
}
|
||||
|
||||
@EndpointExtension(endpoint = SpecializedTestEndpoint.class, filter = SpecializedEndpointFilter.class)
|
||||
public static class SpecializedExtension {
|
||||
|
||||
@ReadOperation
|
||||
public Object getSpecial() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class SpecializedEndpointFilter
|
||||
implements EndpointFilter<SpecializedTestEndpointOperation> {
|
||||
|
||||
@Override
|
||||
public boolean match(EndpointInfo<SpecializedTestEndpointOperation> info,
|
||||
EndpointDiscoverer<SpecializedTestEndpointOperation> discoverer) {
|
||||
return discoverer instanceof SpecializedTestAnnotationEndpointDiscoverer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class TestAnnotationEndpointDiscoverer
|
||||
extends AnnotationEndpointDiscoverer<Method, TestEndpointOperation> {
|
||||
|
||||
TestAnnotationEndpointDiscoverer(ApplicationContext applicationContext) {
|
||||
this(applicationContext, (id) -> null, null);
|
||||
}
|
||||
|
||||
TestAnnotationEndpointDiscoverer(ApplicationContext applicationContext,
|
||||
Function<String, Long> timeToLive) {
|
||||
this(applicationContext, timeToLive, null);
|
||||
}
|
||||
|
||||
TestAnnotationEndpointDiscoverer(ApplicationContext applicationContext,
|
||||
Function<String, Long> timeToLive,
|
||||
Collection<? extends EndpointFilter<TestEndpointOperation>> filters) {
|
||||
super(applicationContext, TestEndpointOperation::new,
|
||||
TestEndpointOperation::getMethod,
|
||||
new ConversionServiceParameterMapper(),
|
||||
Collections.singleton(new CachingOperationInvokerAdvisor(timeToLive)),
|
||||
filters);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class SpecializedTestAnnotationEndpointDiscoverer extends
|
||||
AnnotationEndpointDiscoverer<Method, SpecializedTestEndpointOperation> {
|
||||
|
||||
SpecializedTestAnnotationEndpointDiscoverer(
|
||||
ApplicationContext applicationContext) {
|
||||
this(applicationContext, (id) -> null, null);
|
||||
}
|
||||
|
||||
SpecializedTestAnnotationEndpointDiscoverer(ApplicationContext applicationContext,
|
||||
Collection<? extends EndpointFilter<SpecializedTestEndpointOperation>> filters) {
|
||||
this(applicationContext, (id) -> null, filters);
|
||||
}
|
||||
|
||||
SpecializedTestAnnotationEndpointDiscoverer(ApplicationContext applicationContext,
|
||||
Function<String, Long> timeToLive,
|
||||
Collection<? extends EndpointFilter<SpecializedTestEndpointOperation>> filters) {
|
||||
super(applicationContext, SpecializedTestEndpointOperation::new,
|
||||
SpecializedTestEndpointOperation::getMethod,
|
||||
new ConversionServiceParameterMapper(),
|
||||
Collections.singleton(new CachingOperationInvokerAdvisor(timeToLive)),
|
||||
filters);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class TestEndpointOperation extends Operation {
|
||||
|
||||
private final OperationMethodInfo methodInfo;
|
||||
|
||||
public TestEndpointOperation(String endpointId, OperationMethodInfo methodInfo,
|
||||
Object target, OperationInvoker invoker) {
|
||||
super(methodInfo.getOperationType(), invoker, true);
|
||||
this.methodInfo = methodInfo;
|
||||
}
|
||||
|
||||
public Method getMethod() {
|
||||
return this.methodInfo.getMethod();
|
||||
}
|
||||
|
||||
public OperationMethodInfo getMethodInfo() {
|
||||
return this.methodInfo;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class SpecializedTestEndpointOperation extends TestEndpointOperation {
|
||||
|
||||
public SpecializedTestEndpointOperation(String endpointId,
|
||||
OperationMethodInfo methodInfo, Object target, OperationInvoker invoker) {
|
||||
super(endpointId, methodInfo, target, invoker);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.endpoint.annotation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.OperationType;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link DiscoveredOperationMethod}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class DiscoveredOperationMethodTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void createWhenAnnotationAttributesIsNullShouldThrowException() {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("AnnotationAttributes must not be null");
|
||||
Method method = ReflectionUtils.findMethod(getClass(), "example");
|
||||
new DiscoveredOperationMethod(method, OperationType.READ, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getProducesMediaTypesShouldReturnMediaTypes() {
|
||||
Method method = ReflectionUtils.findMethod(getClass(), "example");
|
||||
AnnotationAttributes annotationAttributes = new AnnotationAttributes();
|
||||
String[] produces = new String[] { "application/json" };
|
||||
annotationAttributes.put("produces", produces);
|
||||
DiscoveredOperationMethod discovered = new DiscoveredOperationMethod(method,
|
||||
OperationType.READ, annotationAttributes);
|
||||
assertThat(discovered.getProducesMediaTypes())
|
||||
.containsExactly("application/json");
|
||||
}
|
||||
|
||||
public void example() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.endpoint.annotation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.OperationType;
|
||||
import org.springframework.boot.actuate.endpoint.invoke.OperationInvoker;
|
||||
import org.springframework.boot.actuate.endpoint.invoke.OperationInvokerAdvisor;
|
||||
import org.springframework.boot.actuate.endpoint.invoke.OperationParameters;
|
||||
import org.springframework.boot.actuate.endpoint.invoke.ParameterValueMapper;
|
||||
import org.springframework.boot.actuate.endpoint.invoke.reflect.OperationMethod;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link DiscoveredOperationsFactory}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class DiscoveredOperationsFactoryTests {
|
||||
|
||||
private TestDiscoveredOperationsFactory factory;
|
||||
|
||||
private ParameterValueMapper parameterValueMapper;
|
||||
|
||||
private List<OperationInvokerAdvisor> invokerAdvisors;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.parameterValueMapper = (parameter, value) -> value.toString();
|
||||
this.invokerAdvisors = new ArrayList<>();
|
||||
this.factory = new TestDiscoveredOperationsFactory(this.parameterValueMapper,
|
||||
this.invokerAdvisors);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createOperationsWhenHasReadMethodShouldCreateOperation() {
|
||||
Collection<TestOperation> operations = this.factory.createOperations("test",
|
||||
new ExampleRead());
|
||||
assertThat(operations).hasSize(1);
|
||||
TestOperation operation = getFirst(operations);
|
||||
assertThat(operation.getType()).isEqualTo(OperationType.READ);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createOperationsWhenHasWriteMethodShouldCreateOperation() {
|
||||
Collection<TestOperation> operations = this.factory.createOperations("test",
|
||||
new ExampleWrite());
|
||||
assertThat(operations).hasSize(1);
|
||||
TestOperation operation = getFirst(operations);
|
||||
assertThat(operation.getType()).isEqualTo(OperationType.WRITE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createOperationsWhenHasDeleteMethodShouldCreateOperation() {
|
||||
Collection<TestOperation> operations = this.factory.createOperations("test",
|
||||
new ExampleDelete());
|
||||
assertThat(operations).hasSize(1);
|
||||
TestOperation operation = getFirst(operations);
|
||||
assertThat(operation.getType()).isEqualTo(OperationType.DELETE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createOperationsWhenMultipleShouldReturnMultiple() {
|
||||
Collection<TestOperation> operations = this.factory.createOperations("test",
|
||||
new ExampleMultiple());
|
||||
assertThat(operations).hasSize(2);
|
||||
assertThat(operations.stream().map(TestOperation::getType))
|
||||
.containsOnly(OperationType.READ, OperationType.WRITE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createOperationsShouldProvideOperationMethod() {
|
||||
TestOperation operation = getFirst(
|
||||
this.factory.createOperations("test", new ExampleWithParams()));
|
||||
OperationMethod operationMethod = operation.getOperationMethod();
|
||||
assertThat(operationMethod.getMethod().getName()).isEqualTo("read");
|
||||
assertThat(operationMethod.getParameters().hasParameters()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createOperationsShouldProviderInvoker() {
|
||||
TestOperation operation = getFirst(
|
||||
this.factory.createOperations("test", new ExampleWithParams()));
|
||||
Map<String, Object> params = Collections.singletonMap("name", 123);
|
||||
Object result = operation.invoke(params);
|
||||
assertThat(result).isEqualTo("123");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createOperationShouldApplyAdvisors() {
|
||||
TestOperationInvokerAdvisor advisor = new TestOperationInvokerAdvisor();
|
||||
this.invokerAdvisors.add(advisor);
|
||||
TestOperation operation = getFirst(
|
||||
this.factory.createOperations("test", new ExampleRead()));
|
||||
operation.invoke(Collections.emptyMap());
|
||||
assertThat(advisor.getEndpointId()).isEqualTo("test");
|
||||
assertThat(advisor.getOperationType()).isEqualTo(OperationType.READ);
|
||||
assertThat(advisor.getParameters()).isEmpty();
|
||||
}
|
||||
|
||||
private <T> T getFirst(Iterable<T> iterable) {
|
||||
return iterable.iterator().next();
|
||||
}
|
||||
|
||||
static class ExampleRead {
|
||||
|
||||
@ReadOperation
|
||||
public String read() {
|
||||
return "read";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class ExampleWrite {
|
||||
|
||||
@WriteOperation
|
||||
public String write() {
|
||||
return "write";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class ExampleDelete {
|
||||
|
||||
@DeleteOperation
|
||||
public String delete() {
|
||||
return "delete";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class ExampleMultiple {
|
||||
|
||||
@ReadOperation
|
||||
public String read() {
|
||||
return "read";
|
||||
}
|
||||
|
||||
@WriteOperation
|
||||
public String write() {
|
||||
return "write";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class ExampleWithParams {
|
||||
|
||||
@ReadOperation
|
||||
public String read(String name) {
|
||||
return name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class TestDiscoveredOperationsFactory
|
||||
extends DiscoveredOperationsFactory<TestOperation> {
|
||||
|
||||
TestDiscoveredOperationsFactory(ParameterValueMapper parameterValueMapper,
|
||||
Collection<OperationInvokerAdvisor> invokerAdvisors) {
|
||||
super(parameterValueMapper, invokerAdvisors);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TestOperation createOperation(String endpointId,
|
||||
DiscoveredOperationMethod operationMethod, OperationInvoker invoker) {
|
||||
return new TestOperation(endpointId, operationMethod, invoker);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class TestOperation extends AbstractDiscoveredOperation {
|
||||
|
||||
TestOperation(String endpointId, DiscoveredOperationMethod operationMethod,
|
||||
OperationInvoker invoker) {
|
||||
super(operationMethod, invoker);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class TestOperationInvokerAdvisor implements OperationInvokerAdvisor {
|
||||
|
||||
private String endpointId;
|
||||
|
||||
private OperationType operationType;
|
||||
|
||||
private OperationParameters parameters;
|
||||
|
||||
@Override
|
||||
public OperationInvoker apply(String endpointId, OperationType operationType,
|
||||
OperationParameters parameters, OperationInvoker invoker) {
|
||||
this.endpointId = endpointId;
|
||||
this.operationType = operationType;
|
||||
this.parameters = parameters;
|
||||
return invoker;
|
||||
}
|
||||
|
||||
public String getEndpointId() {
|
||||
return this.endpointId;
|
||||
}
|
||||
|
||||
public OperationType getOperationType() {
|
||||
return this.operationType;
|
||||
}
|
||||
|
||||
public OperationParameters getParameters() {
|
||||
return this.parameters;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.endpoint.annotation;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.EndpointFilter;
|
||||
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.Operation;
|
||||
import org.springframework.boot.actuate.endpoint.invoke.OperationInvokerAdvisor;
|
||||
import org.springframework.boot.actuate.endpoint.invoke.ParameterValueMapper;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link DiscovererEndpointFilter}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class DiscovererEndpointFilterTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void createWhenDiscovererIsNullShouldThrowException() {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("Discoverer must not be null");
|
||||
new TestDiscovererEndpointFilter(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchWhenDiscoveredByDiscovererShouldReturnTrue() {
|
||||
DiscovererEndpointFilter filter = new TestDiscovererEndpointFilter(
|
||||
TestDiscovererA.class);
|
||||
DiscoveredEndpoint<?> endpoint = mockDiscoveredEndpoint(TestDiscovererA.class);
|
||||
assertThat(filter.match(endpoint)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchWhenNotDiscoveredByDiscovererShouldReturnFalse() {
|
||||
DiscovererEndpointFilter filter = new TestDiscovererEndpointFilter(
|
||||
TestDiscovererA.class);
|
||||
DiscoveredEndpoint<?> endpoint = mockDiscoveredEndpoint(TestDiscovererB.class);
|
||||
assertThat(filter.match(endpoint)).isFalse();
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private DiscoveredEndpoint<?> mockDiscoveredEndpoint(Class<?> discoverer) {
|
||||
DiscoveredEndpoint endpoint = mock(DiscoveredEndpoint.class);
|
||||
given(endpoint.wasDiscoveredBy(discoverer)).willReturn(true);
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
static class TestDiscovererEndpointFilter extends DiscovererEndpointFilter {
|
||||
|
||||
TestDiscovererEndpointFilter(
|
||||
Class<? extends EndpointDiscoverer<?, ?>> discoverer) {
|
||||
super(discoverer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
abstract static class TestDiscovererA
|
||||
extends EndpointDiscoverer<ExposableEndpoint<Operation>, Operation> {
|
||||
|
||||
TestDiscovererA(ApplicationContext applicationContext,
|
||||
ParameterValueMapper parameterValueMapper,
|
||||
Collection<OperationInvokerAdvisor> invokerAdvisors,
|
||||
Collection<EndpointFilter<ExposableEndpoint<Operation>>> filters) {
|
||||
super(applicationContext, parameterValueMapper, invokerAdvisors, filters);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
abstract static class TestDiscovererB
|
||||
extends EndpointDiscoverer<ExposableEndpoint<Operation>, Operation> {
|
||||
|
||||
TestDiscovererB(ApplicationContext applicationContext,
|
||||
ParameterValueMapper parameterValueMapper,
|
||||
Collection<OperationInvokerAdvisor> invokerAdvisors,
|
||||
Collection<EndpointFilter<ExposableEndpoint<Operation>>> filters) {
|
||||
super(applicationContext, parameterValueMapper, invokerAdvisors, filters);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,579 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.endpoint.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
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;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.EndpointFilter;
|
||||
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.Operation;
|
||||
import org.springframework.boot.actuate.endpoint.invoke.OperationInvoker;
|
||||
import org.springframework.boot.actuate.endpoint.invoke.OperationInvokerAdvisor;
|
||||
import org.springframework.boot.actuate.endpoint.invoke.ParameterValueMapper;
|
||||
import org.springframework.boot.actuate.endpoint.invoke.convert.ConversionServiceParameterValueMapper;
|
||||
import org.springframework.boot.actuate.endpoint.invoker.cache.CachingOperationInvoker;
|
||||
import org.springframework.boot.actuate.endpoint.invoker.cache.CachingOperationInvokerAdvisor;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link EndpointDiscoverer}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class EndpointDiscovererTests {
|
||||
|
||||
@Rule
|
||||
public final ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void createWhenApplicationContextIsNullShouldThrowException() {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("ApplicationContext must not be null");
|
||||
new TestEndpointDiscoverer(null, mock(ParameterValueMapper.class),
|
||||
Collections.emptyList(), Collections.emptyList());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createWhenParameterValueMapperIsNullShouldThrowException() {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("ParameterValueMapper must not be null");
|
||||
new TestEndpointDiscoverer(mock(ApplicationContext.class), null,
|
||||
Collections.emptyList(), Collections.emptyList());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createWhenInvokerAdvisorsIsNullShouldThrowException() {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("InvokerAdvisors must not be null");
|
||||
new TestEndpointDiscoverer(mock(ApplicationContext.class),
|
||||
mock(ParameterValueMapper.class), null, Collections.emptyList());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createWhenFiltersIsNullShouldThrowException() {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("Filters must not be null");
|
||||
new TestEndpointDiscoverer(mock(ApplicationContext.class),
|
||||
mock(ParameterValueMapper.class), Collections.emptyList(), null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getEndpointsWhenNoEndpointBeansShouldReturnEmptyCollection() {
|
||||
load(EmptyConfiguration.class, (context) -> {
|
||||
TestEndpointDiscoverer discoverer = new TestEndpointDiscoverer(context);
|
||||
Collection<TestExposableEndpoint> endpoints = discoverer.getEndpoints();
|
||||
assertThat(endpoints).isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getEndpointsWhenHasEndpointShouldReturnEndpoint() {
|
||||
load(TestEndpointConfiguration.class, this::hasTestEndpoint);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getEndpointsWhenHasEndpointInParentContextShouldReturnEndpoint() {
|
||||
AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext(
|
||||
TestEndpointConfiguration.class);
|
||||
loadWithParent(parent, EmptyConfiguration.class, this::hasTestEndpoint);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getEndpointsWhenHasSubclassedEndpointShouldReturnEndpoint() {
|
||||
load(TestEndpointSubclassConfiguration.class, (context) -> {
|
||||
TestEndpointDiscoverer discoverer = new TestEndpointDiscoverer(context);
|
||||
Map<String, TestExposableEndpoint> endpoints = mapEndpoints(
|
||||
discoverer.getEndpoints());
|
||||
assertThat(endpoints).containsOnlyKeys("test");
|
||||
Map<Method, TestOperation> operations = mapOperations(endpoints.get("test"));
|
||||
assertThat(operations).hasSize(5);
|
||||
assertThat(operations).containsKeys(testEndpointMethods());
|
||||
assertThat(operations).containsKeys(ReflectionUtils.findMethod(
|
||||
TestEndpointSubclass.class, "updateWithMoreArguments", String.class,
|
||||
String.class, String.class));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getEndpointsWhenTwoEndpointsHaveTheSameIdShouldThrowException() {
|
||||
load(ClashingEndpointConfiguration.class, (context) -> {
|
||||
this.thrown.expect(IllegalStateException.class);
|
||||
this.thrown.expectMessage("Found two endpoints with the id 'test': ");
|
||||
new TestEndpointDiscoverer(context).getEndpoints();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getEndpointsWhenTtlSetToZeroShouldNotCacheInvokeCalls() {
|
||||
load(TestEndpointConfiguration.class, (context) -> {
|
||||
TestEndpointDiscoverer discoverer = new TestEndpointDiscoverer(context,
|
||||
(endpointId) -> 0L);
|
||||
Map<String, TestExposableEndpoint> endpoints = mapEndpoints(
|
||||
discoverer.getEndpoints());
|
||||
assertThat(endpoints).containsOnlyKeys("test");
|
||||
Map<Method, TestOperation> operations = mapOperations(endpoints.get("test"));
|
||||
operations.values().forEach((operation) -> assertThat(operation.getInvoker())
|
||||
.isNotInstanceOf(CachingOperationInvoker.class));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getEndpointsWhenTtlSetByIdAndIdDoesntMatchShouldNotCacheInvokeCalls() {
|
||||
load(TestEndpointConfiguration.class, (context) -> {
|
||||
TestEndpointDiscoverer discoverer = new TestEndpointDiscoverer(context,
|
||||
(endpointId) -> (endpointId.equals("foo") ? 500L : 0L));
|
||||
Map<String, TestExposableEndpoint> endpoints = mapEndpoints(
|
||||
discoverer.getEndpoints());
|
||||
assertThat(endpoints).containsOnlyKeys("test");
|
||||
Map<Method, TestOperation> operations = mapOperations(endpoints.get("test"));
|
||||
operations.values().forEach((operation) -> assertThat(operation.getInvoker())
|
||||
.isNotInstanceOf(CachingOperationInvoker.class));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getEndpointsWhenTtlSetByIdAndIdMatchesShouldCacheInvokeCalls() {
|
||||
load(TestEndpointConfiguration.class, (context) -> {
|
||||
TestEndpointDiscoverer discoverer = new TestEndpointDiscoverer(context,
|
||||
(endpointId) -> (endpointId.equals("test") ? 500L : 0L));
|
||||
Map<String, TestExposableEndpoint> endpoints = mapEndpoints(
|
||||
discoverer.getEndpoints());
|
||||
assertThat(endpoints).containsOnlyKeys("test");
|
||||
Map<Method, TestOperation> operations = mapOperations(endpoints.get("test"));
|
||||
TestOperation getAll = operations.get(findTestEndpointMethod("getAll"));
|
||||
TestOperation getOne = operations
|
||||
.get(findTestEndpointMethod("getOne", String.class));
|
||||
TestOperation update = operations.get(ReflectionUtils.findMethod(
|
||||
TestEndpoint.class, "update", String.class, String.class));
|
||||
assertThat(((CachingOperationInvoker) getAll.getInvoker()).getTimeToLive())
|
||||
.isEqualTo(500);
|
||||
assertThat(getOne.getInvoker())
|
||||
.isNotInstanceOf(CachingOperationInvoker.class);
|
||||
assertThat(update.getInvoker())
|
||||
.isNotInstanceOf(CachingOperationInvoker.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getEndpointsWhenHasSpecializedFiltersInNonSpecializedDiscovererShouldFilterEndpoints() {
|
||||
load(SpecializedEndpointsConfiguration.class, (context) -> {
|
||||
TestEndpointDiscoverer discoverer = new TestEndpointDiscoverer(context);
|
||||
Map<String, TestExposableEndpoint> endpoints = mapEndpoints(
|
||||
discoverer.getEndpoints());
|
||||
assertThat(endpoints).containsOnlyKeys("test");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getEndpointsWhenHasSpecializedFiltersInSpecializedDiscovererShouldNotFilterEndpoints() {
|
||||
load(SpecializedEndpointsConfiguration.class, (context) -> {
|
||||
SpecializedEndpointDiscoverer discoverer = new SpecializedEndpointDiscoverer(
|
||||
context);
|
||||
Map<String, SpecializedExposableEndpoint> endpoints = mapEndpoints(
|
||||
discoverer.getEndpoints());
|
||||
assertThat(endpoints).containsOnlyKeys("test", "specialized");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getEndpointsShouldApplyExtensions() {
|
||||
load(SpecializedEndpointsConfiguration.class, (context) -> {
|
||||
SpecializedEndpointDiscoverer discoverer = new SpecializedEndpointDiscoverer(
|
||||
context);
|
||||
Map<String, SpecializedExposableEndpoint> endpoints = mapEndpoints(
|
||||
discoverer.getEndpoints());
|
||||
Map<Method, SpecializedOperation> operations = mapOperations(
|
||||
endpoints.get("specialized"));
|
||||
assertThat(operations).containsKeys(
|
||||
ReflectionUtils.findMethod(SpecializedExtension.class, "getSpecial"));
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getEndpointsShouldApplyFilters() {
|
||||
load(SpecializedEndpointsConfiguration.class, (context) -> {
|
||||
EndpointFilter<SpecializedExposableEndpoint> filter = (endpoint) -> {
|
||||
String id = endpoint.getId();
|
||||
return !id.equals("specialized");
|
||||
};
|
||||
SpecializedEndpointDiscoverer discoverer = new SpecializedEndpointDiscoverer(
|
||||
context, Collections.singleton(filter));
|
||||
Map<String, SpecializedExposableEndpoint> endpoints = mapEndpoints(
|
||||
discoverer.getEndpoints());
|
||||
assertThat(endpoints).containsOnlyKeys("test");
|
||||
});
|
||||
}
|
||||
|
||||
private void hasTestEndpoint(AnnotationConfigApplicationContext context) {
|
||||
TestEndpointDiscoverer discoverer = new TestEndpointDiscoverer(context);
|
||||
Map<String, TestExposableEndpoint> endpoints = mapEndpoints(
|
||||
discoverer.getEndpoints());
|
||||
assertThat(endpoints).containsOnlyKeys("test");
|
||||
Map<Method, TestOperation> operations = mapOperations(endpoints.get("test"));
|
||||
assertThat(operations).hasSize(4);
|
||||
assertThat(operations).containsKeys();
|
||||
}
|
||||
|
||||
private Method[] testEndpointMethods() {
|
||||
List<Method> methods = new ArrayList<>();
|
||||
methods.add(findTestEndpointMethod("getAll"));
|
||||
methods.add(findTestEndpointMethod("getOne", String.class));
|
||||
methods.add(findTestEndpointMethod("update", String.class, String.class));
|
||||
methods.add(findTestEndpointMethod("deleteOne", String.class));
|
||||
return methods.toArray(new Method[] {});
|
||||
}
|
||||
|
||||
private Method findTestEndpointMethod(String name, Class<?>... paramTypes) {
|
||||
return ReflectionUtils.findMethod(TestEndpoint.class, name, paramTypes);
|
||||
}
|
||||
|
||||
private <E extends ExposableEndpoint<?>> Map<String, E> mapEndpoints(
|
||||
Collection<E> endpoints) {
|
||||
Map<String, E> byId = new LinkedHashMap<>();
|
||||
endpoints.forEach((endpoint) -> {
|
||||
E existing = byId.put(endpoint.getId(), endpoint);
|
||||
if (existing != null) {
|
||||
throw new AssertionError(String.format(
|
||||
"Found endpoints with duplicate id '%s'", endpoint.getId()));
|
||||
}
|
||||
});
|
||||
return byId;
|
||||
}
|
||||
|
||||
private <O extends Operation> Map<Method, O> mapOperations(
|
||||
ExposableEndpoint<O> endpoint) {
|
||||
Map<Method, O> byMethod = new HashMap<>();
|
||||
endpoint.getOperations().forEach((operation) -> {
|
||||
AbstractDiscoveredOperation discoveredOperation = (AbstractDiscoveredOperation) operation;
|
||||
Method method = discoveredOperation.getOperationMethod().getMethod();
|
||||
O existing = byMethod.put(method, operation);
|
||||
if (existing != null) {
|
||||
throw new AssertionError(String.format(
|
||||
"Found endpoint with duplicate operation method '%s'", method));
|
||||
}
|
||||
});
|
||||
return byMethod;
|
||||
}
|
||||
|
||||
private void load(Class<?> configuration,
|
||||
Consumer<AnnotationConfigApplicationContext> consumer) {
|
||||
load(null, configuration, consumer);
|
||||
}
|
||||
|
||||
private void loadWithParent(ApplicationContext parent, Class<?> configuration,
|
||||
Consumer<AnnotationConfigApplicationContext> consumer) {
|
||||
load(parent, configuration, consumer);
|
||||
}
|
||||
|
||||
private void load(ApplicationContext parent, Class<?> configuration,
|
||||
Consumer<AnnotationConfigApplicationContext> consumer) {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
if (parent != null) {
|
||||
context.setParent(parent);
|
||||
}
|
||||
context.register(configuration);
|
||||
context.refresh();
|
||||
try {
|
||||
consumer.accept(context);
|
||||
}
|
||||
finally {
|
||||
context.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class EmptyConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class TestEndpointConfiguration {
|
||||
|
||||
@Bean
|
||||
public TestEndpoint testEndpoint() {
|
||||
return new TestEndpoint();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class TestEndpointSubclassConfiguration {
|
||||
|
||||
@Bean
|
||||
public TestEndpointSubclass testEndpointSubclass() {
|
||||
return new TestEndpointSubclass();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ClashingEndpointConfiguration {
|
||||
|
||||
@Bean
|
||||
public TestEndpoint testEndpointTwo() {
|
||||
return new TestEndpoint();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TestEndpoint testEndpointOne() {
|
||||
return new TestEndpoint();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Import({ TestEndpoint.class, SpecializedTestEndpoint.class,
|
||||
SpecializedExtension.class })
|
||||
static class SpecializedEndpointsConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@Endpoint(id = "test")
|
||||
static class TestEndpoint {
|
||||
|
||||
@ReadOperation
|
||||
public Object getAll() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ReadOperation
|
||||
public Object getOne(@Selector String id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@WriteOperation
|
||||
public void update(String foo, String bar) {
|
||||
|
||||
}
|
||||
|
||||
@DeleteOperation
|
||||
public void deleteOne(@Selector String id) {
|
||||
|
||||
}
|
||||
|
||||
public void someOtherMethod() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class TestEndpointSubclass extends TestEndpoint {
|
||||
|
||||
@WriteOperation
|
||||
public void updateWithMoreArguments(String foo, String bar, String baz) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Endpoint
|
||||
@FilteredEndpoint(SpecializedEndpointFilter.class)
|
||||
public @interface SpecializedEndpoint {
|
||||
|
||||
@AliasFor(annotation = Endpoint.class)
|
||||
String id();
|
||||
|
||||
}
|
||||
|
||||
@EndpointExtension(endpoint = SpecializedTestEndpoint.class, filter = SpecializedEndpointFilter.class)
|
||||
public static class SpecializedExtension {
|
||||
|
||||
@ReadOperation
|
||||
public Object getSpecial() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class SpecializedEndpointFilter extends DiscovererEndpointFilter {
|
||||
|
||||
SpecializedEndpointFilter() {
|
||||
super(SpecializedEndpointDiscoverer.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SpecializedEndpoint(id = "specialized")
|
||||
static class SpecializedTestEndpoint {
|
||||
|
||||
@ReadOperation
|
||||
public Object getAll() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class TestEndpointDiscoverer
|
||||
extends EndpointDiscoverer<TestExposableEndpoint, TestOperation> {
|
||||
|
||||
TestEndpointDiscoverer(ApplicationContext applicationContext) {
|
||||
this(applicationContext, (id) -> null);
|
||||
}
|
||||
|
||||
TestEndpointDiscoverer(ApplicationContext applicationContext,
|
||||
Function<String, Long> timeToLive) {
|
||||
this(applicationContext, timeToLive, Collections.emptyList());
|
||||
}
|
||||
|
||||
TestEndpointDiscoverer(ApplicationContext applicationContext,
|
||||
Function<String, Long> timeToLive,
|
||||
Collection<EndpointFilter<TestExposableEndpoint>> filters) {
|
||||
this(applicationContext, new ConversionServiceParameterValueMapper(),
|
||||
Collections.singleton(new CachingOperationInvokerAdvisor(timeToLive)),
|
||||
filters);
|
||||
}
|
||||
|
||||
TestEndpointDiscoverer(ApplicationContext applicationContext,
|
||||
ParameterValueMapper parameterValueMapper,
|
||||
Collection<OperationInvokerAdvisor> invokerAdvisors,
|
||||
Collection<EndpointFilter<TestExposableEndpoint>> filters) {
|
||||
super(applicationContext, parameterValueMapper, invokerAdvisors, filters);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TestExposableEndpoint createEndpoint(String id,
|
||||
boolean enabledByDefault, Collection<TestOperation> operations) {
|
||||
return new TestExposableEndpoint(this, id, enabledByDefault, operations);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TestOperation createOperation(String endpointId,
|
||||
DiscoveredOperationMethod operationMethod, OperationInvoker invoker) {
|
||||
return new TestOperation(operationMethod, invoker);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected OperationKey createOperationKey(TestOperation operation) {
|
||||
return new OperationKey(operation.getOperationMethod(),
|
||||
() -> "TestOperation " + operation.getOperationMethod());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class SpecializedEndpointDiscoverer extends
|
||||
EndpointDiscoverer<SpecializedExposableEndpoint, SpecializedOperation> {
|
||||
|
||||
SpecializedEndpointDiscoverer(ApplicationContext applicationContext) {
|
||||
this(applicationContext, Collections.emptyList());
|
||||
}
|
||||
|
||||
SpecializedEndpointDiscoverer(ApplicationContext applicationContext,
|
||||
Collection<EndpointFilter<SpecializedExposableEndpoint>> filters) {
|
||||
super(applicationContext, new ConversionServiceParameterValueMapper(),
|
||||
Collections.emptyList(), filters);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SpecializedExposableEndpoint createEndpoint(String id,
|
||||
boolean enabledByDefault, Collection<SpecializedOperation> operations) {
|
||||
return new SpecializedExposableEndpoint(this, id, enabledByDefault,
|
||||
operations);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SpecializedOperation createOperation(String endpointId,
|
||||
DiscoveredOperationMethod operationMethod, OperationInvoker invoker) {
|
||||
return new SpecializedOperation(operationMethod, invoker);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected OperationKey createOperationKey(SpecializedOperation operation) {
|
||||
return new OperationKey(operation.getOperationMethod(),
|
||||
() -> "TestOperation " + operation.getOperationMethod());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class TestExposableEndpoint extends AbstractDiscoveredEndpoint<TestOperation> {
|
||||
|
||||
TestExposableEndpoint(EndpointDiscoverer<?, ?> discoverer, String id,
|
||||
boolean enabledByDefault,
|
||||
Collection<? extends TestOperation> operations) {
|
||||
super(discoverer, id, enabledByDefault, operations);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class SpecializedExposableEndpoint
|
||||
extends AbstractDiscoveredEndpoint<SpecializedOperation> {
|
||||
|
||||
SpecializedExposableEndpoint(EndpointDiscoverer<?, ?> discoverer, String id,
|
||||
boolean enabledByDefault,
|
||||
Collection<? extends SpecializedOperation> operations) {
|
||||
super(discoverer, id, enabledByDefault, operations);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class TestOperation extends AbstractDiscoveredOperation {
|
||||
|
||||
private final OperationInvoker invoker;
|
||||
|
||||
TestOperation(DiscoveredOperationMethod operationMethod,
|
||||
OperationInvoker invoker) {
|
||||
super(operationMethod, invoker);
|
||||
this.invoker = invoker;
|
||||
}
|
||||
|
||||
public OperationInvoker getInvoker() {
|
||||
return this.invoker;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class SpecializedOperation extends TestOperation {
|
||||
|
||||
SpecializedOperation(DiscoveredOperationMethod operationMethod,
|
||||
OperationInvoker invoker) {
|
||||
super(operationMethod, invoker);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.endpoint.convert;
|
||||
package org.springframework.boot.actuate.endpoint.invoke.convert;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
@@ -22,7 +22,8 @@ import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.reflect.ParameterMappingException;
|
||||
import org.springframework.boot.actuate.endpoint.invoke.OperationParameter;
|
||||
import org.springframework.boot.actuate.endpoint.invoke.ParameterMappingException;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.format.support.DefaultFormattingConversionService;
|
||||
@@ -36,11 +37,11 @@ import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* Tests for {@link ConversionServiceParameterMapper}.
|
||||
* Tests for {@link ConversionServiceParameterValueMapper}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class ConversionServiceParameterMapperTests {
|
||||
public class ConversionServiceParameterValueMapperTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
@@ -49,9 +50,10 @@ public class ConversionServiceParameterMapperTests {
|
||||
public void mapParameterShouldDelegateToConversionService() {
|
||||
DefaultFormattingConversionService conversionService = spy(
|
||||
new DefaultFormattingConversionService());
|
||||
ConversionServiceParameterMapper mapper = new ConversionServiceParameterMapper(
|
||||
ConversionServiceParameterValueMapper mapper = new ConversionServiceParameterValueMapper(
|
||||
conversionService);
|
||||
Integer mapped = mapper.mapParameter("123", Integer.class);
|
||||
Object mapped = mapper
|
||||
.mapParameterValue(new TestOperationParameter(Integer.class), "123");
|
||||
assertThat(mapped).isEqualTo(123);
|
||||
verify(conversionService).convert("123", Integer.class);
|
||||
}
|
||||
@@ -61,34 +63,61 @@ public class ConversionServiceParameterMapperTests {
|
||||
ConversionService conversionService = mock(ConversionService.class);
|
||||
RuntimeException error = new RuntimeException();
|
||||
given(conversionService.convert(any(), any())).willThrow(error);
|
||||
ConversionServiceParameterMapper mapper = new ConversionServiceParameterMapper(
|
||||
ConversionServiceParameterValueMapper mapper = new ConversionServiceParameterValueMapper(
|
||||
conversionService);
|
||||
try {
|
||||
mapper.mapParameter("123", Integer.class);
|
||||
mapper.mapParameterValue(new TestOperationParameter(Integer.class), "123");
|
||||
fail("Did not throw");
|
||||
}
|
||||
catch (ParameterMappingException ex) {
|
||||
assertThat(ex.getInput()).isEqualTo("123");
|
||||
assertThat(ex.getType()).isEqualTo(Integer.class);
|
||||
assertThat(ex.getValue()).isEqualTo("123");
|
||||
assertThat(ex.getParameter().getType()).isEqualTo(Integer.class);
|
||||
assertThat(ex.getCause()).isEqualTo(error);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createShouldRegisterIsoOffsetDateTimeConverter() {
|
||||
ConversionServiceParameterMapper mapper = new ConversionServiceParameterMapper();
|
||||
OffsetDateTime mapped = mapper.mapParameter("2011-12-03T10:15:30+01:00",
|
||||
OffsetDateTime.class);
|
||||
ConversionServiceParameterValueMapper mapper = new ConversionServiceParameterValueMapper();
|
||||
Object mapped = mapper.mapParameterValue(
|
||||
new TestOperationParameter(OffsetDateTime.class),
|
||||
"2011-12-03T10:15:30+01:00");
|
||||
assertThat(mapped).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createWithConversionServiceShouldNotRegisterIsoOffsetDateTimeConverter() {
|
||||
ConversionService conversionService = new DefaultConversionService();
|
||||
ConversionServiceParameterMapper mapper = new ConversionServiceParameterMapper(
|
||||
ConversionServiceParameterValueMapper mapper = new ConversionServiceParameterValueMapper(
|
||||
conversionService);
|
||||
this.thrown.expect(ParameterMappingException.class);
|
||||
mapper.mapParameter("2011-12-03T10:15:30+01:00", OffsetDateTime.class);
|
||||
mapper.mapParameterValue(new TestOperationParameter(OffsetDateTime.class),
|
||||
"2011-12-03T10:15:30+01:00");
|
||||
}
|
||||
|
||||
private static class TestOperationParameter implements OperationParameter {
|
||||
|
||||
private final Class<?> type;
|
||||
|
||||
TestOperationParameter(Class<?> type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "test";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNullable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.endpoint.convert;
|
||||
package org.springframework.boot.actuate.endpoint.invoke.convert;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.endpoint.invoke.reflect;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link OperationMethodParameter}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class OperationMethodParameterTests {
|
||||
|
||||
private Method method = ReflectionUtils.findMethod(getClass(), "example",
|
||||
String.class, String.class);
|
||||
|
||||
@Test
|
||||
public void getNameShouldReturnName() {
|
||||
OperationMethodParameter parameter = new OperationMethodParameter("name",
|
||||
this.method.getParameters()[0]);
|
||||
assertThat(parameter.getName()).isEqualTo("name");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getTypeShouldReturnTyoe() {
|
||||
OperationMethodParameter parameter = new OperationMethodParameter("name",
|
||||
this.method.getParameters()[0]);
|
||||
assertThat(parameter.getType()).isEqualTo(String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isNullableWhenNoAnnotationShouldReturnFalse() {
|
||||
OperationMethodParameter parameter = new OperationMethodParameter("name",
|
||||
this.method.getParameters()[0]);
|
||||
assertThat(parameter.isNullable()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isNullableWhenNullableAnnotationShouldReturnTrue() {
|
||||
OperationMethodParameter parameter = new OperationMethodParameter("name",
|
||||
this.method.getParameters()[1]);
|
||||
assertThat(parameter.isNullable()).isTrue();
|
||||
}
|
||||
|
||||
void example(String one, @Nullable String two) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.endpoint.invoke.reflect;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Spliterator;
|
||||
import java.util.Spliterators;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.invoke.OperationParameter;
|
||||
import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link OperationMethodParameters}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class OperationMethodParametersTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
private Method exampleMethod = ReflectionUtils.findMethod(getClass(), "example",
|
||||
String.class);
|
||||
|
||||
private Method exampleNoParamsMethod = ReflectionUtils.findMethod(getClass(),
|
||||
"exampleNoParams");
|
||||
|
||||
@Test
|
||||
public void createWhenMethodIsNullShouldThrowException() {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("Method must not be null");
|
||||
new OperationMethodParameters(null, mock(ParameterNameDiscoverer.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createWhenParameterNameDiscovererIsNullShouldThrowException() {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("ParameterNameDiscoverer must not be null");
|
||||
new OperationMethodParameters(this.exampleMethod, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createWhenParameterNameDiscovererReturnsNullShouldThrowException() {
|
||||
this.thrown.expect(IllegalStateException.class);
|
||||
this.thrown.expectMessage("Failed to extract parameter names");
|
||||
new OperationMethodParameters(this.exampleMethod,
|
||||
mock(ParameterNameDiscoverer.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasParametersWhenHasParametersShouldReturnTrue() {
|
||||
OperationMethodParameters parameters = new OperationMethodParameters(
|
||||
this.exampleMethod, new DefaultParameterNameDiscoverer());
|
||||
assertThat(parameters.hasParameters()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasParametersWhenHasNoParametersShouldReturnFalse() {
|
||||
OperationMethodParameters parameters = new OperationMethodParameters(
|
||||
this.exampleNoParamsMethod, new DefaultParameterNameDiscoverer());
|
||||
assertThat(parameters.hasParameters()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getParameterCountShouldReturnParameterCount() {
|
||||
OperationMethodParameters parameters = new OperationMethodParameters(
|
||||
this.exampleMethod, new DefaultParameterNameDiscoverer());
|
||||
assertThat(parameters.getParameterCount()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void iteratorShouldIterateOperationParameters() {
|
||||
OperationMethodParameters parameters = new OperationMethodParameters(
|
||||
this.exampleMethod, new DefaultParameterNameDiscoverer());
|
||||
Iterator<OperationParameter> iterator = parameters.iterator();
|
||||
assertParameters(StreamSupport.stream(
|
||||
Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED),
|
||||
false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void streamShouldStreamOperationParameters() {
|
||||
OperationMethodParameters parameters = new OperationMethodParameters(
|
||||
this.exampleMethod, new DefaultParameterNameDiscoverer());
|
||||
assertParameters(parameters.stream());
|
||||
}
|
||||
|
||||
private void assertParameters(Stream<OperationParameter> stream) {
|
||||
List<OperationParameter> parameters = stream.collect(Collectors.toList());
|
||||
assertThat(parameters).hasSize(1);
|
||||
OperationParameter parameter = parameters.get(0);
|
||||
assertThat(parameter.getName()).isEqualTo("name");
|
||||
assertThat(parameter.getType()).isEqualTo(String.class);
|
||||
}
|
||||
|
||||
String example(String name) {
|
||||
return name;
|
||||
}
|
||||
|
||||
String exampleNoParams() {
|
||||
return "example";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.endpoint.invoke.reflect;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.OperationType;
|
||||
import org.springframework.boot.actuate.endpoint.invoke.OperationParameters;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link OperationMethod}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class OperationMethodTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
private Method exampleMethod = ReflectionUtils.findMethod(getClass(), "example",
|
||||
String.class);
|
||||
|
||||
@Test
|
||||
public void createWhenMethodIsNullShouldThrowException() {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("Method must not be null");
|
||||
new OperationMethod(null, OperationType.READ);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createWhenOperationTypeIsNullShouldThrowException() {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("OperationType must not be null");
|
||||
new OperationMethod(this.exampleMethod, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getMethodShouldReturnMethod() {
|
||||
OperationMethod operationMethod = new OperationMethod(this.exampleMethod,
|
||||
OperationType.READ);
|
||||
assertThat(operationMethod.getMethod()).isEqualTo(this.exampleMethod);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getOperationTypeShouldReturnOperationType() {
|
||||
OperationMethod operationMethod = new OperationMethod(this.exampleMethod,
|
||||
OperationType.READ);
|
||||
assertThat(operationMethod.getOperationType()).isEqualTo(OperationType.READ);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getParametersShouldReturnParameters() {
|
||||
OperationMethod operationMethod = new OperationMethod(this.exampleMethod,
|
||||
OperationType.READ);
|
||||
OperationParameters parameters = operationMethod.getParameters();
|
||||
assertThat(parameters.getParameterCount()).isEqualTo(1);
|
||||
assertThat(parameters.iterator().next().getName()).isEqualTo("name");
|
||||
}
|
||||
|
||||
String example(String name) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.endpoint.invoke.reflect;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.OperationType;
|
||||
import org.springframework.boot.actuate.endpoint.invoke.MissingParametersException;
|
||||
import org.springframework.boot.actuate.endpoint.invoke.ParameterValueMapper;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link ReflectiveOperationInvoker}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class ReflectiveOperationInvokerTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
private Example target;
|
||||
|
||||
private OperationMethod operationMethod;
|
||||
|
||||
private ParameterValueMapper parameterValueMapper;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.target = new Example();
|
||||
this.operationMethod = new OperationMethod(
|
||||
ReflectionUtils.findMethod(Example.class, "reverse", String.class),
|
||||
OperationType.READ);
|
||||
this.parameterValueMapper = (parameter,
|
||||
value) -> (value == null ? null : value.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createWhenTargetIsNullShouldThrowException() {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("Target must not be null");
|
||||
new ReflectiveOperationInvoker(null, this.operationMethod,
|
||||
this.parameterValueMapper);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createWhenOperationMethodIsNullShouldThrowException() {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("OperationMethod must not be null");
|
||||
new ReflectiveOperationInvoker(this.target, null, this.parameterValueMapper);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createWhenParamaterValueMapperIsNullShouldThrowException() {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("ParameterValueMapper must not be null");
|
||||
new ReflectiveOperationInvoker(this.target, this.operationMethod, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invokeShouldInvokeMethod() {
|
||||
ReflectiveOperationInvoker invoker = new ReflectiveOperationInvoker(this.target,
|
||||
this.operationMethod, this.parameterValueMapper);
|
||||
Object result = invoker.invoke(Collections.singletonMap("name", "boot"));
|
||||
assertThat(result).isEqualTo("toob");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invokeWhenMissingNonNullableArgmentShouldThrowException() {
|
||||
ReflectiveOperationInvoker invoker = new ReflectiveOperationInvoker(this.target,
|
||||
this.operationMethod, this.parameterValueMapper);
|
||||
this.thrown.expect(MissingParametersException.class);
|
||||
invoker.invoke(Collections.singletonMap("name", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invokeWhenMissingNullableArgumentShouldInvoke() {
|
||||
OperationMethod operationMethod = new OperationMethod(ReflectionUtils.findMethod(
|
||||
Example.class, "reverseNullable", String.class), OperationType.READ);
|
||||
ReflectiveOperationInvoker invoker = new ReflectiveOperationInvoker(this.target,
|
||||
operationMethod, this.parameterValueMapper);
|
||||
Object result = invoker.invoke(Collections.singletonMap("name", null));
|
||||
assertThat(result).isEqualTo("llun");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invokeShouldResolveParameters() {
|
||||
ReflectiveOperationInvoker invoker = new ReflectiveOperationInvoker(this.target,
|
||||
this.operationMethod, this.parameterValueMapper);
|
||||
Object result = invoker.invoke(Collections.singletonMap("name", 1234));
|
||||
assertThat(result).isEqualTo("4321");
|
||||
}
|
||||
|
||||
static class Example {
|
||||
|
||||
String reverse(String name) {
|
||||
return new StringBuilder(name).reverse().toString();
|
||||
}
|
||||
|
||||
String reverseNullable(@Nullable String name) {
|
||||
return new StringBuilder(String.valueOf(name)).reverse().toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.endpoint.cache;
|
||||
package org.springframework.boot.actuate.endpoint.invoker.cache;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.function.Function;
|
||||
@@ -24,10 +24,10 @@ import org.junit.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.OperationInvoker;
|
||||
import org.springframework.boot.actuate.endpoint.OperationType;
|
||||
import org.springframework.boot.actuate.endpoint.reflect.OperationMethodInfo;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.boot.actuate.endpoint.invoke.OperationInvoker;
|
||||
import org.springframework.boot.actuate.endpoint.invoke.OperationParameters;
|
||||
import org.springframework.boot.actuate.endpoint.invoke.reflect.OperationMethod;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
@@ -59,53 +59,62 @@ public class CachingOperationInvokerAdvisorTests {
|
||||
|
||||
@Test
|
||||
public void applyWhenOperationIsNotReadShouldNotAddAdvise() {
|
||||
OperationMethodInfo info = mockInfo(OperationType.WRITE, "get");
|
||||
OperationInvoker advised = this.advisor.apply("foo", info, this.invoker);
|
||||
OperationParameters parameters = getParameters("get");
|
||||
OperationInvoker advised = this.advisor.apply("foo", OperationType.WRITE,
|
||||
parameters, this.invoker);
|
||||
assertThat(advised).isSameAs(this.invoker);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void applyWhenHasParametersShouldNotAddAdvise() {
|
||||
OperationMethodInfo info = mockInfo(OperationType.READ, "getWithParameter",
|
||||
String.class);
|
||||
OperationInvoker advised = this.advisor.apply("foo", info, this.invoker);
|
||||
OperationParameters parameters = getParameters("getWithParameter", String.class);
|
||||
OperationInvoker advised = this.advisor.apply("foo", OperationType.READ,
|
||||
parameters, this.invoker);
|
||||
assertThat(advised).isSameAs(this.invoker);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void applyWhenTimeToLiveReturnsNullShouldNotAddAdvise() {
|
||||
OperationMethodInfo info = mockInfo(OperationType.READ, "get");
|
||||
OperationParameters parameters = getParameters("get");
|
||||
given(this.timeToLive.apply(any())).willReturn(null);
|
||||
OperationInvoker advised = this.advisor.apply("foo", info, this.invoker);
|
||||
OperationInvoker advised = this.advisor.apply("foo", OperationType.READ,
|
||||
parameters, this.invoker);
|
||||
assertThat(advised).isSameAs(this.invoker);
|
||||
verify(this.timeToLive).apply("foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void applyWhenTimeToLiveIsZeroShouldNotAddAdvise() {
|
||||
OperationMethodInfo info = mockInfo(OperationType.READ, "get");
|
||||
OperationParameters parameters = getParameters("get");
|
||||
given(this.timeToLive.apply(any())).willReturn(0L);
|
||||
OperationInvoker advised = this.advisor.apply("foo", info, this.invoker);
|
||||
OperationInvoker advised = this.advisor.apply("foo", OperationType.READ,
|
||||
parameters, this.invoker);
|
||||
assertThat(advised).isSameAs(this.invoker);
|
||||
verify(this.timeToLive).apply("foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void applyShouldAddCacheAdvise() {
|
||||
OperationMethodInfo info = mockInfo(OperationType.READ, "get");
|
||||
OperationParameters parameters = getParameters("get");
|
||||
given(this.timeToLive.apply(any())).willReturn(100L);
|
||||
OperationInvoker advised = this.advisor.apply("foo", info, this.invoker);
|
||||
OperationInvoker advised = this.advisor.apply("foo", OperationType.READ,
|
||||
parameters, this.invoker);
|
||||
assertThat(advised).isInstanceOf(CachingOperationInvoker.class);
|
||||
assertThat(ReflectionTestUtils.getField(advised, "target"))
|
||||
assertThat(ReflectionTestUtils.getField(advised, "invoker"))
|
||||
.isEqualTo(this.invoker);
|
||||
assertThat(ReflectionTestUtils.getField(advised, "timeToLive")).isEqualTo(100L);
|
||||
}
|
||||
|
||||
private OperationMethodInfo mockInfo(OperationType operationType, String methodName,
|
||||
private OperationParameters getParameters(String methodName,
|
||||
Class<?>... parameterTypes) {
|
||||
return getOperationMethod(methodName, parameterTypes).getParameters();
|
||||
}
|
||||
|
||||
private OperationMethod getOperationMethod(String methodName,
|
||||
Class<?>... parameterTypes) {
|
||||
Method method = ReflectionUtils.findMethod(TestOperations.class, methodName,
|
||||
parameterTypes);
|
||||
return new OperationMethodInfo(method, operationType, new AnnotationAttributes());
|
||||
return new OperationMethod(method, OperationType.READ);
|
||||
}
|
||||
|
||||
public static class TestOperations {
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.endpoint.cache;
|
||||
package org.springframework.boot.actuate.endpoint.invoker.cache;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@@ -23,7 +23,7 @@ import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.OperationInvoker;
|
||||
import org.springframework.boot.actuate.endpoint.invoke.OperationInvoker;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
@@ -44,7 +44,7 @@ public class CachingOperationInvokerTests {
|
||||
|
||||
@Test
|
||||
public void createInstanceWithTtlSetToZero() {
|
||||
this.thrown.expect(IllegalStateException.class);
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("TimeToLive");
|
||||
new CachingOperationInvoker(mock(OperationInvoker.class), 0);
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.endpoint.jmx;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.management.MBeanInfo;
|
||||
import javax.management.MBeanOperationInfo;
|
||||
import javax.management.MBeanParameterInfo;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.EndpointInfo;
|
||||
import org.springframework.boot.actuate.endpoint.OperationInvoker;
|
||||
import org.springframework.boot.actuate.endpoint.OperationType;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.entry;
|
||||
|
||||
/**
|
||||
* Tests for {@link EndpointMBeanInfoAssembler}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class EndpointMBeanInfoAssemblerTests {
|
||||
|
||||
private final EndpointMBeanInfoAssembler mBeanInfoAssembler = new EndpointMBeanInfoAssembler(
|
||||
new DummyOperationResponseMapper());
|
||||
|
||||
@Test
|
||||
public void exposeSimpleReadOperation() {
|
||||
JmxOperation operation = new JmxOperation(OperationType.READ,
|
||||
new DummyOperationInvoker(), "getAll", Object.class, "Test operation",
|
||||
Collections.emptyList());
|
||||
EndpointInfo<JmxOperation> endpoint = new EndpointInfo<>("test", true,
|
||||
Collections.singletonList(operation));
|
||||
EndpointMBeanInfo endpointMBeanInfo = this.mBeanInfoAssembler
|
||||
.createEndpointMBeanInfo(endpoint);
|
||||
assertThat(endpointMBeanInfo).isNotNull();
|
||||
assertThat(endpointMBeanInfo.getEndpointId()).isEqualTo("test");
|
||||
assertThat(endpointMBeanInfo.getOperations())
|
||||
.containsOnly(entry("getAll", operation));
|
||||
MBeanInfo mbeanInfo = endpointMBeanInfo.getMbeanInfo();
|
||||
assertThat(mbeanInfo).isNotNull();
|
||||
assertThat(mbeanInfo.getClassName()).isEqualTo(EndpointMBean.class.getName());
|
||||
assertThat(mbeanInfo.getDescription())
|
||||
.isEqualTo("MBean operations for endpoint test");
|
||||
assertThat(mbeanInfo.getAttributes()).isEmpty();
|
||||
assertThat(mbeanInfo.getNotifications()).isEmpty();
|
||||
assertThat(mbeanInfo.getConstructors()).isEmpty();
|
||||
assertThat(mbeanInfo.getOperations()).hasSize(1);
|
||||
MBeanOperationInfo mBeanOperationInfo = mbeanInfo.getOperations()[0];
|
||||
assertThat(mBeanOperationInfo.getName()).isEqualTo("getAll");
|
||||
assertThat(mBeanOperationInfo.getReturnType()).isEqualTo(Object.class.getName());
|
||||
assertThat(mBeanOperationInfo.getImpact()).isEqualTo(MBeanOperationInfo.INFO);
|
||||
assertThat(mBeanOperationInfo.getSignature()).hasSize(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void exposeSimpleWriteOperation() {
|
||||
JmxOperation operation = new JmxOperation(OperationType.WRITE,
|
||||
new DummyOperationInvoker(), "update", Object.class, "Update operation",
|
||||
Collections.singletonList(new JmxEndpointOperationParameterInfo("test",
|
||||
String.class, "Test argument")));
|
||||
EndpointInfo<JmxOperation> endpoint = new EndpointInfo<>("another", true,
|
||||
Collections.singletonList(operation));
|
||||
EndpointMBeanInfo endpointMBeanInfo = this.mBeanInfoAssembler
|
||||
.createEndpointMBeanInfo(endpoint);
|
||||
assertThat(endpointMBeanInfo).isNotNull();
|
||||
assertThat(endpointMBeanInfo.getEndpointId()).isEqualTo("another");
|
||||
assertThat(endpointMBeanInfo.getOperations())
|
||||
.containsOnly(entry("update", operation));
|
||||
MBeanInfo mbeanInfo = endpointMBeanInfo.getMbeanInfo();
|
||||
assertThat(mbeanInfo).isNotNull();
|
||||
assertThat(mbeanInfo.getClassName()).isEqualTo(EndpointMBean.class.getName());
|
||||
assertThat(mbeanInfo.getDescription())
|
||||
.isEqualTo("MBean operations for endpoint another");
|
||||
assertThat(mbeanInfo.getAttributes()).isEmpty();
|
||||
assertThat(mbeanInfo.getNotifications()).isEmpty();
|
||||
assertThat(mbeanInfo.getConstructors()).isEmpty();
|
||||
assertThat(mbeanInfo.getOperations()).hasSize(1);
|
||||
MBeanOperationInfo mBeanOperationInfo = mbeanInfo.getOperations()[0];
|
||||
assertThat(mBeanOperationInfo.getName()).isEqualTo("update");
|
||||
assertThat(mBeanOperationInfo.getReturnType()).isEqualTo(Object.class.getName());
|
||||
assertThat(mBeanOperationInfo.getImpact()).isEqualTo(MBeanOperationInfo.ACTION);
|
||||
assertThat(mBeanOperationInfo.getSignature()).hasSize(1);
|
||||
MBeanParameterInfo mBeanParameterInfo = mBeanOperationInfo.getSignature()[0];
|
||||
assertThat(mBeanParameterInfo.getName()).isEqualTo("test");
|
||||
assertThat(mBeanParameterInfo.getType()).isEqualTo(String.class.getName());
|
||||
assertThat(mBeanParameterInfo.getDescription()).isEqualTo("Test argument");
|
||||
}
|
||||
|
||||
private static class DummyOperationInvoker implements OperationInvoker {
|
||||
|
||||
@Override
|
||||
public Object invoke(Map<String, Object> arguments) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class DummyOperationResponseMapper
|
||||
implements JmxOperationResponseMapper {
|
||||
|
||||
@Override
|
||||
public Object mapResponse(Object response) {
|
||||
return response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> mapResponseType(Class<?> responseType) {
|
||||
return responseType;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.endpoint.jmx;
|
||||
|
||||
import javax.management.InstanceNotFoundException;
|
||||
import javax.management.MBeanRegistrationException;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.MalformedObjectNameException;
|
||||
import javax.management.ObjectName;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.jmx.JmxException;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.BDDMockito.willThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* Tests for {@link EndpointMBeanRegistrar}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class EndpointMBeanRegistrarTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
private MBeanServer mBeanServer = mock(MBeanServer.class);
|
||||
|
||||
@Test
|
||||
public void mBeanServerMustNotBeNull() {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
new EndpointMBeanRegistrar(null, (e) -> new ObjectName("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void objectNameFactoryMustNotBeNull() {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
new EndpointMBeanRegistrar(this.mBeanServer, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void endpointMustNotBeNull() {
|
||||
EndpointMBeanRegistrar registrar = new EndpointMBeanRegistrar(this.mBeanServer,
|
||||
(e) -> new ObjectName("foo"));
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("Endpoint must not be null");
|
||||
registrar.registerEndpointMBean(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void registerEndpointInvokesObjectNameFactory()
|
||||
throws MalformedObjectNameException {
|
||||
EndpointObjectNameFactory factory = mock(EndpointObjectNameFactory.class);
|
||||
EndpointMBean endpointMBean = mock(EndpointMBean.class);
|
||||
ObjectName objectName = mock(ObjectName.class);
|
||||
given(factory.generate(endpointMBean)).willReturn(objectName);
|
||||
EndpointMBeanRegistrar registrar = new EndpointMBeanRegistrar(this.mBeanServer,
|
||||
factory);
|
||||
ObjectName actualObjectName = registrar.registerEndpointMBean(endpointMBean);
|
||||
assertThat(actualObjectName).isSameAs(objectName);
|
||||
verify(factory).generate(endpointMBean);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void registerEndpointInvalidObjectName() throws MalformedObjectNameException {
|
||||
EndpointMBean endpointMBean = mock(EndpointMBean.class);
|
||||
given(endpointMBean.getEndpointId()).willReturn("test");
|
||||
EndpointObjectNameFactory factory = mock(EndpointObjectNameFactory.class);
|
||||
given(factory.generate(endpointMBean))
|
||||
.willThrow(new MalformedObjectNameException());
|
||||
EndpointMBeanRegistrar registrar = new EndpointMBeanRegistrar(this.mBeanServer,
|
||||
factory);
|
||||
this.thrown.expect(IllegalStateException.class);
|
||||
this.thrown.expectMessage("Invalid ObjectName for endpoint with id 'test'");
|
||||
registrar.registerEndpointMBean(endpointMBean);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void registerEndpointFailure() throws Exception {
|
||||
EndpointMBean endpointMBean = mock(EndpointMBean.class);
|
||||
given(endpointMBean.getEndpointId()).willReturn("test");
|
||||
EndpointObjectNameFactory factory = mock(EndpointObjectNameFactory.class);
|
||||
ObjectName objectName = mock(ObjectName.class);
|
||||
given(factory.generate(endpointMBean)).willReturn(objectName);
|
||||
given(this.mBeanServer.registerMBean(endpointMBean, objectName))
|
||||
.willThrow(MBeanRegistrationException.class);
|
||||
EndpointMBeanRegistrar registrar = new EndpointMBeanRegistrar(this.mBeanServer,
|
||||
factory);
|
||||
this.thrown.expect(JmxException.class);
|
||||
this.thrown.expectMessage("Failed to register MBean for endpoint with id 'test'");
|
||||
registrar.registerEndpointMBean(endpointMBean);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unregisterEndpoint() throws Exception {
|
||||
ObjectName objectName = mock(ObjectName.class);
|
||||
EndpointMBeanRegistrar registrar = new EndpointMBeanRegistrar(this.mBeanServer,
|
||||
mock(EndpointObjectNameFactory.class));
|
||||
assertThat(registrar.unregisterEndpointMbean(objectName)).isTrue();
|
||||
verify(this.mBeanServer).unregisterMBean(objectName);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unregisterUnknownEndpoint() throws Exception {
|
||||
ObjectName objectName = mock(ObjectName.class);
|
||||
willThrow(InstanceNotFoundException.class).given(this.mBeanServer)
|
||||
.unregisterMBean(objectName);
|
||||
EndpointMBeanRegistrar registrar = new EndpointMBeanRegistrar(this.mBeanServer,
|
||||
mock(EndpointObjectNameFactory.class));
|
||||
assertThat(registrar.unregisterEndpointMbean(objectName)).isFalse();
|
||||
verify(this.mBeanServer).unregisterMBean(objectName);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,428 +16,136 @@
|
||||
|
||||
package org.springframework.boot.actuate.endpoint.jmx;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.management.Attribute;
|
||||
import javax.management.AttributeList;
|
||||
import javax.management.AttributeNotFoundException;
|
||||
import javax.management.InstanceNotFoundException;
|
||||
import javax.management.InvalidAttributeValueException;
|
||||
import javax.management.MBeanException;
|
||||
import javax.management.MBeanInfo;
|
||||
import javax.management.MBeanOperationInfo;
|
||||
import javax.management.MBeanParameterInfo;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.MBeanServerFactory;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.ReflectionException;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.annotation.DeleteOperation;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
|
||||
import org.springframework.boot.actuate.endpoint.convert.ConversionServiceParameterMapper;
|
||||
import org.springframework.boot.actuate.endpoint.jmx.annotation.JmxAnnotationEndpointDiscoverer;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* Tests for {@link EndpointMBean}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class EndpointMBeanTests {
|
||||
|
||||
private final JmxEndpointMBeanFactory jmxEndpointMBeanFactory = new JmxEndpointMBeanFactory(
|
||||
new TestJmxOperationResponseMapper());
|
||||
private static final Object[] NO_PARAMS = {};
|
||||
|
||||
private MBeanServer server;
|
||||
private static final String[] NO_SIGNATURE = {};
|
||||
|
||||
private EndpointMBeanRegistrar endpointMBeanRegistrar;
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
private EndpointObjectNameFactory objectNameFactory = (endpoint) -> new ObjectName(
|
||||
String.format("org.springframework.boot.test:type=Endpoint,name=%s",
|
||||
UUID.randomUUID().toString()));
|
||||
private TestExposableJmxEndpoint endpoint = new TestExposableJmxEndpoint(
|
||||
new TestJmxOperation());
|
||||
|
||||
@Before
|
||||
public void createMBeanServer() {
|
||||
this.server = MBeanServerFactory.createMBeanServer();
|
||||
this.endpointMBeanRegistrar = new EndpointMBeanRegistrar(this.server,
|
||||
this.objectNameFactory);
|
||||
}
|
||||
private TestJmxOperationResponseMapper responseMapper = new TestJmxOperationResponseMapper();
|
||||
|
||||
@After
|
||||
public void disposeMBeanServer() {
|
||||
if (this.server != null) {
|
||||
MBeanServerFactory.releaseMBeanServer(this.server);
|
||||
}
|
||||
@Test
|
||||
public void createWhenResponseMapperIsNullShouldThrowException() {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("ResponseMapper must not be null");
|
||||
new EndpointMBean(null, mock(ExposableJmxEndpoint.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invokeSimpleEndpoint() {
|
||||
load(FooEndpoint.class, (discoverer) -> {
|
||||
ObjectName objectName = registerEndpoint(discoverer, "foo");
|
||||
try {
|
||||
// getAll
|
||||
Object allResponse = this.server.invoke(objectName, "getAll",
|
||||
new Object[0], new String[0]);
|
||||
assertThat(allResponse).isEqualTo("[ONE, TWO]");
|
||||
|
||||
// getOne
|
||||
Object oneResponse = this.server.invoke(objectName, "getOne",
|
||||
new Object[] { "one" }, new String[] { String.class.getName() });
|
||||
assertThat(oneResponse).isEqualTo("ONE");
|
||||
|
||||
// update
|
||||
Object updateResponse = this.server.invoke(objectName, "update",
|
||||
new Object[] { "one", "1" },
|
||||
new String[] { String.class.getName(), String.class.getName() });
|
||||
assertThat(updateResponse).isNull();
|
||||
|
||||
// getOne validation after update
|
||||
Object updatedOneResponse = this.server.invoke(objectName, "getOne",
|
||||
new Object[] { "one" }, new String[] { String.class.getName() });
|
||||
assertThat(updatedOneResponse).isEqualTo("1");
|
||||
|
||||
// deleteOne
|
||||
Object deleteResponse = this.server.invoke(objectName, "deleteOne",
|
||||
new Object[] { "one" }, new String[] { String.class.getName() });
|
||||
assertThat(deleteResponse).isNull();
|
||||
|
||||
// getOne validation after delete
|
||||
updatedOneResponse = this.server.invoke(objectName, "getOne",
|
||||
new Object[] { "one" }, new String[] { String.class.getName() });
|
||||
assertThat(updatedOneResponse).isNull();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new AssertionError("Failed to invoke method on FooEndpoint", ex);
|
||||
}
|
||||
});
|
||||
public void createWhenEndpointIsNullShouldThrowException() {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("Endpoint must not be null");
|
||||
new EndpointMBean(mock(JmxOperationResponseMapper.class), null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jmxTypesAreProperlyMapped() {
|
||||
load(FooEndpoint.class, (discoverer) -> {
|
||||
ObjectName objectName = registerEndpoint(discoverer, "foo");
|
||||
try {
|
||||
MBeanInfo mBeanInfo = this.server.getMBeanInfo(objectName);
|
||||
Map<String, MBeanOperationInfo> operations = mapOperations(mBeanInfo);
|
||||
assertThat(operations).containsOnlyKeys("getAll", "getOne", "update",
|
||||
"deleteOne");
|
||||
assertOperation(operations.get("getAll"), String.class,
|
||||
MBeanOperationInfo.INFO, new Class<?>[0]);
|
||||
assertOperation(operations.get("getOne"), String.class,
|
||||
MBeanOperationInfo.INFO, new Class<?>[] { String.class });
|
||||
assertOperation(operations.get("update"), Void.TYPE,
|
||||
MBeanOperationInfo.ACTION,
|
||||
new Class<?>[] { String.class, String.class });
|
||||
assertOperation(operations.get("deleteOne"), Void.TYPE,
|
||||
MBeanOperationInfo.ACTION, new Class<?>[] { String.class });
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new AssertionError("Failed to retrieve MBeanInfo of FooEndpoint",
|
||||
ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void assertOperation(MBeanOperationInfo operation, Class<?> returnType,
|
||||
int impact, Class<?>[] types) {
|
||||
assertThat(operation.getReturnType()).isEqualTo(returnType.getName());
|
||||
assertThat(operation.getImpact()).isEqualTo(impact);
|
||||
MBeanParameterInfo[] signature = operation.getSignature();
|
||||
assertThat(signature).hasSize(types.length);
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
assertThat(signature[i].getType()).isEqualTo(types[0].getName());
|
||||
}
|
||||
public void getMBeanInfoShouldReturnMBeanInfo() {
|
||||
EndpointMBean bean = new EndpointMBean(this.responseMapper, this.endpoint);
|
||||
MBeanInfo info = bean.getMBeanInfo();
|
||||
assertThat(info.getDescription()).isEqualTo("MBean operations for endpoint test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invokeReactiveOperation() {
|
||||
load(ReactiveEndpoint.class, (discoverer) -> {
|
||||
ObjectName objectName = registerEndpoint(discoverer, "reactive");
|
||||
try {
|
||||
Object allResponse = this.server.invoke(objectName, "getInfo",
|
||||
new Object[0], new String[0]);
|
||||
assertThat(allResponse).isInstanceOf(String.class);
|
||||
assertThat(allResponse).isEqualTo("HELLO WORLD");
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new AssertionError("Failed to invoke getInfo method", ex);
|
||||
}
|
||||
});
|
||||
|
||||
public void invokeShouldInvokeJmxOperation()
|
||||
throws MBeanException, ReflectionException {
|
||||
EndpointMBean bean = new EndpointMBean(this.responseMapper, this.endpoint);
|
||||
Object result = bean.invoke("testOperation", NO_PARAMS, NO_SIGNATURE);
|
||||
assertThat(result).isEqualTo("result");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invokeUnknownOperation() {
|
||||
load(FooEndpoint.class, (discoverer) -> {
|
||||
ObjectName objectName = registerEndpoint(discoverer, "foo");
|
||||
try {
|
||||
this.server.invoke(objectName, "doesNotExist", new Object[0],
|
||||
new String[0]);
|
||||
throw new AssertionError(
|
||||
"Should have failed to invoke unknown operation");
|
||||
}
|
||||
catch (ReflectionException ex) {
|
||||
assertThat(ex.getCause()).isInstanceOf(IllegalArgumentException.class);
|
||||
assertThat(ex.getCause().getMessage()).contains("doesNotExist", "foo");
|
||||
}
|
||||
catch (MBeanException | InstanceNotFoundException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
|
||||
});
|
||||
public void invokeWhenActionNameIsNotAnOperationShouldThrowException()
|
||||
throws MBeanException, ReflectionException {
|
||||
EndpointMBean bean = new EndpointMBean(this.responseMapper, this.endpoint);
|
||||
this.thrown.expect(ReflectionException.class);
|
||||
this.thrown.expectCause(instanceOf(IllegalArgumentException.class));
|
||||
this.thrown.expectMessage("no operation named missingOperation");
|
||||
bean.invoke("missingOperation", NO_PARAMS, NO_SIGNATURE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dynamicMBeanCannotReadAttribute() {
|
||||
load(FooEndpoint.class, (discoverer) -> {
|
||||
ObjectName objectName = registerEndpoint(discoverer, "foo");
|
||||
try {
|
||||
this.server.getAttribute(objectName, "foo");
|
||||
throw new AssertionError("Should have failed to read attribute foo");
|
||||
}
|
||||
catch (Exception ex) {
|
||||
assertThat(ex).isInstanceOf(AttributeNotFoundException.class);
|
||||
}
|
||||
});
|
||||
public void invokeWhenMonoResultShouldBlockOnMono()
|
||||
throws MBeanException, ReflectionException {
|
||||
TestExposableJmxEndpoint endpoint = new TestExposableJmxEndpoint(
|
||||
new TestJmxOperation((arguments) -> Mono.just("monoResult")));
|
||||
EndpointMBean bean = new EndpointMBean(this.responseMapper, endpoint);
|
||||
Object result = bean.invoke("testOperation", NO_PARAMS, NO_SIGNATURE);
|
||||
assertThat(result).isEqualTo("monoResult");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dynamicMBeanCannotWriteAttribute() {
|
||||
load(FooEndpoint.class, (discoverer) -> {
|
||||
ObjectName objectName = registerEndpoint(discoverer, "foo");
|
||||
try {
|
||||
this.server.setAttribute(objectName, new Attribute("foo", "bar"));
|
||||
throw new AssertionError("Should have failed to write attribute foo");
|
||||
}
|
||||
catch (Exception ex) {
|
||||
assertThat(ex).isInstanceOf(AttributeNotFoundException.class);
|
||||
}
|
||||
});
|
||||
public void invokeShouldCallResponseMapper()
|
||||
throws MBeanException, ReflectionException {
|
||||
TestJmxOperationResponseMapper responseMapper = spy(this.responseMapper);
|
||||
EndpointMBean bean = new EndpointMBean(responseMapper, this.endpoint);
|
||||
bean.invoke("testOperation", NO_PARAMS, NO_SIGNATURE);
|
||||
verify(responseMapper).mapResponseType(String.class);
|
||||
verify(responseMapper).mapResponse("result");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dynamicMBeanCannotReadAttributes() {
|
||||
load(FooEndpoint.class, (discoverer) -> {
|
||||
ObjectName objectName = registerEndpoint(discoverer, "foo");
|
||||
try {
|
||||
AttributeList attributes = this.server.getAttributes(objectName,
|
||||
new String[] { "foo", "bar" });
|
||||
assertThat(attributes).isNotNull();
|
||||
assertThat(attributes).isEmpty();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new AssertionError("Failed to invoke getAttributes", ex);
|
||||
}
|
||||
});
|
||||
public void getAttributeShouldThrowException()
|
||||
throws AttributeNotFoundException, MBeanException, ReflectionException {
|
||||
EndpointMBean bean = new EndpointMBean(this.responseMapper, this.endpoint);
|
||||
this.thrown.expect(AttributeNotFoundException.class);
|
||||
this.thrown.expectMessage("EndpointMBeans do not support attributes");
|
||||
bean.getAttribute("test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dynamicMBeanCannotWriteAttributes() {
|
||||
load(FooEndpoint.class, (discoverer) -> {
|
||||
ObjectName objectName = registerEndpoint(discoverer, "foo");
|
||||
try {
|
||||
AttributeList attributes = new AttributeList();
|
||||
attributes.add(new Attribute("foo", 1));
|
||||
attributes.add(new Attribute("bar", 42));
|
||||
AttributeList attributesSet = this.server.setAttributes(objectName,
|
||||
attributes);
|
||||
assertThat(attributesSet).isNotNull();
|
||||
assertThat(attributesSet).isEmpty();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new AssertionError("Failed to invoke setAttributes", ex);
|
||||
}
|
||||
});
|
||||
public void setAttributeShouldThrowException() throws AttributeNotFoundException,
|
||||
InvalidAttributeValueException, MBeanException, ReflectionException {
|
||||
EndpointMBean bean = new EndpointMBean(this.responseMapper, this.endpoint);
|
||||
this.thrown.expect(AttributeNotFoundException.class);
|
||||
this.thrown.expectMessage("EndpointMBeans do not support attributes");
|
||||
bean.setAttribute(new Attribute("test", "test"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invokeWithParameterMappingExceptionMapsToIllegalArgumentException() {
|
||||
load(FooEndpoint.class, (discoverer) -> {
|
||||
ObjectName objectName = registerEndpoint(discoverer, "foo");
|
||||
try {
|
||||
this.server.invoke(objectName, "getOne", new Object[] { "wrong" },
|
||||
new String[] { String.class.getName() });
|
||||
}
|
||||
catch (Exception ex) {
|
||||
assertThat(ex.getCause())
|
||||
.isExactlyInstanceOf(IllegalArgumentException.class);
|
||||
assertThat(ex.getCause().getMessage()).isEqualTo(
|
||||
String.format("Failed to map wrong of type " + "%s to type %s",
|
||||
String.class, FooName.class));
|
||||
}
|
||||
});
|
||||
public void getAttributesShouldReturnEmptyAttributeList() {
|
||||
EndpointMBean bean = new EndpointMBean(this.responseMapper, this.endpoint);
|
||||
AttributeList attributes = bean.getAttributes(new String[] { "test" });
|
||||
assertThat(attributes).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invokeWithMissingRequiredParameterExceptionMapsToIllegalArgumentException() {
|
||||
load(RequiredParametersEndpoint.class, (discoverer) -> {
|
||||
ObjectName objectName = registerEndpoint(discoverer, "requiredparameters");
|
||||
try {
|
||||
this.server.invoke(objectName, "read", new Object[] {},
|
||||
new String[] { String.class.getName() });
|
||||
}
|
||||
catch (Exception ex) {
|
||||
assertThat(ex.getCause())
|
||||
.isExactlyInstanceOf(IllegalArgumentException.class);
|
||||
assertThat(ex.getCause().getMessage())
|
||||
.isEqualTo("Failed to invoke operation because the following "
|
||||
+ "required parameters were missing: foo,baz");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invokeWithMissingNullableParameter() {
|
||||
load(RequiredParametersEndpoint.class, (discoverer) -> {
|
||||
ObjectName objectName = registerEndpoint(discoverer, "requiredparameters");
|
||||
try {
|
||||
this.server.invoke(objectName, "read",
|
||||
new Object[] { null, "hello", "world" },
|
||||
new String[] { String.class.getName() });
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new AssertionError("Nullable parameter should not be required.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private ObjectName registerEndpoint(JmxAnnotationEndpointDiscoverer discoverer,
|
||||
String endpointId) {
|
||||
Collection<EndpointMBean> mBeans = this.jmxEndpointMBeanFactory
|
||||
.createMBeans(discoverer.discoverEndpoints());
|
||||
assertThat(mBeans).hasSize(1);
|
||||
EndpointMBean endpointMBean = mBeans.iterator().next();
|
||||
assertThat(endpointMBean.getEndpointId()).isEqualTo(endpointId);
|
||||
return this.endpointMBeanRegistrar.registerEndpointMBean(endpointMBean);
|
||||
}
|
||||
|
||||
private Map<String, MBeanOperationInfo> mapOperations(MBeanInfo info) {
|
||||
Map<String, MBeanOperationInfo> operations = new HashMap<>();
|
||||
for (MBeanOperationInfo mBeanOperationInfo : info.getOperations()) {
|
||||
operations.put(mBeanOperationInfo.getName(), mBeanOperationInfo);
|
||||
}
|
||||
return operations;
|
||||
}
|
||||
|
||||
private void load(Class<?> configuration,
|
||||
Consumer<JmxAnnotationEndpointDiscoverer> consumer) {
|
||||
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
||||
configuration)) {
|
||||
ConversionServiceParameterMapper parameterMapper = new ConversionServiceParameterMapper(
|
||||
DefaultConversionService.getSharedInstance());
|
||||
JmxAnnotationEndpointDiscoverer discoverer = new JmxAnnotationEndpointDiscoverer(
|
||||
context, parameterMapper, null, null);
|
||||
consumer.accept(discoverer);
|
||||
}
|
||||
}
|
||||
|
||||
@Endpoint(id = "foo")
|
||||
static class FooEndpoint {
|
||||
|
||||
private final Map<FooName, Foo> all = new LinkedHashMap<>();
|
||||
|
||||
FooEndpoint() {
|
||||
this.all.put(FooName.ONE, new Foo("one"));
|
||||
this.all.put(FooName.TWO, new Foo("two"));
|
||||
}
|
||||
|
||||
@ReadOperation
|
||||
public Collection<Foo> getAll() {
|
||||
return this.all.values();
|
||||
}
|
||||
|
||||
@ReadOperation
|
||||
public Foo getOne(FooName name) {
|
||||
return this.all.get(name);
|
||||
}
|
||||
|
||||
@WriteOperation
|
||||
public void update(FooName name, String value) {
|
||||
this.all.put(name, new Foo(value));
|
||||
}
|
||||
|
||||
@DeleteOperation
|
||||
public void deleteOne(FooName name) {
|
||||
this.all.remove(name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Endpoint(id = "requiredparameters")
|
||||
static class RequiredParametersEndpoint {
|
||||
|
||||
@ReadOperation
|
||||
public String read(@Nullable String bar, String foo, String baz) {
|
||||
return foo;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Endpoint(id = "reactive")
|
||||
static class ReactiveEndpoint {
|
||||
|
||||
@ReadOperation
|
||||
public Mono<String> getInfo() {
|
||||
return Mono.defer(() -> Mono.just("Hello World"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
enum FooName {
|
||||
|
||||
ONE, TWO, THREE
|
||||
|
||||
}
|
||||
|
||||
static class Foo {
|
||||
|
||||
private final String name;
|
||||
|
||||
Foo(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class TestJmxOperationResponseMapper
|
||||
implements JmxOperationResponseMapper {
|
||||
|
||||
@Override
|
||||
public Object mapResponse(Object response) {
|
||||
return (response != null ? response.toString().toUpperCase() : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> mapResponseType(Class<?> responseType) {
|
||||
if (responseType == Void.TYPE) {
|
||||
return Void.TYPE;
|
||||
}
|
||||
return String.class;
|
||||
}
|
||||
public void setAttributesShouldReturnEmptyAttributeList() {
|
||||
EndpointMBean bean = new EndpointMBean(this.responseMapper, this.endpoint);
|
||||
AttributeList sourceAttributes = new AttributeList();
|
||||
sourceAttributes.add(new Attribute("test", "test"));
|
||||
AttributeList attributes = bean.setAttributes(sourceAttributes);
|
||||
assertThat(attributes).isEmpty();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.endpoint.jmx;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.fasterxml.jackson.databind.JavaType;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.test.json.BasicJsonTester;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* Tests for {@link JacksonJmxOperationResponseMapper}
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class JacksonJmxOperationResponseMapperTests {
|
||||
|
||||
private JacksonJmxOperationResponseMapper mapper = new JacksonJmxOperationResponseMapper(
|
||||
null);
|
||||
|
||||
private final BasicJsonTester json = new BasicJsonTester(getClass());
|
||||
|
||||
@Test
|
||||
public void createWhenObjectMapperIsNullShouldUseDefaultObjectMapper() {
|
||||
JacksonJmxOperationResponseMapper mapper = new JacksonJmxOperationResponseMapper(
|
||||
null);
|
||||
Object mapped = mapper.mapResponse(Collections.singleton("test"));
|
||||
assertThat(this.json.from(mapped.toString())).isEqualToJson("[test]");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createWhenObjectMapperIsSpecifiedShouldUseObjectMapper() {
|
||||
ObjectMapper objectMapper = spy(ObjectMapper.class);
|
||||
JacksonJmxOperationResponseMapper mapper = new JacksonJmxOperationResponseMapper(
|
||||
objectMapper);
|
||||
Set<String> response = Collections.singleton("test");
|
||||
mapper.mapResponse(response);
|
||||
verify(objectMapper).convertValue(eq(response), any(JavaType.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mapResponseTypeWhenCharSequenceShouldReturnString() {
|
||||
assertThat(this.mapper.mapResponseType(String.class)).isEqualTo(String.class);
|
||||
assertThat(this.mapper.mapResponseType(StringBuilder.class))
|
||||
.isEqualTo(String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mapResponseTypeWhenArrayShouldReturnList() {
|
||||
assertThat(this.mapper.mapResponseType(String[].class)).isEqualTo(List.class);
|
||||
assertThat(this.mapper.mapResponseType(Object[].class)).isEqualTo(List.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mapResponseTypeWhenCollectionShouldReturnList() {
|
||||
assertThat(this.mapper.mapResponseType(Collection.class)).isEqualTo(List.class);
|
||||
assertThat(this.mapper.mapResponseType(Set.class)).isEqualTo(List.class);
|
||||
assertThat(this.mapper.mapResponseType(List.class)).isEqualTo(List.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mapResponseTypeWhenOtherShouldReturnMap() {
|
||||
assertThat(this.mapper.mapResponseType(ExampleBean.class)).isEqualTo(Map.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mapResponseWhenNullShouldReturnNull() {
|
||||
assertThat(this.mapper.mapResponse(null)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mapResponseWhenCharSequenceShouldReturnString() {
|
||||
assertThat(this.mapper.mapResponse(new StringBuilder("test"))).isEqualTo("test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mapResponseWhenArrayShouldReturnJsonArray() {
|
||||
Object mapped = this.mapper.mapResponse(new int[] { 1, 2, 3 });
|
||||
assertThat(this.json.from(mapped.toString())).isEqualToJson("[1,2,3]");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mapResponseWhenCollectionShouldReturnJsonArray() {
|
||||
Object mapped = this.mapper.mapResponse(Arrays.asList("a", "b", "c"));
|
||||
assertThat(this.json.from(mapped.toString())).isEqualToJson("[a,b,c]");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mapResponseWhenOtherShouldReturnMap() {
|
||||
ExampleBean bean = new ExampleBean();
|
||||
bean.setName("boot");
|
||||
Object mapped = this.mapper.mapResponse(bean);
|
||||
assertThat(this.json.from(mapped.toString())).isEqualToJson("{'name':'boot'}");
|
||||
}
|
||||
|
||||
public static class ExampleBean {
|
||||
|
||||
private String name;
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.endpoint.jmx;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.management.InstanceNotFoundException;
|
||||
import javax.management.MBeanRegistrationException;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.MalformedObjectNameException;
|
||||
import javax.management.ObjectName;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import org.springframework.jmx.JmxException;
|
||||
import org.springframework.jmx.export.MBeanExportException;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.BDDMockito.willThrow;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* Tests for {@link JmxEndpointExporter}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class JmxEndpointExporterTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Mock
|
||||
private MBeanServer mBeanServer;
|
||||
|
||||
private EndpointObjectNameFactory objectNameFactory = spy(
|
||||
new TestEndpointObjectNameFactory());
|
||||
|
||||
private JmxOperationResponseMapper responseMapper = new TestJmxOperationResponseMapper();
|
||||
|
||||
private List<ExposableJmxEndpoint> endpoints = new ArrayList<>();
|
||||
|
||||
@Captor
|
||||
private ArgumentCaptor<Object> objectCaptor;
|
||||
|
||||
@Captor
|
||||
private ArgumentCaptor<ObjectName> objectNameCaptor;
|
||||
|
||||
private JmxEndpointExporter exporter;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
this.exporter = new JmxEndpointExporter(this.mBeanServer, this.objectNameFactory,
|
||||
this.responseMapper, this.endpoints);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createWhenMBeanServerIsNullShouldThrowException() {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("MBeanServer must not be null");
|
||||
new JmxEndpointExporter(null, this.objectNameFactory, this.responseMapper,
|
||||
this.endpoints);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createWhenObjectNameFactoryIsNullShouldThrowException() {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("ObjectNameFactory must not be null");
|
||||
new JmxEndpointExporter(this.mBeanServer, null, this.responseMapper,
|
||||
this.endpoints);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createWhenResponseMapperIsNullShouldThrowException() {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("ResponseMapper must not be null");
|
||||
new JmxEndpointExporter(this.mBeanServer, this.objectNameFactory, null,
|
||||
this.endpoints);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createWhenEndpointsIsNullShouldThrowException() {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("Endpoints must not be null");
|
||||
new JmxEndpointExporter(this.mBeanServer, this.objectNameFactory,
|
||||
this.responseMapper, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void afterPropertiesSetShouldRegisterMBeans() throws Exception {
|
||||
this.endpoints.add(new TestExposableJmxEndpoint(new TestJmxOperation()));
|
||||
this.exporter.afterPropertiesSet();
|
||||
verify(this.mBeanServer).registerMBean(this.objectCaptor.capture(),
|
||||
this.objectNameCaptor.capture());
|
||||
assertThat(this.objectCaptor.getValue()).isInstanceOf(EndpointMBean.class);
|
||||
assertThat(this.objectNameCaptor.getValue().getKeyProperty("name"))
|
||||
.isEqualTo("test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void registerShouldUseObjectNameFactory() throws Exception {
|
||||
this.endpoints.add(new TestExposableJmxEndpoint(new TestJmxOperation()));
|
||||
this.exporter.afterPropertiesSet();
|
||||
verify(this.objectNameFactory).getObjectName(any(ExposableJmxEndpoint.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void registerWhenObjectNameIsMalformedShouldThrowException() throws Exception {
|
||||
given(this.objectNameFactory.getObjectName(any(ExposableJmxEndpoint.class)))
|
||||
.willThrow(MalformedObjectNameException.class);
|
||||
this.endpoints.add(new TestExposableJmxEndpoint(new TestJmxOperation()));
|
||||
this.thrown.expect(IllegalStateException.class);
|
||||
this.thrown.expectMessage("Invalid ObjectName for endpoint 'test'");
|
||||
this.exporter.afterPropertiesSet();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void registerWhenRegistrationFailsShouldThrowException() throws Exception {
|
||||
given(this.mBeanServer.registerMBean(any(), any(ObjectName.class)))
|
||||
.willThrow(new MBeanRegistrationException(new RuntimeException()));
|
||||
this.endpoints.add(new TestExposableJmxEndpoint(new TestJmxOperation()));
|
||||
this.thrown.expect(MBeanExportException.class);
|
||||
this.thrown.expectMessage("Failed to register MBean for endpoint 'test");
|
||||
this.exporter.afterPropertiesSet();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void destroyShouldUnregisterMBeans() throws Exception {
|
||||
this.endpoints.add(new TestExposableJmxEndpoint(new TestJmxOperation()));
|
||||
this.exporter.afterPropertiesSet();
|
||||
this.exporter.destroy();
|
||||
verify(this.mBeanServer).unregisterMBean(this.objectNameCaptor.capture());
|
||||
assertThat(this.objectNameCaptor.getValue().getKeyProperty("name"))
|
||||
.isEqualTo("test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unregisterWhenInstanceNotFoundShouldContinue() throws Exception {
|
||||
this.endpoints.add(new TestExposableJmxEndpoint(new TestJmxOperation()));
|
||||
this.exporter.afterPropertiesSet();
|
||||
willThrow(InstanceNotFoundException.class).given(this.mBeanServer)
|
||||
.unregisterMBean(any(ObjectName.class));
|
||||
this.exporter.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unregisterWhenUnregisterThrowsExceptionShouldThrowException()
|
||||
throws Exception {
|
||||
this.endpoints.add(new TestExposableJmxEndpoint(new TestJmxOperation()));
|
||||
this.exporter.afterPropertiesSet();
|
||||
willThrow(new MBeanRegistrationException(new RuntimeException()))
|
||||
.given(this.mBeanServer).unregisterMBean(any(ObjectName.class));
|
||||
this.thrown.expect(JmxException.class);
|
||||
this.thrown.expectMessage("Failed to unregister MBean with ObjectName 'boot");
|
||||
this.exporter.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test {@link EndpointObjectNameFactory}.
|
||||
*/
|
||||
private static class TestEndpointObjectNameFactory
|
||||
implements EndpointObjectNameFactory {
|
||||
|
||||
@Override
|
||||
public ObjectName getObjectName(ExposableJmxEndpoint endpoint)
|
||||
throws MalformedObjectNameException {
|
||||
return (endpoint == null ? null
|
||||
: new ObjectName("boot:type=Endpoint,name=" + endpoint.getId()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.endpoint.jmx;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.management.MBeanInfo;
|
||||
import javax.management.MBeanOperationInfo;
|
||||
import javax.management.MBeanParameterInfo;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.OperationType;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link MBeanInfoFactory}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class MBeanInfoFactoryTests {
|
||||
|
||||
private MBeanInfoFactory factory = new MBeanInfoFactory(
|
||||
new TestJmxOperationResponseMapper());
|
||||
|
||||
@Test
|
||||
public void getMBeanInfoShouldReturnMBeanInfo() {
|
||||
MBeanInfo info = this.factory
|
||||
.getMBeanInfo(new TestExposableJmxEndpoint(new TestJmxOperation()));
|
||||
assertThat(info).isNotNull();
|
||||
assertThat(info.getClassName()).isEqualTo(EndpointMBean.class.getName());
|
||||
assertThat(info.getDescription()).isEqualTo("MBean operations for endpoint test");
|
||||
assertThat(info.getAttributes()).isEmpty();
|
||||
assertThat(info.getNotifications()).isEmpty();
|
||||
assertThat(info.getConstructors()).isEmpty();
|
||||
assertThat(info.getOperations()).hasSize(1);
|
||||
MBeanOperationInfo operationInfo = info.getOperations()[0];
|
||||
assertThat(operationInfo.getName()).isEqualTo("testOperation");
|
||||
assertThat(operationInfo.getReturnType()).isEqualTo(String.class.getName());
|
||||
assertThat(operationInfo.getImpact()).isEqualTo(MBeanOperationInfo.INFO);
|
||||
assertThat(operationInfo.getSignature()).hasSize(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getMBeanInfoWhenReadOperationShouldHaveInfoImpact() {
|
||||
MBeanInfo info = this.factory.getMBeanInfo(
|
||||
new TestExposableJmxEndpoint(new TestJmxOperation(OperationType.READ)));
|
||||
assertThat(info.getOperations()[0].getImpact())
|
||||
.isEqualTo(MBeanOperationInfo.INFO);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getMBeanInfoWhenWriteOperationShouldHaveActionImpact() {
|
||||
MBeanInfo info = this.factory.getMBeanInfo(
|
||||
new TestExposableJmxEndpoint(new TestJmxOperation(OperationType.WRITE)));
|
||||
assertThat(info.getOperations()[0].getImpact())
|
||||
.isEqualTo(MBeanOperationInfo.ACTION);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getMBeanInfoWhenDeleteOperationShouldHaveActionImpact() {
|
||||
MBeanInfo info = this.factory.getMBeanInfo(
|
||||
new TestExposableJmxEndpoint(new TestJmxOperation(OperationType.DELETE)));
|
||||
assertThat(info.getOperations()[0].getImpact())
|
||||
.isEqualTo(MBeanOperationInfo.ACTION);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public void getMBeanInfoShouldUseJmxOperationResponseMapper() {
|
||||
JmxOperationResponseMapper mapper = mock(JmxOperationResponseMapper.class);
|
||||
given(mapper.mapResponseType(String.class)).willReturn((Class) Integer.class);
|
||||
MBeanInfoFactory factory = new MBeanInfoFactory(mapper);
|
||||
MBeanInfo info = factory
|
||||
.getMBeanInfo(new TestExposableJmxEndpoint(new TestJmxOperation()));
|
||||
MBeanOperationInfo operationInfo = info.getOperations()[0];
|
||||
assertThat(operationInfo.getReturnType()).isEqualTo(Integer.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getMBeanShouldMapOperationParameters() {
|
||||
List<JmxOperationParameter> parameters = new ArrayList<>();
|
||||
parameters.add(mockParameter("one", String.class, "myone"));
|
||||
parameters.add(mockParameter("two", Object.class, null));
|
||||
TestJmxOperation operation = new TestJmxOperation(parameters);
|
||||
MBeanInfo info = this.factory
|
||||
.getMBeanInfo(new TestExposableJmxEndpoint(operation));
|
||||
MBeanOperationInfo operationInfo = info.getOperations()[0];
|
||||
MBeanParameterInfo[] signature = operationInfo.getSignature();
|
||||
assertThat(signature).hasSize(2);
|
||||
assertThat(signature[0].getName()).isEqualTo("one");
|
||||
assertThat(signature[0].getType()).isEqualTo(String.class.getName());
|
||||
assertThat(signature[0].getDescription()).isEqualTo("myone");
|
||||
assertThat(signature[1].getName()).isEqualTo("two");
|
||||
assertThat(signature[1].getType()).isEqualTo(Object.class.getName());
|
||||
assertThat(signature[1].getDescription()).isNull();
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
private JmxOperationParameter mockParameter(String name, Class<?> type,
|
||||
String description) {
|
||||
JmxOperationParameter parameter = mock(JmxOperationParameter.class);
|
||||
given(parameter.getName()).willReturn(name);
|
||||
given(parameter.getType()).willReturn((Class) type);
|
||||
given(parameter.getDescription()).willReturn(description);
|
||||
return parameter;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.endpoint.jmx;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Test {@link ExposableJmxEndpoint} implementation.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class TestExposableJmxEndpoint implements ExposableJmxEndpoint {
|
||||
|
||||
private final Collection<JmxOperation> operations;
|
||||
|
||||
public TestExposableJmxEndpoint(JmxOperation... operations) {
|
||||
this(Arrays.asList(operations));
|
||||
}
|
||||
|
||||
public TestExposableJmxEndpoint(Collection<JmxOperation> operations) {
|
||||
this.operations = operations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "test";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnableByDefault() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<JmxOperation> getOperations() {
|
||||
return this.operations;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.endpoint.jmx;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.OperationType;
|
||||
|
||||
/**
|
||||
* Test {@link JmxOperation} implementation.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class TestJmxOperation implements JmxOperation {
|
||||
|
||||
private final OperationType operationType;
|
||||
|
||||
private final Function<Map<String, Object>, Object> invoke;
|
||||
|
||||
private final List<JmxOperationParameter> parameters;
|
||||
|
||||
public TestJmxOperation() {
|
||||
this.operationType = OperationType.READ;
|
||||
this.invoke = null;
|
||||
this.parameters = Collections.emptyList();
|
||||
}
|
||||
|
||||
public TestJmxOperation(OperationType operationType) {
|
||||
this.operationType = operationType;
|
||||
this.invoke = null;
|
||||
this.parameters = Collections.emptyList();
|
||||
}
|
||||
|
||||
public TestJmxOperation(Function<Map<String, Object>, Object> invoke) {
|
||||
this.operationType = OperationType.READ;
|
||||
this.invoke = invoke;
|
||||
this.parameters = Collections.emptyList();
|
||||
}
|
||||
|
||||
public TestJmxOperation(List<JmxOperationParameter> parameters) {
|
||||
this.operationType = OperationType.READ;
|
||||
this.invoke = null;
|
||||
this.parameters = parameters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OperationType getType() {
|
||||
return this.operationType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(Map<String, Object> arguments) {
|
||||
return (this.invoke == null ? "result" : this.invoke.apply(arguments));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "testOperation";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getOutputType() {
|
||||
return String.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Test JMX operation";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<JmxOperationParameter> getParameters() {
|
||||
return this.parameters;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.endpoint.jmx;
|
||||
|
||||
/**
|
||||
* Test {@link JmxOperationResponseMapper} implementation.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class TestJmxOperationResponseMapper implements JmxOperationResponseMapper {
|
||||
|
||||
@Override
|
||||
public Object mapResponse(Object response) {
|
||||
return response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> mapResponseType(Class<?> responseType) {
|
||||
return responseType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.endpoint.jmx.annotation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.time.Instant;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.OperationType;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.DiscoveredOperationMethod;
|
||||
import org.springframework.boot.actuate.endpoint.invoke.OperationInvoker;
|
||||
import org.springframework.boot.actuate.endpoint.jmx.JmxOperationParameter;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.jmx.export.annotation.ManagedOperation;
|
||||
import org.springframework.jmx.export.annotation.ManagedOperationParameter;
|
||||
import org.springframework.jmx.export.annotation.ManagedOperationParameters;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link DiscoveredJmxOperation}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class DiscoveredJmxOperationTests {
|
||||
|
||||
@Test
|
||||
public void getNameShouldReturnMethodName() {
|
||||
DiscoveredJmxOperation operation = getOperation("getEnum");
|
||||
assertThat(operation.getName()).isEqualTo("getEnum");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getOutputTypeShouldReturnJmxType() {
|
||||
assertThat(getOperation("getEnum").getOutputType()).isEqualTo(String.class);
|
||||
assertThat(getOperation("getDate").getOutputType()).isEqualTo(String.class);
|
||||
assertThat(getOperation("getInstant").getOutputType()).isEqualTo(String.class);
|
||||
assertThat(getOperation("getInteger").getOutputType()).isEqualTo(Integer.class);
|
||||
assertThat(getOperation("getVoid").getOutputType()).isEqualTo(void.class);
|
||||
assertThat(getOperation("getApplicationContext").getOutputType())
|
||||
.isEqualTo(Object.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDescriptionWhenHasManagedOperationDescriptionShouldUseValueFromAnnotation() {
|
||||
DiscoveredJmxOperation operation = getOperation(
|
||||
"withManagedOperationDescription");
|
||||
assertThat(operation.getDescription()).isEqualTo("fromannotation");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDescriptionWhenHasNoManagedOperationShouldGenerateDescription() {
|
||||
DiscoveredJmxOperation operation = getOperation("getEnum");
|
||||
assertThat(operation.getDescription())
|
||||
.isEqualTo("Invoke getEnum for endpoint test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getParametersWhenHasNoParametersShouldReturnEmptyList() {
|
||||
DiscoveredJmxOperation operation = getOperation("getEnum");
|
||||
assertThat(operation.getParameters()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getParametersShouldReturnJmxTypes() {
|
||||
DiscoveredJmxOperation operation = getOperation("params");
|
||||
List<JmxOperationParameter> parameters = operation.getParameters();
|
||||
assertThat(parameters.get(0).getType()).isEqualTo(String.class);
|
||||
assertThat(parameters.get(1).getType()).isEqualTo(String.class);
|
||||
assertThat(parameters.get(2).getType()).isEqualTo(String.class);
|
||||
assertThat(parameters.get(3).getType()).isEqualTo(Integer.class);
|
||||
assertThat(parameters.get(4).getType()).isEqualTo(Object.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getParametersWhenHasManagedOperationParameterShouldUseValuesFromAnnotation() {
|
||||
DiscoveredJmxOperation operation = getOperation("withManagedOperationParameters");
|
||||
List<JmxOperationParameter> parameters = operation.getParameters();
|
||||
assertThat(parameters.get(0).getName()).isEqualTo("a1");
|
||||
assertThat(parameters.get(1).getName()).isEqualTo("a2");
|
||||
assertThat(parameters.get(0).getDescription()).isEqualTo("d1");
|
||||
assertThat(parameters.get(1).getDescription()).isEqualTo("d2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getParametersWhenHasNoManagedOperationParameterShouldDeducedValuesName() {
|
||||
DiscoveredJmxOperation operation = getOperation("params");
|
||||
List<JmxOperationParameter> parameters = operation.getParameters();
|
||||
assertThat(parameters.get(0).getName()).isEqualTo("enumParam");
|
||||
assertThat(parameters.get(1).getName()).isEqualTo("dateParam");
|
||||
assertThat(parameters.get(2).getName()).isEqualTo("instantParam");
|
||||
assertThat(parameters.get(3).getName()).isEqualTo("integerParam");
|
||||
assertThat(parameters.get(4).getName()).isEqualTo("applicationContextParam");
|
||||
assertThat(parameters.get(0).getDescription()).isNull();
|
||||
assertThat(parameters.get(1).getDescription()).isNull();
|
||||
assertThat(parameters.get(2).getDescription()).isNull();
|
||||
assertThat(parameters.get(3).getDescription()).isNull();
|
||||
assertThat(parameters.get(4).getDescription()).isNull();
|
||||
}
|
||||
|
||||
private DiscoveredJmxOperation getOperation(String methodName) {
|
||||
Method method = findMethod(methodName);
|
||||
AnnotationAttributes annotationAttributes = new AnnotationAttributes();
|
||||
annotationAttributes.put("produces", "application/xml");
|
||||
DiscoveredOperationMethod operationMethod = new DiscoveredOperationMethod(method,
|
||||
OperationType.READ, annotationAttributes);
|
||||
DiscoveredJmxOperation operation = new DiscoveredJmxOperation("test",
|
||||
operationMethod, mock(OperationInvoker.class));
|
||||
return operation;
|
||||
}
|
||||
|
||||
private Method findMethod(String methodName) {
|
||||
Map<String, Method> methods = new HashMap<>();
|
||||
ReflectionUtils.doWithMethods(Example.class,
|
||||
(method) -> methods.put(method.getName(), method));
|
||||
return methods.get(methodName);
|
||||
}
|
||||
|
||||
interface Example {
|
||||
|
||||
OperationType getEnum();
|
||||
|
||||
Date getDate();
|
||||
|
||||
Instant getInstant();
|
||||
|
||||
Integer getInteger();
|
||||
|
||||
void getVoid();
|
||||
|
||||
ApplicationContext getApplicationContext();
|
||||
|
||||
Object params(OperationType enumParam, Date dateParam, Instant instantParam,
|
||||
Integer integerParam, ApplicationContext applicationContextParam);
|
||||
|
||||
@ManagedOperation(description = "fromannotation")
|
||||
Object withManagedOperationDescription();
|
||||
|
||||
@ManagedOperationParameters({
|
||||
@ManagedOperationParameter(name = "a1", description = "d1"),
|
||||
@ManagedOperationParameter(name = "a2", description = "d2") })
|
||||
Object withManagedOperationParameters(Object one, Object two);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -28,17 +28,16 @@ import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.EndpointInfo;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.DeleteOperation;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
|
||||
import org.springframework.boot.actuate.endpoint.cache.CachingOperationInvoker;
|
||||
import org.springframework.boot.actuate.endpoint.cache.CachingOperationInvokerAdvisor;
|
||||
import org.springframework.boot.actuate.endpoint.convert.ConversionServiceParameterMapper;
|
||||
import org.springframework.boot.actuate.endpoint.jmx.JmxEndpointOperationParameterInfo;
|
||||
import org.springframework.boot.actuate.endpoint.invoke.convert.ConversionServiceParameterValueMapper;
|
||||
import org.springframework.boot.actuate.endpoint.invoker.cache.CachingOperationInvoker;
|
||||
import org.springframework.boot.actuate.endpoint.invoker.cache.CachingOperationInvokerAdvisor;
|
||||
import org.springframework.boot.actuate.endpoint.jmx.ExposableJmxEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.jmx.JmxOperation;
|
||||
import org.springframework.boot.actuate.endpoint.reflect.ReflectiveOperationInvoker;
|
||||
import org.springframework.boot.actuate.endpoint.jmx.JmxOperationParameter;
|
||||
import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpoint;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -47,30 +46,31 @@ import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.jmx.export.annotation.ManagedOperation;
|
||||
import org.springframework.jmx.export.annotation.ManagedOperationParameter;
|
||||
import org.springframework.jmx.export.annotation.ManagedOperationParameters;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link JmxAnnotationEndpointDiscoverer}.
|
||||
* Tests for {@link JmxEndpointDiscoverer}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class JmxAnnotationEndpointDiscovererTests {
|
||||
public class JmxEndpointDiscovererTests {
|
||||
|
||||
@Rule
|
||||
public final ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void discoveryWorksWhenThereAreNoEndpoints() {
|
||||
public void getEndpointsWhenNoEndpointBeansShouldReturnEmptyCollection() {
|
||||
load(EmptyConfiguration.class,
|
||||
(discoverer) -> assertThat(discoverer.discoverEndpoints()).isEmpty());
|
||||
(discoverer) -> assertThat(discoverer.getEndpoints()).isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void standardEndpointIsDiscovered() {
|
||||
public void getEndpointsShouldDiscoverStandardEndpoints() {
|
||||
load(TestEndpoint.class, (discoverer) -> {
|
||||
Map<String, EndpointInfo<JmxOperation>> endpoints = discover(discoverer);
|
||||
Map<String, ExposableJmxEndpoint> endpoints = discover(discoverer);
|
||||
assertThat(endpoints).containsOnlyKeys("test");
|
||||
Map<String, JmxOperation> operationByName = mapOperations(
|
||||
endpoints.get("test").getOperations());
|
||||
@@ -81,8 +81,6 @@ public class JmxAnnotationEndpointDiscovererTests {
|
||||
.isEqualTo("Invoke getAll for endpoint test");
|
||||
assertThat(getAll.getOutputType()).isEqualTo(Object.class);
|
||||
assertThat(getAll.getParameters()).isEmpty();
|
||||
assertThat(getAll.getInvoker())
|
||||
.isInstanceOf(ReflectiveOperationInvoker.class);
|
||||
JmxOperation getSomething = operationByName.get("getSomething");
|
||||
assertThat(getSomething.getDescription())
|
||||
.isEqualTo("Invoke getSomething for endpoint test");
|
||||
@@ -103,42 +101,41 @@ public class JmxAnnotationEndpointDiscovererTests {
|
||||
assertThat(deleteSomething.getParameters()).hasSize(1);
|
||||
hasDefaultParameter(deleteSomething, 0, String.class);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onlyJmxEndpointsAreDiscovered() {
|
||||
public void getEndpointsWhenHasFilteredEndpointShouldOnlyDiscoverJmxEndpoints() {
|
||||
load(MultipleEndpointsConfiguration.class, (discoverer) -> {
|
||||
Map<String, EndpointInfo<JmxOperation>> endpoints = discover(discoverer);
|
||||
Map<String, ExposableJmxEndpoint> endpoints = discover(discoverer);
|
||||
assertThat(endpoints).containsOnlyKeys("test", "jmx");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jmxExtensionMustHaveEndpoint() {
|
||||
public void getEndpointsWhenJmxExtensionIsMissingEndpointShouldThrowException() {
|
||||
load(TestJmxEndpointExtension.class, (discoverer) -> {
|
||||
this.thrown.expect(IllegalStateException.class);
|
||||
this.thrown.expectMessage("Invalid extension");
|
||||
this.thrown.expectMessage(TestJmxEndpointExtension.class.getName());
|
||||
this.thrown.expectMessage("no endpoint found");
|
||||
this.thrown.expectMessage(TestEndpoint.class.getName());
|
||||
discoverer.discoverEndpoints();
|
||||
this.thrown.expectMessage(
|
||||
"Invalid extension 'jmxEndpointDiscovererTests.TestJmxEndpointExtension': "
|
||||
+ "no endpoint found with type '"
|
||||
+ TestEndpoint.class.getName() + "'");
|
||||
discoverer.getEndpoints();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jmxEndpointOverridesStandardEndpoint() {
|
||||
public void getEndpointsWhenHasJmxExtensionShouldOverrideStandardEndpoint() {
|
||||
load(OverriddenOperationJmxEndpointConfiguration.class, (discoverer) -> {
|
||||
Map<String, EndpointInfo<JmxOperation>> endpoints = discover(discoverer);
|
||||
Map<String, ExposableJmxEndpoint> endpoints = discover(discoverer);
|
||||
assertThat(endpoints).containsOnlyKeys("test");
|
||||
assertJmxTestEndpoint(endpoints.get("test"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jmxEndpointAddsExtraOperation() {
|
||||
public void getEndpointsWhenHasJmxExtensionWithNewOperationAddsExtraOperation() {
|
||||
load(AdditionalOperationJmxEndpointConfiguration.class, (discoverer) -> {
|
||||
Map<String, EndpointInfo<JmxOperation>> endpoints = discover(discoverer);
|
||||
Map<String, ExposableJmxEndpoint> endpoints = discover(discoverer);
|
||||
assertThat(endpoints).containsOnlyKeys("test");
|
||||
Map<String, JmxOperation> operationByName = mapOperations(
|
||||
endpoints.get("test").getOperations());
|
||||
@@ -152,131 +149,120 @@ public class JmxAnnotationEndpointDiscovererTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void endpointMainReadOperationIsCachedWithMatchingId() {
|
||||
public void getEndpointsWhenHasCacheWithTtlShouldCacheReadOperationWithTtlValue() {
|
||||
load(TestEndpoint.class, (id) -> 500L, (discoverer) -> {
|
||||
Map<String, EndpointInfo<JmxOperation>> endpoints = discover(discoverer);
|
||||
Map<String, ExposableJmxEndpoint> endpoints = discover(discoverer);
|
||||
assertThat(endpoints).containsOnlyKeys("test");
|
||||
Map<String, JmxOperation> operationByName = mapOperations(
|
||||
endpoints.get("test").getOperations());
|
||||
assertThat(operationByName).containsOnlyKeys("getAll", "getSomething",
|
||||
"update", "deleteSomething");
|
||||
JmxOperation getAll = operationByName.get("getAll");
|
||||
assertThat(getAll.getInvoker()).isInstanceOf(CachingOperationInvoker.class);
|
||||
assertThat(((CachingOperationInvoker) getAll.getInvoker()).getTimeToLive())
|
||||
assertThat(getInvoker(getAll)).isInstanceOf(CachingOperationInvoker.class);
|
||||
assertThat(((CachingOperationInvoker) getInvoker(getAll)).getTimeToLive())
|
||||
.isEqualTo(500);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extraReadOperationsAreCached() {
|
||||
public void getEndpointsShouldCacheReadOperations() {
|
||||
load(AdditionalOperationJmxEndpointConfiguration.class, (id) -> 500L,
|
||||
(discoverer) -> {
|
||||
Map<String, EndpointInfo<JmxOperation>> endpoints = discover(
|
||||
discoverer);
|
||||
Map<String, ExposableJmxEndpoint> endpoints = discover(discoverer);
|
||||
assertThat(endpoints).containsOnlyKeys("test");
|
||||
Map<String, JmxOperation> operationByName = mapOperations(
|
||||
endpoints.get("test").getOperations());
|
||||
assertThat(operationByName).containsOnlyKeys("getAll", "getSomething",
|
||||
"update", "deleteSomething", "getAnother");
|
||||
JmxOperation getAll = operationByName.get("getAll");
|
||||
assertThat(getAll.getInvoker())
|
||||
assertThat(getInvoker(getAll))
|
||||
.isInstanceOf(CachingOperationInvoker.class);
|
||||
assertThat(((CachingOperationInvoker) getAll.getInvoker())
|
||||
assertThat(((CachingOperationInvoker) getInvoker(getAll))
|
||||
.getTimeToLive()).isEqualTo(500);
|
||||
JmxOperation getAnother = operationByName.get("getAnother");
|
||||
assertThat(getAnother.getInvoker())
|
||||
assertThat(getInvoker(getAnother))
|
||||
.isInstanceOf(CachingOperationInvoker.class);
|
||||
assertThat(((CachingOperationInvoker) getAnother.getInvoker())
|
||||
assertThat(((CachingOperationInvoker) getInvoker(getAnother))
|
||||
.getTimeToLive()).isEqualTo(500);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void discoveryFailsWhenTwoExtensionsHaveTheSameEndpointType() {
|
||||
public void getEndpointsWhenTwoExtensionsHaveTheSameEndpointTypeShouldThrowException() {
|
||||
load(ClashingJmxEndpointConfiguration.class, (discoverer) -> {
|
||||
this.thrown.expect(IllegalStateException.class);
|
||||
this.thrown.expectMessage("Found two extensions for the same endpoint");
|
||||
this.thrown.expectMessage(TestEndpoint.class.getName());
|
||||
this.thrown.expectMessage(TestJmxEndpointExtension.class.getName());
|
||||
discoverer.discoverEndpoints();
|
||||
this.thrown.expectMessage("Found multiple extensions for the endpoint bean "
|
||||
+ "testEndpoint (testExtensionOne, testExtensionTwo)");
|
||||
discoverer.getEndpoints();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void discoveryFailsWhenTwoStandardEndpointsHaveTheSameId() {
|
||||
public void getEndpointsWhenTwoStandardEndpointsHaveTheSameIdShouldThrowException() {
|
||||
load(ClashingStandardEndpointConfiguration.class, (discoverer) -> {
|
||||
this.thrown.expect(IllegalStateException.class);
|
||||
this.thrown.expectMessage("Found two endpoints with the id 'test': ");
|
||||
discoverer.discoverEndpoints();
|
||||
discoverer.getEndpoints();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void discoveryFailsWhenEndpointHasTwoOperationsWithTheSameName() {
|
||||
public void getEndpointsWhenWhenEndpointHasTwoOperationsWithTheSameNameShouldThrowException() {
|
||||
load(ClashingOperationsEndpoint.class, (discoverer) -> {
|
||||
this.thrown.expect(IllegalStateException.class);
|
||||
this.thrown.expectMessage("Found multiple JMX operations with the same name");
|
||||
this.thrown.expectMessage("getAll");
|
||||
this.thrown.expectMessage(ReflectionUtils
|
||||
.findMethod(ClashingOperationsEndpoint.class, "getAll").toString());
|
||||
this.thrown.expectMessage(ReflectionUtils
|
||||
.findMethod(ClashingOperationsEndpoint.class, "getAll", String.class)
|
||||
.toString());
|
||||
discoverer.discoverEndpoints();
|
||||
this.thrown.expectMessage("Unable to map duplicate endpoint operations: "
|
||||
+ "[MBean call 'getAll'] to jmxEndpointDiscovererTests.ClashingOperationsEndpoint");
|
||||
discoverer.getEndpoints();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void discoveryFailsWhenExtensionHasTwoOperationsWithTheSameName() {
|
||||
public void getEndpointsWhenWhenExtensionHasTwoOperationsWithTheSameNameShouldThrowException() {
|
||||
load(AdditionalClashingOperationsConfiguration.class, (discoverer) -> {
|
||||
this.thrown.expect(IllegalStateException.class);
|
||||
this.thrown.expectMessage("Found multiple JMX operations with the same name");
|
||||
this.thrown.expectMessage("getAll");
|
||||
this.thrown.expectMessage(ReflectionUtils
|
||||
.findMethod(ClashingOperationsJmxEndpointExtension.class, "getAll")
|
||||
.toString());
|
||||
this.thrown.expectMessage(ReflectionUtils
|
||||
.findMethod(ClashingOperationsJmxEndpointExtension.class, "getAll",
|
||||
String.class)
|
||||
.toString());
|
||||
discoverer.discoverEndpoints();
|
||||
this.thrown.expectMessage("Unable to map duplicate endpoint operations: "
|
||||
+ "[MBean call 'getAll'] to testEndpoint (clashingOperationsJmxEndpointExtension)");
|
||||
discoverer.getEndpoints();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void discoveryFailsWhenExtensionIsNotCompatibleWithTheEndpointType() {
|
||||
public void getEndpointsWhenExtensionIsNotCompatibleWithTheEndpointTypeShouldThrowException() {
|
||||
load(InvalidJmxExtensionConfiguration.class, (discoverer) -> {
|
||||
this.thrown.expect(IllegalStateException.class);
|
||||
this.thrown.expectMessage("Invalid extension");
|
||||
this.thrown.expectMessage(NonJmxJmxEndpointExtension.class.getName());
|
||||
this.thrown.expectMessage(NonJmxEndpoint.class.getName());
|
||||
discoverer.discoverEndpoints();
|
||||
this.thrown.expectMessage("Endpoint bean 'nonJmxEndpoint' cannot support the "
|
||||
+ "extension bean 'nonJmxJmxEndpointExtension'");
|
||||
discoverer.getEndpoints();
|
||||
});
|
||||
}
|
||||
|
||||
private void assertJmxTestEndpoint(EndpointInfo<JmxOperation> endpoint) {
|
||||
Map<String, JmxOperation> operationByName = mapOperations(
|
||||
private Object getInvoker(JmxOperation operation) {
|
||||
return ReflectionTestUtils.getField(operation, "invoker");
|
||||
}
|
||||
|
||||
private void assertJmxTestEndpoint(ExposableJmxEndpoint endpoint) {
|
||||
Map<String, JmxOperation> operationsByName = mapOperations(
|
||||
endpoint.getOperations());
|
||||
assertThat(operationByName).containsOnlyKeys("getAll", "getSomething", "update",
|
||||
assertThat(operationsByName).containsOnlyKeys("getAll", "getSomething", "update",
|
||||
"deleteSomething");
|
||||
JmxOperation getAll = operationByName.get("getAll");
|
||||
JmxOperation getAll = operationsByName.get("getAll");
|
||||
assertThat(getAll.getDescription()).isEqualTo("Get all the things");
|
||||
assertThat(getAll.getOutputType()).isEqualTo(Object.class);
|
||||
assertThat(getAll.getParameters()).isEmpty();
|
||||
JmxOperation getSomething = operationByName.get("getSomething");
|
||||
JmxOperation getSomething = operationsByName.get("getSomething");
|
||||
assertThat(getSomething.getDescription())
|
||||
.isEqualTo("Get something based on a timeUnit");
|
||||
assertThat(getSomething.getOutputType()).isEqualTo(String.class);
|
||||
assertThat(getSomething.getParameters()).hasSize(1);
|
||||
hasDocumentedParameter(getSomething, 0, "unitMs", Long.class,
|
||||
"Number of milliseconds");
|
||||
JmxOperation update = operationByName.get("update");
|
||||
JmxOperation update = operationsByName.get("update");
|
||||
assertThat(update.getDescription()).isEqualTo("Update something based on bar");
|
||||
assertThat(update.getOutputType()).isEqualTo(Void.TYPE);
|
||||
assertThat(update.getParameters()).hasSize(2);
|
||||
hasDocumentedParameter(update, 0, "foo", String.class, "Foo identifier");
|
||||
hasDocumentedParameter(update, 1, "bar", String.class, "Bar value");
|
||||
JmxOperation deleteSomething = operationByName.get("deleteSomething");
|
||||
JmxOperation deleteSomething = operationsByName.get("deleteSomething");
|
||||
assertThat(deleteSomething.getDescription())
|
||||
.isEqualTo("Delete something based on a timeUnit");
|
||||
assertThat(deleteSomething.getOutputType()).isEqualTo(Void.TYPE);
|
||||
@@ -285,58 +271,172 @@ public class JmxAnnotationEndpointDiscovererTests {
|
||||
"Number of milliseconds");
|
||||
}
|
||||
|
||||
private void hasDefaultParameter(JmxOperation operation, int index, Class<?> type) {
|
||||
assertThat(index).isLessThan(operation.getParameters().size());
|
||||
JmxEndpointOperationParameterInfo parameter = operation.getParameters()
|
||||
.get(index);
|
||||
assertThat(parameter.getType()).isEqualTo(type);
|
||||
assertThat(parameter.getDescription()).isNull();
|
||||
}
|
||||
|
||||
private void hasDocumentedParameter(JmxOperation operation, int index, String name,
|
||||
Class<?> type, String description) {
|
||||
assertThat(index).isLessThan(operation.getParameters().size());
|
||||
JmxEndpointOperationParameterInfo parameter = operation.getParameters()
|
||||
.get(index);
|
||||
JmxOperationParameter parameter = operation.getParameters().get(index);
|
||||
assertThat(parameter.getName()).isEqualTo(name);
|
||||
assertThat(parameter.getType()).isEqualTo(type);
|
||||
assertThat(parameter.getDescription()).isEqualTo(description);
|
||||
}
|
||||
|
||||
private Map<String, EndpointInfo<JmxOperation>> discover(
|
||||
JmxAnnotationEndpointDiscoverer discoverer) {
|
||||
Map<String, EndpointInfo<JmxOperation>> endpointsById = new HashMap<>();
|
||||
discoverer.discoverEndpoints()
|
||||
.forEach((endpoint) -> endpointsById.put(endpoint.getId(), endpoint));
|
||||
return endpointsById;
|
||||
// FIXME rename
|
||||
private void hasDefaultParameter(JmxOperation operation, int index, Class<?> type) {
|
||||
JmxOperationParameter parameter = operation.getParameters().get(index);
|
||||
assertThat(parameter.getType()).isEqualTo(type);
|
||||
}
|
||||
|
||||
private Map<String, ExposableJmxEndpoint> discover(JmxEndpointDiscoverer discoverer) {
|
||||
Map<String, ExposableJmxEndpoint> byId = new HashMap<>();
|
||||
discoverer.getEndpoints()
|
||||
.forEach((endpoint) -> byId.put(endpoint.getId(), endpoint));
|
||||
return byId;
|
||||
}
|
||||
|
||||
private Map<String, JmxOperation> mapOperations(Collection<JmxOperation> operations) {
|
||||
Map<String, JmxOperation> operationByName = new HashMap<>();
|
||||
operations.forEach((operation) -> operationByName
|
||||
.put(operation.getOperationName(), operation));
|
||||
return operationByName;
|
||||
Map<String, JmxOperation> byName = new HashMap<>();
|
||||
operations.forEach((operation) -> byName.put(operation.getName(), operation));
|
||||
return byName;
|
||||
}
|
||||
|
||||
private void load(Class<?> configuration,
|
||||
Consumer<JmxAnnotationEndpointDiscoverer> consumer) {
|
||||
private void load(Class<?> configuration, Consumer<JmxEndpointDiscoverer> consumer) {
|
||||
load(configuration, (id) -> null, consumer);
|
||||
}
|
||||
|
||||
private void load(Class<?> configuration, Function<String, Long> timeToLive,
|
||||
Consumer<JmxAnnotationEndpointDiscoverer> consumer) {
|
||||
Consumer<JmxEndpointDiscoverer> consumer) {
|
||||
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
||||
configuration)) {
|
||||
ConversionServiceParameterMapper parameterMapper = new ConversionServiceParameterMapper(
|
||||
ConversionServiceParameterValueMapper parameterMapper = new ConversionServiceParameterValueMapper(
|
||||
DefaultConversionService.getSharedInstance());
|
||||
JmxAnnotationEndpointDiscoverer discoverer = new JmxAnnotationEndpointDiscoverer(
|
||||
context, parameterMapper,
|
||||
JmxEndpointDiscoverer discoverer = new JmxEndpointDiscoverer(context,
|
||||
parameterMapper,
|
||||
Collections.singleton(new CachingOperationInvokerAdvisor(timeToLive)),
|
||||
null);
|
||||
Collections.emptyList());
|
||||
consumer.accept(discoverer);
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class EmptyConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class MultipleEndpointsConfiguration {
|
||||
|
||||
@Bean
|
||||
public TestEndpoint testEndpoint() {
|
||||
return new TestEndpoint();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TestJmxEndpoint testJmxEndpoint() {
|
||||
return new TestJmxEndpoint();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public NonJmxEndpoint nonJmxEndpoint() {
|
||||
return new NonJmxEndpoint();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class OverriddenOperationJmxEndpointConfiguration {
|
||||
|
||||
@Bean
|
||||
public TestEndpoint testEndpoint() {
|
||||
return new TestEndpoint();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TestJmxEndpointExtension testJmxEndpointExtension() {
|
||||
return new TestJmxEndpointExtension();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class AdditionalOperationJmxEndpointConfiguration {
|
||||
|
||||
@Bean
|
||||
public TestEndpoint testEndpoint() {
|
||||
return new TestEndpoint();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AdditionalOperationJmxEndpointExtension additionalOperationJmxEndpointExtension() {
|
||||
return new AdditionalOperationJmxEndpointExtension();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class AdditionalClashingOperationsConfiguration {
|
||||
|
||||
@Bean
|
||||
public TestEndpoint testEndpoint() {
|
||||
return new TestEndpoint();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ClashingOperationsJmxEndpointExtension clashingOperationsJmxEndpointExtension() {
|
||||
return new ClashingOperationsJmxEndpointExtension();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ClashingJmxEndpointConfiguration {
|
||||
|
||||
@Bean
|
||||
public TestEndpoint testEndpoint() {
|
||||
return new TestEndpoint();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TestJmxEndpointExtension testExtensionOne() {
|
||||
return new TestJmxEndpointExtension();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TestJmxEndpointExtension testExtensionTwo() {
|
||||
return new TestJmxEndpointExtension();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ClashingStandardEndpointConfiguration {
|
||||
|
||||
@Bean
|
||||
public TestEndpoint testEndpointTwo() {
|
||||
return new TestEndpoint();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TestEndpoint testEndpointOne() {
|
||||
return new TestEndpoint();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class InvalidJmxExtensionConfiguration {
|
||||
|
||||
@Bean
|
||||
public NonJmxEndpoint nonJmxEndpoint() {
|
||||
return new NonJmxEndpoint();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public NonJmxJmxEndpointExtension nonJmxJmxEndpointExtension() {
|
||||
return new NonJmxJmxEndpointExtension();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Endpoint(id = "test")
|
||||
private static class TestEndpoint {
|
||||
|
||||
@@ -469,124 +569,4 @@ public class JmxAnnotationEndpointDiscovererTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class EmptyConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class MultipleEndpointsConfiguration {
|
||||
|
||||
@Bean
|
||||
public TestEndpoint testEndpoint() {
|
||||
return new TestEndpoint();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TestJmxEndpoint testJmxEndpoint() {
|
||||
return new TestJmxEndpoint();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public NonJmxEndpoint nonJmxEndpoint() {
|
||||
return new NonJmxEndpoint();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class OverriddenOperationJmxEndpointConfiguration {
|
||||
|
||||
@Bean
|
||||
public TestEndpoint testEndpoint() {
|
||||
return new TestEndpoint();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TestJmxEndpointExtension testJmxEndpointExtension() {
|
||||
return new TestJmxEndpointExtension();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class AdditionalOperationJmxEndpointConfiguration {
|
||||
|
||||
@Bean
|
||||
public TestEndpoint testEndpoint() {
|
||||
return new TestEndpoint();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AdditionalOperationJmxEndpointExtension additionalOperationJmxEndpointExtension() {
|
||||
return new AdditionalOperationJmxEndpointExtension();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class AdditionalClashingOperationsConfiguration {
|
||||
|
||||
@Bean
|
||||
public TestEndpoint testEndpoint() {
|
||||
return new TestEndpoint();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ClashingOperationsJmxEndpointExtension clashingOperationsJmxEndpointExtension() {
|
||||
return new ClashingOperationsJmxEndpointExtension();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ClashingJmxEndpointConfiguration {
|
||||
|
||||
@Bean
|
||||
public TestEndpoint testEndpoint() {
|
||||
return new TestEndpoint();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TestJmxEndpointExtension testExtensionOne() {
|
||||
return new TestJmxEndpointExtension();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TestJmxEndpointExtension testExtensionTwo() {
|
||||
return new TestJmxEndpointExtension();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ClashingStandardEndpointConfiguration {
|
||||
|
||||
@Bean
|
||||
public TestEndpoint testEndpointTwo() {
|
||||
return new TestEndpoint();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TestEndpoint testEndpointOne() {
|
||||
return new TestEndpoint();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class InvalidJmxExtensionConfiguration {
|
||||
|
||||
@Bean
|
||||
public NonJmxEndpoint nonJmxEndpoint() {
|
||||
return new NonJmxEndpoint();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public NonJmxJmxEndpointExtension nonJmxJmxEndpointExtension() {
|
||||
return new NonJmxJmxEndpointExtension();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,17 +16,19 @@
|
||||
|
||||
package org.springframework.boot.actuate.endpoint.web;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.assertj.core.api.Condition;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.EndpointInfo;
|
||||
import org.springframework.boot.actuate.endpoint.OperationType;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link EndpointLinksResolver}.
|
||||
@@ -57,13 +59,16 @@ public class EndpointLinksResolverTests {
|
||||
|
||||
@Test
|
||||
public void resolvedLinksContainsALinkForEachEndpointOperation() {
|
||||
List<WebOperation> operations = new ArrayList<>();
|
||||
operations.add(operationWithPath("/alpha", "alpha"));
|
||||
operations.add(operationWithPath("/alpha/{name}", "alpha-name"));
|
||||
ExposableWebEndpoint endpoint = mock(ExposableWebEndpoint.class);
|
||||
given(endpoint.getId()).willReturn("alpha");
|
||||
given(endpoint.isEnableByDefault()).willReturn(true);
|
||||
given(endpoint.getOperations()).willReturn(operations);
|
||||
String requestUrl = "https://api.example.com/actuator";
|
||||
Map<String, Link> links = this.linksResolver
|
||||
.resolveLinks(
|
||||
Arrays.asList(new EndpointInfo<>("alpha", true,
|
||||
Arrays.asList(operationWithPath("/alpha", "alpha"),
|
||||
operationWithPath("/alpha/{name}",
|
||||
"alpha-name")))),
|
||||
"https://api.example.com/actuator");
|
||||
.resolveLinks(Collections.singletonList(endpoint), requestUrl);
|
||||
assertThat(links).hasSize(3);
|
||||
assertThat(links).hasEntrySatisfying("self",
|
||||
linkWithHref("https://api.example.com/actuator"));
|
||||
@@ -74,10 +79,14 @@ public class EndpointLinksResolverTests {
|
||||
}
|
||||
|
||||
private WebOperation operationWithPath(String path, String id) {
|
||||
return new WebOperation(OperationType.READ, null, false,
|
||||
new OperationRequestPredicate(path, WebEndpointHttpMethod.GET,
|
||||
Collections.emptyList(), Collections.emptyList()),
|
||||
id);
|
||||
WebOperationRequestPredicate predicate = new WebOperationRequestPredicate(path,
|
||||
WebEndpointHttpMethod.GET, Collections.emptyList(),
|
||||
Collections.emptyList());
|
||||
WebOperation operation = mock(WebOperation.class);
|
||||
given(operation.getId()).willReturn(id);
|
||||
given(operation.getType()).willReturn(OperationType.READ);
|
||||
given(operation.getRequestPredicate()).willReturn(predicate);
|
||||
return operation;
|
||||
}
|
||||
|
||||
private Condition<Link> linkWithHref(String href) {
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.endpoint.web;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link EndpointMediaTypes}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class EndpointMediaTypesTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void createWhenProducedIsNullShouldThrowException() {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("Produced must not be null");
|
||||
new EndpointMediaTypes(null, Collections.emptyList());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createWhenConsumedIsNullShouldThrowException() {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("Consumed must not be null");
|
||||
new EndpointMediaTypes(Collections.emptyList(), null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getProducedShouldReturnProduced() {
|
||||
List<String> produced = Arrays.asList("a", "b", "c");
|
||||
EndpointMediaTypes types = new EndpointMediaTypes(produced,
|
||||
Collections.emptyList());
|
||||
assertThat(types.getProduced()).isEqualTo(produced);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getConsumedShouldReturnConsumed() {
|
||||
List<String> consumed = Arrays.asList("a", "b", "c");
|
||||
EndpointMediaTypes types = new EndpointMediaTypes(Collections.emptyList(),
|
||||
consumed);
|
||||
assertThat(types.getConsumed()).isEqualTo(consumed);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.endpoint.web;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link Link}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class LinkTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void createWhenHrefIsNullShouldThrowException() {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("HREF must not be null");
|
||||
new Link(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getHrefShouldReturnHref() {
|
||||
String href = "http://example.com";
|
||||
Link link = new Link(href);
|
||||
assertThat(link.getHref()).isEqualTo(href);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isTemplatedWhenContainsPlaceholderShouldReturnTrue() {
|
||||
String href = "http://example.com/{path}";
|
||||
Link link = new Link(href);
|
||||
assertThat(link.isTemplated()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isTemplatedWhenContainsNoPlaceholderShouldReturnFalse() {
|
||||
String href = "http://example.com/path";
|
||||
Link link = new Link(href);
|
||||
assertThat(link.isTemplated()).isFalse();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.endpoint.web;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link WebEndpointResponse}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class WebEndpointResponseTests {
|
||||
|
||||
@Test
|
||||
public void createWithNoParamsShouldReturn200() {
|
||||
WebEndpointResponse<Object> response = new WebEndpointResponse<>();
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
assertThat(response.getBody()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createWithStatusShouldReturnStatus() {
|
||||
WebEndpointResponse<Object> response = new WebEndpointResponse<>(404);
|
||||
assertThat(response.getStatus()).isEqualTo(404);
|
||||
assertThat(response.getBody()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createWithBodyShouldReturnBody() {
|
||||
WebEndpointResponse<Object> response = new WebEndpointResponse<>("body");
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
assertThat(response.getBody()).isEqualTo("body");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createWhithBodyAndStatusShouldReturnStatusAndBody() {
|
||||
WebEndpointResponse<Object> response = new WebEndpointResponse<>("body", 500);
|
||||
assertThat(response.getStatus()).isEqualTo(500);
|
||||
assertThat(response.getBody()).isEqualTo("body");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,11 +23,11 @@ import org.junit.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link OperationRequestPredicate}.
|
||||
* Tests for {@link WebOperationRequestPredicate}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class OperationRequestPredicateTests {
|
||||
public class WebOperationRequestPredicateTests {
|
||||
|
||||
@Test
|
||||
public void predicatesWithIdenticalPathsAreEqual() {
|
||||
@@ -63,8 +63,8 @@ public class OperationRequestPredicateTests {
|
||||
.isEqualTo(predicateWithPath("/path/{foo2}/more/{bar2}"));
|
||||
}
|
||||
|
||||
private OperationRequestPredicate predicateWithPath(String path) {
|
||||
return new OperationRequestPredicate(path, WebEndpointHttpMethod.GET,
|
||||
private WebOperationRequestPredicate predicateWithPath(String path) {
|
||||
return new WebOperationRequestPredicate(path, WebEndpointHttpMethod.GET,
|
||||
Collections.emptyList(), Collections.emptyList());
|
||||
}
|
||||
|
||||
@@ -14,11 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.endpoint.web;
|
||||
package org.springframework.boot.actuate.endpoint.web.annotation;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -29,22 +28,17 @@ import java.util.function.Consumer;
|
||||
import org.junit.Test;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.DeleteOperation;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Selector;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
|
||||
import org.springframework.boot.actuate.endpoint.convert.ConversionServiceParameterMapper;
|
||||
import org.springframework.boot.actuate.endpoint.reflect.ParameterMapper;
|
||||
import org.springframework.boot.actuate.endpoint.web.annotation.WebAnnotationEndpointDiscoverer;
|
||||
import org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader;
|
||||
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
@@ -53,7 +47,6 @@ import org.springframework.lang.Nullable;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
@@ -362,47 +355,6 @@ public abstract class AbstractWebEndpointIntegrationTests<T extends Configurable
|
||||
(context, client) -> clientConsumer.accept(client));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class BaseConfiguration {
|
||||
|
||||
@Bean
|
||||
public EndpointDelegate endpointDelegate() {
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
if (classLoader instanceof TomcatEmbeddedWebappClassLoader) {
|
||||
Thread.currentThread().setContextClassLoader(classLoader.getParent());
|
||||
}
|
||||
try {
|
||||
return mock(EndpointDelegate.class);
|
||||
}
|
||||
finally {
|
||||
Thread.currentThread().setContextClassLoader(classLoader);
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
public EndpointMediaTypes endpointMediaTypes() {
|
||||
List<String> mediaTypes = Arrays.asList("application/vnd.test+json",
|
||||
"application/json");
|
||||
return new EndpointMediaTypes(mediaTypes, mediaTypes);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public WebAnnotationEndpointDiscoverer webEndpointDiscoverer(
|
||||
ApplicationContext applicationContext) {
|
||||
ParameterMapper parameterMapper = new ConversionServiceParameterMapper(
|
||||
DefaultConversionService.getSharedInstance());
|
||||
return new WebAnnotationEndpointDiscoverer(applicationContext,
|
||||
parameterMapper, endpointMediaTypes(),
|
||||
EndpointPathResolver.useEndpointId(), null, null);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() {
|
||||
return new PropertyPlaceholderConfigurer();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(BaseConfiguration.class)
|
||||
protected static class TestEndpointConfiguration {
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.endpoint.web.annotation;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
|
||||
import org.springframework.boot.actuate.endpoint.invoke.ParameterValueMapper;
|
||||
import org.springframework.boot.actuate.endpoint.invoke.convert.ConversionServiceParameterValueMapper;
|
||||
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
|
||||
import org.springframework.boot.actuate.endpoint.web.EndpointPathResolver;
|
||||
import org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Base configuration shared by tests.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@Configuration
|
||||
class BaseConfiguration {
|
||||
|
||||
@Bean
|
||||
public AbstractWebEndpointIntegrationTests.EndpointDelegate endpointDelegate() {
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
if (classLoader instanceof TomcatEmbeddedWebappClassLoader) {
|
||||
Thread.currentThread().setContextClassLoader(classLoader.getParent());
|
||||
}
|
||||
try {
|
||||
return mock(AbstractWebEndpointIntegrationTests.EndpointDelegate.class);
|
||||
}
|
||||
finally {
|
||||
Thread.currentThread().setContextClassLoader(classLoader);
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
public EndpointMediaTypes endpointMediaTypes() {
|
||||
List<String> mediaTypes = Arrays.asList("application/vnd.test+json",
|
||||
"application/json");
|
||||
return new EndpointMediaTypes(mediaTypes, mediaTypes);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public WebEndpointDiscoverer webEndpointDiscoverer(
|
||||
ApplicationContext applicationContext) {
|
||||
ParameterValueMapper parameterMapper = new ConversionServiceParameterValueMapper(
|
||||
DefaultConversionService.getSharedInstance());
|
||||
return new WebEndpointDiscoverer(applicationContext, parameterMapper,
|
||||
endpointMediaTypes(), EndpointPathResolver.useEndpointId(),
|
||||
Collections.emptyList(), Collections.emptyList());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() {
|
||||
return new PropertyPlaceholderConfigurer();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.endpoint.web;
|
||||
package org.springframework.boot.actuate.endpoint.web.annotation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -33,20 +33,21 @@ import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.EndpointInfo;
|
||||
import org.springframework.boot.actuate.endpoint.OperationInvoker;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.DeleteOperation;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Selector;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
|
||||
import org.springframework.boot.actuate.endpoint.cache.CachingOperationInvoker;
|
||||
import org.springframework.boot.actuate.endpoint.cache.CachingOperationInvokerAdvisor;
|
||||
import org.springframework.boot.actuate.endpoint.convert.ConversionServiceParameterMapper;
|
||||
import org.springframework.boot.actuate.endpoint.invoke.convert.ConversionServiceParameterValueMapper;
|
||||
import org.springframework.boot.actuate.endpoint.invoker.cache.CachingOperationInvoker;
|
||||
import org.springframework.boot.actuate.endpoint.invoker.cache.CachingOperationInvokerAdvisor;
|
||||
import org.springframework.boot.actuate.endpoint.jmx.annotation.JmxEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.web.AbstractWebEndpointIntegrationTests.BaseConfiguration;
|
||||
import org.springframework.boot.actuate.endpoint.web.annotation.EndpointWebExtension;
|
||||
import org.springframework.boot.actuate.endpoint.web.annotation.WebAnnotationEndpointDiscoverer;
|
||||
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
|
||||
import org.springframework.boot.actuate.endpoint.web.EndpointPathResolver;
|
||||
import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.web.WebEndpointHttpMethod;
|
||||
import org.springframework.boot.actuate.endpoint.web.WebOperation;
|
||||
import org.springframework.boot.actuate.endpoint.web.WebOperationRequestPredicate;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -54,55 +55,55 @@ import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link WebAnnotationEndpointDiscoverer}.
|
||||
* Tests for {@link WebEndpointDiscoverer}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class WebAnnotationEndpointDiscovererTests {
|
||||
public class WebEndpointDiscovererTests {
|
||||
|
||||
@Rule
|
||||
public final ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void discoveryWorksWhenThereAreNoEndpoints() {
|
||||
public void getEndpointsWhenNoEndpointBeansShouldReturnEmptyCollection() {
|
||||
load(EmptyConfiguration.class,
|
||||
(discoverer) -> assertThat(discoverer.discoverEndpoints()).isEmpty());
|
||||
(discoverer) -> assertThat(discoverer.getEndpoints()).isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void webExtensionMustHaveEndpoint() {
|
||||
public void getEndpointsWhenWebExtensionIsMissingEndpointShouldThrowException() {
|
||||
load(TestWebEndpointExtensionConfiguration.class, (discoverer) -> {
|
||||
this.thrown.expect(IllegalStateException.class);
|
||||
this.thrown.expectMessage("Invalid extension");
|
||||
this.thrown.expectMessage(TestWebEndpointExtension.class.getName());
|
||||
this.thrown.expectMessage("no endpoint found");
|
||||
this.thrown.expectMessage(TestEndpoint.class.getName());
|
||||
discoverer.discoverEndpoints();
|
||||
this.thrown.expectMessage(
|
||||
"Invalid extension 'endpointExtension': no endpoint found with type '"
|
||||
+ TestEndpoint.class.getName() + "'");
|
||||
discoverer.getEndpoints();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onlyWebEndpointsAreDiscovered() {
|
||||
public void getEndpointsWhenHasFilteredEndpointShouldOnlyDiscoverWebEndpoints() {
|
||||
load(MultipleEndpointsConfiguration.class, (discoverer) -> {
|
||||
Map<String, EndpointInfo<WebOperation>> endpoints = mapEndpoints(
|
||||
discoverer.discoverEndpoints());
|
||||
Map<String, ExposableWebEndpoint> endpoints = mapEndpoints(
|
||||
discoverer.getEndpoints());
|
||||
assertThat(endpoints).containsOnlyKeys("test");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void oneOperationIsDiscoveredWhenExtensionOverridesOperation() {
|
||||
public void getEndpointsWhenHasWebExtensionShouldOverrideStandardEndpoint() {
|
||||
load(OverriddenOperationWebEndpointExtensionConfiguration.class, (discoverer) -> {
|
||||
Map<String, EndpointInfo<WebOperation>> endpoints = mapEndpoints(
|
||||
discoverer.discoverEndpoints());
|
||||
Map<String, ExposableWebEndpoint> endpoints = mapEndpoints(
|
||||
discoverer.getEndpoints());
|
||||
assertThat(endpoints).containsOnlyKeys("test");
|
||||
EndpointInfo<WebOperation> endpoint = endpoints.get("test");
|
||||
ExposableWebEndpoint endpoint = endpoints.get("test");
|
||||
assertThat(requestPredicates(endpoint)).has(
|
||||
requestPredicates(path("test").httpMethod(WebEndpointHttpMethod.GET)
|
||||
.consumes().produces("application/json")));
|
||||
@@ -110,12 +111,12 @@ public class WebAnnotationEndpointDiscovererTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void twoOperationsAreDiscoveredWhenExtensionAddsOperation() {
|
||||
public void getEndpointsWhenExtensionAddsOperationShouldHaveBothOperations() {
|
||||
load(AdditionalOperationWebEndpointConfiguration.class, (discoverer) -> {
|
||||
Map<String, EndpointInfo<WebOperation>> endpoints = mapEndpoints(
|
||||
discoverer.discoverEndpoints());
|
||||
Map<String, ExposableWebEndpoint> endpoints = mapEndpoints(
|
||||
discoverer.getEndpoints());
|
||||
assertThat(endpoints).containsOnlyKeys("test");
|
||||
EndpointInfo<WebOperation> endpoint = endpoints.get("test");
|
||||
ExposableWebEndpoint endpoint = endpoints.get("test");
|
||||
assertThat(requestPredicates(endpoint)).has(requestPredicates(
|
||||
path("test").httpMethod(WebEndpointHttpMethod.GET).consumes()
|
||||
.produces("application/json"),
|
||||
@@ -125,12 +126,12 @@ public class WebAnnotationEndpointDiscovererTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void predicateForWriteOperationThatReturnsVoidHasNoProducedMediaTypes() {
|
||||
public void getEndpointsWhenPredicateForWriteOperationThatReturnsVoidShouldHaveNoProducedMediaTypes() {
|
||||
load(VoidWriteOperationEndpointConfiguration.class, (discoverer) -> {
|
||||
Map<String, EndpointInfo<WebOperation>> endpoints = mapEndpoints(
|
||||
discoverer.discoverEndpoints());
|
||||
Map<String, ExposableWebEndpoint> endpoints = mapEndpoints(
|
||||
discoverer.getEndpoints());
|
||||
assertThat(endpoints).containsOnlyKeys("voidwrite");
|
||||
EndpointInfo<WebOperation> endpoint = endpoints.get("voidwrite");
|
||||
ExposableWebEndpoint endpoint = endpoints.get("voidwrite");
|
||||
assertThat(requestPredicates(endpoint)).has(requestPredicates(
|
||||
path("voidwrite").httpMethod(WebEndpointHttpMethod.POST).produces()
|
||||
.consumes("application/json")));
|
||||
@@ -138,91 +139,78 @@ public class WebAnnotationEndpointDiscovererTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void discoveryFailsWhenTwoExtensionsHaveTheSameEndpointType() {
|
||||
public void getEndpointsWhenTwoExtensionsHaveTheSameEndpointTypeShouldThrowException() {
|
||||
load(ClashingWebEndpointConfiguration.class, (discoverer) -> {
|
||||
this.thrown.expect(IllegalStateException.class);
|
||||
this.thrown.expectMessage("Found two extensions for the same endpoint");
|
||||
this.thrown.expectMessage(TestEndpoint.class.getName());
|
||||
this.thrown.expectMessage(TestWebEndpointExtension.class.getName());
|
||||
discoverer.discoverEndpoints();
|
||||
this.thrown.expectMessage("Found multiple extensions for the endpoint bean "
|
||||
+ "testEndpoint (testExtensionOne, testExtensionTwo)");
|
||||
discoverer.getEndpoints();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void discoveryFailsWhenTwoStandardEndpointsHaveTheSameId() {
|
||||
public void getEndpointsWhenTwoStandardEndpointsHaveTheSameIdShouldThrowException() {
|
||||
load(ClashingStandardEndpointConfiguration.class, (discoverer) -> {
|
||||
this.thrown.expect(IllegalStateException.class);
|
||||
this.thrown.expectMessage("Found two endpoints with the id 'test': ");
|
||||
discoverer.discoverEndpoints();
|
||||
discoverer.getEndpoints();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void discoveryFailsWhenEndpointHasClashingOperations() {
|
||||
public void getEndpointsWhenWhenEndpointHasTwoOperationsWithTheSameNameShouldThrowException() {
|
||||
load(ClashingOperationsEndpointConfiguration.class, (discoverer) -> {
|
||||
this.thrown.expect(IllegalStateException.class);
|
||||
this.thrown.expectMessage(
|
||||
"Found multiple web operations with matching request predicates:");
|
||||
this.thrown.expectMessage(ReflectionUtils
|
||||
.findMethod(ClashingOperationsEndpoint.class, "getAll").toString());
|
||||
this.thrown.expectMessage(ReflectionUtils
|
||||
.findMethod(ClashingOperationsEndpoint.class, "getAgain").toString());
|
||||
discoverer.discoverEndpoints();
|
||||
this.thrown.expectMessage("Unable to map duplicate endpoint operations: "
|
||||
+ "[web request predicate GET to path 'test' "
|
||||
+ "produces: application/json] to clashingOperationsEndpoint");
|
||||
discoverer.getEndpoints();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void discoveryFailsWhenExtensionIsNotCompatibleWithTheEndpointType() {
|
||||
public void getEndpointsWhenExtensionIsNotCompatibleWithTheEndpointTypeShouldThrowException() {
|
||||
load(InvalidWebExtensionConfiguration.class, (discoverer) -> {
|
||||
this.thrown.expect(IllegalStateException.class);
|
||||
this.thrown.expectMessage("Invalid extension");
|
||||
this.thrown.expectMessage(NonWebWebEndpointExtension.class.getName());
|
||||
this.thrown.expectMessage(NonWebEndpoint.class.getName());
|
||||
discoverer.discoverEndpoints();
|
||||
this.thrown.expectMessage("Endpoint bean 'nonWebEndpoint' cannot support the "
|
||||
+ "extension bean 'nonWebWebEndpointExtension'");
|
||||
discoverer.getEndpoints();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void twoOperationsOnSameEndpointClashWhenSelectorsHaveDifferentNames() {
|
||||
public void getEndpointsWhenWhenExtensionHasTwoOperationsWithTheSameNameShouldThrowException() {
|
||||
load(ClashingSelectorsWebEndpointExtensionConfiguration.class, (discoverer) -> {
|
||||
this.thrown.expect(IllegalStateException.class);
|
||||
this.thrown.expectMessage(
|
||||
"Found multiple web operations with matching request predicates:");
|
||||
this.thrown.expectMessage(ReflectionUtils
|
||||
.findMethod(ClashingSelectorsWebEndpointExtension.class, "readOne",
|
||||
String.class, String.class)
|
||||
.toString());
|
||||
this.thrown.expectMessage(ReflectionUtils
|
||||
.findMethod(ClashingSelectorsWebEndpointExtension.class, "readTwo",
|
||||
String.class, String.class)
|
||||
.toString());
|
||||
discoverer.discoverEndpoints();
|
||||
this.thrown.expectMessage("Unable to map duplicate endpoint operations");
|
||||
this.thrown.expectMessage("to testEndpoint (clashingSelectorsExtension)");
|
||||
discoverer.getEndpoints();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void endpointMainReadOperationIsCachedWithMatchingId() {
|
||||
public void getEndpointsWhenHasCacheWithTtlShouldCacheReadOperationWithTtlValue() {
|
||||
load((id) -> 500L, (id) -> id, TestEndpointConfiguration.class, (discoverer) -> {
|
||||
Map<String, EndpointInfo<WebOperation>> endpoints = mapEndpoints(
|
||||
discoverer.discoverEndpoints());
|
||||
Map<String, ExposableWebEndpoint> endpoints = mapEndpoints(
|
||||
discoverer.getEndpoints());
|
||||
assertThat(endpoints).containsOnlyKeys("test");
|
||||
EndpointInfo<WebOperation> endpoint = endpoints.get("test");
|
||||
ExposableWebEndpoint endpoint = endpoints.get("test");
|
||||
assertThat(endpoint.getOperations()).hasSize(1);
|
||||
OperationInvoker operationInvoker = endpoint.getOperations().iterator().next()
|
||||
.getInvoker();
|
||||
assertThat(operationInvoker).isInstanceOf(CachingOperationInvoker.class);
|
||||
assertThat(((CachingOperationInvoker) operationInvoker).getTimeToLive())
|
||||
WebOperation operation = endpoint.getOperations().iterator().next();
|
||||
Object invoker = ReflectionTestUtils.getField(operation, "invoker");
|
||||
assertThat(invoker).isInstanceOf(CachingOperationInvoker.class);
|
||||
assertThat(((CachingOperationInvoker) invoker).getTimeToLive())
|
||||
.isEqualTo(500);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void operationsThatReturnResourceProduceApplicationOctetStream() {
|
||||
public void getEndpointsWhenOperationReturnsResourceShouldProduceApplicationOctetStream() {
|
||||
load(ResourceEndpointConfiguration.class, (discoverer) -> {
|
||||
Map<String, EndpointInfo<WebOperation>> endpoints = mapEndpoints(
|
||||
discoverer.discoverEndpoints());
|
||||
Map<String, ExposableWebEndpoint> endpoints = mapEndpoints(
|
||||
discoverer.getEndpoints());
|
||||
assertThat(endpoints).containsOnlyKeys("resource");
|
||||
EndpointInfo<WebOperation> endpoint = endpoints.get("resource");
|
||||
ExposableWebEndpoint endpoint = endpoints.get("resource");
|
||||
assertThat(requestPredicates(endpoint)).has(requestPredicates(
|
||||
path("resource").httpMethod(WebEndpointHttpMethod.GET).consumes()
|
||||
.produces("application/octet-stream")));
|
||||
@@ -230,12 +218,12 @@ public class WebAnnotationEndpointDiscovererTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void operationCanProduceCustomMediaTypes() {
|
||||
public void getEndpointsWhenHasCustomMediaTypeShouldProduceCustomMediaType() {
|
||||
load(CustomMediaTypesEndpointConfiguration.class, (discoverer) -> {
|
||||
Map<String, EndpointInfo<WebOperation>> endpoints = mapEndpoints(
|
||||
discoverer.discoverEndpoints());
|
||||
Map<String, ExposableWebEndpoint> endpoints = mapEndpoints(
|
||||
discoverer.getEndpoints());
|
||||
assertThat(endpoints).containsOnlyKeys("custommediatypes");
|
||||
EndpointInfo<WebOperation> endpoint = endpoints.get("custommediatypes");
|
||||
ExposableWebEndpoint endpoint = endpoints.get("custommediatypes");
|
||||
assertThat(requestPredicates(endpoint)).has(requestPredicates(
|
||||
path("custommediatypes").httpMethod(WebEndpointHttpMethod.GET)
|
||||
.consumes().produces("text/plain"),
|
||||
@@ -247,14 +235,14 @@ public class WebAnnotationEndpointDiscovererTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void endpointPathCanBeCustomized() {
|
||||
public void getEndpointsWhenHasCustomPathShouldReturnCustomPath() {
|
||||
load((id) -> null, (id) -> "custom/" + id,
|
||||
AdditionalOperationWebEndpointConfiguration.class, (discoverer) -> {
|
||||
Map<String, EndpointInfo<WebOperation>> endpoints = mapEndpoints(
|
||||
discoverer.discoverEndpoints());
|
||||
Map<String, ExposableWebEndpoint> endpoints = mapEndpoints(
|
||||
discoverer.getEndpoints());
|
||||
assertThat(endpoints).containsOnlyKeys("test");
|
||||
EndpointInfo<WebOperation> endpoint = endpoints.get("test");
|
||||
Condition<List<? extends OperationRequestPredicate>> expected = requestPredicates(
|
||||
ExposableWebEndpoint endpoint = endpoints.get("test");
|
||||
Condition<List<? extends WebOperationRequestPredicate>> expected = requestPredicates(
|
||||
path("custom/test").httpMethod(WebEndpointHttpMethod.GET)
|
||||
.consumes().produces("application/json"),
|
||||
path("custom/test/{id}").httpMethod(WebEndpointHttpMethod.GET)
|
||||
@@ -263,26 +251,25 @@ public class WebAnnotationEndpointDiscovererTests {
|
||||
});
|
||||
}
|
||||
|
||||
private void load(Class<?> configuration,
|
||||
Consumer<WebAnnotationEndpointDiscoverer> consumer) {
|
||||
private void load(Class<?> configuration, Consumer<WebEndpointDiscoverer> consumer) {
|
||||
this.load((id) -> null, (id) -> id, configuration, consumer);
|
||||
}
|
||||
|
||||
private void load(Function<String, Long> timeToLive,
|
||||
EndpointPathResolver endpointPathResolver, Class<?> configuration,
|
||||
Consumer<WebAnnotationEndpointDiscoverer> consumer) {
|
||||
Consumer<WebEndpointDiscoverer> consumer) {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
||||
configuration);
|
||||
try {
|
||||
ConversionServiceParameterMapper parameterMapper = new ConversionServiceParameterMapper(
|
||||
ConversionServiceParameterValueMapper parameterMapper = new ConversionServiceParameterValueMapper(
|
||||
DefaultConversionService.getSharedInstance());
|
||||
EndpointMediaTypes mediaTypes = new EndpointMediaTypes(
|
||||
Collections.singletonList("application/json"),
|
||||
Collections.singletonList("application/json"));
|
||||
WebAnnotationEndpointDiscoverer discoverer = new WebAnnotationEndpointDiscoverer(
|
||||
context, parameterMapper, mediaTypes, endpointPathResolver,
|
||||
WebEndpointDiscoverer discoverer = new WebEndpointDiscoverer(context,
|
||||
parameterMapper, mediaTypes, endpointPathResolver,
|
||||
Collections.singleton(new CachingOperationInvokerAdvisor(timeToLive)),
|
||||
null);
|
||||
Collections.emptyList());
|
||||
consumer.accept(discoverer);
|
||||
}
|
||||
finally {
|
||||
@@ -290,27 +277,27 @@ public class WebAnnotationEndpointDiscovererTests {
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, EndpointInfo<WebOperation>> mapEndpoints(
|
||||
Collection<EndpointInfo<WebOperation>> endpoints) {
|
||||
Map<String, EndpointInfo<WebOperation>> endpointById = new HashMap<>();
|
||||
private Map<String, ExposableWebEndpoint> mapEndpoints(
|
||||
Collection<ExposableWebEndpoint> endpoints) {
|
||||
Map<String, ExposableWebEndpoint> endpointById = new HashMap<>();
|
||||
endpoints.forEach((endpoint) -> endpointById.put(endpoint.getId(), endpoint));
|
||||
return endpointById;
|
||||
}
|
||||
|
||||
private List<OperationRequestPredicate> requestPredicates(
|
||||
EndpointInfo<WebOperation> endpoint) {
|
||||
private List<WebOperationRequestPredicate> requestPredicates(
|
||||
ExposableWebEndpoint endpoint) {
|
||||
return endpoint.getOperations().stream().map(WebOperation::getRequestPredicate)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private Condition<List<? extends OperationRequestPredicate>> requestPredicates(
|
||||
private Condition<List<? extends WebOperationRequestPredicate>> requestPredicates(
|
||||
RequestPredicateMatcher... matchers) {
|
||||
return new Condition<>((predicates) -> {
|
||||
if (predicates.size() != matchers.length) {
|
||||
return false;
|
||||
}
|
||||
Map<OperationRequestPredicate, Long> matchCounts = new HashMap<>();
|
||||
for (OperationRequestPredicate predicate : predicates) {
|
||||
Map<WebOperationRequestPredicate, Long> matchCounts = new HashMap<>();
|
||||
for (WebOperationRequestPredicate predicate : predicates) {
|
||||
matchCounts.put(predicate, Stream.of(matchers)
|
||||
.filter(matcher -> matcher.matches(predicate)).count());
|
||||
}
|
||||
@@ -327,165 +314,6 @@ public class WebAnnotationEndpointDiscovererTests {
|
||||
|
||||
}
|
||||
|
||||
@EndpointWebExtension(endpoint = TestEndpoint.class)
|
||||
static class TestWebEndpointExtension {
|
||||
|
||||
@ReadOperation
|
||||
public Object getAll() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ReadOperation
|
||||
public Object getOne(@Selector String id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@WriteOperation
|
||||
public void update(String foo, String bar) {
|
||||
|
||||
}
|
||||
|
||||
public void someOtherMethod() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Endpoint(id = "test")
|
||||
static class TestEndpoint {
|
||||
|
||||
@ReadOperation
|
||||
public Object getAll() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EndpointWebExtension(endpoint = TestEndpoint.class)
|
||||
static class OverriddenOperationWebEndpointExtension {
|
||||
|
||||
@ReadOperation
|
||||
public Object getAll() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EndpointWebExtension(endpoint = TestEndpoint.class)
|
||||
static class AdditionalOperationWebEndpointExtension {
|
||||
|
||||
@ReadOperation
|
||||
public Object getOne(@Selector String id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Endpoint(id = "test")
|
||||
static class ClashingOperationsEndpoint {
|
||||
|
||||
@ReadOperation
|
||||
public Object getAll() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ReadOperation
|
||||
public Object getAgain() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EndpointWebExtension(endpoint = TestEndpoint.class)
|
||||
static class ClashingOperationsWebEndpointExtension {
|
||||
|
||||
@ReadOperation
|
||||
public Object getAll() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ReadOperation
|
||||
public Object getAgain() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EndpointWebExtension(endpoint = TestEndpoint.class)
|
||||
static class ClashingSelectorsWebEndpointExtension {
|
||||
|
||||
@ReadOperation
|
||||
public Object readOne(@Selector String oneA, @Selector String oneB) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ReadOperation
|
||||
public Object readTwo(@Selector String twoA, @Selector String twoB) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@JmxEndpoint(id = "nonweb")
|
||||
static class NonWebEndpoint {
|
||||
|
||||
@ReadOperation
|
||||
public Object getData() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EndpointWebExtension(endpoint = NonWebEndpoint.class)
|
||||
static class NonWebWebEndpointExtension {
|
||||
|
||||
@ReadOperation
|
||||
public Object getSomething(@Selector String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Endpoint(id = "voidwrite")
|
||||
static class VoidWriteOperationEndpoint {
|
||||
|
||||
@WriteOperation
|
||||
public void write(String foo, String bar) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Endpoint(id = "resource")
|
||||
static class ResourceEndpoint {
|
||||
|
||||
@ReadOperation
|
||||
public Resource read() {
|
||||
return new ByteArrayResource(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Endpoint(id = "custommediatypes")
|
||||
static class CustomMediaTypesEndpoint {
|
||||
|
||||
@ReadOperation(produces = "text/plain")
|
||||
public String read() {
|
||||
return "read";
|
||||
}
|
||||
|
||||
@WriteOperation(produces = { "a/b", "c/d" })
|
||||
public String write() {
|
||||
return "write";
|
||||
|
||||
}
|
||||
|
||||
@DeleteOperation(produces = "text/plain")
|
||||
public String delete() {
|
||||
return "delete";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class MultipleEndpointsConfiguration {
|
||||
|
||||
@@ -660,6 +488,165 @@ public class WebAnnotationEndpointDiscovererTests {
|
||||
|
||||
}
|
||||
|
||||
@EndpointWebExtension(endpoint = TestEndpoint.class)
|
||||
static class TestWebEndpointExtension {
|
||||
|
||||
@ReadOperation
|
||||
public Object getAll() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ReadOperation
|
||||
public Object getOne(@Selector String id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@WriteOperation
|
||||
public void update(String foo, String bar) {
|
||||
|
||||
}
|
||||
|
||||
public void someOtherMethod() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Endpoint(id = "test")
|
||||
static class TestEndpoint {
|
||||
|
||||
@ReadOperation
|
||||
public Object getAll() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EndpointWebExtension(endpoint = TestEndpoint.class)
|
||||
static class OverriddenOperationWebEndpointExtension {
|
||||
|
||||
@ReadOperation
|
||||
public Object getAll() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EndpointWebExtension(endpoint = TestEndpoint.class)
|
||||
static class AdditionalOperationWebEndpointExtension {
|
||||
|
||||
@ReadOperation
|
||||
public Object getOne(@Selector String id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Endpoint(id = "test")
|
||||
static class ClashingOperationsEndpoint {
|
||||
|
||||
@ReadOperation
|
||||
public Object getAll() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ReadOperation
|
||||
public Object getAgain() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EndpointWebExtension(endpoint = TestEndpoint.class)
|
||||
static class ClashingOperationsWebEndpointExtension {
|
||||
|
||||
@ReadOperation
|
||||
public Object getAll() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ReadOperation
|
||||
public Object getAgain() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EndpointWebExtension(endpoint = TestEndpoint.class)
|
||||
static class ClashingSelectorsWebEndpointExtension {
|
||||
|
||||
@ReadOperation
|
||||
public Object readOne(@Selector String oneA, @Selector String oneB) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ReadOperation
|
||||
public Object readTwo(@Selector String twoA, @Selector String twoB) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@JmxEndpoint(id = "nonweb")
|
||||
static class NonWebEndpoint {
|
||||
|
||||
@ReadOperation
|
||||
public Object getData() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EndpointWebExtension(endpoint = NonWebEndpoint.class)
|
||||
static class NonWebWebEndpointExtension {
|
||||
|
||||
@ReadOperation
|
||||
public Object getSomething(@Selector String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Endpoint(id = "voidwrite")
|
||||
static class VoidWriteOperationEndpoint {
|
||||
|
||||
@WriteOperation
|
||||
public void write(String foo, String bar) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Endpoint(id = "resource")
|
||||
static class ResourceEndpoint {
|
||||
|
||||
@ReadOperation
|
||||
public Resource read() {
|
||||
return new ByteArrayResource(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Endpoint(id = "custommediatypes")
|
||||
static class CustomMediaTypesEndpoint {
|
||||
|
||||
@ReadOperation(produces = "text/plain")
|
||||
public String read() {
|
||||
return "read";
|
||||
}
|
||||
|
||||
@WriteOperation(produces = { "a/b", "c/d" })
|
||||
public String write() {
|
||||
return "write";
|
||||
|
||||
}
|
||||
|
||||
@DeleteOperation(produces = "text/plain")
|
||||
public String delete() {
|
||||
return "delete";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class RequestPredicateMatcher {
|
||||
|
||||
private final String path;
|
||||
@@ -689,7 +676,7 @@ public class WebAnnotationEndpointDiscovererTests {
|
||||
return this;
|
||||
}
|
||||
|
||||
private boolean matches(OperationRequestPredicate predicate) {
|
||||
private boolean matches(WebOperationRequestPredicate predicate) {
|
||||
return (this.path == null || this.path.equals(predicate.getPath()))
|
||||
&& (this.httpMethod == null
|
||||
|| this.httpMethod == predicate.getHttpMethod())
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -27,10 +27,9 @@ import org.glassfish.jersey.server.ResourceConfig;
|
||||
import org.glassfish.jersey.server.model.Resource;
|
||||
import org.glassfish.jersey.servlet.ServletContainer;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.EndpointDiscoverer;
|
||||
import org.springframework.boot.actuate.endpoint.web.AbstractWebEndpointIntegrationTests;
|
||||
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
|
||||
import org.springframework.boot.actuate.endpoint.web.WebOperation;
|
||||
import org.springframework.boot.actuate.endpoint.web.annotation.AbstractWebEndpointIntegrationTests;
|
||||
import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpointDiscoverer;
|
||||
import org.springframework.boot.endpoint.web.EndpointMapping;
|
||||
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
|
||||
import org.springframework.boot.web.servlet.ServletRegistrationBean;
|
||||
@@ -82,13 +81,13 @@ public class JerseyWebEndpointIntegrationTests extends
|
||||
|
||||
@Bean
|
||||
public ResourceConfig resourceConfig(Environment environment,
|
||||
EndpointDiscoverer<WebOperation> endpointDiscoverer,
|
||||
WebEndpointDiscoverer endpointDiscoverer,
|
||||
EndpointMediaTypes endpointMediaTypes) {
|
||||
ResourceConfig resourceConfig = new ResourceConfig();
|
||||
Collection<Resource> resources = new JerseyEndpointResourceFactory()
|
||||
.createEndpointResources(
|
||||
new EndpointMapping(environment.getProperty("endpointPath")),
|
||||
endpointDiscoverer.discoverEndpoints(), endpointMediaTypes);
|
||||
endpointDiscoverer.getEndpoints(), endpointMediaTypes);
|
||||
resourceConfig.registerResources(new HashSet<>(resources));
|
||||
resourceConfig.register(JacksonFeature.class);
|
||||
resourceConfig.register(new ObjectMapperContextResolver(new ObjectMapper()),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,10 +20,9 @@ import java.util.Arrays;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.EndpointDiscoverer;
|
||||
import org.springframework.boot.actuate.endpoint.web.AbstractWebEndpointIntegrationTests;
|
||||
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
|
||||
import org.springframework.boot.actuate.endpoint.web.WebOperation;
|
||||
import org.springframework.boot.actuate.endpoint.web.annotation.AbstractWebEndpointIntegrationTests;
|
||||
import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpointDiscoverer;
|
||||
import org.springframework.boot.endpoint.web.EndpointMapping;
|
||||
import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;
|
||||
import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext;
|
||||
@@ -112,15 +111,14 @@ public class WebFluxEndpointIntegrationTests
|
||||
|
||||
@Bean
|
||||
public WebFluxEndpointHandlerMapping webEndpointHandlerMapping(
|
||||
Environment environment,
|
||||
EndpointDiscoverer<WebOperation> endpointDiscoverer,
|
||||
Environment environment, WebEndpointDiscoverer endpointDiscoverer,
|
||||
EndpointMediaTypes endpointMediaTypes) {
|
||||
CorsConfiguration corsConfiguration = new CorsConfiguration();
|
||||
corsConfiguration.setAllowedOrigins(Arrays.asList("http://example.com"));
|
||||
corsConfiguration.setAllowedMethods(Arrays.asList("GET", "POST"));
|
||||
return new WebFluxEndpointHandlerMapping(
|
||||
new EndpointMapping(environment.getProperty("endpointPath")),
|
||||
endpointDiscoverer.discoverEndpoints(), endpointMediaTypes,
|
||||
endpointDiscoverer.getEndpoints(), endpointMediaTypes,
|
||||
corsConfiguration);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,10 +20,9 @@ import java.util.Arrays;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.EndpointDiscoverer;
|
||||
import org.springframework.boot.actuate.endpoint.web.AbstractWebEndpointIntegrationTests;
|
||||
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
|
||||
import org.springframework.boot.actuate.endpoint.web.WebOperation;
|
||||
import org.springframework.boot.actuate.endpoint.web.annotation.AbstractWebEndpointIntegrationTests;
|
||||
import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpointDiscoverer;
|
||||
import org.springframework.boot.endpoint.web.EndpointMapping;
|
||||
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
|
||||
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
|
||||
@@ -105,15 +104,14 @@ public class MvcWebEndpointIntegrationTests extends
|
||||
|
||||
@Bean
|
||||
public WebMvcEndpointHandlerMapping webEndpointHandlerMapping(
|
||||
Environment environment,
|
||||
EndpointDiscoverer<WebOperation> webEndpointDiscoverer,
|
||||
Environment environment, WebEndpointDiscoverer endpointDiscoverer,
|
||||
EndpointMediaTypes endpointMediaTypes) {
|
||||
CorsConfiguration corsConfiguration = new CorsConfiguration();
|
||||
corsConfiguration.setAllowedOrigins(Arrays.asList("http://example.com"));
|
||||
corsConfiguration.setAllowedMethods(Arrays.asList("GET", "POST"));
|
||||
return new WebMvcEndpointHandlerMapping(
|
||||
new EndpointMapping(environment.getProperty("endpointPath")),
|
||||
webEndpointDiscoverer.discoverEndpoints(), endpointMediaTypes,
|
||||
endpointDiscoverer.getEndpoints(), endpointMediaTypes,
|
||||
corsConfiguration);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,6 +18,7 @@ package org.springframework.boot.actuate.endpoint.web.test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
@@ -28,11 +29,11 @@ import org.glassfish.jersey.server.model.Resource;
|
||||
import org.junit.runners.BlockJUnit4ClassRunner;
|
||||
import org.junit.runners.model.InitializationError;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.convert.ConversionServiceParameterMapper;
|
||||
import org.springframework.boot.actuate.endpoint.http.ActuatorMediaType;
|
||||
import org.springframework.boot.actuate.endpoint.invoke.convert.ConversionServiceParameterValueMapper;
|
||||
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
|
||||
import org.springframework.boot.actuate.endpoint.web.EndpointPathResolver;
|
||||
import org.springframework.boot.actuate.endpoint.web.annotation.WebAnnotationEndpointDiscoverer;
|
||||
import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpointDiscoverer;
|
||||
import org.springframework.boot.actuate.endpoint.web.jersey.JerseyEndpointResourceFactory;
|
||||
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
|
||||
@@ -97,12 +98,13 @@ class JerseyEndpointsRunner extends AbstractWebEndpointRunner {
|
||||
ActuatorMediaType.V2_JSON);
|
||||
EndpointMediaTypes endpointMediaTypes = new EndpointMediaTypes(mediaTypes,
|
||||
mediaTypes);
|
||||
WebAnnotationEndpointDiscoverer discoverer = new WebAnnotationEndpointDiscoverer(
|
||||
this.applicationContext, new ConversionServiceParameterMapper(),
|
||||
endpointMediaTypes, EndpointPathResolver.useEndpointId(), null, null);
|
||||
WebEndpointDiscoverer discoverer = new WebEndpointDiscoverer(
|
||||
this.applicationContext, new ConversionServiceParameterValueMapper(),
|
||||
endpointMediaTypes, EndpointPathResolver.useEndpointId(),
|
||||
Collections.emptyList(), Collections.emptyList());
|
||||
Collection<Resource> resources = new JerseyEndpointResourceFactory()
|
||||
.createEndpointResources(new EndpointMapping("/actuator"),
|
||||
discoverer.discoverEndpoints(), endpointMediaTypes);
|
||||
discoverer.getEndpoints(), endpointMediaTypes);
|
||||
config.registerResources(new HashSet<>(resources));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,16 +17,17 @@
|
||||
package org.springframework.boot.actuate.endpoint.web.test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.runners.BlockJUnit4ClassRunner;
|
||||
import org.junit.runners.model.InitializationError;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.convert.ConversionServiceParameterMapper;
|
||||
import org.springframework.boot.actuate.endpoint.http.ActuatorMediaType;
|
||||
import org.springframework.boot.actuate.endpoint.invoke.convert.ConversionServiceParameterValueMapper;
|
||||
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
|
||||
import org.springframework.boot.actuate.endpoint.web.EndpointPathResolver;
|
||||
import org.springframework.boot.actuate.endpoint.web.annotation.WebAnnotationEndpointDiscoverer;
|
||||
import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpointDiscoverer;
|
||||
import org.springframework.boot.actuate.endpoint.web.reactive.WebFluxEndpointHandlerMapping;
|
||||
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
|
||||
@@ -103,11 +104,12 @@ class WebFluxEndpointsRunner extends AbstractWebEndpointRunner {
|
||||
ActuatorMediaType.V2_JSON);
|
||||
EndpointMediaTypes endpointMediaTypes = new EndpointMediaTypes(mediaTypes,
|
||||
mediaTypes);
|
||||
WebAnnotationEndpointDiscoverer discoverer = new WebAnnotationEndpointDiscoverer(
|
||||
this.applicationContext, new ConversionServiceParameterMapper(),
|
||||
endpointMediaTypes, EndpointPathResolver.useEndpointId(), null, null);
|
||||
WebEndpointDiscoverer discoverer = new WebEndpointDiscoverer(
|
||||
this.applicationContext, new ConversionServiceParameterValueMapper(),
|
||||
endpointMediaTypes, EndpointPathResolver.useEndpointId(),
|
||||
Collections.emptyList(), Collections.emptyList());
|
||||
return new WebFluxEndpointHandlerMapping(new EndpointMapping("/actuator"),
|
||||
discoverer.discoverEndpoints(), endpointMediaTypes,
|
||||
discoverer.getEndpoints(), endpointMediaTypes,
|
||||
new CorsConfiguration());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,16 +17,17 @@
|
||||
package org.springframework.boot.actuate.endpoint.web.test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.runners.BlockJUnit4ClassRunner;
|
||||
import org.junit.runners.model.InitializationError;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.convert.ConversionServiceParameterMapper;
|
||||
import org.springframework.boot.actuate.endpoint.http.ActuatorMediaType;
|
||||
import org.springframework.boot.actuate.endpoint.invoke.convert.ConversionServiceParameterValueMapper;
|
||||
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
|
||||
import org.springframework.boot.actuate.endpoint.web.EndpointPathResolver;
|
||||
import org.springframework.boot.actuate.endpoint.web.annotation.WebAnnotationEndpointDiscoverer;
|
||||
import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpointDiscoverer;
|
||||
import org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping;
|
||||
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
|
||||
@@ -86,11 +87,12 @@ class WebMvcEndpointRunner extends AbstractWebEndpointRunner {
|
||||
ActuatorMediaType.V2_JSON);
|
||||
EndpointMediaTypes endpointMediaTypes = new EndpointMediaTypes(mediaTypes,
|
||||
mediaTypes);
|
||||
WebAnnotationEndpointDiscoverer discoverer = new WebAnnotationEndpointDiscoverer(
|
||||
this.applicationContext, new ConversionServiceParameterMapper(),
|
||||
endpointMediaTypes, EndpointPathResolver.useEndpointId(), null, null);
|
||||
WebEndpointDiscoverer discoverer = new WebEndpointDiscoverer(
|
||||
this.applicationContext, new ConversionServiceParameterValueMapper(),
|
||||
endpointMediaTypes, EndpointPathResolver.useEndpointId(),
|
||||
Collections.emptyList(), Collections.emptyList());
|
||||
return new WebMvcEndpointHandlerMapping(new EndpointMapping("/actuator"),
|
||||
discoverer.discoverEndpoints(), endpointMediaTypes,
|
||||
discoverer.getEndpoints(), endpointMediaTypes,
|
||||
new CorsConfiguration());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user