Package org.jgroups.protocols

Source Code of org.jgroups.protocols.TUNNEL$StubConnectionListener

// $Id: TUNNEL.java,v 1.51 2008/10/31 08:38:44 belaban Exp $

package org.jgroups.protocols;

import org.jgroups.Address;
import org.jgroups.Event;
import org.jgroups.Message;
import org.jgroups.annotations.GuardedBy;
import org.jgroups.annotations.Property;
import org.jgroups.stack.RouterStub;
import org.jgroups.stack.IpAddress;
import org.jgroups.util.Util;

import java.io.DataInputStream;
import java.io.IOException;
import java.net.SocketException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
* Replacement for UDP. Instead of sending packets via UDP, a TCP connection is
* opened to a Router (using the RouterStub client-side stub), the IP
* address/port of which was given using channel properties
* <code>router_host</code> and <code>router_port</code>. All outgoing
* traffic is sent via this TCP socket to the Router which distributes it to all
* connected TUNNELs in this group. Incoming traffic received from Router will
* simply be passed up the stack.
*
* <p>
* A TUNNEL layer can be used to penetrate a firewall, most firewalls allow
* creating TCP connections to the outside world, however, they do not permit
* outside hosts to initiate a TCP connection to a host inside the firewall.
* Therefore, the connection created by the inside host is reused by Router to
* send traffic from an outside host to a host inside the firewall.
*
* @author Bela Ban
*/
public class TUNNEL extends TP {
   
    /* -----------------------------------------    Properties     -------------------------------------------------- */
   
    @Property(description="Router host address")
    private String router_host = null;

    @Property(description="Router port")
    private int router_port = 0;   

    @Property(description="Interval in msec to attempt connecting back to router in case of torn connection. Default is 5000 msec")
    private long reconnect_interval = 5000;
   
   
   
    /* --------------------------------------------- Fields ------------------------------------------------------ */
   
   
    private RouterStub stub;
   
    /*
     * flag indicating if tunnel was destroyed intentionally (disconnect, channel destroy etc)
     */
    private volatile boolean intentionallyTornDown = false;

    /** time to wait in ms between reconnect attempts */

    @GuardedBy("reconnectorLock")
    private Future<?> reconnectorFuture = null;

    private final Lock reconnectorLock = new ReentrantLock();

    public TUNNEL(){}

    public String toString() {
        return "Protocol TUNNEL(local_addr=" + local_addr + ')';
    }

    public String getRouterHost() {
        return router_host;
    }

    public void setRouterHost(String router_host) {
        this.router_host=router_host;
    }

    public int getRouterPort() {
        return router_port;
    }

    public void setRouterPort(int router_port) {
        this.router_port=router_port;
    }

    public long getReconnectInterval() {
        return reconnect_interval;
    }

    public void setReconnectInterval(long reconnect_interval) {
        this.reconnect_interval=reconnect_interval;
    }

    /*------------------------------ Protocol interface ------------------------------ */

    public String getName() {
        return "TUNNEL";
    }

    public void init() throws Exception {       
        super.init();
        if(timer == null)
            throw new Exception("TUNNEL.init(): timer cannot be retrieved from protocol stack");
       
        if(log.isDebugEnabled()){
            log.debug("router_host=" + router_host + ";router_port=" + router_port);
        }

        if(router_host == null || router_port == 0){
            throw new Exception("both router_host and router_port have to be set !");           
        }
    }

    public void start() throws Exception {
        // loopback turned on is mandatory
        loopback = true;
        intentionallyTornDown = false;

        stub = new RouterStub(router_host, router_port, bind_addr);
        stub.setConnectionListener(new StubConnectionListener());
        local_addr = stub.getLocalAddress();
        if(additional_data != null && local_addr instanceof IpAddress)
            ((IpAddress) local_addr).setAdditionalData(additional_data);
        super.start();
    }

    public void stop() {       
        teardownTunnel();
        super.stop();       
        local_addr = null;
    }

    /** Tears the TCP connection to the router down */
    void teardownTunnel() {
        intentionallyTornDown = true;
        stopReconnecting();
        stub.disconnect();
    }

    public Object handleDownEvent(Event evt) {
        Object retEvent = super.handleDownEvent(evt);
        switch(evt.getType()){
        case Event.CONNECT:
        case Event.CONNECT_WITH_STATE_TRANSFER:
            try{
                stub.connect(channel_name);
            }catch(Exception e){
                if(log.isErrorEnabled())
                    log.error("failed connecting to GossipRouter at " + router_host
                              + ":"
                              + router_port);
                startReconnecting();
            }
            break;

        case Event.DISCONNECT:
            teardownTunnel();
            break;
        }
        return retEvent;
    }

    private void startReconnecting() {
        reconnectorLock.lock();
        try{
            if(reconnectorFuture == null || reconnectorFuture.isDone()){
                final Runnable reconnector = new Runnable() {
                    public void run() {
                        try{
                            if(!intentionallyTornDown){
                                if(log.isDebugEnabled()){
                                    log.debug("Reconnecting " + getLocalAddress()
                                              + " to router at "
                                              + router_host
                                              + ":"
                                              + router_port);
                                }
                                stub.connect(channel_name);
                            }
                        }catch(Exception ex){
                            if(log.isTraceEnabled())
                                log.trace("failed reconnecting", ex);
                        }
                    }
                };
                reconnectorFuture = timer.scheduleWithFixedDelay(reconnector,
                                                                 0,
                                                                 reconnect_interval,
                                                                 TimeUnit.MILLISECONDS);
            }
        }finally{
            reconnectorLock.unlock();
        }
    }

    private void stopReconnecting() {
        reconnectorLock.lock();
        try{
            if(reconnectorFuture != null){
                reconnectorFuture.cancel(true);
                reconnectorFuture = null;
            }
        }finally{
            reconnectorLock.unlock();
        }
    }

    private class StubConnectionListener implements RouterStub.ConnectionListener {

        private volatile int currentState = RouterStub.STATUS_DISCONNECTED;

        public void connectionStatusChange(int newState) {           
            if(newState == RouterStub.STATUS_DISCONNECTED){
                startReconnecting();
            }else if(currentState != RouterStub.STATUS_CONNECTED && newState == RouterStub.STATUS_CONNECTED){
                stopReconnecting();
                Thread t = global_thread_factory.newThread(new TunnelReceiver(), "TUNNEL receiver");
                t.setDaemon(true);
                t.start();
            }
            currentState = newState;
        }
    }

    private class TunnelReceiver implements Runnable {
        public void run() {
            while(stub.isConnected()){
                Address dest = null;               
                int len;
                byte[] data = null;
                DataInputStream input = null;
                try{
                    input = stub.getInputStream();
                    dest = Util.readAddress(input);                   
                    len = input.readInt();
                    if(len > 0){
                        data = new byte[len];
                        input.readFully(data, 0, len);                       
                        receive(dest, null/*src will be read from data*/, data, 0, len);
                    }
                }catch(SocketException se){
                    // if(log.isWarnEnabled()) log.warn("failure in TUNNEL
                    // receiver thread", se);
                }catch(IOException ioe){
                    // if(log.isWarnEnabled()) log.warn("failure in TUNNEL
                    // receiver thread", ioe);
                }catch(Exception e){
                    if(log.isWarnEnabled())
                        log.warn("failure in TUNNEL receiver thread", e);
                }
            }
        }
    }

    public void sendToAllMembers(byte[] data, int offset, int length) throws Exception {
        stub.sendToAllMembers(data, offset, length);
    }

    public void sendToSingleMember(Address dest, byte[] data, int offset, int length) throws Exception {
        stub.sendToSingleMember(dest, data, offset, length);
    }

    public String getInfo() {
        if(stub != null)
            return stub.toString();
        else
            return "RouterStub not yet initialized";
    }

    public void postUnmarshalling(Message msg, Address dest, Address src, boolean multicast) {
        msg.setDest(dest);
    }

    public void postUnmarshallingList(Message msg, Address dest, boolean multicast) {
        msg.setDest(dest);
    }
}
TOP

Related Classes of org.jgroups.protocols.TUNNEL$StubConnectionListener

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.