Merge branch 'master' into 2.0.x

# Conflicts:
#	docs/pom.xml
#	pom.xml
#	spring-cloud-commons-dependencies/pom.xml
#	spring-cloud-commons/pom.xml
#	spring-cloud-context/pom.xml
#	spring-cloud-starter/pom.xml
This commit is contained in:
Spencer Gibb
2017-04-06 21:43:05 -06:00
27 changed files with 671 additions and 233 deletions

View File

@@ -321,6 +321,33 @@ By default, implementations of `DiscoveryClient` will auto-register the local Sp
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.
[source,java,indent=0]
----
@Configuration
@EnableDiscoveryClient(autoRegister=false)
public class MyConfiguration {
private ServiceRegistry registry;
public MyConfiguration(ServiceRegistry registry) {
this.registry = registry;
}
// called via some external process, such as an event or a custom actuator endpoint
public void register() {
Registration registration = constructRegistration();
this.registry.register(registration);
}
}
----
Each `ServiceRegistry` implementation has its own `Registry` implementation.
==== ServiceRegistry Auto-Registration
By default, the `ServiceRegistry` implementation will auto-register the running service. To disable that behavior, there are two methods. You can set `@EnableDiscoveryClient(autoRegister=false)` to permanently disable auto-registration. You can also set `spring.cloud.service-registry.auto-registration.enabled=false` to disable the behavior via configuration.
==== 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.
@@ -362,9 +389,10 @@ for details of how the `RestTemplate` is set up.
==== Retrying Failed Requests
A load balanced `RestTemplate` can be configured to retry failed requests.
By default this logic is disabled, you can enable it by setting
`spring.cloud.loadbalancer.retry.enabled=true`. The load balanced `RestTemplate` will
honor some of the Ribbon configuration values related to retrying failed requests.
By default this logic is disabled, you can enable it by adding link:https://github.com/spring-projects/spring-retry[Spring Retry] to your application's classpath. The load balanced `RestTemplate` will
honor some of the Ribbon configuration values related to retrying failed requests. If
you would like to disable the retry logic with Spring Retry on the classpath
you can set `spring.cloud.loadbalancer.retry.enabled=false`.
The properties you can use are `client.ribbon.MaxAutoRetries`,
`client.ribbon.MaxAutoRetriesNextServer`, and `client.ribbon.OkToRetryOnAllOperations`.
See the https://github.com/Netflix/ribbon/wiki/Getting-Started#the-properties-file-sample-clientproperties[Ribbon documentation]

8
mvnw vendored
View File

@@ -238,6 +238,14 @@ else
echo $MAVEN_ARGS | grep -q milestone && MAVEN_ARGS=$(echo $MAVEN_ARGS | sed -e 's/-Pmilestone//')
fi
if echo $VERSION | egrep -q 'RELEASE'; then
echo Activating \"central\" profile for version=\"$VERSION\"
echo $MAVEN_ARGS | grep -q milestone || MAVEN_ARGS="$MAVEN_ARGS -Pcentral"
else
echo Deactivating \"central\" profile for version=\"$VERSION\"
echo $MAVEN_ARGS | grep -q central && MAVEN_ARGS=$(echo $MAVEN_ARGS | sed -e 's/-Pcentral//')
fi
exec "$JAVACMD" \
$MAVEN_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \

286
mvnw.cmd vendored
View File

@@ -1,145 +1,145 @@
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM http://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Maven2 Start Up Batch script
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@REM M2_HOME - location of maven2's installed home dir
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
@REM ----------------------------------------------------------------------------
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@echo off
@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
:skipRcPre
@setlocal
set ERROR_CODE=0
@REM To isolate internal variables from possible post scripts, we use another setlocal
@setlocal
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
echo.
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
echo.
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
@REM ==== END VALIDATION ====
:init
set MAVEN_CMD_LINE_ARGS=%*
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
set EXEC_DIR=%CD%
set WDIR=%EXEC_DIR%
:findBaseDir
IF EXIST "%WDIR%"\.mvn goto baseDirFound
cd ..
IF "%WDIR%"=="%CD%" goto baseDirNotFound
set WDIR=%CD%
goto findBaseDir
:baseDirFound
set MAVEN_PROJECTBASEDIR=%WDIR%
cd "%EXEC_DIR%"
goto endDetectBaseDir
:baseDirNotFound
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
cd "%EXEC_DIR%"
:endDetectBaseDir
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
@setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
:endReadAdditionalConfig
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM http://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Maven2 Start Up Batch script
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@REM M2_HOME - location of maven2's installed home dir
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
@REM ----------------------------------------------------------------------------
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@echo off
@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
:skipRcPre
@setlocal
set ERROR_CODE=0
@REM To isolate internal variables from possible post scripts, we use another setlocal
@setlocal
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
echo.
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
echo.
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
@REM ==== END VALIDATION ====
:init
set MAVEN_CMD_LINE_ARGS=%*
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
set EXEC_DIR=%CD%
set WDIR=%EXEC_DIR%
:findBaseDir
IF EXIST "%WDIR%"\.mvn goto baseDirFound
cd ..
IF "%WDIR%"=="%CD%" goto baseDirNotFound
set WDIR=%CD%
goto findBaseDir
:baseDirFound
set MAVEN_PROJECTBASEDIR=%WDIR%
cd "%EXEC_DIR%"
goto endDetectBaseDir
:baseDirNotFound
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
cd "%EXEC_DIR%"
:endDetectBaseDir
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
@setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
:endReadAdditionalConfig
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR="".\.mvn\wrapper\maven-wrapper.jar""
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS%
if ERRORLEVEL 1 goto error
goto end
:error
set ERROR_CODE=1
:end
@endlocal & set ERROR_CODE=%ERROR_CODE%
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
@REM check for post script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
:skipRcPost
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%" == "on" pause
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
exit /B %ERROR_CODE%
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS%
if ERRORLEVEL 1 goto error
goto end
:error
set ERROR_CODE=1
:end
@endlocal & set ERROR_CODE=%ERROR_CODE%
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
@REM check for post script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
:skipRcPost
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%" == "on" pause
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
exit /B %ERROR_CODE%

34
pom.xml
View File

@@ -26,23 +26,6 @@
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<executions>
<execution>
<id>aggregate</id>
<goals>
<goal>aggregate-jar</goal>
</goals>
<phase>package</phase>
<inherited>false</inherited>
<configuration>
<aggregate>true</aggregate>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
@@ -68,23 +51,6 @@
</additionalConfig>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
<configuration>
<excludes>
<exclude>**/*.properties</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
<profiles>

View File

@@ -13,6 +13,28 @@
<packaging>jar</packaging>
<name>Spring Cloud Commons</name>
<description>Spring Cloud Commons</description>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<phase>test-compile</phase>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
<configuration>
<excludes>
<exclude>**/*.properties</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>

View File

@@ -21,7 +21,7 @@ import java.util.List;
import org.springframework.cloud.client.ServiceInstance;
/**
* DiscoveryClient represents operations commonly available to Discovery service such as
* DiscoveryClient represents read operations commonly available to Discovery service such as
* Netflix Eureka or consul.io
* @author Spencer Gibb
*/

View File

@@ -24,8 +24,13 @@ import org.springframework.cloud.client.discovery.DiscoveryClient;
/**
* DiscoveryClient used when no implementations are found on the classpath
*
* @deprecated Use {@link org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClient instead}
*
* @author Dave Syer
*/
@Deprecated
public class NoopDiscoveryClient implements DiscoveryClient {
private final ServiceInstance instance;

View File

@@ -39,12 +39,17 @@ import org.springframework.core.env.Environment;
import lombok.extern.apachecommons.CommonsLog;
/**
*
* @deprecated Use
* {@link org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration instead}
*
* @author Dave Syer
*/
@Configuration
@EnableConfigurationProperties
@ConditionalOnMissingBean(DiscoveryClient.class)
@CommonsLog
@Deprecated
public class NoopDiscoveryClientAutoConfiguration
implements ApplicationListener<ContextRefreshedEvent> {

View File

@@ -0,0 +1,50 @@
package org.springframework.cloud.client.discovery.simple;
import java.util.ArrayList;
import java.util.List;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryProperties.SimpleServiceInstance;
/**
* A {@link org.springframework.cloud.client.discovery.DiscoveryClient} that will use the
* properties file as a source of service instances
*
* @author Biju Kunjummen
*/
public class SimpleDiscoveryClient implements DiscoveryClient {
private SimpleDiscoveryProperties simpleDiscoveryProperties;
public SimpleDiscoveryClient(SimpleDiscoveryProperties simpleDiscoveryProperties) {
this.simpleDiscoveryProperties = simpleDiscoveryProperties;
}
@Override
public String description() {
return "Simple Discovery Client";
}
@Override
public ServiceInstance getLocalServiceInstance() {
return null;
}
@Override
public List<ServiceInstance> getInstances(String serviceId) {
List<ServiceInstance> serviceInstances = new ArrayList<>();
List<SimpleServiceInstance> serviceInstanceForService = this.simpleDiscoveryProperties
.getInstances().get(serviceId);
if (serviceInstanceForService != null) {
serviceInstances.addAll(serviceInstanceForService);
}
return serviceInstances;
}
@Override
public List<String> getServices() {
return new ArrayList<>(this.simpleDiscoveryProperties.getInstances().keySet());
}
}

View File

@@ -0,0 +1,31 @@
package org.springframework.cloud.client.discovery.simple;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.discovery.noop.NoopDiscoveryClientAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Spring Boot Auto-Configuration for Simple Properties based Discovery Client
*
* @author Biju Kunjummen
*/
@Configuration
@ConditionalOnMissingBean(DiscoveryClient.class)
@EnableConfigurationProperties(SimpleDiscoveryProperties.class)
@AutoConfigureBefore(NoopDiscoveryClientAutoConfiguration.class)
public class SimpleDiscoveryClientAutoConfiguration {
@Autowired
private SimpleDiscoveryProperties simpleDiscoveryProperties;
@Bean
public DiscoveryClient simpleDiscoveryClient() {
return new SimpleDiscoveryClient(simpleDiscoveryProperties);
}
}

View File

@@ -0,0 +1,85 @@
package org.springframework.cloud.client.discovery.simple;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.client.ServiceInstance;
import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Properties to hold the details of a
* {@link org.springframework.cloud.client.discovery.DiscoveryClient} service instances
* for a given service
*
* @author Biju Kunjummen
*/
@ConfigurationProperties(prefix = "spring.cloud.discovery.client.simple")
public class SimpleDiscoveryProperties {
private Map<String, List<SimpleServiceInstance>> instances = new HashMap<>();
public Map<String, List<SimpleServiceInstance>> getInstances() {
return instances;
}
public void setInstances(Map<String, List<SimpleServiceInstance>> instances) {
this.instances = instances;
}
public static class SimpleServiceInstance implements ServiceInstance {
private URI resolvedUri;
private String host;
private int port;
private boolean secure;
public SimpleServiceInstance() {
}
public SimpleServiceInstance(String uri) {
setUri(uri);
}
public void setUri(String uri) {
this.resolvedUri = URI.create(uri);
this.host = this.resolvedUri.getHost();
this.port = this.resolvedUri.getPort();
String scheme = this.resolvedUri.getScheme();
if ("https".equals(scheme)) {
this.secure = true;
}
}
@Override
public String getServiceId() {
return null;
}
@Override
public String getHost() {
return this.host;
}
@Override
public int getPort() {
return this.port;
}
@Override
public boolean isSecure() {
return this.secure;
}
@Override
public URI getUri() {
return this.resolvedUri;
}
@Override
public Map<String, String> getMetadata() {
return null;
}
}
}

View File

@@ -49,4 +49,16 @@ public interface LoadBalancedRetryPolicy {
* @param throwable the throwable from the failed execution.
*/
public abstract void registerThrowable(LoadBalancedRetryContext context, Throwable throwable);
/**
* If an exception is not thrown when making a request, than this method will be
* called to see if the client would like to retry the request based on the status
* code returned. For example in CloudFoundry the router will return a <code>404</code>
* when an app is not available. Since HTTP clients do not throw an exception when
* a <code>404</code> is returned than <code>retryableStatusCode</code> allows
* clients to force a retry.
* @param statusCode The HTTP status code.
* @return True if a retry should be attempted, false to just return the response
*/
public boolean retryableStatusCode(int statusCode);
}

View File

@@ -23,7 +23,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
*/
@ConfigurationProperties("spring.cloud.loadbalancer.retry")
public class LoadBalancerRetryProperties {
private boolean enabled = false;
private boolean enabled = true;
/**
* Returns true if the load balancer should retry failed requests.

View File

@@ -1,7 +1,10 @@
package org.springframework.cloud.client.serviceregistry;
/**
* A marker interface used by a {@link ServiceRegistry}.
*
* @author Spencer Gibb
* @since 1.2.0
*/
public interface Registration {

View File

@@ -1,19 +1,48 @@
package org.springframework.cloud.client.serviceregistry;
/**
* TODO: write javadoc
* Contract to register and deregister instances with a Service Registry.
*
* @author Spencer Gibb
* @since 1.2.0
*/
public interface ServiceRegistry<R extends Registration> {
/**
* Register the registration. Registrations typically have information about
* instances such as: hostname and port.
* @param registration the registraion
*/
void register(R registration);
/**
* Deregister the registration.
* @param registration
*/
void deregister(R registration);
/**
* Close the ServiceRegistry. This a lifecycle method.
*/
void close();
// TODO: return value for success?
/**
* Sets the status of the registration. The status values are determined
* by the individual implementations.
*
* @see org.springframework.cloud.client.serviceregistry.endpoint.ServiceRegistryEndpoint
* @param registration the registration to update
* @param status the status to set
*/
void setStatus(R registration, String status);
// TODO: concrete return value? Interface?
Object getStatus(R registration);
/**
* Gets the status of a particular registration.
*
* @see org.springframework.cloud.client.serviceregistry.endpoint.ServiceRegistryEndpoint
* @param registration the registration to query
* @param <T> the type of the status
* @return the status of the registration
*/
<T> T getStatus(R registration);
}

View File

@@ -6,7 +6,8 @@ org.springframework.cloud.client.hypermedia.CloudHypermediaAutoConfiguration,\
org.springframework.cloud.client.loadbalancer.AsyncLoadBalancerAutoConfiguration,\
org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration,\
org.springframework.cloud.client.serviceregistry.ServiceRegistryAutoConfiguration,\
org.springframework.cloud.commons.util.UtilAutoConfiguration
org.springframework.cloud.commons.util.UtilAutoConfiguration,\
org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration
# Environment Post Processors

View File

@@ -1,4 +1,4 @@
package org.springframework.cloud.client.discovery.noop;
package org.springframework.cloud.client.discovery.simple;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -10,22 +10,22 @@ import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit4.SpringRunner;
import static org.junit.Assert.assertTrue;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests if @EnableDiscoveryClient is NOT used, then NoopDiscoveryClient is created.
* @author Spencer Gibb
* DiscoveryClient implementation defaults to {@link SimpleDiscoveryClient}
* @author Biju Kunjummen
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = NoopDiscoveryClientConfigurationTests.App.class)
public class NoopDiscoveryClientConfigurationTests {
@SpringBootTest(classes = DiscoveryClientAutoConfigurationDefaultTests.App.class)
public class DiscoveryClientAutoConfigurationDefaultTests {
@Autowired
DiscoveryClient discoveryClient;
@Test
public void testDiscoveryClientIsNoop() {
assertTrue("discoveryClient is wrong instance type", discoveryClient instanceof NoopDiscoveryClient);
public void simpleDiscoveryClientShouldBeTheDefault() {
assertThat(discoveryClient).isInstanceOf(SimpleDiscoveryClient.class);
}
@EnableAutoConfiguration

View File

@@ -0,0 +1,88 @@
package org.springframework.cloud.client.discovery.simple;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit4.SpringRunner;
import java.net.URI;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for mapping properties to instances in {@link SimpleDiscoveryClient}
*
* @author Biju Kunjummen
*/
@RunWith(SpringRunner.class)
@SpringBootTest(properties = {
"spring.cloud.discovery.client.simple.instances.service1[0].uri=http://s1-1:8080",
"spring.cloud.discovery.client.simple.instances.service1[1].uri=https://s1-2:8443",
"spring.cloud.discovery.client.simple.instances.service2[0].uri=https://s2-1:8080",
"spring.cloud.discovery.client.simple.instances.service2[1].uri=https://s2-2:443" })
public class SimpleDiscoveryClientPropertiesMappingTests {
@Autowired
private SimpleDiscoveryProperties props;
@Autowired
private DiscoveryClient discoveryClient;
@Test
public void propsShouldGetCleanlyMapped() {
assertThat(props.getInstances().size()).isEqualTo(2);
assertThat(props.getInstances().get("service1").size()).isEqualTo(2);
assertThat(props.getInstances().get("service1").get(0).getHost())
.isEqualTo("s1-1");
assertThat(props.getInstances().get("service1").get(0).getPort()).isEqualTo(8080);
assertThat(props.getInstances().get("service1").get(0).getUri())
.isEqualTo(URI.create("http://s1-1:8080"));
assertThat(props.getInstances().get("service1").get(0).isSecure())
.isEqualTo(false);
assertThat(props.getInstances().get("service2").size()).isEqualTo(2);
assertThat(props.getInstances().get("service2").get(0).getHost())
.isEqualTo("s2-1");
assertThat(props.getInstances().get("service2").get(0).getPort()).isEqualTo(8080);
assertThat(props.getInstances().get("service2").get(0).getUri())
.isEqualTo(URI.create("https://s2-1:8080"));
assertThat(props.getInstances().get("service2").get(0).isSecure())
.isEqualTo(true);
}
@Test
public void testDiscoveryClientShouldResolveSimpleValues() {
assertThat(this.discoveryClient.description())
.isEqualTo("Simple Discovery Client");
assertThat(this.discoveryClient.getInstances("service1")).hasSize(2);
ServiceInstance s1 = this.discoveryClient.getInstances("service1").get(0);
assertThat(s1.getHost()).isEqualTo("s1-1");
assertThat(s1.getPort()).isEqualTo(8080);
assertThat(s1.getUri()).isEqualTo(URI.create("http://s1-1:8080"));
assertThat(s1.isSecure()).isEqualTo(false);
}
@Test
public void testGetServices() {
assertThat(this.discoveryClient.getServices())
.containsExactlyInAnyOrder("service1", "service2");
}
@Test
public void testGetANonExistentServiceShouldReturnAnEmptyList() {
assertThat(this.discoveryClient.getInstances("nonexistent")).isNotNull();
assertThat(this.discoveryClient.getInstances("nonexistent")).isEmpty();
}
@Configuration
@EnableAutoConfiguration
public static class SampleConfig {
}
}

View File

@@ -0,0 +1,47 @@
package org.springframework.cloud.client.discovery.simple;
import java.net.URI;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryProperties.SimpleServiceInstance;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Biju Kunjummen
*/
public class SimpleDiscoveryClientTests {
private SimpleDiscoveryClient simpleDiscoveryClient;
@Before
public void setUp() {
SimpleDiscoveryProperties simpleDiscoveryProperties = new SimpleDiscoveryProperties();
Map<String, List<SimpleServiceInstance>> map = new HashMap<>();
SimpleServiceInstance service1Inst1 = new SimpleServiceInstance(
"http://host1:8080");
SimpleServiceInstance service1Inst2 = new SimpleServiceInstance(
"https://host2:8443");
map.put("service1", Arrays.asList(service1Inst1, service1Inst2));
simpleDiscoveryProperties.setInstances(map);
this.simpleDiscoveryClient = new SimpleDiscoveryClient(simpleDiscoveryProperties);
}
@Test
public void shouldBeAbleToRetrieveServiceDetailsByName() {
List<ServiceInstance> instances = this.simpleDiscoveryClient
.getInstances("service1");
assertThat(instances.size()).isEqualTo(2);
assertThat(instances.get(0).getHost()).isEqualTo("host1");
assertThat(instances.get(0).getPort()).isEqualTo(8080);
assertThat(instances.get(0).getUri()).isEqualTo(URI.create("http://host1:8080"));
assertThat(instances.get(0).isSecure()).isEqualTo(false);
}
}

View File

@@ -1,6 +1,4 @@
package org.springframework.cloud.client.discovery.noop;
import java.util.List;
package org.springframework.cloud.client.discovery.simple;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -14,22 +12,26 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit4.SpringRunner;
import static org.junit.Assert.assertFalse;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests if @EnableDiscoveryClient is NOT used, then NoopDiscoveryClient is created.
* @author Spencer Gibb
* @author Biju Kunjummen
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = NoopDiscoveryClientConfigurationAdditionalTests.App.class)
public class NoopDiscoveryClientConfigurationAdditionalTests {
@SpringBootTest(classes = UserDefinedDiscoveryClientOverridesDefaultsTests.App.class)
public class UserDefinedDiscoveryClientOverridesDefaultsTests {
@Autowired
DiscoveryClient discoveryClient;
@Test
public void testDiscoveryClientIsNotNoop() {
assertFalse("discoveryClient is wrong instance type", discoveryClient instanceof NoopDiscoveryClient);
assertThat(discoveryClient).isNotInstanceOf(SimpleDiscoveryClient.class);
assertThat(discoveryClient.description())
.isEqualTo("user defined discovery client");
}
@EnableAutoConfiguration
@@ -41,7 +43,7 @@ public class NoopDiscoveryClientConfigurationAdditionalTests {
return new DiscoveryClient() {
@Override
public String description() {
return null;
return "user defined discovery client";
}
@Override

View File

@@ -59,5 +59,12 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -30,6 +30,7 @@ import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguratio
import org.springframework.cloud.context.environment.EnvironmentManager;
import org.springframework.cloud.context.refresh.ContextRefresher;
import org.springframework.cloud.context.scope.refresh.RefreshScope;
import org.springframework.cloud.endpoint.event.RefreshEventListener;
import org.springframework.cloud.logging.LoggingRebinder;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
@@ -89,4 +90,10 @@ public class RefreshAutoConfiguration {
return new ContextRefresher(context, scope);
}
@Bean
public RefreshEventListener refreshEventListener(
ContextRefresher contextRefresher) {
return new RefreshEventListener(contextRefresher);
}
}

View File

@@ -16,16 +16,10 @@
package org.springframework.cloud.autoconfigure;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.actuate.autoconfigure.ConditionalOnEnabledHealthIndicator;
import org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration;
import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.actuate.endpoint.InfoEndpoint;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@@ -34,18 +28,14 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClas
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration;
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
import org.springframework.cloud.context.properties.ConfigurationPropertiesRebinder;
import org.springframework.cloud.context.refresh.ContextRefresher;
import org.springframework.cloud.context.restart.RestartEndpoint;
import org.springframework.cloud.context.scope.refresh.RefreshScope;
import org.springframework.cloud.endpoint.RefreshEndpoint;
import org.springframework.cloud.endpoint.event.RefreshEventListener;
import org.springframework.cloud.health.RefreshScopeHealthIndicator;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.integration.monitor.IntegrationMBeanExporter;
/**
@@ -119,11 +109,5 @@ public class RefreshEndpointAutoConfiguration {
return endpoint;
}
@Bean
public RefreshEventListener refreshEventListener(
RefreshEndpoint refreshEndpoint) {
return new RefreshEventListener(refreshEndpoint);
}
}
}

View File

@@ -21,6 +21,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.bootstrap.encrypt.KeyProperties.KeyStore;
import org.springframework.cloud.context.encrypt.EncryptorFactory;
@@ -109,21 +110,22 @@ public class EncryptionBootstrapConfiguration {
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
if (hasProperty(environment, "encrypt.keyStore.location")) {
if (hasProperty(environment, "encrypt.keyStore.password")) {
RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(environment);
if (hasProperty(propertyResolver, environment, "encrypt.keyStore.location")) {
if (hasProperty(propertyResolver, environment, "encrypt.keyStore.password")) {
return ConditionOutcome.match("Keystore found in Environment");
}
return ConditionOutcome
.noMatch("Keystore found but no password in Environment");
}
else if (hasProperty(environment, "encrypt.key")) {
else if (hasProperty(propertyResolver, environment, "encrypt.key")) {
return ConditionOutcome.match("Key found in Environment");
}
return ConditionOutcome.noMatch("Keystore nor key found in Environment");
}
private boolean hasProperty(Environment environment, String key) {
String value = environment.getProperty(key);
private boolean hasProperty(RelaxedPropertyResolver propertyResolver, Environment environment, String key) {
String value = propertyResolver.getProperty(key);
if (value == null) {
return false;
}

View File

@@ -1,25 +1,25 @@
package org.springframework.cloud.endpoint.event;
import java.util.Arrays;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.cloud.endpoint.RefreshEndpoint;
import org.springframework.cloud.context.refresh.ContextRefresher;
import org.springframework.context.event.EventListener;
import lombok.extern.apachecommons.CommonsLog;
/**
* Calls {@link RefreshEndpoint#refresh()} when a {@link RefreshEvent} is received.
* Calls {@link RefreshEventListener#refresh} when a {@link RefreshEvent} is received.
* Only responds to {@link RefreshEvent} after receiving an {@link ApplicationReadyEvent} as the RefreshEvent's might come to early in the application lifecycle.
* @author Spencer Gibb
*/
@CommonsLog
public class RefreshEventListener {
private RefreshEndpoint refresh;
private ContextRefresher refresh;
private AtomicBoolean ready = new AtomicBoolean(false);
public RefreshEventListener(RefreshEndpoint refresh) {
public RefreshEventListener(ContextRefresher refresh) {
this.refresh = refresh;
}
@@ -32,8 +32,8 @@ public class RefreshEventListener {
public void handle(RefreshEvent event) {
if (this.ready.get()) { // don't handle events before app is ready
log.debug("Event received " + event.getEventDesc());
String[] keys = this.refresh.refresh();
log.info("Refresh keys changed: " + Arrays.asList(keys));
Set<String> keys = this.refresh.refresh();
log.info("Refresh keys changed: " + keys);
}
}
}

View File

@@ -0,0 +1,42 @@
package org.springframework.cloud.autoconfigure;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.ClassPathExclusions;
import org.springframework.cloud.FilteredClassPathRunner;
import org.springframework.cloud.endpoint.event.RefreshEventListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import static org.junit.Assert.assertFalse;
/**
* @author Spencer Gibb
*/
@RunWith(FilteredClassPathRunner.class)
@ClassPathExclusions({"spring-boot-actuator-*.jar", "spring-boot-starter-actuator-*.jar"})
public class RefreshAutoConfigurationClassPathTests {
@Test
public void refreshEventListenerCreated() {
try (ConfigurableApplicationContext context = getApplicationContext(
Config.class)) {
assertFalse(context.getBeansOfType(RefreshEventListener.class).isEmpty());
assertFalse(context.containsBean("refeshEndpoint"));
}
}
private static ConfigurableApplicationContext getApplicationContext(
Class<?> configuration, String... properties) {
return new SpringApplicationBuilder(configuration).web(false)
.properties(properties).run();
}
@Configuration
@EnableAutoConfiguration
static class Config {
}
}

View File

@@ -19,6 +19,20 @@ public class EncryptionBootstrapConfigurationTests {
.run();
TextEncryptor encryptor = context.getBean(TextEncryptor.class);
assertEquals("foo", encryptor.decrypt(encryptor.encrypt("foo")));
context.close();
}
@Test
public void rsaKeyStoreWithRelaxedProperties() {
ConfigurableApplicationContext context = new SpringApplicationBuilder(
EncryptionBootstrapConfiguration.class).web(false).properties(
"encrypt.key-store.location:classpath:/server.jks",
"encrypt.key-store.password:letmein",
"encrypt.key-store.alias:mytestkey", "encrypt.key-store.secret:changeme")
.run();
TextEncryptor encryptor = context.getBean(TextEncryptor.class);
assertEquals("foo", encryptor.decrypt(encryptor.encrypt("foo")));
context.close();
}
}