Skip to content

Commit

Permalink
Add JAXRS3Decoder to allow using jakarta.ws.rs.core.Response as respo…
Browse files Browse the repository at this point in the history
…nse type

fixes OpenFeign#188
  • Loading branch information
Christofer Siemens committed Mar 20, 2024
1 parent 69b60b0 commit 8341538
Show file tree
Hide file tree
Showing 3 changed files with 215 additions and 0 deletions.
5 changes: 5 additions & 0 deletions jaxrs3/pom.xml
Expand Up @@ -49,6 +49,11 @@
<artifactId>jakarta.ws.rs-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.0</version>
</dependency>

<!-- for example -->
<dependency>
Expand Down
39 changes: 39 additions & 0 deletions jaxrs3/src/main/java/feign/jaxrs3/JAXRS3Decoder.java
@@ -0,0 +1,39 @@
package feign.jaxrs3;

import com.fasterxml.jackson.databind.ObjectMapper;
import feign.Response;
import feign.codec.Decoder;

import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Map;

public class JAXRS3Decoder implements Decoder {

private final Decoder delegate;
private final ObjectMapper objectMapper;

public JAXRS3Decoder(Decoder delegate, ObjectMapper objectMapper) {
this.delegate = delegate;
this.objectMapper = objectMapper;
}

@Override
public Object decode(Response response, Type type) throws IOException {

if (jakarta.ws.rs.core.Response.class.equals(type)) {
var bodyString = new String(response.body().asInputStream().readAllBytes(), StandardCharsets.UTF_8);
jakarta.ws.rs.core.Response.ResponseBuilder responseBuilder = jakarta.ws.rs.core.Response
.status(response.status())
.entity(bodyString);
for (Map.Entry<String, Collection<String>> entry : response.headers().entrySet()) {
responseBuilder.header(entry.getKey(), entry.getValue());
}

return new JAXRS3Response(responseBuilder.build(), objectMapper);
}
return delegate.decode(response, type);
}
}
171 changes: 171 additions & 0 deletions jaxrs3/src/main/java/feign/jaxrs3/JAXRS3Response.java
@@ -0,0 +1,171 @@
package feign.jaxrs3;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import jakarta.ws.rs.core.*;

import java.lang.annotation.Annotation;
import java.net.URI;
import java.util.Date;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

/**
* Wrapper class around an existing response. Using jersey with
* jakarta.ws.rs.core.Response
* .entity()
* .build()
* will create an OutboundJaxrsResponse which doesn't implement readEntity-methods.
* Only readEntity-Methods are implemented. Everything else will call the original response methods.
*/
public class JAXRS3Response extends Response {

private final Response originalResponse;
private final ObjectMapper objectMapper;

public JAXRS3Response(Response originalResponse, ObjectMapper objectMapper) {
this.originalResponse = originalResponse;
this.objectMapper = objectMapper;
}

@Override
public int getStatus() {
return originalResponse.getStatus();
}

@Override
public StatusType getStatusInfo() {
return originalResponse.getStatusInfo();
}

@Override
public Object getEntity() {
return originalResponse.getEntity();
}

@Override
public <T> T readEntity(Class<T> entityType) {
return readEntity(TypeFactory.defaultInstance().constructType(entityType));
}

@Override
public <T> T readEntity(GenericType<T> entityType) {
return readEntity(objectMapper.constructType(entityType.getType()));
}

@Override
public <T> T readEntity(Class<T> entityType, Annotation[] annotations) {
return readEntity(entityType);
}

@Override
public <T> T readEntity(GenericType<T> entityType, Annotation[] annotations) {
return readEntity(entityType);
}

@Override
public boolean hasEntity() {
return originalResponse.hasEntity();
}

@Override
public boolean bufferEntity() {
return originalResponse.bufferEntity();
}

@Override
public void close() {
originalResponse.close();
}

@Override
public MediaType getMediaType() {
return originalResponse.getMediaType();
}

@Override
public Locale getLanguage() {
return originalResponse.getLanguage();
}

@Override
public int getLength() {
return originalResponse.getLength();
}

@Override
public Set<String> getAllowedMethods() {
return originalResponse.getAllowedMethods();
}

@Override
public Map<String, NewCookie> getCookies() {
return originalResponse.getCookies();
}

@Override
public EntityTag getEntityTag() {
return originalResponse.getEntityTag();
}

@Override
public Date getDate() {
return originalResponse.getDate();
}

@Override
public Date getLastModified() {
return originalResponse.getLastModified();
}

@Override
public URI getLocation() {
return originalResponse.getLocation();
}

@Override
public Set<Link> getLinks() {
return originalResponse.getLinks();
}

@Override
public boolean hasLink(String s) {
return originalResponse.hasLink(s);
}

@Override
public Link getLink(String s) {
return originalResponse.getLink(s);
}

@Override
public Link.Builder getLinkBuilder(String s) {
return originalResponse.getLinkBuilder(s);
}

@Override
public MultivaluedMap<String, Object> getMetadata() {
return originalResponse.getMetadata();
}

@Override
public MultivaluedMap<String, String> getStringHeaders() {
return originalResponse.getStringHeaders();
}

@Override
public String getHeaderString(String s) {
return originalResponse.getHeaderString(s);
}

private <T> T readEntity(JavaType type) {
try {
return objectMapper.readValue((String) getEntity(), type);
} catch (JsonProcessingException e) {
throw new IllegalStateException(e);
}
}
}

0 comments on commit 8341538

Please sign in to comment.