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

org.jgroups.protocols.TUNNEL Maven / Gradle / Ivy

There is a newer version: 5.3.13.Final
Show newest version
package org.jgroups.protocols;


import org.jgroups.Address;
import org.jgroups.Event;
import org.jgroups.PhysicalAddress;
import org.jgroups.annotations.Component;
import org.jgroups.annotations.ManagedAttribute;
import org.jgroups.annotations.ManagedOperation;
import org.jgroups.annotations.Property;
import org.jgroups.conf.AttributeType;
import org.jgroups.stack.GossipData;
import org.jgroups.stack.IpAddress;
import org.jgroups.stack.RouterStub;
import org.jgroups.stack.RouterStubManager;
import org.jgroups.util.SocketFactory;
import org.jgroups.util.TLS;
import org.jgroups.util.Util;

import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
 * 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} and {@code router_port}. 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.
 * 
 * 

* 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 * @author Vladimir Blagojevic */ public class TUNNEL extends TP implements RouterStub.StubReceiver { public interface TUNNELPolicy { void sendToAllMembers(String group, Address sender, byte[] data, int offset, int length) throws Exception; void sendToSingleMember(String group, Address dest, Address sender, byte[] data, int offset, int length) throws Exception; } /* ----------------------------------------- Properties -------------------------------------------------- */ @Property(description = "Interval in msec to attempt connecting back to router in case of torn connection", type=AttributeType.TIME) protected long reconnect_interval=5000; @Property(description="Should TCP no delay flag be turned on") protected boolean tcp_nodelay; @Property(description="Whether to use blocking (false) or non-blocking (true) connections. If GossipRouter is used, " + "this needs to be false; if GossipRouterNio is used, it needs to be true") protected boolean use_nio; @Property(description="A comma-separated list of GossipRouter hosts, e.g. HostA[12001],HostB[12001]") protected String gossip_router_hosts; @Property(description="Sends a heartbeat to the GossipRouter every heartbeat_interval ms (0 disables this)", type=AttributeType.TIME) protected long heartbeat_interval; @Property(description="Max time (ms) with no received message or heartbeat after which the connection to a " + "GossipRouter is closed. Ignored when heartbeat_interval is 0.", type=AttributeType.TIME) protected long heartbeat_timeout; @Property(description="SO_LINGER in seconds. Default of -1 disables it") protected int linger=-1; // SO_LINGER (number of seconds, -1 disables it) @Property(description="use bounded queues for sending (https://issues.redhat.com/browse/JGRP-2759)") protected boolean non_blocking_sends; @Property(description="when sending and non_blocking, how many messages to queue max") protected int max_send_queue=128; /* ------------------------------------------ Fields ----------------------------------------------------- */ protected final List gossip_routers=new ArrayList<>(); protected TUNNELPolicy tunnel_policy=new DefaultTUNNELPolicy(); protected DatagramSocket sock; // used to get a unique client address protected volatile RouterStubManager stubManager; @Component(name="tls",description="Contains the attributes for TLS (SSL sockets) when enabled=true") protected TLS tls=new TLS(); public TUNNEL() { } public long getReconnectInterval() {return reconnect_interval;} public TUNNEL setReconnectInterval(long r) {this.reconnect_interval=r; return this;} public boolean isTcpNodelay() {return tcp_nodelay;} public TUNNEL setTcpNodelay(boolean nd) {this.tcp_nodelay=nd;return this;} public boolean useNio() {return use_nio;} public TUNNEL useNio(boolean use_nio) {this.use_nio=use_nio; return this;} public TLS tls() {return tls;} public TUNNEL tls(TLS t) {this.tls=t; return this;} public int getLinger() {return linger;} public TUNNEL setLinger(int l) {this.linger=l; return this;} public boolean nonBlockingSends() {return non_blocking_sends;} public TUNNEL nonBlockingSends(boolean b) {this.non_blocking_sends=b; return this;} public int maxSendQueue() {return max_send_queue;} public TUNNEL maxSendQueue(int s) {this.max_send_queue=s; return this;} /** We can simply send a message with dest == null and the GossipRouter will take care of routing it to all * members in the cluster */ public boolean supportsMulticasting() { return true; } public TUNNEL setGossipRouterHosts(String hosts) throws UnknownHostException { gossip_routers.clear(); // if we get passed value of List#toString() we have to strip [] if(hosts.startsWith("[") && hosts.endsWith("]")) hosts=hosts.substring(1, hosts.length() - 1); gossip_router_hosts=hosts; return this; } @ManagedAttribute(description="Is the reconnector task running?") public boolean isReconnectorTaskRunning() { return stubManager != null && stubManager.reconnectorRunning(); } @ManagedAttribute(description="Is the heartbeat task running?") public boolean isHeartbeatTaskRunning() { return stubManager != null && stubManager.heartbeaterRunning(); } @ManagedAttribute(description="Is the timeout check task running?") public boolean isTimeoutCheckTaskRunning() { return stubManager != null && stubManager.timeouterRunning(); } @ManagedOperation(description="Prints all stubs and the reconnect list") public String print() { RouterStubManager mgr=stubManager; return mgr != null? mgr.print() : "n/a"; } @ManagedOperation(description="Prints all currently connected stubs") public String printStubs() { RouterStubManager mgr=stubManager; return mgr != null? mgr.printStubs() : "n/a"; } @ManagedOperation(description="Prints the reconnect list") public String printReconnectList() { RouterStubManager mgr=stubManager; return mgr != null? mgr.printReconnectList() : "n/a"; } public RouterStubManager getStubManager() {return stubManager;} public String toString() { return "TUNNEL"; } /*------------------------------ Protocol interface ------------------------------ */ public synchronized TUNNEL setTUNNELPolicy(TUNNELPolicy policy) { if (policy == null) throw new IllegalArgumentException("Tunnel policy has to be non null"); tunnel_policy = policy; return this; } public void init() throws Exception { super.init(); if(timer == null) throw new Exception("timer cannot be retrieved from protocol stack"); if(port_range > 0) { log.warn("%s: port_range=%d; setting it to 0 (https://issues.redhat.com/browse/JGRP-2806)", local_addr, port_range); port_range=0; // https://issues.redhat.com/browse/JGRP-2806 } gossip_routers.clear(); gossip_routers.addAll(Util.parseCommaDelimitedHosts2(gossip_router_hosts, port_range)); if(gossip_routers.isEmpty()) throw new IllegalStateException("gossip_router_hosts needs to contain at least one address of a GossipRouter"); log.debug("gossip routers are %s", gossip_routers); stubManager=RouterStubManager.emptyGossipClientStubManager(log, timer).useNio(this.use_nio) .nonBlockingSends(non_blocking_sends).maxSendQueue(max_send_queue); sock=getSocketFactory().createDatagramSocket("jgroups.tunnel.ucast_sock", 0, bind_addr); } @Override public void start() throws Exception { super.start(); if(tls.enabled()) { SocketFactory factory=tls.createSocketFactory(); setSocketFactory(factory); } } public void destroy() { if(stubManager != null) stubManager.destroyStubs(); Util.close(sock); super.destroy(); } private void disconnectStub() { stubManager.disconnectStubs(); } @Override public Object down(Event evt) { Object retEvent = super.down(evt); switch (evt.getType()) { case Event.CONNECT: case Event.CONNECT_WITH_STATE_TRANSFER: case Event.CONNECT_USE_FLUSH: case Event.CONNECT_WITH_STATE_TRANSFER_USE_FLUSH: String group=evt.getArg(); Address local=local_addr; if(stubManager != null) stubManager.destroyStubs(); PhysicalAddress physical_addr=getPhysicalAddressFromCache(local); String logical_name=org.jgroups.util.NameCache.get(local); stubManager=new RouterStubManager(log,timer,group,local, logical_name, physical_addr, reconnect_interval) .useNio(this.use_nio).socketFactory(getSocketFactory()).heartbeat(heartbeat_interval, heartbeat_timeout) .nonBlockingSends(non_blocking_sends).maxSendQueue(max_send_queue); for(InetSocketAddress gr: gossip_routers) { try { InetSocketAddress target=gr.isUnresolved()? new InetSocketAddress(gr.getHostString(), gr.getPort()) : new InetSocketAddress(gr.getAddress(), gr.getPort()); stubManager.createAndRegisterStub(new InetSocketAddress(bind_addr, bind_port), target, linger) .receiver(this).tcpNoDelay(tcp_nodelay); } catch(Throwable t) { log.error("%s: failed creating stub to %s: %s", local, bind_addr + ":" + bind_port, t); } } stubManager.connectStubs(); break; case Event.DISCONNECT: disconnectStub(); break; } return retEvent; } @Override public void receive(GossipData data) { switch (data.getType()) { case MESSAGE: if(Objects.equals(local_addr, data.getSender())) return; byte[] msg=data.getBuffer(); receive(data.getSender(), msg, 0, msg.length); break; case SUSPECT: Address suspect=data.getAddress(); if(suspect != null) { log.debug("%s: firing suspect event for %s", local_addr, suspect); up(new Event(Event.SUSPECT, Collections.singletonList(suspect))); } break; } } @Override public void sendToAll(byte[] data, int offset, int length) throws Exception { String group=cluster_name != null? cluster_name.toString() : null; tunnel_policy.sendToAllMembers(group, local_addr, data, offset, length); } public void sendUnicast(PhysicalAddress dest, byte[] data, int offset, int length) throws Exception { String group=cluster_name != null? cluster_name.toString() : null; tunnel_policy.sendToSingleMember(group, dest, local_addr, data, offset, length); } protected void sendTo(final Address dest, byte[] buf, int offset, int length) throws Exception { if(dest instanceof PhysicalAddress) throw new IllegalArgumentException(String.format("destination %s cannot be a physical address", dest)); sendUnicast(dest, buf, offset, length); } protected void sendUnicast(Address dest, byte[] data, int offset, int length) throws Exception { String group=cluster_name != null? cluster_name.toString() : null; tunnel_policy.sendToSingleMember(group, dest, local_addr, data, offset, length); } public String getInfo() { return stubManager.printStubs(); } protected PhysicalAddress getPhysicalAddress() { return sock != null ? new IpAddress(bind_addr, sock.getLocalPort()) : null; } private class DefaultTUNNELPolicy implements TUNNELPolicy { public void sendToAllMembers(final String group, Address sender, final byte[] data, final int offset, final int length) throws Exception { stubManager.forAny( stub -> { try { if(log.isTraceEnabled()) log.trace("%s: sending a message to all members, GR used %s", local_addr, stub.gossipRouterAddress()); stub.sendToAllMembers(group, sender, data, offset, length); } catch(Exception ex) { log.warn("%s: failed sending a message to all members, router used %s: %s", local_addr, stub.gossipRouterAddress(), ex); } }); } public void sendToSingleMember(final String group, final Address dest, Address sender, final byte[] data, final int offset, final int length) throws Exception { stubManager.forAny( stub -> { try { if(log.isTraceEnabled()) log.trace("%s: sending a message to %s (router used %s)", local_addr, dest, stub.gossipRouterAddress()); stub.sendToMember(group, dest, sender, data, offset, length); } catch(Exception ex) { log.warn("%s: failed sending a message to %s (router used %s): %s", local_addr, dest, stub.gossipRouterAddress(), ex); } }); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy