Browse Source

FIXED unsupported jaxrs-2.1 annotations should not break entire interface (#672)

* FIXED unsupported jaxrs-2.1 annotations should not break entire interface, resolving #669

* UPDATED jaxrs: more defensive jaxrs2 support

* ADDED jsr311-api dependency to httpclient (as jsr311 is `provided` in feign-jaxrs now)

* UPDATED httpclient `jsr311-api` scope to test
UPDATED jaxrs readme
pull/695/head
masc 7 years ago committed by Marvin Froeder
parent
commit
94ce07122e
  1. 7
      httpclient/pom.xml
  2. 3
      jaxrs/pom.xml
  3. 49
      jaxrs/src/main/java/feign/jaxrs/JAXRSContract.java
  4. 37
      jaxrs2/README.md
  5. 65
      jaxrs2/pom.xml
  6. 35
      jaxrs2/src/main/java/feign/jaxrs/JAXRS2Contract.java
  7. 7
      pom.xml

7
httpclient/pom.xml

@ -55,6 +55,13 @@ @@ -55,6 +55,13 @@
<scope>test</scope>
</dependency>
<dependency>
<artifactId>jsr311-api</artifactId>
<version>1.1.1</version>
<groupId>javax.ws.rs</groupId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>mockwebserver</artifactId>

3
jaxrs/pom.xml

@ -37,9 +37,10 @@ @@ -37,9 +37,10 @@
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId>
<version>1.1.1</version>
<groupId>javax.ws.rs</groupId>
<scope>provided</scope>
</dependency>
<!-- for example -->

49
jaxrs/src/main/java/feign/jaxrs/JAXRSContract.java

@ -13,23 +13,15 @@ @@ -13,23 +13,15 @@
*/
package feign.jaxrs;
import feign.Contract;
import feign.MethodMetadata;
import javax.ws.rs.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import javax.ws.rs.Consumes;
import javax.ws.rs.FormParam;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import feign.Contract;
import feign.MethodMetadata;
import static feign.Util.checkState;
import static feign.Util.emptyToNull;
@ -37,7 +29,7 @@ import static feign.Util.emptyToNull; @@ -37,7 +29,7 @@ import static feign.Util.emptyToNull;
* Please refer to the <a href="https://github.com/Netflix/feign/tree/master/feign-jaxrs">Feign
* JAX-RS README</a>.
*/
public final class JAXRSContract extends Contract.BaseContract {
public class JAXRSContract extends Contract.BaseContract {
static final String ACCEPT = "Accept";
static final String CONTENT_TYPE = "Content-Type";
@ -58,8 +50,8 @@ public final class JAXRSContract extends Contract.BaseContract { @@ -58,8 +50,8 @@ public final class JAXRSContract extends Contract.BaseContract {
pathValue = "/" + pathValue;
}
if (pathValue.endsWith("/")) {
// Strip off any trailing slashes, since the template has already had slashes appropriately added
pathValue = pathValue.substring(0, pathValue.length() - 1);
// Strip off any trailing slashes, since the template has already had slashes appropriately added
pathValue = pathValue.substring(0, pathValue.length() - 1);
}
data.template().insert(0, pathValue);
}
@ -80,8 +72,8 @@ public final class JAXRSContract extends Contract.BaseContract { @@ -80,8 +72,8 @@ public final class JAXRSContract extends Contract.BaseContract {
HttpMethod http = annotationType.getAnnotation(HttpMethod.class);
if (http != null) {
checkState(data.template().method() == null,
"Method %s contains multiple HTTP methods. Found: %s and %s", method.getName(),
data.template().method(), http.value());
"Method %s contains multiple HTTP methods. Found: %s and %s", method.getName(),
data.template().method(), http.value());
data.template().method(http.value());
} else if (annotationType == Path.class) {
String pathValue = emptyToNull(Path.class.cast(methodAnnotation).value());
@ -119,22 +111,35 @@ public final class JAXRSContract extends Contract.BaseContract { @@ -119,22 +111,35 @@ public final class JAXRSContract extends Contract.BaseContract {
data.template().header(CONTENT_TYPE, clientProduces);
}
/**
* Allows derived contracts to specify unsupported jax-rs parameter annotations which should be ignored.
* Required for JAX-RS 2 compatibility.
*/
protected boolean isUnsupportedHttpParameterAnnotation(Annotation parameterAnnotation) {
return false;
}
@Override
protected boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[] annotations,
int paramIndex) {
boolean isHttpParam = false;
for (Annotation parameterAnnotation : annotations) {
Class<? extends Annotation> annotationType = parameterAnnotation.annotationType();
if (annotationType == PathParam.class) {
// masc20180327. parameter with unsupported jax-rs annotations should not be passed as body params.
// this will prevent interfaces from becoming unusable entirely due to single (unsupported) endpoints.
// https://github.com/OpenFeign/feign/issues/669
if (this.isUnsupportedHttpParameterAnnotation(parameterAnnotation)) {
isHttpParam = true;
} else if (annotationType == PathParam.class) {
String name = PathParam.class.cast(parameterAnnotation).value();
checkState(emptyToNull(name) != null, "PathParam.value() was empty on parameter %s",
paramIndex);
paramIndex);
nameParam(data, name, paramIndex);
isHttpParam = true;
} else if (annotationType == QueryParam.class) {
String name = QueryParam.class.cast(parameterAnnotation).value();
checkState(emptyToNull(name) != null, "QueryParam.value() was empty on parameter %s",
paramIndex);
paramIndex);
Collection<String> query = addTemplatedParam(data.template().queries().get(name), name);
data.template().query(name, query);
nameParam(data, name, paramIndex);
@ -142,7 +147,7 @@ public final class JAXRSContract extends Contract.BaseContract { @@ -142,7 +147,7 @@ public final class JAXRSContract extends Contract.BaseContract {
} else if (annotationType == HeaderParam.class) {
String name = HeaderParam.class.cast(parameterAnnotation).value();
checkState(emptyToNull(name) != null, "HeaderParam.value() was empty on parameter %s",
paramIndex);
paramIndex);
Collection<String> header = addTemplatedParam(data.template().headers().get(name), name);
data.template().header(name, header);
nameParam(data, name, paramIndex);
@ -150,7 +155,7 @@ public final class JAXRSContract extends Contract.BaseContract { @@ -150,7 +155,7 @@ public final class JAXRSContract extends Contract.BaseContract {
} else if (annotationType == FormParam.class) {
String name = FormParam.class.cast(parameterAnnotation).value();
checkState(emptyToNull(name) != null, "FormParam.value() was empty on parameter %s",
paramIndex);
paramIndex);
data.formParams().add(name);
nameParam(data, name, paramIndex);
isHttpParam = true;

37
jaxrs2/README.md

@ -0,0 +1,37 @@ @@ -0,0 +1,37 @@
# Feign JAXRS 2
This module overrides annotation processing to instead use standard ones supplied by the JAX-RS specification. This is currently targeted at the 1.1 spec.
## Limitations
While it may appear possible to reuse the same interface across client and server, bear in mind that JAX-RS resource
annotations were not designed to be processed by clients. Moreover, JAX-RS 2.0 has a different package hierarchy for
client invocation. Finally, JAX-RS is a large spec and attempts to implement it completely would be a project larger
than feign itself. In other words, this implementation is *best efforts* and concedes far from 100% compatibility with
server interface behavior.
## Currently Supported Annotation Processing
Feign only supports processing java interfaces (not abstract or concrete classes).
ISE is raised when any annotation's value is empty or null. Ex. `Path("")` raises an ISE.
Here are a list of behaviors currently supported.
### Type Annotations
#### `@Path`
Appends the value to `Target.url()`. Can have tokens corresponding to `@PathParam` annotations.
### Method Annotations
#### `@HttpMethod` meta-annotation (present on `@GET`, `@POST`, etc.)
Sets the request method.
#### `@Path`
Appends the value to `Target.url()`. Can have tokens corresponding to `@PathParam` annotations.
#### `@Produces`
Adds the first value as the `Accept` header.
#### `@Consumes`
Adds the first value as the `Content-Type` header.
### Parameter Annotations
#### `@PathParam`
Links the value of the corresponding parameter to a template variable declared in the path.
#### `@QueryParam`
Links the value of the corresponding parameter to a query parameter. When invoked, null will skip the query param.
#### `@HeaderParam`
Links the value of the corresponding parameter to a header.
#### `@FormParam`
Links the value of the corresponding parameter to a key passed to `Encoder.Text<Map<String, Object>>.encode()`.

65
jaxrs2/pom.xml

@ -0,0 +1,65 @@ @@ -0,0 +1,65 @@
<!--
Copyright 2012-2018 The Feign 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.github.openfeign</groupId>
<artifactId>parent</artifactId>
<version>9.7.0-SNAPSHOT</version>
</parent>
<artifactId>feign-jaxrs2</artifactId>
<name>Feign JAX-RS 2</name>
<description>Feign JAX-RS 2</description>
<properties>
<main.basedir>${project.basedir}/..</main.basedir>
</properties>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>feign-core</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>feign-jaxrs</artifactId>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<!-- for example -->
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>feign-gson</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>feign-core</artifactId>
<type>test-jar</type>
<scope>test</scope>
</dependency>
</dependencies>
</project>

35
jaxrs2/src/main/java/feign/jaxrs/JAXRS2Contract.java

@ -0,0 +1,35 @@ @@ -0,0 +1,35 @@
/**
* Copyright 2012-2018 The Feign 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 feign.jaxrs;
import javax.ws.rs.container.Suspended;
import javax.ws.rs.core.Context;
import java.lang.annotation.Annotation;
/**
* Please refer to the <a href="https://github.com/Netflix/feign/tree/master/feign-jaxrs2">Feign
* JAX-RS 2 README</a>.
*/
public final class JAXRS2Contract extends JAXRSContract {
@Override
protected boolean isUnsupportedHttpParameterAnnotation(Annotation parameterAnnotation) {
Class<? extends Annotation> annotationType = parameterAnnotation.annotationType();
// masc20180327. parameter with unsupported jax-rs annotations should not be passed as body params.
// this will prevent interfaces from becoming unusable entirely due to single (unsupported) endpoints.
// https://github.com/OpenFeign/feign/issues/669
return (annotationType == Suspended.class ||
annotationType == Context.class);
}
}

7
pom.xml

@ -31,6 +31,7 @@ @@ -31,6 +31,7 @@
<module>jackson</module>
<module>jaxb</module>
<module>jaxrs</module>
<module>jaxrs2</module>
<module>okhttp</module>
<module>ribbon</module>
<module>sax</module>
@ -184,6 +185,12 @@ @@ -184,6 +185,12 @@
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>feign-jaxrs2</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>feign-okhttp</artifactId>

Loading…
Cancel
Save