org.mashti.jetson.json.JsonResponseDecoder.java Source code

Java tutorial

Introduction

Here is the source code for org.mashti.jetson.json.JsonResponseDecoder.java

Source

/**
 * This file is part of jetson.
 *
 * jetson is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * jetson is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with jetson.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.mashti.jetson.json;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufInputStream;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import org.mashti.jetson.FutureResponse;
import org.mashti.jetson.ResponseDecoder;
import org.mashti.jetson.exception.InternalServerException;
import org.mashti.jetson.exception.InvalidResponseException;
import org.mashti.jetson.exception.RPCException;
import org.mashti.jetson.exception.ServerRuntimeException;
import org.mashti.jetson.exception.TransportException;
import org.mashti.jetson.util.CloseableUtil;
import org.mashti.jetson.util.JsonParserUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Sharable
public class JsonResponseDecoder extends ResponseDecoder {

    private static final Logger LOGGER = LoggerFactory.getLogger(JsonResponseDecoder.class);
    private final JsonFactory json_factory;

    JsonResponseDecoder(final JsonFactory json_factory) {

        this.json_factory = json_factory;
    }

    protected FutureResponse decode(final ChannelHandlerContext context, final ByteBuf in) throws RPCException {

        JsonParser parser = null;
        FutureResponse future_response = null;
        try {
            parser = json_factory.createParser(new ByteBufInputStream(in));
            parser.nextToken();
            final Integer id = validateAndReadResponseId(parser);
            future_response = getFutureResponseById(context, id);
            readAndValidateVersion(parser);

            final Type expected_return_type = future_response.getMethod().getGenericReturnType();
            setResponseResultOrError(parser, future_response,
                    ((ParameterizedType) expected_return_type).getActualTypeArguments()[0]);
            parser.nextToken();
        } catch (final JsonParseException e) {
            LOGGER.debug("failed to parse response", e);

            checkFutureResponse(future_response);
            future_response.completeExceptionally(new InvalidResponseException(e));
        } catch (final JsonGenerationException e) {
            LOGGER.debug("failed to generate response", e);

            checkFutureResponse(future_response);
            future_response.completeExceptionally(new InternalServerException(e));
        } catch (final IOException e) {
            LOGGER.debug("IO error occurred while decoding response", e);

            checkFutureResponse(future_response);
            future_response.completeExceptionally(new TransportException("failed to process response", e));
        } catch (final RuntimeException e) {
            LOGGER.debug("runtime error while decoding response", e);

            checkFutureResponse(future_response);
            future_response.completeExceptionally(new ServerRuntimeException(e));
        } finally {
            CloseableUtil.closeQuietly(parser);
        }

        return future_response;
    }

    private void checkFutureResponse(final FutureResponse future_response) throws RPCException {

        if (future_response == null) {
            throw new RPCException("failed to process enough response to determine pending future");
        }
    }

    private Integer validateAndReadResponseId(final JsonParser parser) throws IOException {

        return JsonParserUtil.readFieldValueAs(parser, JsonRequestEncoder.ID_KEY, Integer.class);
    }

    private void setResponseResultOrError(final JsonParser parser, final FutureResponse response,
            final Type expected_return_type) throws IOException {

        final String next_field_name = JsonParserUtil.expectFieldNames(parser, JsonResponseEncoder.RESULT_KEY,
                JsonResponseEncoder.ERROR_KEY);
        if (JsonResponseEncoder.ERROR_KEY.equals(next_field_name)) {
            setResponseError(parser, response);
        } else {
            setResponseResult(parser, response, expected_return_type);
        }
    }

    private void setResponseResult(final JsonParser parser, final FutureResponse response,
            final Type expected_return_type) throws IOException {

        final Object result = JsonParserUtil.readValueAs(parser, expected_return_type);
        response.complete(result);
    }

    private void setResponseError(final JsonParser parser, final FutureResponse response) throws IOException {

        final JsonRpcError error = JsonParserUtil.readValueAs(parser, JsonRpcError.class);
        if (error == null) {
            throw new InvalidResponseException("error in response must not be null");
        }

        final Throwable throwable = JsonRpcExceptions.fromJsonRpcError(error);
        response.completeExceptionally(throwable);
    }

    private void readAndValidateVersion(final JsonParser parser) throws IOException {

        final String version = JsonParserUtil.readFieldValueAs(parser, JsonRequestEncoder.VERSION_KEY,
                String.class);
        validateVersion(version);
    }

    private void validateVersion(final String version) throws InvalidResponseException {

        if (version == null || !version.equals(JsonRequestEncoder.DEFAULT_VERSION)) {
            LOGGER.debug("expected JSON RPC {}, but relieved ", JsonRequestEncoder.DEFAULT_VERSION, version);
            throw InvalidResponseException.fromMessage("version must be equal to ",
                    JsonRequestEncoder.DEFAULT_VERSION);
        }
    }
}