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

org.jgroups.stack.RouterStub Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

There is a newer version: 33.0.2.Final
Show newest version
package org.jgroups.stack;

import org.jgroups.Address;
import org.jgroups.PhysicalAddress;
import org.jgroups.annotations.GuardedBy;
import org.jgroups.logging.Log;
import org.jgroups.logging.LogFactory;
import org.jgroups.protocols.PingData;
import org.jgroups.protocols.TUNNEL.StubReceiver;
import org.jgroups.util.Responses;
import org.jgroups.util.Util;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketException;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Client stub that talks to a remote GossipRouter
 * @author Bela Ban
 */
public class RouterStub implements Comparable {

    public static enum ConnectionStatus {INITIAL, CONNECTION_BROKEN, CONNECTION_ESTABLISHED, CONNECTED,DISCONNECTED};

    protected final String router_host; // name of the router host

    protected final int router_port; // port on which router listens on

    protected Socket sock=null; // socket connecting to the router

    protected DataOutputStream output=null;

    protected DataInputStream input=null;

    protected volatile ConnectionStatus connectionState=ConnectionStatus.INITIAL;

    protected static final Log log=LogFactory.getLog(RouterStub.class);

    protected final ConnectionListener conn_listener;

    protected final InetAddress bind_addr;

    protected int sock_conn_timeout=3000; // max number of ms to wait for socket establishment to
    // GossipRouter

    protected int sock_read_timeout=3000; // max number of ms to wait for socket reads (0 means block
    // forever, or until the sock is closed)

    protected boolean tcp_nodelay=true;
    
    protected volatile StubReceiver receiver;

    // used to synchronize access to socket, and input and output stream
    protected final ReentrantLock lock=new ReentrantLock();

    public interface ConnectionListener {
        void connectionStatusChange(RouterStub stub, ConnectionStatus state);
    }

    /**
     * Creates a stub for a remote Router object.
     * @param routerHost The name of the router's host
     * @param routerPort The router's port
     * @throws SocketException
     */
    public RouterStub(String routerHost, int routerPort, InetAddress bindAddress, ConnectionListener l) {
        router_host=routerHost != null? routerHost : "localhost";
        router_port=routerPort;
        bind_addr=bindAddress;
        conn_listener=l;        
    }

    public RouterStub(InetSocketAddress addr) {
        this(addr.getHostName(), addr.getPort(), null, null);
    }

    public void setReceiver(StubReceiver receiver) {
        this.receiver = receiver;
    }
    
    public StubReceiver  getReceiver() {
        return receiver;
    }

    public boolean isTcpNoDelay() {
        return tcp_nodelay;
    }

    public void setTcpNoDelay(boolean tcp_nodelay) {
        this.tcp_nodelay=tcp_nodelay;
    }

    // Note that this would fail to return 0 if we had a dotted decimal and a symbolic addr resolving to the same host !
    public int compareTo(RouterStub o) {
        int rc=router_host.compareTo(o.router_host);
        if(rc != 0)
            return rc;
        return router_port < o.router_port? -1 : router_port > o.router_port? 1 : 0;
    }

    public boolean equals(Object obj) {
        RouterStub o=(RouterStub)obj;
        return compareTo(o) == 0;
    }

    public int hashCode() {
        return router_host.hashCode() + router_port;
    }

    public void interrupt() {
        StubReceiver tmp=receiver;
        if(tmp != null) {
            Thread thread=tmp.getThread();
            if(thread != null)
                thread.interrupt();
        }
    }
    
    public void join(long wait) throws InterruptedException {
        StubReceiver tmp=receiver;
        if(tmp != null) {
            Thread thread=tmp.getThread();
            if(thread != null)
                thread.join(wait);
        }
    }


    public int getSocketConnectionTimeout() {
        return sock_conn_timeout;
    }

    public void setSocketConnectionTimeout(int sock_conn_timeout) {
        this.sock_conn_timeout=sock_conn_timeout;
    }

    public int getSocketReadTimeout() {
        return sock_read_timeout;
    }

    public void setSocketReadTimeout(int sock_read_timeout) {
        this.sock_read_timeout=sock_read_timeout;
    }

    public boolean isConnected() {
        return !(connectionState == ConnectionStatus.CONNECTION_BROKEN || connectionState == ConnectionStatus.INITIAL);
    }

    public ConnectionStatus getConnectionStatus() {
        return connectionState;
    }


    /**
     * Register this process with the router under group.
     * @param group The name of the group under which to register
     */
    public void connect(String group, Address addr, String logical_name, PhysicalAddress phys_addr) throws Exception {
        lock.lock();
        try {
            _doConnect();
            GossipData request=new GossipData(GossipRouter.CONNECT, group, addr, logical_name, phys_addr);
            request.writeTo(output);
            output.flush();
            byte result = input.readByte();
            if(result == GossipRouter.CONNECT_OK) {
                connectionStateChanged(ConnectionStatus.CONNECTED);
            } else {
                connectionStateChanged(ConnectionStatus.DISCONNECTED);
                throw new Exception("Connect failed received from GR " + getGossipRouterAddress());
            }
        }
        finally {
            if(lock.isHeldByCurrentThread())
                lock.unlock();
        }
    }

    public void doConnect() throws Exception {
        lock.lock();
        try {
            _doConnect();
        }
        finally {
            if(lock.isHeldByCurrentThread())
                lock.unlock();
        }
    }

    @GuardedBy("lock")
    protected void _doConnect() throws Exception {
        if(!isConnected()) {
            try {
                sock=new Socket();
                InetSocketAddress dest=new InetSocketAddress(router_host,router_port);
                InetAddress tmp_bind_addr=bind_addr;
                if(!Util.sameAddresses(bind_addr, tmp_bind_addr))
                    tmp_bind_addr=null;
                sock.bind(new InetSocketAddress(tmp_bind_addr, 0));
                sock.setSoTimeout(sock_read_timeout);
                sock.setSoLinger(true, 2);
                sock.setTcpNoDelay(tcp_nodelay);
                sock.setKeepAlive(true);
                Util.connect(sock, dest, sock_conn_timeout);
                output=new DataOutputStream(sock.getOutputStream());
                input=new DataInputStream(sock.getInputStream());
                connectionStateChanged(ConnectionStatus.CONNECTION_ESTABLISHED);
            }
            catch(Exception e) {
                Util.close(sock);
                Util.close(input, output);
                connectionStateChanged(ConnectionStatus.CONNECTION_BROKEN);
                throw new Exception("Could not connect to " + getGossipRouterAddress() , e);
            }
        }
    }


    /**
     * Checks whether the connection is open
     * @return
     */
    public void checkConnection() {
        GossipData request=new GossipData(GossipRouter.PING);
        lock.lock();
        try {
            request.writeTo(output);
            output.flush();
        }
        catch(Exception e) {
            connectionStateChanged(ConnectionStatus.CONNECTION_BROKEN);
        }
        finally {
            if(lock.isHeldByCurrentThread())
                lock.unlock();
        }
    }


    public void disconnect(String group, Address addr) {
        lock.lock();
        try {
            GossipData request=new GossipData(GossipRouter.DISCONNECT, group, addr);
            request.writeTo(output);
            output.flush();
        }
        catch(Exception e) {
        }
        finally {
            connectionStateChanged(ConnectionStatus.DISCONNECTED);
            if(lock.isHeldByCurrentThread()) // not needed as connectionStateChanged() unlocks, but this gets rid of the warning
                lock.unlock();
        }
    }

    public void destroy() {
        lock.lock();
        try {
            GossipData request = new GossipData(GossipRouter.CLOSE);
            request.writeTo(output);
            output.flush();
        }
        catch (Exception e) {
        }
        finally {
            Util.close(output);
            Util.close(input);
            Util.close(sock);
            if(lock.isHeldByCurrentThread())
                lock.unlock();
        }
    }
    
    
    /*
     * Used only in testing, never access socket directly
     * 
     */
    public Socket getSocket() {
        return sock;
    }


    public void getMembers(final String group, Responses rsps) throws Exception {
        lock.lock();
        try {
            if(!isConnected() || input == null) throw new Exception ("not connected");
            // we might get a spurious SUSPECT message from the router, just ignore it
            if(input.available() > 0) // fixes https://jira.jboss.org/jira/browse/JGRP-1151
                input.skipBytes(input.available());

            GossipData request=new GossipData(GossipRouter.GOSSIP_GET, group, null);
            request.writeTo(output);
            output.flush();

            short num_rsps=input.readShort();
            for(int i=0; i < num_rsps; i++) {
                PingData rsp=new PingData();
                rsp.readFrom(input);
                rsps.addResponse(rsp, false);
            }
        }
        catch(Exception e) {           
            connectionStateChanged(ConnectionStatus.CONNECTION_BROKEN);
            throw new Exception("Connection to " + getGossipRouterAddress() + " broken. Could not send GOSSIP_GET request", e);
        }
        finally {
            if(lock.isHeldByCurrentThread())
                lock.unlock();
        }
    }

    public InetSocketAddress getGossipRouterAddress() {
        return new InetSocketAddress(router_host, router_port);
    }
    
    public String toString() {
        return "RouterStub[localsocket=" + ((sock != null) ? sock.getLocalSocketAddress()
                        : "null")+ ",router_host=" + router_host + "::" + router_port + ",connected=" + isConnected() + "]";
    }

    public void sendToAllMembers(String group, byte[] data, int offset, int length) throws Exception {
        sendToMember(group, null, data, offset, length); // null destination represents mcast
    }

    public void sendToMember(String group, Address dest, byte[] data, int offset, int length) throws Exception {
        lock.lock();
        try {
            GossipData request = new GossipData(GossipRouter.MESSAGE, group, dest, data, offset, length);
            request.writeTo(output);
            output.flush();
        }
        catch (Exception e) {
            connectionStateChanged(ConnectionStatus.CONNECTION_BROKEN);
            throw new Exception("Connection to " + getGossipRouterAddress()
                            + " broken. Could not send message to " + dest, e);
        }
        finally {
            if(lock.isHeldByCurrentThread())
                lock.unlock();
        }
    }

    public DataInputStream getInputStream() {
        return input;
    }

    protected void connectionStateChanged(ConnectionStatus newState) {
        boolean notify=connectionState != newState;
        connectionState=newState;
        if(notify && conn_listener != null) {
            // release lock as the callback below might block: https://issues.jboss.org/browse/JGRP-1526
            if(lock.isHeldByCurrentThread())
                lock.unlock();
            try {
                conn_listener.connectionStatusChange(this, newState);
            }
            catch(Throwable t) {
                log.error("failed notifying ConnectionListener " + conn_listener, t);
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy