Stub definitions for dependencies

fixes gh-37
This commit is contained in:
Marcin Grzejszczak
2015-10-01 09:05:54 +02:00
committed by Spencer Gibb
parent e20feb1461
commit 6c802b83dd
11 changed files with 219 additions and 33 deletions

View File

@@ -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.

View File

@@ -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;
}
});
}

View File

@@ -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;
}
}

View File

@@ -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()));
}
}

View File

@@ -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;

View File

@@ -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()) {

View File

@@ -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

View File

@@ -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
}
}

View File

@@ -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

View File

@@ -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 {

View File

@@ -12,6 +12,7 @@ spring.cloud.zookeeper:
header2:
- value2
required: false
stubs: org.springframework:foo:stubs
testInstance2:
path: somePath2
loadBalancerType: ROUND_ROBIN