Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ Name | Description
`TEST_PROXY_USERNAME` | _(Optional)_ The username for a proxy to route all requests through
`TEST_SKIPSSLVALIDATION` | _(Optional)_ Whether to skip SSL validation when connecting to the Cloud Foundry instance. Defaults to `false`.
`UAA_API_REQUEST_LIMIT` | _(Optional)_ If your UAA server does rate limiting and returns 429 errors, set this variable to the smallest limit configured there. Whether your server limits UAA calls is shown in the log, together with the location of the configuration file on the server. Defaults to `0` (no limit).
`VERSION_MISMATCH_CONFIG` | _(Optional)_ If Cient and Cloud Foundry instance have different versions, it may happen that the response messages can not be parsed because of new properties that are not known to the client. The json file named in this environment variable allows to ignore such properties without failing the client. A sample configuration for ignoring any unknown property is available in file `versionMismatchConfigIgnoreAll.json`.

If you do not have access to a CloudFoundry instance with admin access, you can run one locally using [bosh-deployment](https://github.com/cloudfoundry/bosh-deployment) & [cf-deployment](https://github.com/cloudfoundry/cf-deployment/) and Virtualbox.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@

package org.cloudfoundry.reactor.util;

import com.fasterxml.jackson.core.JsonPointer;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import io.netty.handler.codec.http.HttpHeaderNames;
Expand All @@ -25,7 +27,10 @@
import io.netty.handler.codec.json.JsonObjectDecoder;
import java.nio.charset.Charset;
import java.util.function.BiFunction;
import org.cloudfoundry.reactor.util.JsonDeserializationProblemHandler.RetryException;
import org.reactivestreams.Publisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.Exceptions;
import reactor.core.publisher.Mono;
import reactor.netty.ByteBufFlux;
Expand All @@ -35,6 +40,7 @@
public final class JsonCodec {

private static final int MAX_PAYLOAD_SIZE = 100 * 1024 * 1024;
private static final Logger LOGGER = LoggerFactory.getLogger("cloudfoundry-client");

public static <T> Mono<T> decode(
ObjectMapper objectMapper, ByteBufFlux responseBody, Class<T> responseType) {
Expand All @@ -43,17 +49,57 @@ public static <T> Mono<T> decode(
.asByteArray()
.map(
payload -> {
try {
return objectMapper.readValue(payload, responseType);
} catch (Throwable t) {
throw new JsonParsingException(
t.getMessage(),
t,
new String(payload, Charset.defaultCharset()));
}
return decodeInternal(objectMapper, payload, responseType);
});
}

// decode the payload into an object.
// If that fails, check the exception and retry.
private static <T> T decodeInternal(
ObjectMapper objectMapper, byte[] payload, Class<T> responseType) {
try {
return objectMapper.readValue(payload, responseType);
} catch (Throwable t) {
T result = null;
if (t instanceof JsonMappingException) {
Throwable cause = t.getCause();
if (cause != null) {
if (cause instanceof RetryException) {
result = retry(objectMapper, responseType, (RetryException) cause, payload);
if (result != null) {
return result;
}
}
}
}
if (t instanceof RetryException) {
result = retry(objectMapper, responseType, (RetryException) t, payload);
if (result != null) {
return result;
}
}
String payloadStr = new String(payload, Charset.defaultCharset());
LOGGER.warn("unable to parse the following json message:");
LOGGER.warn(payloadStr);
throw new JsonParsingException(t.getMessage(), t, payloadStr);
}
}

// drop the problematic element from the payload and try again.
private static <T> T retry(
ObjectMapper objectMapper,
Class<T> responseType,
RetryException cause,
byte[] payload) {
String property = cause.getProperty();
JsonPointer pointer = cause.getJsonPointer(); // JsonPointer.compile(pointerStr);
payload = JsonDeserializationProblemHandler.dropProperty(payload, property, pointer);
if (payload != null) {
return decodeInternal(objectMapper, payload, responseType);
}
return null;
}

public static void setDecodeHeaders(HttpHeaders httpHeaders) {
httpHeaders.set(HttpHeaderNames.ACCEPT, HttpHeaderValues.APPLICATION_JSON);
}
Expand Down
Loading
Loading