org.waarp.openr66.protocol.http.rest.client.HttpRestR66ClientResponseHandler.java Source code

Java tutorial

Introduction

Here is the source code for org.waarp.openr66.protocol.http.rest.client.HttpRestR66ClientResponseHandler.java

Source

/*
 * Copyright 2009 Red Hat, Inc.
 * 
 * Red Hat licenses this file to you 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 org.waarp.openr66.protocol.http.rest.client;

import java.net.ConnectException;
import java.nio.channels.ClosedChannelException;
import java.nio.charset.UnsupportedCharsetException;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpObject;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.LastHttpContent;

import org.waarp.common.crypto.ssl.WaarpSslUtility;
import org.waarp.common.json.JsonHandler;
import org.waarp.common.logging.WaarpLogger;
import org.waarp.common.logging.WaarpLoggerFactory;
import org.waarp.common.utility.WaarpStringUtils;
import org.waarp.gateway.kernel.exception.HttpIncorrectRequestException;
import org.waarp.gateway.kernel.exception.HttpInvalidAuthenticationException;
import org.waarp.gateway.kernel.rest.RestArgument;
import org.waarp.gateway.kernel.rest.DataModelRestMethodHandler.COMMAND_TYPE;
import org.waarp.gateway.kernel.rest.client.HttpRestClientSimpleResponseHandler;
import org.waarp.gateway.kernel.rest.client.RestFuture;
import org.waarp.openr66.protocol.http.rest.handler.HttpRestAbstractR66Handler.ACTIONS_TYPE;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;

/**
 * Rest client response handler.
 * 
 * Note: by default, no connection are closed except in case of error or if in HTTP 1.0 or explicitly to be closed.
 * 
 * @author Frederic Bregier
 */
public abstract class HttpRestR66ClientResponseHandler extends SimpleChannelInboundHandler<HttpObject> {
    /**
     * Internal Logger
     */
    private static final WaarpLogger logger = WaarpLoggerFactory.getLogger(HttpRestR66ClientResponseHandler.class);

    private ByteBuf cumulativeBody = null;
    protected JsonNode jsonObject = null;

    protected void addContent(FullHttpResponse response) throws HttpIncorrectRequestException {
        ByteBuf content = response.content();
        if (content != null && content.isReadable()) {
            content.retain();
            if (cumulativeBody != null) {
                cumulativeBody = Unpooled.wrappedBuffer(cumulativeBody, content);
            } else {
                cumulativeBody = content;
            }
            // get the Json equivalent of the Body
            try {
                String json = cumulativeBody.toString(WaarpStringUtils.UTF8);
                jsonObject = JsonHandler.getFromString(json);
            } catch (UnsupportedCharsetException e2) {
                logger.warn("Error", e2);
                throw new HttpIncorrectRequestException(e2);
            }
            cumulativeBody = null;
        }
    }

    /**
     * Setting the RestArgument to the RestFuture and validating RestFuture.
     * 
     * @param channel
     * @throws HttpInvalidAuthenticationException
     */
    protected void actionFromResponse(Channel channel) throws HttpInvalidAuthenticationException {
        boolean includeValidation = false;
        RestArgument ra = new RestArgument((ObjectNode) jsonObject);
        if (jsonObject == null) {
            logger.debug("Recv: EMPTY");
        }
        RestFuture restFuture = channel.attr(HttpRestClientSimpleResponseHandler.RESTARGUMENT).get();
        restFuture.setRestArgument(ra);
        switch (ra.getMethod()) {
        case CONNECT:
            break;
        case DELETE:
            includeValidation = delete(channel, ra);
            break;
        case GET:
            includeValidation = get(channel, ra);
            break;
        case HEAD:
            break;
        case OPTIONS:
            includeValidation = options(channel, ra);
            break;
        case PATCH:
            break;
        case POST:
            includeValidation = post(channel, ra);
            break;
        case PUT:
            includeValidation = put(channel, ra);
            break;
        case TRACE:
            break;
        default:
            break;
        }
        if (!includeValidation) {
            // finalize the future
            restFuture.setSuccess();
        }
    }

    /**
     * Method calls when a action REST command is raised as answer
     * 
     * @param channel
     * @param ra
     * @param act
     * @return if validation is done (or suppose to be)
     * @throws HttpInvalidAuthenticationException
     */
    protected abstract boolean action(Channel channel, RestArgument ra, ACTIONS_TYPE act)
            throws HttpInvalidAuthenticationException;

    /**
     * Method calls when a REST Get command is raised as answer
     * 
     * @param channel
     * @param ra
     * @return if validation is done (or suppose to be)
     * @throws HttpInvalidAuthenticationException
     */
    protected abstract boolean afterDbGet(Channel channel, RestArgument ra)
            throws HttpInvalidAuthenticationException;

    /**
     * Method calls when a REST Post command is raised as answer
     * 
     * @param channel
     * @param ra
     * @return if validation is done (or suppose to be)
     * @throws HttpInvalidAuthenticationException
     */
    protected abstract boolean afterDbPost(Channel channel, RestArgument ra)
            throws HttpInvalidAuthenticationException;

    /**
     * Method calls when a REST Put command is raised as answer
     * 
     * @param channel
     * @param ra
     * @return if validation is done (or suppose to be)
     * @throws HttpInvalidAuthenticationException
     */
    protected abstract boolean afterDbPut(Channel channel, RestArgument ra)
            throws HttpInvalidAuthenticationException;

    /**
     * Method calls when a REST Delete command is raised as answer
     * 
     * @param channel
     * @param ra
     * @return if validation is done (or suppose to be)
     * @throws HttpInvalidAuthenticationException
     */
    protected abstract boolean afterDbDelete(Channel channel, RestArgument ra)
            throws HttpInvalidAuthenticationException;

    /**
     * Method calls when a REST GetMultiple command is raised as answer
     * 
     * @param channel
     * @param ra
     * @return if validation is done (or suppose to be)
     * @throws HttpInvalidAuthenticationException
     */
    protected abstract boolean afterDbGetMultiple(Channel channel, RestArgument ra)
            throws HttpInvalidAuthenticationException;

    /**
     * Method calls when a REST Options command is raised as answer
     * 
     * @param channel
     * @param ra
     * @return if validation is done (or suppose to be)
     * @throws HttpInvalidAuthenticationException
     */
    protected abstract boolean afterDbOptions(Channel channel, RestArgument ra)
            throws HttpInvalidAuthenticationException;

    /**
     * Method calls when a REST command is in error
     * 
     * @param channel
     * @param ra
     *            (might be null)
     * @return if validation is done (or suppose to be)
     * @throws HttpInvalidAuthenticationException
     */
    protected abstract boolean afterError(Channel channel, RestArgument ra)
            throws HttpInvalidAuthenticationException;

    protected boolean get(Channel channel, RestArgument ra) throws HttpInvalidAuthenticationException {
        if (logger.isDebugEnabled()) {
            logger.debug(ra.prettyPrint());
        }
        if (ra.getCommand() == COMMAND_TYPE.GET) {
            return afterDbGet(channel, ra);
        } else if (ra.getCommand() == COMMAND_TYPE.MULTIGET) {
            return afterDbGetMultiple(channel, ra);
        } else {
            String cmd = ra.getCommandField();
            try {
                ACTIONS_TYPE act = ACTIONS_TYPE.valueOf(cmd);
                return action(channel, ra, act);
            } catch (Exception e) {
                return false;
            }
        }
    }

    protected boolean put(Channel channel, RestArgument ra) throws HttpInvalidAuthenticationException {
        if (logger.isDebugEnabled()) {
            logger.debug(ra.prettyPrint());
        }
        if (ra.getCommand() == COMMAND_TYPE.UPDATE) {
            return afterDbPut(channel, ra);
        } else {
            String cmd = ra.getCommandField();
            try {
                ACTIONS_TYPE act = ACTIONS_TYPE.valueOf(cmd);
                return action(channel, ra, act);
            } catch (Exception e) {
                return false;
            }
        }
    }

    protected boolean post(Channel channel, RestArgument ra) throws HttpInvalidAuthenticationException {
        if (logger.isDebugEnabled()) {
            logger.debug(ra.prettyPrint());
        }
        if (ra.getCommand() == COMMAND_TYPE.CREATE) {
            return afterDbPost(channel, ra);
        } else {
            String cmd = ra.getCommandField();
            try {
                ACTIONS_TYPE act = ACTIONS_TYPE.valueOf(cmd);
                return action(channel, ra, act);
            } catch (Exception e) {
                return false;
            }
        }
    }

    protected boolean delete(Channel channel, RestArgument ra) throws HttpInvalidAuthenticationException {
        if (logger.isDebugEnabled()) {
            logger.debug(ra.prettyPrint());
        }
        if (ra.getCommand() == COMMAND_TYPE.DELETE) {
            return afterDbDelete(channel, ra);
        } else {
            String cmd = ra.getCommandField();
            try {
                ACTIONS_TYPE act = ACTIONS_TYPE.valueOf(cmd);
                return action(channel, ra, act);
            } catch (Exception e) {
                return false;
            }
        }
    }

    protected boolean options(Channel channel, RestArgument ra) throws HttpInvalidAuthenticationException {
        if (logger.isDebugEnabled()) {
            logger.debug(ra.prettyPrint());
        }
        if (ra.getCommand() == COMMAND_TYPE.OPTIONS) {
            return afterDbOptions(channel, ra);
        } else {
            String cmd = ra.getCommandField();
            try {
                ACTIONS_TYPE act = ACTIONS_TYPE.valueOf(cmd);
                return action(channel, ra, act);
            } catch (Exception e) {
                return false;
            }
        }
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
        HttpObject obj = msg;
        if (obj instanceof HttpResponse) {
            HttpResponse response = (HttpResponse) msg;
            HttpResponseStatus status = response.status();
            logger.debug(HttpHeaderNames.REFERER + ": " + response.headers().get(HttpHeaderNames.REFERER)
                    + " STATUS: " + status);
            if (response.status().code() != 200) {
                if (response instanceof FullHttpResponse) {
                    addContent((FullHttpResponse) response);
                }
                RestArgument ra = null;
                if (jsonObject != null) {
                    ra = new RestArgument((ObjectNode) jsonObject);
                    RestFuture restFuture = ctx.channel().attr(HttpRestClientSimpleResponseHandler.RESTARGUMENT)
                            .get();
                    restFuture.setRestArgument(ra);
                    logger.error("Error: " + response.status().code() + " " + response.status().reasonPhrase()
                            + "\n" + ra.prettyPrint());
                } else {
                    logger.error("Error: " + response.status().code() + " " + response.status().reasonPhrase());
                }
                if (!afterError(ctx.channel(), ra)) {
                    RestFuture restFuture = ctx.channel().attr(HttpRestClientSimpleResponseHandler.RESTARGUMENT)
                            .get();
                    restFuture.cancel();
                }
                if (ctx.channel().isActive()) {
                    logger.debug("Will close");
                    WaarpSslUtility.closingSslChannel(ctx.channel());
                }
            } else {
                if (response instanceof FullHttpResponse) {
                    addContent((FullHttpResponse) response);
                    actionFromResponse(ctx.channel());
                }
            }
        } else {
            HttpContent chunk = (HttpContent) msg;
            if (chunk instanceof LastHttpContent) {
                ByteBuf content = chunk.content();
                if (content != null && content.isReadable()) {
                    content.retain();
                    if (cumulativeBody != null) {
                        cumulativeBody = Unpooled.wrappedBuffer(cumulativeBody, content);
                    } else {
                        cumulativeBody = content;
                    }
                }
                // get the Json equivalent of the Body
                if (cumulativeBody == null) {
                    jsonObject = JsonHandler.createObjectNode();
                } else {
                    try {
                        String json = cumulativeBody.toString(WaarpStringUtils.UTF8);
                        jsonObject = JsonHandler.getFromString(json);
                    } catch (Throwable e2) {
                        logger.warn("Error", e2);
                        throw new HttpIncorrectRequestException(e2);
                    }
                    cumulativeBody = null;
                }
                actionFromResponse(ctx.channel());
            } else {
                ByteBuf content = chunk.content();
                if (content != null && content.isReadable()) {
                    content.retain();
                    if (cumulativeBody != null) {
                        cumulativeBody = Unpooled.wrappedBuffer(cumulativeBody, content);
                    } else {
                        cumulativeBody = content;
                    }
                }
            }
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        RestFuture restFuture = ctx.channel().attr(HttpRestClientSimpleResponseHandler.RESTARGUMENT).get();
        if (cause instanceof ClosedChannelException) {
            logger.debug("Close before ending");
            restFuture.setFailure(cause);
            return;
        } else if (cause instanceof ConnectException) {
            if (ctx.channel().isActive()) {
                logger.debug("Will close");
                restFuture.setFailure(cause);
                WaarpSslUtility.closingSslChannel(ctx.channel());
            }
            return;
        }
        logger.warn("Error", cause);
        if (ctx.channel() != null && restFuture != null) {
            restFuture.setFailure(cause);
        }
        logger.debug("Will close");
        WaarpSslUtility.closingSslChannel(ctx.channel());
    }

}