Stub definitions for dependencies
fixes gh-37
This commit is contained in:
committed by
Spencer Gibb
parent
e20feb1461
commit
6c802b83dd
@@ -112,6 +112,7 @@ spring.cloud.zookeeper:
|
||||
header2:
|
||||
- value2
|
||||
required: false
|
||||
stubs: org.springframework:foo:stubs
|
||||
mailing:
|
||||
path: /path/where/mailing/has/registered/in/zookeeper
|
||||
loadBalancerType: ROUND_ROBIN
|
||||
@@ -210,6 +211,28 @@ In other words your application won't be able to start if the required dependenc
|
||||
|
||||
You can read more about Spring Cloud Zookeeper Presence Checker in the following sections.
|
||||
|
||||
=== Stubs
|
||||
|
||||
You can provide a colon separated path to the JAR containing stubs of the dependency. Example
|
||||
|
||||
```
|
||||
stubs: org.springframework:foo:stubs
|
||||
```
|
||||
|
||||
means that for a particular dependencies can be found under:
|
||||
|
||||
* groupId: `org.springframework`
|
||||
* artifactId: `foo`
|
||||
* classifier: `stubs` - this is the default value
|
||||
|
||||
This is actually equal to
|
||||
|
||||
```
|
||||
stubs: org.springframework:foo
|
||||
```
|
||||
|
||||
since `stubs` is the default classifier.
|
||||
|
||||
=== Configuring Spring Cloud Zookeeper Dependencies
|
||||
|
||||
There is a bunch of properties that you can set to enable / disable parts of Zookeeper Dependencies functionalities.
|
||||
|
||||
@@ -18,9 +18,9 @@ package org.springframework.cloud.zookeeper.discovery.dependency;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -36,11 +36,7 @@ import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
/**
|
||||
*
|
||||
* Customizes RestTemplate to support passing of params from dependency
|
||||
*
|
||||
* @author Marcin Grzejszczak, 4financeIT
|
||||
@@ -79,21 +75,19 @@ public class DependencyRestTemplateAutoConfiguration {
|
||||
}
|
||||
|
||||
private Map<String, Collection<String>> convertHeadersFromListToCollection(HttpHeaders headers) {
|
||||
return Maps.transformValues(headers, new Function<List<String>, Collection<String>>() {
|
||||
@Override
|
||||
public Collection<String> apply(List<String> input) {
|
||||
return input;
|
||||
}
|
||||
});
|
||||
Map<String, Collection<String>> transformedHeaders = new HashMap<>();
|
||||
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
|
||||
transformedHeaders.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return transformedHeaders;
|
||||
}
|
||||
|
||||
private Map<String, List<String>> convertHeadersFromCollectionToList(Map<String, Collection<String>> headers) {
|
||||
return Maps.transformValues(headers, new Function<Collection<String>, List<String>>() {
|
||||
@Override
|
||||
public List<String> apply(Collection<String> input) {
|
||||
return (List<String>) input;
|
||||
}
|
||||
});
|
||||
private Map<String, List<String>> convertHeadersFromCollectionToList(Map<String, Collection<String>> headers) {
|
||||
Map<String, List<String>> transformedHeaders = new HashMap<>();
|
||||
for (Map.Entry<String, Collection<String>> entry : headers.entrySet()) {
|
||||
transformedHeaders.put(entry.getKey(), (List<String>) entry.getValue());
|
||||
}
|
||||
return transformedHeaders;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
package org.springframework.cloud.zookeeper.discovery.dependency;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* Representation of a stubs location
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@Data
|
||||
public class StubsConfiguration {
|
||||
private static final String DEFAULT_STUBS_CLASSIFIER = "stubs";
|
||||
private static final String STUB_COLON_DELIMITER = ":";
|
||||
private static final String PATH_SLASH_DELIMITER = "/";
|
||||
|
||||
private final String stubsGroupId;
|
||||
private final String stubsArtifactId;
|
||||
private final String stubsClassifier;
|
||||
|
||||
public StubsConfiguration(String stubsGroupId, String stubsArtifactId, String stubsClassifier) {
|
||||
this.stubsGroupId = stubsGroupId;
|
||||
this.stubsArtifactId = stubsArtifactId;
|
||||
this.stubsClassifier = StringUtils.hasText(stubsClassifier) ? stubsClassifier : DEFAULT_STUBS_CLASSIFIER;
|
||||
}
|
||||
|
||||
public StubsConfiguration(String stubPath) {
|
||||
String[] parsedPath = parsedPathEmptyByDefault(stubPath, STUB_COLON_DELIMITER);
|
||||
this.stubsGroupId = parsedPath[0];
|
||||
this.stubsArtifactId = parsedPath[1];
|
||||
this.stubsClassifier = parsedPath[2];
|
||||
}
|
||||
|
||||
public StubsConfiguration(DependencyPath path) {
|
||||
String[] parsedPath = parsedPathEmptyByDefault(path.getPath(), PATH_SLASH_DELIMITER);
|
||||
this.stubsGroupId = parsedPath[0];
|
||||
this.stubsArtifactId = parsedPath[1];
|
||||
this.stubsClassifier = parsedPath[2];
|
||||
}
|
||||
|
||||
private String[] parsedPathEmptyByDefault(String path, String delimiter) {
|
||||
String[] splitPath = path.split(delimiter);
|
||||
String stubsGroupId = "";
|
||||
String stubsArtifactId = "";
|
||||
String stubsClassifier = "";
|
||||
if (splitPath.length >= 2) {
|
||||
stubsGroupId = splitPath[0];
|
||||
stubsArtifactId = splitPath[1];
|
||||
stubsClassifier = splitPath.length == 3 ? splitPath[2] : DEFAULT_STUBS_CLASSIFIER;
|
||||
}
|
||||
return new String[]{stubsGroupId, stubsArtifactId, stubsClassifier};
|
||||
}
|
||||
|
||||
private boolean isDefined() {
|
||||
return StringUtils.hasText(stubsGroupId) && StringUtils.hasText(stubsArtifactId);
|
||||
}
|
||||
|
||||
public String toColonSeparatedDependencyNotation() {
|
||||
if(!isDefined()) {
|
||||
return "";
|
||||
}
|
||||
return StringUtils.collectionToDelimitedString(Arrays.asList(getStubsGroupId(), getStubsArtifactId(), getStubsClassifier()), STUB_COLON_DELIMITER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marker class to discern between the stubs location and dependency registration path
|
||||
*/
|
||||
@Data
|
||||
static class DependencyPath {
|
||||
private final String path;
|
||||
}
|
||||
}
|
||||
@@ -20,11 +20,11 @@ import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.cloud.zookeeper.discovery.dependency.StubsConfiguration.DependencyPath;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import lombok.Data;
|
||||
@@ -70,6 +70,16 @@ public class ZookeeperDependencies {
|
||||
if (StringUtils.hasText(prefix)) {
|
||||
value.setPath(prefix + value.getPath());
|
||||
}
|
||||
|
||||
setStubDefinition(value);
|
||||
}
|
||||
}
|
||||
|
||||
private void setStubDefinition(ZookeeperDependency value) {
|
||||
if (!StringUtils.hasText(value.getStubs())) {
|
||||
value.setStubsConfiguration(new StubsConfiguration(new DependencyPath(value.getPath())));
|
||||
} else {
|
||||
value.setStubsConfiguration(new StubsConfiguration(value.getStubs()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,15 +16,14 @@
|
||||
|
||||
package org.springframework.cloud.zookeeper.discovery.dependency;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
/**
|
||||
@@ -32,8 +31,6 @@ import static java.util.Collections.singletonList;
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class ZookeeperDependency {
|
||||
|
||||
private static final String VERSION_PLACEHOLDER_REGEX = "\\$version";
|
||||
@@ -77,6 +74,32 @@ public class ZookeeperDependency {
|
||||
*/
|
||||
private boolean required;
|
||||
|
||||
/**
|
||||
* Colon separated notation of the stubs. E.g. {@code org.springframework:zookeeper-sample:stubs}. If not provided
|
||||
* the {@code path} will be parsed to try to split it into groupId and artifactId. If not provided the classifier
|
||||
* will by default equal {@code stubs}
|
||||
*/
|
||||
private String stubs;
|
||||
|
||||
public ZookeeperDependency() {
|
||||
}
|
||||
|
||||
public ZookeeperDependency(String path, LoadBalancerType loadBalancerType, String contentTypeTemplate,
|
||||
String version, Map<String, Collection<String>> headers, boolean required, String stubs) {
|
||||
this.path = path;
|
||||
this.loadBalancerType = loadBalancerType;
|
||||
this.contentTypeTemplate = contentTypeTemplate;
|
||||
this.version = version;
|
||||
this.headers = headers;
|
||||
this.required = required;
|
||||
this.stubs = stubs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parsed stubs path
|
||||
*/
|
||||
private StubsConfiguration stubsConfiguration;
|
||||
|
||||
public ZookeeperDependency(String path) {
|
||||
if (StringUtils.hasText(path)) {
|
||||
this.path = path;
|
||||
|
||||
@@ -15,13 +15,12 @@
|
||||
*/
|
||||
package org.springframework.cloud.zookeeper.discovery.watcher.presence;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.curator.x.discovery.ServiceInstance;
|
||||
|
||||
import lombok.extern.apachecommons.CommonsLog;
|
||||
|
||||
/**
|
||||
*
|
||||
* Will log the missing microservice dependency
|
||||
@@ -29,10 +28,9 @@ import org.apache.curator.x.discovery.ServiceInstance;
|
||||
* @author Marcin Grzejszczak, 4financeIT
|
||||
* @author Tomasz Dziurko, 4financeIT
|
||||
*/
|
||||
@CommonsLog
|
||||
public class LogMissingDependencyChecker implements PresenceChecker {
|
||||
|
||||
private static final Log log = LogFactory.getLog(MethodHandles.lookup().lookupClass());
|
||||
|
||||
@Override
|
||||
public void checkPresence(String dependencyName, List<ServiceInstance<?>> serviceInstances) {
|
||||
if (serviceInstances.isEmpty()) {
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.springframework.cloud.zookeeper.discovery.TestServiceRegistrar
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.context.annotation.Profile
|
||||
import org.springframework.test.annotation.DirtiesContext
|
||||
import org.springframework.test.context.ActiveProfiles
|
||||
import org.springframework.test.context.ContextConfiguration
|
||||
import org.springframework.util.SocketUtils
|
||||
@@ -41,6 +42,7 @@ import spock.util.environment.RestoreSystemProperties
|
||||
@ContextConfiguration(classes = Config, loader = SpringApplicationContextLoader)
|
||||
@ActiveProfiles('loadbalancerclient')
|
||||
@WebIntegrationTest(randomPort = true)
|
||||
@DirtiesContext
|
||||
class StickyRuleISpec extends Specification implements PollingUtils {
|
||||
|
||||
@Autowired LoadBalancerClient loadBalancerClient
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package org.springframework.cloud.zookeeper.discovery.dependency
|
||||
import spock.lang.Specification
|
||||
import spock.lang.Unroll
|
||||
|
||||
class StubsConfigurationSpec extends Specification {
|
||||
|
||||
@Unroll
|
||||
def "should return empty colon separated dependency notation if empty or invalid path [#path] has been provided"() {
|
||||
when:
|
||||
StubsConfiguration stubsConfiguration = new StubsConfiguration(path)
|
||||
then:
|
||||
'' == stubsConfiguration.toColonSeparatedDependencyNotation()
|
||||
where:
|
||||
path << ['', 'pl/']
|
||||
}
|
||||
|
||||
def "should properly parse invalid colon separated path into empty notation"() {
|
||||
given:
|
||||
String path = 'pl/a'
|
||||
when:
|
||||
StubsConfiguration stubsConfiguration = new StubsConfiguration(path)
|
||||
then:
|
||||
'' == stubsConfiguration.toColonSeparatedDependencyNotation()
|
||||
'' == stubsConfiguration.stubsGroupId
|
||||
'' == stubsConfiguration.stubsArtifactId
|
||||
'' == stubsConfiguration.stubsClassifier
|
||||
}
|
||||
|
||||
def "should parse the path into group, artifact and classifier"() {
|
||||
given:
|
||||
String path = 'pl/a'
|
||||
when:
|
||||
StubsConfiguration stubsConfiguration = new StubsConfiguration(new StubsConfiguration.DependencyPath(path))
|
||||
then:
|
||||
'pl:a:stubs' == stubsConfiguration.toColonSeparatedDependencyNotation()
|
||||
'pl' == stubsConfiguration.stubsGroupId
|
||||
'a' == stubsConfiguration.stubsArtifactId
|
||||
'stubs' == stubsConfiguration.stubsClassifier
|
||||
}
|
||||
|
||||
def "should properly set group, artifact and classifier"() {
|
||||
when:
|
||||
StubsConfiguration stubsConfiguration = new StubsConfiguration('pl', 'a', 'superstubs')
|
||||
then:
|
||||
'pl:a:superstubs' == stubsConfiguration.toColonSeparatedDependencyNotation()
|
||||
'pl' == stubsConfiguration.stubsGroupId
|
||||
'a' == stubsConfiguration.stubsArtifactId
|
||||
'superstubs' == stubsConfiguration.stubsClassifier
|
||||
}
|
||||
}
|
||||
@@ -26,8 +26,9 @@ class ZookeeperDependenciesSpec extends Specification {
|
||||
LoadBalancerType.RANDOM,
|
||||
'contentTypeTemplate',
|
||||
'version',
|
||||
[header: 'value'],
|
||||
false
|
||||
[header: ['value']],
|
||||
false,
|
||||
""
|
||||
)
|
||||
private static final Map<String, ZookeeperDependency> DEPENDENCIES = [
|
||||
alias: EXPECTED_DEPENDENCY
|
||||
|
||||
@@ -52,6 +52,15 @@ class ZookeeperDiscoveryWithDependenciesISpec extends Specification implements P
|
||||
}
|
||||
}
|
||||
|
||||
def 'should fill out properly the stub section of a dependency'() {
|
||||
given:
|
||||
StubsConfiguration stubsConfiguration = zookeeperDependencies.dependencies.get('someAlias').stubsConfiguration
|
||||
expect:
|
||||
stubsConfiguration.stubsGroupId == 'org.springframework'
|
||||
stubsConfiguration.stubsArtifactId == 'foo'
|
||||
stubsConfiguration.stubsClassifier == 'stubs'
|
||||
}
|
||||
|
||||
def 'should find an instance using feign via serviceID when alias is not found'() {
|
||||
expect:
|
||||
conditions.eventually willPass {
|
||||
|
||||
@@ -12,6 +12,7 @@ spring.cloud.zookeeper:
|
||||
header2:
|
||||
- value2
|
||||
required: false
|
||||
stubs: org.springframework:foo:stubs
|
||||
testInstance2:
|
||||
path: somePath2
|
||||
loadBalancerType: ROUND_ROBIN
|
||||
|
||||
Reference in New Issue
Block a user