com.gemstone.org.jgroups.protocols.obsolete.TCP.txt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gemfire-jgroups Show documentation
Show all versions of gemfire-jgroups Show documentation
SnappyData store based off Pivotal GemFireXD
// $Id: TCP.java.txt,v 1.1 2005/06/27 08:48:12 belaban Exp $
package org.jgroups.protocols;
import org.jgroups.Address;
import org.jgroups.Event;
import org.jgroups.Message;
import org.jgroups.View;
import org.jgroups.blocks.ConnectionTable;
import org.jgroups.stack.IpAddress;
import org.jgroups.stack.Protocol;
import org.jgroups.util.BoundedList;
import org.jgroups.util.Util;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Properties;
import java.util.Vector;
/**
* TCP based protocol. Creates a server socket, which gives us the local address of this group member. For
* each accept() on the server socket, a new thread is created that listens on the socket.
* For each outgoing message m, if m.dest is in the ougoing hashtable, the associated socket will be reused
* to send message, otherwise a new socket is created and put in the hashtable.
* When a socket connection breaks or a member is removed from the group, the corresponding items in the
* incoming and outgoing hashtables will be removed as well.
* This functionality is in ConnectionTable, which isT used by TCP. TCP sends messages using ct.send() and
* registers with the connection table to receive all incoming messages.
* @author Bela Ban
*/
public class TCP extends Protocol implements ConnectionTable.Receiver {
private ConnectionTable ct=null;
protected Address local_addr=null;
private String group_addr=null;
private InetAddress bind_addr=null; // local IP address to bind srv sock to (m-homed systems)
private InetAddress external_addr=null; // the IP address which is broadcast to other group members
private int start_port=7800; // find first available port starting at this port
private int end_port=0; // maximum port to bind to
private final Vector members=new Vector(11);
private long reaper_interval=0; // time in msecs between connection reaps
private long conn_expire_time=0; // max time a conn can be idle before being reaped
boolean loopback=false; // loops back msgs to self if true
/** If set it will be added to local_addr. Used to implement
* for example transport independent addresses */
byte[] additional_data=null;
/** List the maintains the currently suspected members. This is used so we don't send too many SUSPECT
* events up the stack (one per message !)
*/
final BoundedList suspected_mbrs=new BoundedList(20);
/** Should we drop unicast messages to suspected members or not */
boolean skip_suspected_members=true;
int recv_buf_size=150000;
int send_buf_size=150000;
int sock_conn_timeout=2000; // max time in millis for a socket creation in ConnectionTable
static final String name="TCP";
static final String IGNORE_BIND_ADDRESS_PROPERTY="ignore.bind.address";
int num_msgs_sent=0, num_msgs_received=0;
public TCP() {
}
public String toString() {
return "Protocol TCP(local address: " + local_addr + ')';
}
public String getName() {
return "TCP";
}
public long getNumMessagesSent() {return num_msgs_sent;}
public long getNumMessagesReceived() {return num_msgs_received;}
public int getOpenConnections() {return ct.getNumConnections();}
public InetAddress getBindAddr() {return bind_addr;}
public void setBindAddr(InetAddress bind_addr) {this.bind_addr=bind_addr;}
public int getStartPort() {return start_port;}
public void setStartPort(int start_port) {this.start_port=start_port;}
public int getEndPort() {return end_port;}
public void setEndPort(int end_port) {this.end_port=end_port;}
public long getReaperInterval() {return reaper_interval;}
public void setReaperInterval(long reaper_interval) {this.reaper_interval=reaper_interval;}
public long getConnExpireTime() {return conn_expire_time;}
public void setConnExpireTime(long conn_expire_time) {this.conn_expire_time=conn_expire_time;}
public boolean isLoopback() {return loopback;}
public void setLoopback(boolean loopback) {this.loopback=loopback;}
public String printConnections() {return ct.toString();}
public void resetStats() {
super.resetStats();
num_msgs_sent=num_msgs_received=0;
}
protected final Vector getMembers() {
return members;
}
/**
DON'T REMOVE ! This prevents the up-handler thread to be created, which essentially is superfluous:
messages are received from the network rather than from a layer below.
*/
public void startUpHandler() {
;
}
public void start() throws Exception {
ct=getConnectionTable(reaper_interval,conn_expire_time,bind_addr,external_addr,start_port,end_port);
// ct.addConnectionListener(this);
ct.setReceiveBufferSize(recv_buf_size);
ct.setSendBufferSize(send_buf_size);
ct.setSocketConnectionTimeout(sock_conn_timeout);
local_addr=ct.getLocalAddress();
if(additional_data != null && local_addr instanceof IpAddress)
((IpAddress)local_addr).setAdditionalData(additional_data);
passUp(new Event(Event.SET_LOCAL_ADDRESS, local_addr));
}
/**
* @param reaperInterval
* @param connExpireTime
* @param bindAddress
* @param startPort
* @throws Exception
* @return ConnectionTable
* Sub classes overrides this method to initialize a different version of
* ConnectionTable.
*/
protected ConnectionTable getConnectionTable(long reaperInterval, long connExpireTime, InetAddress bindAddress,
InetAddress externalAddress, int startPort, int endPort) throws Exception {
ConnectionTable cTable=null;
if(reaperInterval == 0 && connExpireTime == 0) {
cTable=new ConnectionTable(this, bindAddress, externalAddress, startPort, endPort);
}
else {
if(reaperInterval == 0) {
reaperInterval=5000;
if(log.isWarnEnabled()) log.warn("reaper_interval was 0, set it to " + reaperInterval);
}
if(connExpireTime == 0) {
connExpireTime=1000 * 60 * 5;
if(log.isWarnEnabled()) log.warn("conn_expire_time was 0, set it to " + connExpireTime);
}
cTable=new ConnectionTable(this, bindAddress, externalAddress, startPort, endPort,
reaperInterval, connExpireTime);
}
return cTable;
}
public void stop() {
ct.stop();
}
/**
Sent to destination(s) using the ConnectionTable class.
*/
public void down(Event evt) {
Message msg;
Object dest_addr;
if(evt.getType() != Event.MSG) {
handleDownEvent(evt);
return;
}
msg=(Message)evt.getArg();
num_msgs_sent++;
if(group_addr != null) { // added patch sent by Roland Kurmann (bela March 20 2003)
/* Add header (includes channel name) */
msg.putHeader(name, new TcpHeader(group_addr));
}
dest_addr=msg.getDest();
/* Because we don't call Protocol.passDown(), we notify the observer directly (e.g. PerfObserver). This way,
we still have performance numbers for TCP */
if(observer != null)
observer.passDown(evt);
if(dest_addr == null) { // broadcast (to all members)
if(group_addr == null) {
if(log.isWarnEnabled()) log.warn("dest address of message is null, and " +
"sending to default address fails as group_addr is null, too !" +
" Discarding message.");
return;
}
else {
sendMulticastMessage(msg); // send to current membership
}
}
else {
sendUnicastMessage(msg); // send to a single member
}
}
/** ConnectionTable.Receiver interface */
public void receive(Message msg) {
TcpHeader hdr=null;
Event evt=new Event(Event.MSG, msg);
/* Because Protocol.up() is never called by this bottommost layer, we call up() directly in the observer.
This allows e.g. PerfObserver to get the time of reception of a message */
if(observer != null)
observer.up(evt, up_queue.size());
if(log.isTraceEnabled()) log.trace("received msg " + msg);
num_msgs_received++;
hdr=(TcpHeader)msg.removeHeader(name);
if(hdr != null) {
/* Discard all messages destined for a channel with a different name */
String ch_name=null;
if(hdr.group_addr != null)
ch_name=hdr.group_addr;
// below lines were commented as patch sent by Roland Kurmann (bela March 20 2003)
// if(group_addr == null) {
// if(log.isWarnEnabled()) log.warn("TCP.receive()", "group address in header was null, discarded");
// return;
// }
// Discard if message's group name is not the same as our group name unless the
// message is a diagnosis message (special group name DIAG_GROUP)
if(ch_name != null && !group_addr.equals(ch_name) &&
!ch_name.equals(Util.DIAG_GROUP)) {
if(log.isWarnEnabled()) log.warn("discarded message from different group (" +
ch_name + "). Sender was " + msg.getSrc());
return;
}
}
passUp(evt);
}
// ConnectionTable.ConnectionListener interface
// public void connectionOpened(Address peer_addr) {
// if(log.isTraceEnabled()) log.trace("opened connection to " + peer_addr);
// }
//
// public void connectionClosed(Address peer_addr) {
// if(peer_addr != null)
// if(log.isTraceEnabled()) log.trace("closed connection to " + peer_addr);
// }
/** Setup the Protocol instance acording to the configuration string */
public boolean setProperties(Properties props) {
String str, tmp=null;
super.setProperties(props);
str=props.getProperty("start_port");
if(str != null) {
start_port=Integer.parseInt(str);
props.remove("start_port");
}
str=props.getProperty("end_port");
if(str != null) {
end_port=Integer.parseInt(str);
props.remove("end_port");
}
// PropertyPermission not granted if running in an untrusted environment with JNLP.
try {
tmp=System.getProperty("bind.address"); // set by JBoss
if(Boolean.getBoolean(IGNORE_BIND_ADDRESS_PROPERTY)) {
tmp=null;
}
}
catch (SecurityException ex){
}
if(tmp != null)
str=tmp;
else
str=props.getProperty("bind_addr");
if(str != null) {
try {
bind_addr=InetAddress.getByName(str);
}
catch(UnknownHostException unknown) {
if(log.isFatalEnabled()) log.fatal("(bind_addr): host " + str + " not known");
return false;
}
props.remove("bind_addr");
}
str=props.getProperty("external_addr");
if(str != null) {
try {
external_addr=InetAddress.getByName(str);
}
catch(UnknownHostException unknown) {
if(log.isFatalEnabled()) log.fatal("(external_addr): host " + str + " not known");
return false;
}
props.remove("external_addr");
}
str=props.getProperty("reaper_interval");
if(str != null) {
reaper_interval=Long.parseLong(str);
props.remove("reaper_interval");
}
str=props.getProperty("conn_expire_time");
if(str != null) {
conn_expire_time=Long.parseLong(str);
props.remove("conn_expire_time");
}
str=props.getProperty("sock_conn_timeout");
if(str != null) {
sock_conn_timeout=Integer.parseInt(str);
props.remove("sock_conn_timeout");
}
str=props.getProperty("recv_buf_size");
if(str != null) {
recv_buf_size=Integer.parseInt(str);
props.remove("recv_buf_size");
}
str=props.getProperty("send_buf_size");
if(str != null) {
send_buf_size=Integer.parseInt(str);
props.remove("send_buf_size");
}
str=props.getProperty("loopback");
if(str != null) {
loopback=Boolean.valueOf(str).booleanValue();
props.remove("loopback");
}
str=props.getProperty("skip_suspected_members");
if(str != null) {
skip_suspected_members=Boolean.valueOf(str).booleanValue();
props.remove("skip_suspected_members");
}
if(props.size() > 0) {
log.error("TCP.setProperties(): the following properties are not recognized: " + props);
return false;
}
return true;
}
/**
If the sender is null, set our own address. We cannot just go ahead and set the address
anyway, as we might be sending a message on behalf of someone else ! E.g. in case of
retransmission, when the original sender has crashed, or in a FLUSH protocol when we
have to return all unstable messages with the FLUSH_OK response.
*/
private void setSourceAddress(Message msg) {
if(msg.getSrc() == null)
msg.setSrc(local_addr);
}
/** Send a message to the address specified in msg.dest */
private void sendUnicastMessage(Message msg) {
IpAddress dest;
Message copy;
Object hdr;
Event evt;
dest=(IpAddress)msg.getDest(); // guaranteed not to be null
if(!(dest instanceof IpAddress)) {
if(log.isErrorEnabled()) log.error("destination address is not of type IpAddress !");
return;
}
setSourceAddress(msg);
/* Don't send if destination is local address. Instead, switch dst and src and put in up_queue */
if(loopback && local_addr != null && dest != null && dest.equals(local_addr)) {
copy=msg.copy();
hdr=copy.getHeader(name);
if(hdr != null && hdr instanceof TcpHeader)
copy.removeHeader(name);
copy.setSrc(local_addr);
copy.setDest(local_addr);
evt=new Event(Event.MSG, copy);
/* Because Protocol.up() is never called by this bottommost layer, we call up() directly in the observer.
This allows e.g. PerfObserver to get the time of reception of a message */
if(observer != null)
observer.up(evt, up_queue.size());
passUp(evt);
return;
}
if(log.isTraceEnabled()) log.trace("dest=" + msg.getDest() + ", hdrs:\n" + msg.printObjectHeaders());
try {
if(skip_suspected_members) {
if(suspected_mbrs.contains(dest)) {
if(log.isTraceEnabled()) log.trace("will not send unicast message to " + dest +
" as it is currently suspected");
return;
}
}
ct.send(msg);
}
catch(SocketException e) {
if(members.contains(dest)) {
if(!suspected_mbrs.contains(dest)) {
suspected_mbrs.add(dest);
passUp(new Event(Event.SUSPECT, dest));
}
}
}
}
protected void sendMulticastMessage(Message msg) {
Address dest;
Vector mbrs=(Vector)members.clone();
for(int i=0; i < mbrs.size(); i++) {
dest=(Address)mbrs.elementAt(i);
msg.setDest(dest);
sendUnicastMessage(msg);
}
}
protected void handleDownEvent(Event evt) {
switch(evt.getType()) {
case Event.TMP_VIEW:
case Event.VIEW_CHANGE:
suspected_mbrs.removeAll();
synchronized(members) {
members.clear();
members.addAll(((View)evt.getArg()).getMembers());
}
break;
case Event.GET_LOCAL_ADDRESS: // return local address -> Event(SET_LOCAL_ADDRESS, local)
passUp(new Event(Event.SET_LOCAL_ADDRESS, local_addr));
break;
case Event.CONNECT:
group_addr=(String)evt.getArg();
// removed March 18 2003 (bela), not needed (handled by GMS)
// Can't remove it; otherwise TCPGOSSIP breaks (bela May 8 2003) !
passUp(new Event(Event.CONNECT_OK));
break;
case Event.DISCONNECT:
passUp(new Event(Event.DISCONNECT_OK));
break;
case Event.CONFIG:
if(log.isTraceEnabled()) log.trace("received CONFIG event: " + evt.getArg());
handleConfigEvent((HashMap)evt.getArg());
break;
}
}
void handleConfigEvent(HashMap map) {
if(map == null) return;
if(map.containsKey("additional_data"))
additional_data=(byte[])map.get("additional_data");
}
}