Package net.tomp2p.connection

Source Code of net.tomp2p.connection.RequestHandler

/*
* Copyright 2009 Thomas Bocek
*
* Licensed 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 net.tomp2p.connection;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import net.tomp2p.futures.FutureResponse;
import net.tomp2p.message.Message;
import net.tomp2p.message.MessageID;
import net.tomp2p.peers.PeerStatusListener;
import net.tomp2p.rpc.RPC;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Is able to send UDP messages (as a request) and processes incoming replies. It is important that this class handles
* close() because if we shutdown the connections, the we need to notify the futures. In case of error set the peer to
* offline. A similar class is {@link RequestHandlerTCP}, which is used for TCP.
*
* @author Thomas Bocek
* @param <K>
*            The type of future to handle
*/
public class RequestHandler<K extends FutureResponse> extends SimpleChannelInboundHandler<Message> {
    private static final Logger LOG = LoggerFactory.getLogger(RequestHandler.class);

    // The future response which is currently be waited for
    private final K futureResponse;

    // The node this request handler is associated with
    private final PeerBean peerBean;
    private final ConnectionBean connectionBean;

    private final Message message;

    private final MessageID sendMessageID;

    // modifiable variables
    private final int idleTCPSeconds; // = ConnectionBean.DEFAULT_TCP_IDLE_SECONDS;
    private final int idleUDPSeconds; // = ConnectionBean.DEFAULT_UDP_IDLE_SECONDS;
    private final int connectionTimeoutTCPMillis; // = ConnectionBean.DEFAULT_CONNECTION_TIMEOUT_TCP;

    /**
     * Create a request handler that can send UDP messages.
     *
     * @param futureResponse
     *            The future that will be called when we get an answer
     * @param peerBean
     *            The peer bean
     * @param connectionBean
     *            The connection bean
     * @param configuration
     *            the client side connection configuration
     */
    public RequestHandler(final K futureResponse, final PeerBean peerBean,
            final ConnectionBean connectionBean, final ConnectionConfiguration configuration) {
        this.peerBean = peerBean;
        this.connectionBean = connectionBean;
        this.futureResponse = futureResponse;
        this.message = futureResponse.request();
        this.sendMessageID = new MessageID(message);
        this.idleTCPSeconds = configuration.idleTCPSeconds();
        this.idleUDPSeconds = configuration.idleUDPSeconds();
        this.connectionTimeoutTCPMillis = configuration.connectionTimeoutTCPMillis();
    }

    /**
     * @return The future response that will be called when we get an answer
     */
    public K futureResponse() {
        return futureResponse;
    }

    /**
     * @return The peer bean
     */
    public PeerBean peerBean() {
        return peerBean;
    }

    /**
     * @return The connection bean
     */
    public ConnectionBean connectionBean() {
        return connectionBean;
    }

    /**
     * @return The time that a TCP connection can be idle
     */
    public int idleTCPSeconds() {
        return idleTCPSeconds;
    }

    /**
     * @return The time that a UDP connection can be idle
     */
    public int idleUDPSeconds() {
        return idleUDPSeconds;
    }

    /**
     * @return The time a TCP connection is allowed to be established
     */
    public int connectionTimeoutTCPMillis() {
        return connectionTimeoutTCPMillis;
    }

    /**
     * Send a UDP message and expect a reply.
     *
     * @param channelCreator
     *            The channel creator will create a UDP connection
     * @return The future that was added in the constructor
     */
    public K sendUDP(final ChannelCreator channelCreator) {
        connectionBean.sender().sendUDP(this, futureResponse, message, channelCreator, idleUDPSeconds, false);
        return futureResponse;
    }

    /**
     * Send a UDP message and don't expect a reply.
     *
     * @param channelCreator
     *            The channel creator will create a UDP connection
     * @return The future that was added in the constructor
     */
    public K fireAndForgetUDP(final ChannelCreator channelCreator) {
        connectionBean.sender().sendUDP(null, futureResponse, message, channelCreator, 0, false);
        return futureResponse;
    }

    /**
     * Broadcast a UDP message (layer 2) and expect a reply.
     *
     * @param channelCreator
     *            The channel creator will create a UDP connection
     * @return The future that was added in the constructor
     */
    public K sendBroadcastUDP(final ChannelCreator channelCreator) {
        connectionBean.sender().sendUDP(this, futureResponse, message, channelCreator, idleUDPSeconds, true);
        return futureResponse;
    }

    /**
     * Send a TCP message and expect a reply.
     *
     * @param channelCreator
     *            The channel creator will create a TCP connection
     * @return The future that was added in the constructor
     */
    public K sendTCP(final ChannelCreator channelCreator) {
        connectionBean.sender().sendTCP(this, futureResponse, message, channelCreator, idleTCPSeconds,
                connectionTimeoutTCPMillis, null);
        return futureResponse;
    }
   
    public K sendTCP(final PeerConnection peerConnection) {
        connectionBean.sender().sendTCP(this, futureResponse, message, null, idleTCPSeconds,
                connectionTimeoutTCPMillis, peerConnection);
        return futureResponse;
    }
   
    /**
     * Send a TCP message and expect a reply.
     *
     * @param channelCreator
     *            The channel creator will create a TCP connection
     * @return The future that was added in the constructor
     */
    public K sendTCP(final ChannelCreator channelCreator, final PeerConnection peerConnection) {
        connectionBean.sender().sendTCP(this, futureResponse, message, channelCreator, idleTCPSeconds,
                connectionTimeoutTCPMillis, peerConnection);
        return futureResponse;
    }

    @Override
    public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) throws Exception {
        LOG.debug("Error originating from: {}, cause {}", futureResponse.request(), cause);
        if (futureResponse.isCompleted()) {
            LOG.warn("Got exception, but ignored (future response completed): {}",
                    futureResponse.failedReason());
        } else {
            if (LOG.isDebugEnabled()) {
                LOG.debug("exception caugth, but handled properly: " + cause.toString());
            }
            if (cause instanceof PeerException) {
                PeerException pe = (PeerException) cause;
                if (pe.abortCause() != PeerException.AbortCause.USER_ABORT) {
                    // do not force if we ran into a timeout, the peer may be
                    // busy
                    synchronized (peerBean.peerStatusListeners()) {
                      for (PeerStatusListener peerStatusListener : peerBean.peerStatusListeners()) {
              peerStatusListener.peerFailed(futureResponse.request().recipient(), pe);
            }
                    }
                    LOG.debug("removed from map, cause: {} msg: {}", pe.toString(), message);
                } else {
                    LOG.warn("error in request", cause);
                }
            } else {
              synchronized (peerBean.peerStatusListeners()) {
                for (PeerStatusListener peerStatusListener : peerBean.peerStatusListeners()) {
            peerStatusListener.peerFailed(futureResponse.request().recipient(), new PeerException(cause));
          }
              }
            }
        }
       
        LOG.debug("report failure", cause);
        futureResponse.failedLater(cause);
        ctx.close();
    }

    @Override
    protected void channelRead0(final ChannelHandlerContext ctx, final Message responseMessage)
            throws Exception {
        MessageID recvMessageID = new MessageID(responseMessage);
        // Error handling
        if (responseMessage.type() == Message.Type.UNKNOWN_ID) {
            String msg = "Message was not delivered successfully, unknow id (peer may be offline or unknown RPC handler): " + this.message;
            exceptionCaught(ctx, new PeerException(PeerException.AbortCause.PEER_ABORT, msg));
            return;
        } else if (responseMessage.type() == Message.Type.EXCEPTION) {
            String msg = "Message caused an exception on the other side, handle as peer_abort: "
                    + this.message;
            exceptionCaught(ctx, new PeerException(PeerException.AbortCause.PEER_ABORT, msg));
            return;
        } else if (responseMessage.isRequest()) {
            ctx.fireChannelRead(responseMessage);
            return;
        } else if (!sendMessageID.equals(recvMessageID)) {
            String msg = "Message [" + responseMessage
                    + "] sent to the node is not the same as we expect. We sent [" + this.message + "]";
            exceptionCaught(ctx, new PeerException(PeerException.AbortCause.PEER_ABORT, msg));
            return;
        } else if (responseMessage.command() != RPC.Commands.RCON.getNr() && message.recipient().isRelayed() != responseMessage.sender().isRelayed()) {
          String msg = "Message [" + responseMessage
                    + "] sent has a different relay flag than we sent [" + this.message + "]. Recipient ("+message.recipient().isRelayed()+") / Sender ("+responseMessage.sender().isRelayed()+")";
            exceptionCaught(ctx, new PeerException(PeerException.AbortCause.PEER_ABORT, msg));
            return;
        }

        // We got a good answer, let's mark the sender as alive
    if (responseMessage.isOk() || responseMessage.isNotOk()) {
      synchronized (peerBean.peerStatusListeners()) {
        for (PeerStatusListener peerStatusListener : peerBean.peerStatusListeners()) {
          peerStatusListener.peerFound(responseMessage.sender(), null, null);
        }
      }
    }
       
        // call this for streaming support
        futureResponse.progress(responseMessage);
        if (!responseMessage.isDone()) {
            LOG.debug("good message is streaming {}", responseMessage);
            return;
        }
       
        // support slow, unreachable devices which cannot respond instantly
        if(this.message.recipient().isRelayed() && this.message.recipient().isSlow() && responseMessage.type() == Message.Type.PARTIALLY_OK) {
          LOG.debug("Received partially ok by the relay peer. Wait for answer of the unreachable peer.");
          // wait for the (real) answer of the unreachable peer.
          connectionBean.dispatcher().addPendingRequest(message.messageId(), futureResponse);
          // close the channel to the relay peer
          ctx.close();
          return;
        }
       
        if (!message.isKeepAlive()) {
          LOG.debug("good message, we can close {}, {}", responseMessage, ctx.channel());
            //set the success now, but trigger the notify when we closed the channel.
            futureResponse.responseLater(responseMessage);
            //the channel creater adds a listener that sets futureResponse.setResponseNow, when the channel is closed
            ctx.close();
        } else {
          LOG.debug("good message, leave open {}", responseMessage);
            futureResponse.response(responseMessage);
        }
    }
}
TOP

Related Classes of net.tomp2p.connection.RequestHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.