All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.urbanairship.api.client.ResponseAsyncHandler Maven / Gradle / Ivy

There is a newer version: 9.4.2
Show newest version
/*
 * Copyright (c) 2013-2016.  Urban Airship and Contributors
 */

package com.urbanairship.api.client;

import com.fasterxml.jackson.core.JsonParseException;
import io.netty.handler.codec.http.HttpHeaders;
import org.apache.commons.lang.StringUtils;
import org.asynchttpclient.AsyncHandler;
import org.asynchttpclient.HttpResponseBodyPart;
import org.asynchttpclient.HttpResponseStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Asynchronous API response handler.
 * @param  The response type.
 */
class ResponseAsyncHandler implements AsyncHandler {
    private static final Logger log = LoggerFactory.getLogger(ResponseAsyncHandler.class);
    private static final String CONTENT_TYPE_KEY = "Content-type";

    private final Response.Builder responseBuilder = new Response.Builder<>();
    private final ClientException.Builder exceptionBuilder = ClientException.newBuilder();
    private final ServerException.Builder serverExceptionBuilder = ServerException.newBuilder();


    private final Optional clientCallback;
    private final ResponseParser parser;
    private final StringBuilder bodyBuilder = new StringBuilder();

    private AtomicInteger retryCount = new AtomicInteger(0);
    private String exceptionContentType;
    private boolean isSuccessful;
    private boolean clientError;
    private boolean serverError;
    private Integer statusCode;
    /**
     * ResponseAsyncHandler constructor.
     *
     * @param clientCallback An optional ResponseCallback for handling the response on completion or error.
     * @param parser The response parser.
     */
    public ResponseAsyncHandler(Optional clientCallback, ResponseParser parser) {
        this.clientCallback = clientCallback;
        this.parser = parser;
    }

    @Override
    public State onStatusReceived(HttpResponseStatus responseStatus) throws Exception {
        statusCode = responseStatus.getStatusCode();

        if (statusCode == 401 || statusCode == 403) {
            exceptionBuilder.setStatusCode(statusCode);
            exceptionBuilder.setStatusText(responseStatus.getStatusText());
            clientError = true;
        } else if (statusCode >= 500) {
            serverExceptionBuilder.setStatusCode(statusCode);
            serverExceptionBuilder.setStatusText(responseStatus.getStatusText());
            serverError = true;
        }
        else {
            responseBuilder.setStatus(responseStatus.getStatusCode());
            isSuccessful = true;
        }

        return State.CONTINUE;
    }

    @Override
    public State onHeadersReceived(HttpHeaders httpHeaders) throws Exception {
        if (isSuccessful) {
            responseBuilder.setHeaders(getHeaders(httpHeaders));
        } else {
            exceptionContentType = httpHeaders.get(CONTENT_TYPE_KEY);
        }

        return State.CONTINUE;
    }

    @Override
    public State onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exception {
        String body = new String(bodyPart.getBodyPartBytes(), StandardCharsets.UTF_8);

        if (clientError) {
            // The response body for an error won't be very big, so we can throw here without needing to aggregate.
            RequestError error = RequestError.errorFromResponse(body, exceptionContentType);
            exceptionBuilder.setRequestError(error);
            throw exceptionBuilder.build();
        }
        else if (serverError) {
            RequestError error = RequestError.errorFromResponse(body, exceptionContentType);
            serverExceptionBuilder.setRequestError(error);
            throw serverExceptionBuilder.build();
        }

        bodyBuilder.append(body);
        return State.CONTINUE;
    }

    @Override
    public Response onCompleted() throws Exception {
        if (StringUtils.isNotBlank(bodyBuilder.toString())) {
            try {
                responseBuilder.setBody(parser.parse(bodyBuilder.toString()));
            } catch (JsonParseException e) {
                log.debug(String.format("Could not parse the response: %s", bodyBuilder.toString()), e);
                onThrowable(e);
                return responseBuilder.build();
            }
        }

        Response response = responseBuilder.build();
        if (clientCallback.isPresent()) {
            clientCallback.get().completed(response);
        }

        log.debug("Response processing completed for " + response.getBody());
        return response;
    }

    @Override
    public void onThrowable(Throwable t) {
        log.error("Exception thrown during response processing", t);
        if (clientCallback.isPresent()) {
            clientCallback.get().error(t);
        }
    }

    /**
     * Retrieves the response headers.
     * @param httpResponse The HttpResponse.
     * @return An immutable map of response headers.
     */
    private Map getHeaders(HttpHeaders httpResponse) {
        Map headers = new HashMap<>();
        for (Map.Entry entry : httpResponse.entries()) {
            headers.put(entry.getKey(), entry.getValue());
        }
        return headers;
    }

    /**
     * Retrieves the request retry count.
     *
     * @return The retry count.
     */
    public int getRetryCount() {
        return retryCount.get();
    }

    /**
     * Increment the request retry count.
     */
    public void incrementRetryCount() {
        retryCount.incrementAndGet();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy