Create a ServiceRegistry interface.

Allow service discovery systems to register more than one instance.

Automatic registration will still take place.

Add boolean autoRegister() to EnableDiscoveryClient, defaults to true.

Add ServiceRegistryEndpoint.

fixes gh-9
This commit is contained in:
Spencer Gibb
2016-07-01 23:16:00 -06:00
parent d850aad13f
commit bf4b6e3293
19 changed files with 823 additions and 5 deletions

View File

@@ -311,6 +311,20 @@ For a Spring Boot Actuator application there are some additional management endp
Patterns such as service discovery, load balancing and circuit breakers lend themselves to a common abstraction layer that can be consumed by all Spring Cloud clients, independent of the implementation (e.g. discovery via Eureka or Consul).
=== @EnableDiscoveryClient
Commons provides the `@EnableDiscoveryClient` annotation. This looks for implementations of the `DiscoveryClient` interface via `META-INF/spring.factories`. Implementations of Discovery Client will add a configuration class to `spring.factories` under the `org.springframework.cloud.client.discovery.EnableDiscoveryClient` key. Examples of `DiscoveryClient` implementations: are http://cloud.spring.io/spring-cloud-netflix/[Spring Cloud Netflix Eureka], http://cloud.spring.io/spring-cloud-consul/[Spring Cloud Consul Discovery] and http://cloud.spring.io/spring-cloud-zookeeper/[Spring Cloud Zookeeper Discovery].
By default, implementations of `DiscoveryClient` will auto-register the local Spring Boot server with the remote discovery server. This can be disabled by setting `autoRegister=false` in `@EnableDiscoveryClient`.
=== ServiceRegistry
Commons now provides a `ServiceRegistry` interface which provides methods like `register(Registration)` and `deregister(Registration)` which allow you to provide custom registered services. `Registration` is a marker interface.
==== Service Registry Actuator Endpoint
A `/service-registry` actuator endpoint is provided by Commons. This endpoint relys on a `Registration` bean in the Spring Application Context. Calling `/service-registry/instance-status` via a GET will return the status of the `Registration`. A POST to the same endpoint with a `String` body will change the status of the current `Registration` to the new value. Please see the documentation of the `ServiceRegistry` implementation you are using for the allowed values for updating the status and the values retured for the status.
=== Spring RestTemplate as a Load Balancer Client
`RestTemplate` can be automatically configured to use ribbon. To create a load balanced `RestTemplate` create a `RestTemplate` `@Bean` and use the `@LoadBalanced` qualifier.

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>spring-cloud-dependencies-parent</artifactId>
<groupId>org.springframework.cloud</groupId>
<version>1.1.2.RELEASE</version>
<version>1.2.2.BUILD-SNAPSHOT</version>
<relativePath/>
</parent>
<artifactId>spring-cloud-commons-dependencies</artifactId>

View File

@@ -26,6 +26,7 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent;
import org.springframework.cloud.client.discovery.event.InstanceRegisteredEvent;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
@@ -33,8 +34,12 @@ import org.springframework.core.env.Environment;
/**
* Lifecycle methods that may be useful and common to various DiscoveryClient implementations.
*
* @deprecated use {@link org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration} instead. This class will be removed in the next release train.
*
* @author Spencer Gibb
*/
@Deprecated
public abstract class AbstractDiscoveryLifecycle implements DiscoveryLifecycle,
ApplicationContextAware, ApplicationListener<EmbeddedServletContainerInitializedEvent> {
@@ -89,6 +94,9 @@ public abstract class AbstractDiscoveryLifecycle implements DiscoveryLifecycle,
@Override
public void start() {
if (!isEnabled()) {
if (logger.isDebugEnabled()) {
logger.debug("Discovery Lifecycle disabled. Not starting");
}
return;
}
@@ -103,7 +111,7 @@ public abstract class AbstractDiscoveryLifecycle implements DiscoveryLifecycle,
if (shouldRegisterManagement()) {
registerManagement();
}
this.context .publishEvent(new InstanceRegisteredEvent<>(this,
this.context.publishEvent(new InstanceRegisteredEvent<>(this,
getConfiguration()));
this.running.compareAndSet(false, true);
}
@@ -113,17 +121,18 @@ public abstract class AbstractDiscoveryLifecycle implements DiscoveryLifecycle,
protected abstract void setConfiguredPort(int port);
/**
* @return if the management service should be registered with the DiscoveryService
* @return if the management service should be registered with the {@link ServiceRegistry}
*/
protected boolean shouldRegisterManagement() {
return getManagementPort() != null && ManagementServerPortUtils.isDifferent(this.context);
}
/**
* @return the object used to configure the DiscoveryClient
* @return the object used to configure the registration
*/
protected abstract Object getConfiguration();
/**
* Register the local service with the DiscoveryClient
*/
@@ -147,7 +156,7 @@ public abstract class AbstractDiscoveryLifecycle implements DiscoveryLifecycle,
}
/**
* @return if the DiscoveryClient is enabled
* @return true, if the {@link DiscoveryLifecycle} is enabled
*/
protected abstract boolean isEnabled();
@@ -201,6 +210,10 @@ public abstract class AbstractDiscoveryLifecycle implements DiscoveryLifecycle,
return this.running.get();
}
protected AtomicBoolean getRunning() {
return running;
}
@Override
public int getOrder() {
return this.order;

View File

@@ -36,4 +36,8 @@ import org.springframework.context.annotation.Import;
@Import(EnableDiscoveryClientImportSelector.class)
public @interface EnableDiscoveryClient {
/**
* If true, the ServiceRegistry will automatically register the local server.
*/
boolean autoRegister() default true;
}

View File

@@ -19,7 +19,13 @@ package org.springframework.cloud.client.discovery;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.cloud.commons.util.SpringFactoryImportSelector;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.Order;
import org.springframework.core.type.AnnotationMetadata;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author Spencer Gibb
@@ -28,6 +34,24 @@ import org.springframework.core.annotation.Order;
public class EnableDiscoveryClientImportSelector
extends SpringFactoryImportSelector<EnableDiscoveryClient> {
@Override
public String[] selectImports(AnnotationMetadata metadata) {
String[] imports = super.selectImports(metadata);
AnnotationAttributes attributes = AnnotationAttributes.fromMap(
metadata.getAnnotationAttributes(getAnnotationClass().getName(), true));
boolean autoRegister = attributes.getBoolean("autoRegister");
if (autoRegister) {
List<String> importsList = new ArrayList<>(Arrays.asList(imports));
importsList.add("org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration");
imports = importsList.toArray(new String[0]);
}
return imports;
}
@Override
protected boolean isEnabled() {
return new RelaxedPropertyResolver(getEnvironment()).getProperty(

View File

@@ -0,0 +1,74 @@
package org.springframework.cloud.client.serviceregistry;
import org.springframework.cloud.client.discovery.AbstractDiscoveryLifecycle;
/**
* Lifecycle methods that may be useful and common to {@link ServiceRegistry} implementations.
*
* TODO: document the lifecycle
*
* @param <R> registration type passed to the {@link ServiceRegistry}.
*
* @author Spencer Gibb
*/
@SuppressWarnings("deprecation")
public abstract class AbstractAutoServiceRegistration<R extends Registration> extends AbstractDiscoveryLifecycle implements AutoServiceRegistration {
private ServiceRegistry<R> serviceRegistry;
protected AbstractAutoServiceRegistration(ServiceRegistry<R> serviceRegistry) {
this.serviceRegistry = serviceRegistry;
}
protected ServiceRegistry<R> getServiceRegistry() {
return this.serviceRegistry;
}
protected abstract R getRegistration();
protected abstract R getManagementRegistration();
/**
* Register the local service with the {@link ServiceRegistry}
*/
@Override
protected void register() {
this.serviceRegistry.register(getRegistration());
}
/**
* Register the local management service with the {@link ServiceRegistry}
*/
@Override
protected void registerManagement() {
this.serviceRegistry.register(getManagementRegistration());
}
/**
* De-register the local service with the {@link ServiceRegistry}
*/
@Override
protected void deregister() {
this.serviceRegistry.deregister(getRegistration());
}
/**
* De-register the local management service with the {@link ServiceRegistry}
*/
@Override
protected void deregisterManagement() {
this.serviceRegistry.deregister(getManagementRegistration());
}
@Override
public void stop() {
if (this.getRunning().compareAndSet(true, false) && isEnabled()) {
deregister();
if (shouldRegisterManagement()) {
deregisterManagement();
}
this.serviceRegistry.close();
}
}
}

View File

@@ -0,0 +1,7 @@
package org.springframework.cloud.client.serviceregistry;
/**
* @author Spencer Gibb
*/
public interface AutoServiceRegistration {
}

View File

@@ -0,0 +1,28 @@
package org.springframework.cloud.client.serviceregistry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
/**
* @author Spencer Gibb
*/
@Configuration
@EnableConfigurationProperties(AutoServiceRegistrationProperties.class)
public class AutoServiceRegistrationConfiguration {
@Autowired(required = false)
private AutoServiceRegistration autoServiceRegistration;
@Autowired
private AutoServiceRegistrationProperties properties;
@PostConstruct
protected void init() {
if (autoServiceRegistration == null && this.properties.isFailFast()) {
throw new IllegalStateException("Auto Service Registration has been requested, but there is no AutoServiceRegistration bean");
}
}
}

View File

@@ -0,0 +1,21 @@
package org.springframework.cloud.client.serviceregistry;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author Spencer Gibb
*/
@ConfigurationProperties("spring.cloud.service-registry.auto-registration")
public class AutoServiceRegistrationProperties {
/** Should startup fail if there is no AutoServiceRegistration, default to false. */
private boolean failFast = false;
public boolean isFailFast() {
return failFast;
}
public void setFailFast(boolean failFast) {
this.failFast = failFast;
}
}

View File

@@ -0,0 +1,7 @@
package org.springframework.cloud.client.serviceregistry;
/**
* @author Spencer Gibb
*/
public interface Registration {
}

View File

@@ -0,0 +1,19 @@
package org.springframework.cloud.client.serviceregistry;
/**
* TODO: write javadoc
* @author Spencer Gibb
*/
public interface ServiceRegistry<R extends Registration> {
void register(R registration);
void deregister(R registration);
void close();
// TODO: return value for success?
void setStatus(R registration, String status);
// TODO: concrete return value? Interface?
Object getStatus(R registration);
}

View File

@@ -0,0 +1,28 @@
package org.springframework.cloud.client.serviceregistry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.cloud.client.serviceregistry.endpoint.ServiceRegistryEndpoint;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Spencer Gibb
*/
@ConditionalOnBean(ServiceRegistry.class)
@Configuration
public class ServiceRegistryAutoConfiguration {
@Autowired(required = false)
private Registration registration;
@ConditionalOnClass(Endpoint.class)
@Bean
public ServiceRegistryEndpoint serviceRegistryEndpoint(ServiceRegistry serviceRegistry) {
ServiceRegistryEndpoint endpoint = new ServiceRegistryEndpoint(serviceRegistry);
endpoint.setRegistration(registration);
return endpoint;
}
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright 2013-2016 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.cloud.client.serviceregistry.endpoint;
import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* Endpoint to display and set the service instance status using the service registry.
*
* @author Spencer Gibb
*/
@ManagedResource(description = "Can be used to display and set the service instance status using the service registry")
@SuppressWarnings("unchecked")
public class ServiceRegistryEndpoint implements MvcEndpoint {
private final ServiceRegistry serviceRegistry;
private Registration registration;
public ServiceRegistryEndpoint(ServiceRegistry<?> serviceRegistry) {
this.serviceRegistry = serviceRegistry;
}
public void setRegistration(Registration registration) {
this.registration = registration;
}
@RequestMapping(path = "instance-status", method = RequestMethod.POST)
@ResponseBody
@ManagedOperation
public ResponseEntity<?> setStatus(@RequestBody String status) {
Assert.notNull(status, "status may not by null");
if (this.registration == null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("no registration found");
}
this.serviceRegistry.setStatus(this.registration, status);
return ResponseEntity.ok().build();
}
@RequestMapping(path = "instance-status", method = RequestMethod.GET)
@ResponseBody
@ManagedAttribute
public ResponseEntity getStatus() {
if (this.registration == null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("no registration found");
}
return ResponseEntity.ok().body(this.serviceRegistry.getStatus(this.registration));
}
@Override
public String getPath() {
return "/service-registry";
}
@Override
public boolean isSensitive() {
return true;
}
@Override
public Class<? extends Endpoint<?>> getEndpointType() {
return null;
}
}

View File

@@ -4,6 +4,7 @@ org.springframework.cloud.client.CommonsClientAutoConfiguration,\
org.springframework.cloud.client.discovery.noop.NoopDiscoveryClientAutoConfiguration,\
org.springframework.cloud.client.hypermedia.CloudHypermediaAutoConfiguration,\
org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration,\
org.springframework.cloud.client.serviceregistry.ServiceRegistryAutoConfiguration,\
org.springframework.cloud.commons.util.UtilAutoConfiguration

View File

@@ -0,0 +1,58 @@
package org.springframework.cloud.client.discovery;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.mock.env.MockEnvironment;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
/**
* @author Spencer Gibb
*/
public class EnableDiscoveryClientImportSelectorTests {
private final EnableDiscoveryClientImportSelector importSelector = new EnableDiscoveryClientImportSelector();
private final MockEnvironment environment = new MockEnvironment();
@Mock
private AnnotationMetadata annotationMetadata;
@Mock
private AnnotationAttributes annotationAttributes;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.importSelector.setBeanClassLoader(getClass().getClassLoader());
this.importSelector.setEnvironment(this.environment);
}
@Test
public void autoRegistrationIsEnabled() {
configureAnnotation(true);
String[] imports = this.importSelector.selectImports(this.annotationMetadata);
assertThat(imports).hasSize(1);
}
@Test
public void autoRegistrationIsDisabled() {
configureAnnotation(false);
String[] imports = this.importSelector.selectImports(this.annotationMetadata);
assertThat(imports).isEmpty();
}
private void configureAnnotation(boolean autoRegistration) {
String annotationName = EnableDiscoveryClient.class.getName();
given(this.annotationMetadata.isAnnotated(annotationName)).willReturn(true);
given(this.annotationMetadata.getAnnotationAttributes(annotationName, true))
.willReturn(this.annotationAttributes);
given(this.annotationAttributes.getBoolean("autoRegister"))
.willReturn(autoRegistration);
}
}

View File

@@ -0,0 +1,150 @@
package org.springframework.cloud.client.serviceregistry;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.boot.test.WebIntegrationTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author Spencer Gibb
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = AbstractAutoServiceRegistrationTests.Config.class)
@WebIntegrationTest(randomPort = true, value = "management.port=0")
public class AbstractAutoServiceRegistrationTests {
@Autowired
private TestAutoServiceRegistration autoRegistration;
@Value("${local.server.port}")
private int port;
@Value("${local.management.port}")
private int managementPort;
@Test
public void portsWork() {
assertNotEquals("Lifecycle port is zero", 0, autoRegistration.getPort().get());
assertNotEquals("Lifecycle port is management port", managementPort, autoRegistration.getPort().get());
assertEquals("Lifecycle port is wrong", port, autoRegistration.getPort().get());
assertTrue("Lifecycle not running", autoRegistration.isRunning());
assertThat("ServiceRegistry is wrong type", autoRegistration.getServiceRegistry(), is(instanceOf(TestServiceRegistry.class)));
TestServiceRegistry serviceRegistry = (TestServiceRegistry) autoRegistration.getServiceRegistry();
assertTrue("Lifecycle not registered", serviceRegistry.isRegistered());
assertEquals("Lifecycle appName is wrong", "application", autoRegistration.getAppName());
}
@EnableAutoConfiguration
@Configuration
public static class Config {
@Bean
public TestAutoServiceRegistration testAutoServiceRegistration() {
return new TestAutoServiceRegistration();
}
}
public static class TestRegistration implements Registration {
}
public static class TestServiceRegistry implements ServiceRegistry<TestRegistration> {
private boolean registered = false;
private boolean deregistered = false;
@Override
public void register(TestRegistration registration) {
this.registered = true;
}
@Override
public void deregister(TestRegistration registration) {
this.deregistered = true;
}
@Override
public void close() { }
@Override
public void setStatus(TestRegistration registration, String status) {
//TODO: test setStatus
}
@Override
public Object getStatus(TestRegistration registration) {
//TODO: test getStatus
return null;
}
boolean isRegistered() {
return registered;
}
boolean isDeregistered() {
return deregistered;
}
}
public static class TestAutoServiceRegistration extends AbstractAutoServiceRegistration<TestRegistration> {
private int port = 0;
@Override
protected AtomicInteger getPort() {
return super.getPort();
}
@Override
protected String getAppName() {
return super.getAppName();
}
protected TestAutoServiceRegistration() {
super(new TestServiceRegistry());
}
@Override
protected int getConfiguredPort() {
return port;
}
@Override
protected void setConfiguredPort(int port) {
this.port = port;
}
@Override
protected TestRegistration getRegistration() {
return null;
}
@Override
protected TestRegistration getManagementRegistration() {
return null;
}
@Override
protected Object getConfiguration() {
return null;
}
@Override
protected boolean isEnabled() {
return true;
}
}
}

View File

@@ -0,0 +1,81 @@
package org.springframework.cloud.client.serviceregistry;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
import org.hamcrest.Matchers;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.boot.test.util.EnvironmentTestUtils;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Spencer Gibb
*/
public class AutoServiceRegistrationConfigurationTests {
@Rule
public ExpectedException exception = ExpectedException.none();
@Test
public void hasAutoServiceRegistration() {
try(AnnotationConfigApplicationContext context = setup(HasAutoServiceRegistrationConfiguration.class)) {
AutoServiceRegistration autoServiceRegistration = context.getBean(AutoServiceRegistration.class);
assertThat(autoServiceRegistration).isNotNull();
}
}
@Test
public void noAutoServiceRegistrationAndFailFast() {
this.exception.expect(BeanCreationException.class);
this.exception.expectMessage(Matchers.containsString("no AutoServiceRegistration"));
try(AnnotationConfigApplicationContext context = setup("spring.cloud.service-registry.auto-registration.failFast=true")) {
assertNoBean(context);
}
}
@Test
public void noAutoServiceRegistrationAndFailFastFalse() {
try(AnnotationConfigApplicationContext context = setup()) {
assertNoBean(context);
}
}
private void assertNoBean(AnnotationConfigApplicationContext context) {
Map<String, AutoServiceRegistration> beans = context.getBeansOfType(AutoServiceRegistration.class);
assertThat(beans).isEmpty();
}
@Configuration
static class HasAutoServiceRegistrationConfiguration {
@Bean
public AutoServiceRegistration autoServiceRegistration() {
return new AutoServiceRegistration() {};
}
}
private AnnotationConfigApplicationContext setup(Class... classes) {
return setup(null, classes);
}
private AnnotationConfigApplicationContext setup(String property, Class... classes) {
ArrayList<Class> list = new ArrayList<>();
list.add(AutoServiceRegistrationConfiguration.class);
list.addAll(Arrays.asList(classes));
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(list.toArray(new Class[0]));
if (StringUtils.hasText(property)) {
EnvironmentTestUtils.addEnvironment(context, property);
}
context.refresh();
return context;
}
}

View File

@@ -0,0 +1,70 @@
package org.springframework.cloud.client.serviceregistry.endpoint;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.ManagementServerPropertiesAutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import org.springframework.cloud.client.serviceregistry.endpoint.ServiceRegistryEndpointTests.TestServiceRegistry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* @author Spencer Gibb
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ServiceRegistryEndpointNoRegistrationTests.TestConfiguration.class)
public class ServiceRegistryEndpointNoRegistrationTests {
@Autowired
private WebApplicationContext context;
private MockMvc mvc;
@Before
public void setUp() {
this.mvc = MockMvcBuilders.webAppContextSetup(this.context).build();
}
@Test
public void testGet() throws Exception {
this.mvc.perform(get("/service-registry/instance-status")).andExpect(status().isNotFound());
}
@Test
public void testPost() throws Exception {
this.mvc.perform(post("/service-registry/instance-status").content("newstatus")).andExpect(status().isNotFound());
}
@Import({JacksonAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
EndpointWebMvcAutoConfiguration.class, WebMvcAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class})
@Configuration
public static class TestConfiguration {
@Bean
ServiceRegistryEndpoint serviceRegistryEndpoint() {
return new ServiceRegistryEndpoint(serviceRegistry());
}
@Bean
ServiceRegistry serviceRegistry() {
return new TestServiceRegistry() ;
}
}
}

View File

@@ -0,0 +1,124 @@
package org.springframework.cloud.client.serviceregistry.endpoint;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.ManagementServerPropertiesAutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import java.util.concurrent.atomic.AtomicReference;
import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Spencer Gibb
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ServiceRegistryEndpointTests.TestConfiguration.class)
public class ServiceRegistryEndpointTests {
private static final String UPDATED_STATUS = "updatedstatus";
private static final String MYSTATUS = "mystatus";
@Autowired
private WebApplicationContext context;
@Autowired
private TestServiceRegistry serviceRegistry;
private MockMvc mvc;
@Before
public void setUp() {
this.mvc = MockMvcBuilders.webAppContextSetup(this.context).build();
}
@Test
public void testGet() throws Exception {
this.mvc.perform(get("/service-registry/instance-status")).andExpect(status().isOk())
.andExpect(content().string(containsString(MYSTATUS)));
}
@Test
public void testPost() throws Exception {
this.mvc.perform(post("/service-registry/instance-status").content(UPDATED_STATUS)).andExpect(status().isOk());
assertThat(this.serviceRegistry.getUpdatedStatus().get()).isEqualTo(UPDATED_STATUS);
}
@Import({JacksonAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
EndpointWebMvcAutoConfiguration.class, WebMvcAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class})
@Configuration
public static class TestConfiguration {
@Bean
Registration registration() {
return new Registration() {
};
}
@Bean
ServiceRegistryEndpoint serviceRegistryEndpoint(Registration reg) {
ServiceRegistryEndpoint endpoint = new ServiceRegistryEndpoint(serviceRegistry());
endpoint.setRegistration(reg);
return endpoint;
}
@Bean
ServiceRegistry serviceRegistry() {
return new TestServiceRegistry() ;
}
}
static class TestServiceRegistry implements ServiceRegistry {
AtomicReference<String> updatedStatus = new AtomicReference<>();
@Override
public void register(Registration registration) {
}
@Override
public void deregister(Registration registration) {
}
@Override
public void close() {
}
@Override
public void setStatus(Registration registration, String status) {
updatedStatus.compareAndSet(null, status);
}
@Override
public Object getStatus(Registration registration) {
return MYSTATUS;
}
public AtomicReference<String> getUpdatedStatus() {
return updatedStatus;
}
}
}