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:
@@ -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.
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package org.springframework.cloud.client.serviceregistry;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public interface AutoServiceRegistration {
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package org.springframework.cloud.client.serviceregistry;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public interface Registration {
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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() ;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user