Compare commits

...
13 Commits
9 changed files with 42 additions and 123 deletions
+2 -16
View File
@@ -66,23 +66,9 @@ the `.mvn` configuration, so if you find you have to do it to make a
build succeed, please raise a ticket to get the settings added to build succeed, please raise a ticket to get the settings added to
source control. source control.
For hints on how to build the project look in `.travis.yml` if there The projects that require middleware (i.e. Redis) for testing generally
is one. There should be a "script" and maybe "install" command. Also require that a local instance of [Docker](https://www.docker.com/get-started) is installed and running.
look at the "services" section to see if any services need to be
running locally (e.g. mongo or rabbit). Ignore the git-related bits
that you might find in "before_install" since they're related to setting git
credentials and you already have those.
The projects that require middleware generally include a
`docker-compose.yml`, so consider using
https://docs.docker.com/compose/[Docker Compose] to run the middeware servers
in Docker containers. See the README in the
https://github.com/spring-cloud-samples/scripts[scripts demo
repository] for specific instructions about the common cases of mongo,
rabbit and redis.
NOTE: If all else fails, build with the command from `.travis.yml` (usually
`./mvnw install`).
=== Documentation === Documentation
+1 -1
View File
@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>org.springframework.cloud</groupId> <groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign</artifactId> <artifactId>spring-cloud-openfeign</artifactId>
<version>2.2.9.RELEASE</version> <version>2.2.11.BUILD-SNAPSHOT</version>
</parent> </parent>
<artifactId>spring-cloud-openfeign-docs</artifactId> <artifactId>spring-cloud-openfeign-docs</artifactId>
<packaging>pom</packaging> <packaging>pom</packaging>
@@ -523,10 +523,7 @@ public interface UserClient extends UserService {
} }
---- ----
NOTE: It is generally not advisable to share an interface between a WARNING: `@FeignClient` interfaces should not be shared between server and client and annotating `@FeignClient` interfaces with `@RequestMapping` on class level is no longer supported.
server and a client. It introduces tight coupling, and also actually
doesn't work with Spring MVC in its current form (method parameter
mapping is not inherited).
=== Feign request/response compression === Feign request/response compression
+2 -2
View File
@@ -4,14 +4,14 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-openfeign</artifactId> <artifactId>spring-cloud-openfeign</artifactId>
<version>2.2.9.RELEASE</version> <version>2.2.11.BUILD-SNAPSHOT</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<name>Spring Cloud OpenFeign</name> <name>Spring Cloud OpenFeign</name>
<description>Spring Cloud OpenFeign</description> <description>Spring Cloud OpenFeign</description>
<parent> <parent>
<groupId>org.springframework.cloud</groupId> <groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-build</artifactId> <artifactId>spring-cloud-build</artifactId>
<version>2.3.5.RELEASE</version> <version>2.3.6.BUILD-SNAPSHOT</version>
<relativePath/> <relativePath/>
</parent> </parent>
<scm> <scm>
+1 -1
View File
@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>org.springframework.cloud</groupId> <groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign</artifactId> <artifactId>spring-cloud-openfeign</artifactId>
<version>2.2.9.RELEASE</version> <version>2.2.11.BUILD-SNAPSHOT</version>
<relativePath>..</relativePath> <!-- lookup parent from repository --> <relativePath>..</relativePath> <!-- lookup parent from repository -->
</parent> </parent>
<artifactId>spring-cloud-openfeign-core</artifactId> <artifactId>spring-cloud-openfeign-core</artifactId>
@@ -35,6 +35,8 @@ import feign.Feign;
import feign.MethodMetadata; import feign.MethodMetadata;
import feign.Param; import feign.Param;
import feign.Request; import feign.Request;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.openfeign.AnnotatedParameterProcessor; import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
import org.springframework.cloud.openfeign.CollectionFormat; import org.springframework.cloud.openfeign.CollectionFormat;
@@ -83,6 +85,8 @@ import static org.springframework.core.annotation.AnnotatedElementUtils.findMerg
public class SpringMvcContract extends Contract.BaseContract public class SpringMvcContract extends Contract.BaseContract
implements ResourceLoaderAware { implements ResourceLoaderAware {
private static final Log LOG = LogFactory.getLog(SpringMvcContract.class);
private static final String ACCEPT = "Accept"; private static final String ACCEPT = "Accept";
private static final String CONTENT_TYPE = "Content-Type"; private static final String CONTENT_TYPE = "Content-Type";
@@ -181,49 +185,19 @@ public class SpringMvcContract extends Contract.BaseContract
@Override @Override
protected void processAnnotationOnClass(MethodMetadata data, Class<?> clz) { protected void processAnnotationOnClass(MethodMetadata data, Class<?> clz) {
if (clz.getInterfaces().length == 0) { RequestMapping classAnnotation = findMergedAnnotation(clz, RequestMapping.class);
RequestMapping classAnnotation = findMergedAnnotation(clz, if (classAnnotation != null) {
RequestMapping.class); LOG.error("Cannot process class: " + clz.getName()
if (classAnnotation != null) { + ". @RequestMapping annotation is not allowed on @FeignClient interfaces.");
// Prepend path from class annotation if specified throw new IllegalArgumentException(
if (classAnnotation.value().length > 0) { "@RequestMapping annotation not allowed on @FeignClient interfaces");
String pathValue = emptyToNull(classAnnotation.value()[0]);
pathValue = resolve(pathValue);
if (!pathValue.startsWith("/")) {
pathValue = "/" + pathValue;
}
data.template().uri(pathValue);
if (data.template().decodeSlash() != decodeSlash) {
data.template().decodeSlash(decodeSlash);
}
}
}
} }
} }
@Override @Override
public MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) { public MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {
processedMethods.put(Feign.configKey(targetType, method), method); processedMethods.put(Feign.configKey(targetType, method), method);
MethodMetadata md = super.parseAndValidateMetadata(targetType, method); return super.parseAndValidateMetadata(targetType, method);
RequestMapping classAnnotation = findMergedAnnotation(targetType,
RequestMapping.class);
if (classAnnotation != null) {
// produces - use from class annotation only if method has not specified this
if (!md.template().headers().containsKey(ACCEPT)) {
parseProduces(md, method, classAnnotation);
}
// consumes -- use from class annotation only if method has not specified this
if (!md.template().headers().containsKey(CONTENT_TYPE)) {
parseConsumes(md, method, classAnnotation);
}
// headers -- class annotation is inherited to methods, always write these if
// present
parseHeaders(md, method, classAnnotation);
}
return md;
} }
@Override @Override
@@ -62,6 +62,7 @@ import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.ANY;
import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.NONE; import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.NONE;
import static feign.CollectionFormat.SSV; import static feign.CollectionFormat.SSV;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.junit.Assume.assumeTrue; import static org.junit.Assume.assumeTrue;
/** /**
@@ -182,18 +183,12 @@ public class SpringMvcContractTests {
} }
@Test @Test
public void testProcessAnnotations_Class_AnnotationsGetSpecificTest() public void testProcessAnnotations_Class_Annotations_RequestMapping() {
throws Exception { assertThatIllegalArgumentException().isThrownBy(() -> {
Method method = TestTemplate_Class_Annotations.class Method method = TestTemplate_Class_RequestMapping.class
.getDeclaredMethod("getSpecificTest", String.class, String.class); .getDeclaredMethod("getSpecificTest", String.class, String.class);
MethodMetadata data = contract contract.parseAndValidateMetadata(method.getDeclaringClass(), method);
.parseAndValidateMetadata(method.getDeclaringClass(), method); });
assertThat(data.template().url()).isEqualTo("/prepend/{classId}/test/{testId}");
assertThat(data.template().method()).isEqualTo("GET");
assertThat(data.indexToName().get(0).iterator().next()).isEqualTo("classId");
assertThat(data.indexToName().get(1).iterator().next()).isEqualTo("testId");
} }
@Test @Test
@@ -203,7 +198,7 @@ public class SpringMvcContractTests {
MethodMetadata data = contract MethodMetadata data = contract
.parseAndValidateMetadata(method.getDeclaringClass(), method); .parseAndValidateMetadata(method.getDeclaringClass(), method);
assertThat(data.template().url()).isEqualTo("/prepend/{classId}"); assertThat(data.template().url()).isEqualTo("/");
assertThat(data.template().method()).isEqualTo("GET"); assertThat(data.template().method()).isEqualTo("GET");
assertThat(data.indexToName().get(0).iterator().next()).isEqualTo("classId"); assertThat(data.indexToName().get(0).iterator().next()).isEqualTo("classId");
@@ -211,22 +206,6 @@ public class SpringMvcContractTests {
assertThat(data.template().decodeSlash()).isTrue(); assertThat(data.template().decodeSlash()).isTrue();
} }
@Test
public void testProcessAnnotations_Class_AnnotationsGetAllTests_EncodeSlash()
throws Exception {
contract = new SpringMvcContract(Collections.emptyList(), getConversionService(),
false);
Method method = TestTemplate_Class_Annotations.class
.getDeclaredMethod("getAllTests", String.class);
MethodMetadata data = contract
.parseAndValidateMetadata(method.getDeclaringClass(), method);
assertThat(data.template().url()).isEqualTo("/prepend/{classId}");
assertThat(data.template().decodeSlash()).isFalse();
}
@Test @Test
public void testProcessAnnotations_ExtendedInterface() throws Exception { public void testProcessAnnotations_ExtendedInterface() throws Exception {
Method extendedMethod = TestTemplate_Extended.class.getMethod("getAllTests", Method extendedMethod = TestTemplate_Extended.class.getMethod("getAllTests",
@@ -249,27 +228,6 @@ public class SpringMvcContractTests {
assertThat(data.template().decodeSlash()).isTrue(); assertThat(data.template().decodeSlash()).isTrue();
} }
@Test
public void testProcessAnnotations_ExtendedInterface_EncodeSlash() throws Exception {
contract = new SpringMvcContract(Collections.emptyList(), getConversionService(),
false);
Method extendedMethod = TestTemplate_Extended.class.getMethod("getAllTests",
String.class);
MethodMetadata extendedData = contract.parseAndValidateMetadata(
extendedMethod.getDeclaringClass(), extendedMethod);
Method method = TestTemplate_Class_Annotations.class
.getDeclaredMethod("getAllTests", String.class);
MethodMetadata data = contract
.parseAndValidateMetadata(method.getDeclaringClass(), method);
assertThat(data.template().url()).isEqualTo(extendedData.template().url());
assertThat(data.template().method()).isEqualTo(extendedData.template().method());
assertThat(data.template().decodeSlash()).isFalse();
}
@Test @Test
public void testProcessAnnotations_SimplePost() throws Exception { public void testProcessAnnotations_SimplePost() throws Exception {
Method method = TestTemplate_Simple.class.getDeclaredMethod("postTest", Method method = TestTemplate_Simple.class.getDeclaredMethod("postTest",
@@ -305,8 +263,7 @@ public class SpringMvcContractTests {
MethodMetadata data = contract MethodMetadata data = contract
.parseAndValidateMetadata(method.getDeclaringClass(), method); .parseAndValidateMetadata(method.getDeclaringClass(), method);
assertThat(data.template().url()) assertThat(data.template().url()).isEqualTo("/test/{id}?amount=" + "{amount}");
.isEqualTo("/advanced/test/{id}?amount=" + "{amount}");
assertThat(data.template().method()).isEqualTo("PUT"); assertThat(data.template().method()).isEqualTo("PUT");
assertThat(data.template().headers().get("Accept").iterator().next()) assertThat(data.template().headers().get("Accept").iterator().next())
.isEqualTo(MediaType.APPLICATION_JSON_VALUE); .isEqualTo(MediaType.APPLICATION_JSON_VALUE);
@@ -341,8 +298,7 @@ public class SpringMvcContractTests {
MethodMetadata data = contract MethodMetadata data = contract
.parseAndValidateMetadata(method.getDeclaringClass(), method); .parseAndValidateMetadata(method.getDeclaringClass(), method);
assertThat(data.template().url()) assertThat(data.template().url()).isEqualTo("/test/{id}?amount=" + "{amount}");
.isEqualTo("/advanced/test/{id}?amount=" + "{amount}");
assertThat(data.template().method()).isEqualTo("PUT"); assertThat(data.template().method()).isEqualTo("PUT");
assertThat(data.template().headers().get("Accept").iterator().next()) assertThat(data.template().headers().get("Accept").iterator().next())
.isEqualTo(MediaType.APPLICATION_JSON_VALUE); .isEqualTo(MediaType.APPLICATION_JSON_VALUE);
@@ -366,8 +322,7 @@ public class SpringMvcContractTests {
MethodMetadata data = contract MethodMetadata data = contract
.parseAndValidateMetadata(method.getDeclaringClass(), method); .parseAndValidateMetadata(method.getDeclaringClass(), method);
assertThat(data.template().url()) assertThat(data.template().url()).isEqualTo("/test2?amount=" + "{amount}");
.isEqualTo("/advanced/test2?amount=" + "{amount}");
assertThat(data.template().method()).isEqualTo("PUT"); assertThat(data.template().method()).isEqualTo("PUT");
assertThat(data.template().headers().get("Accept").iterator().next()) assertThat(data.template().headers().get("Accept").iterator().next())
.isEqualTo(MediaType.APPLICATION_JSON_VALUE); .isEqualTo(MediaType.APPLICATION_JSON_VALUE);
@@ -429,7 +384,7 @@ public class SpringMvcContractTests {
MethodMetadata data = contract MethodMetadata data = contract
.parseAndValidateMetadata(method.getDeclaringClass(), method); .parseAndValidateMetadata(method.getDeclaringClass(), method);
assertThat(data.template().url()).isEqualTo("/advanced"); assertThat(data.template().url()).isEqualTo("/");
assertThat(data.template().method()).isEqualTo("GET"); assertThat(data.template().method()).isEqualTo("GET");
assertThat(data.template().headers().get("Accept").iterator().next()) assertThat(data.template().headers().get("Accept").iterator().next())
.isEqualTo(MediaType.APPLICATION_JSON_VALUE); .isEqualTo(MediaType.APPLICATION_JSON_VALUE);
@@ -539,7 +494,7 @@ public class SpringMvcContractTests {
.parseAndValidateMetadata(method.getDeclaringClass(), method); .parseAndValidateMetadata(method.getDeclaringClass(), method);
assertThat(data.template().url()) assertThat(data.template().url())
.isEqualTo("/advanced/testfallback/{id}?amount=" + "{amount}"); .isEqualTo("/testfallback/{id}?amount=" + "{amount}");
assertThat(data.template().method()).isEqualTo("PUT"); assertThat(data.template().method()).isEqualTo("PUT");
assertThat(data.template().headers().get("Accept").iterator().next()) assertThat(data.template().headers().get("Accept").iterator().next())
.isEqualTo(MediaType.APPLICATION_JSON_VALUE); .isEqualTo(MediaType.APPLICATION_JSON_VALUE);
@@ -708,6 +663,14 @@ public class SpringMvcContractTests {
} }
@RequestMapping("/prepend/{classId}") @RequestMapping("/prepend/{classId}")
public interface TestTemplate_Class_RequestMapping {
@RequestMapping(value = "/test/{testId}", method = RequestMethod.GET)
TestObject getSpecificTest(@PathVariable("classId") String classId,
@PathVariable("testId") String testId);
}
public interface TestTemplate_Class_Annotations { public interface TestTemplate_Class_Annotations {
@RequestMapping(value = "/test/{testId}", method = RequestMethod.GET) @RequestMapping(value = "/test/{testId}", method = RequestMethod.GET)
@@ -812,7 +775,6 @@ public class SpringMvcContractTests {
} }
@JsonAutoDetect @JsonAutoDetect
@RequestMapping("/advanced")
public interface TestTemplate_Advanced { public interface TestTemplate_Advanced {
@CollectionFormat(SSV) @CollectionFormat(SSV)
+2 -2
View File
@@ -6,11 +6,11 @@
<parent> <parent>
<artifactId>spring-cloud-dependencies-parent</artifactId> <artifactId>spring-cloud-dependencies-parent</artifactId>
<groupId>org.springframework.cloud</groupId> <groupId>org.springframework.cloud</groupId>
<version>2.3.5.RELEASE</version> <version>2.3.6.BUILD-SNAPSHOT</version>
<relativePath/> <relativePath/>
</parent> </parent>
<artifactId>spring-cloud-openfeign-dependencies</artifactId> <artifactId>spring-cloud-openfeign-dependencies</artifactId>
<version>2.2.9.RELEASE</version> <version>2.2.11.BUILD-SNAPSHOT</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<name>spring-cloud-openfeign-dependencies</name> <name>spring-cloud-openfeign-dependencies</name>
<description>Spring Cloud OpenFeign Dependencies</description> <description>Spring Cloud OpenFeign Dependencies</description>
+1 -1
View File
@@ -5,7 +5,7 @@
<parent> <parent>
<groupId>org.springframework.cloud</groupId> <groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign</artifactId> <artifactId>spring-cloud-openfeign</artifactId>
<version>2.2.9.RELEASE</version> <version>2.2.11.BUILD-SNAPSHOT</version>
<relativePath>..</relativePath> <relativePath>..</relativePath>
</parent> </parent>
<artifactId>spring-cloud-starter-openfeign</artifactId> <artifactId>spring-cloud-starter-openfeign</artifactId>