// $Id: GossipRouter.java,v 1.22.2.6 2009/01/08 07:59:37 jiwils Exp $
package org.jgroups.stack;
import EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jgroups.Address;
import org.jgroups.conf.ClassConfigurator;
import org.jgroups.util.Util;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;
/**
* Router for TCP based group comunication (using layer TCP instead of UDP).
* Instead of the TCP layer sending packets point-to-point to each other
* member, it sends the packet to the router which - depending on the target
* address - multicasts or unicasts it to the group / or single member.<p>
* This class is especially interesting for applets which cannot directly make
* connections (neither UDP nor TCP) to a host different from the one they were
* loaded from. Therefore, an applet would create a normal channel plus
* protocol stack, but the bottom layer would have to be the TCP layer which
* sends all packets point-to-point (over a TCP connection) to the router,
* which in turn forwards them to their end location(s) (also over TCP). A
* centralized router would therefore have to be running on the host the applet
* was loaded from.<p>
* An alternative for running JGroups in an applet (IP multicast is not allows
* in applets as of 1.2), is to use point-to-point UDP communication via the
* gossip server. However, then the appplet has to be signed which involves
* additional administrative effort on the part of the user.<p>
* @author Bela Ban
* @author Ovidiu Feodorov <ovidiuf@users.sourceforge.net>
* @since 2.1.1
*/
public class GossipRouter {
public static final byte CONNECT=1; // CONNECT(group, addr) --> local address
public static final byte DISCONNECT=2; // DISCONNECT(group, addr)
public static final byte REGISTER=3; // REGISTER(group, addr)
public static final byte GOSSIP_GET=4; // GET(group) --> List<addr> (members)
public static final byte ROUTER_GET=5; // GET(group) --> List<addr> (members)
public static final byte GET_RSP=6; // GET_RSP(List<addr>)
public static final byte UNREGISTER=7; // UNREGISTER(group, addr)
public static final byte DUMP=8; // DUMP
public static final byte SHUTDOWN=9;
public static final int PORT=8980;
public static final long EXPIRY_TIME=30000;
public static final long GOSSIP_REQUEST_TIMEOUT=1000;
public static final long ROUTING_CLIENT_REPLY_TIMEOUT=120000;
private int port;
private String bindAddressString;
// time (in msecs) until a cached 'gossip' member entry expires
private long expiryTime;
// number of millisecs the main thread waits to receive a gossip request
// after connection was established; upon expiration, the router initiates
// the routing protocol on the connection. Don't set the interval too big,
// otherwise the router will appear slow in answering routing requests.
private long gossipRequestTimeout;
// time (in ms) main thread waits for a router client to send the routing
// request type and the group afiliation before it declares the request
// failed.
private long routingClientReplyTimeout;
// HashMap<String, Map<Address,AddressEntry> >. Maintains associations between groups and their members. Keys=group
// names, values = maps of logical address / AddressEntry associations
private final Map routingTable=new ConcurrentHashMap();
private ServerSocket srvSock=null;
private InetAddress bindAddress=null;
/** Time (in millis) for setting SO_LINGER on sockets returned from accept(). 0 means don't set SO_LINGER */
private long linger_timeout=2000L;
/** Time (in millis) for SO_TIMEOUT on sockets returned from accept(). 0 means don't set SO_TIMEOUT */
private long sock_read_timeout=3000L;
/** The max queue size of backlogged connections */
private int backlog=1000;
private boolean up=true;
/** whether to discard message sent to self */
private boolean discard_loopbacks=false;
// the cache sweeper
Timer timer=null;
protected final Log log=LogFactory.getLog(this.getClass());
//
// JMX INSTRUMENTATION - MANAGEMENT INTERFACE
//
public GossipRouter() {
this(PORT);
}
public GossipRouter(int port) {
this(port, null);
}
public GossipRouter(int port, String bindAddressString) {
this(port, bindAddressString, EXPIRY_TIME);
}
public GossipRouter(int port, String bindAddressString,
long expiryTime) {
this(port, bindAddressString, expiryTime,
GOSSIP_REQUEST_TIMEOUT,
ROUTING_CLIENT_REPLY_TIMEOUT);
}
public GossipRouter(int port, String bindAddressString,
long expiryTime, long gossipRequestTimeout,
long routingClientReplyTimeout) {
this.port=port;
this.bindAddressString=bindAddressString;
this.expiryTime=expiryTime;
this.gossipRequestTimeout=gossipRequestTimeout;
this.routingClientReplyTimeout=routingClientReplyTimeout;
}
//
// MANAGED ATTRIBUTES
//
public void setPort(int port) {
this.port=port;
}
public int getPort() {
return port;
}
public void setBindAddress(String bindAddress) {
bindAddressString=bindAddress;
}
public String getBindAddress() {
return bindAddressString;
}
public int getBacklog() {
return backlog;
}
public void setBacklog(int backlog) {
this.backlog=backlog;
}
public void setExpiryTime(long expiryTime) {
this.expiryTime=expiryTime;
}
public long getExpiryTime() {
return expiryTime;
}
public void setGossipRequestTimeout(long gossipRequestTimeout) {
this.gossipRequestTimeout=gossipRequestTimeout;
}
public long getGossipRequestTimeout() {
return gossipRequestTimeout;
}
public void setRoutingClientReplyTimeout(long routingClientReplyTimeout) {
this.routingClientReplyTimeout=routingClientReplyTimeout;
}
public long getRoutingClientReplyTimeout() {
return routingClientReplyTimeout;
}
public boolean isStarted() {
return srvSock != null;
}
public boolean isDiscardLoopbacks() {
return discard_loopbacks;
}
public void setDiscardLoopbacks(boolean discard_loopbacks) {
this.discard_loopbacks=discard_loopbacks;
}
public long getLingerTimeout() {
return linger_timeout;
}
public void setLingerTimeout(long linger_timeout) {
this.linger_timeout=linger_timeout;
}
public long getSocketReadTimeout() {
return sock_read_timeout;
}
public void setSocketReadTimeout(long sock_read_timeout) {
this.sock_read_timeout=sock_read_timeout;
}
public static String type2String(int type) {
switch(type) {
case CONNECT:
return "CONNECT";
case DISCONNECT:
return "DISCONNECT";
case REGISTER:
return "REGISTER";
case GOSSIP_GET:
return "GOSSIP_GET";
case ROUTER_GET:
return "ROUTER_GET";
case GET_RSP:
return "GET_RSP";
case UNREGISTER:
return "UNREGISTER";
case DUMP:
return "DUMP";
case SHUTDOWN:
return "SHUTDOWN";
default:
return "unknown";
}
}
//
// JBoss MBean LIFECYCLE OPERATIONS
//
/**
* JBoss MBean lifecycle operation.
*/
public void create() throws Exception {
// not used
}
/**
* JBoss MBean lifecycle operation. Called after create(). When this method
* is called, the managed attributes have already been set.<br>
* Brings the Router in fully functional state.
*/
public void start() throws Exception {
if(srvSock != null) {
throw new Exception("Router already started.");
}
if(bindAddressString != null) {
bindAddress=InetAddress.getByName(bindAddressString);
srvSock=new ServerSocket(port, backlog, bindAddress);
}
else {
srvSock=new ServerSocket(port, backlog);
}
up=true;
// start the main server thread
new Thread(new Runnable() {
public void run() {
mainLoop();
cleanup();
}
}, "GossipRouter").start();
// starts the cache sweeper as daemon thread, so we won't block on it
// upon termination
timer=new Timer(true);
timer.schedule(new TimerTask() {
public void run() {
sweep();
}
}, expiryTime, expiryTime);
}
/**
* JBoss MBean lifecycle operation. The JMX agent allways calls this method
* before destroy(). Close connections and frees resources.
*/
public void stop() {
up=false;
if(srvSock == null) {
if(log.isWarnEnabled()) log.warn("router already stopped");
return;
}
timer.cancel();
shutdown();
try {
srvSock.close();
}
catch(Exception e) {
if(log.isErrorEnabled()) log.error("Failed to close server socket: " + e);
}
// exiting the mainLoop will clean the tables
srvSock=null;
if(log.isInfoEnabled()) log.info("router stopped");
}
/**
* JBoss MBean lifecycle operation.
*/
public void destroy() {
// not used
}
//
// ORDINARY OPERATIONS
//
public String dumpRoutingTable() {
String label="routing";
StringBuffer sb=new StringBuffer();
if(routingTable.isEmpty()) {
sb.append("empty ").append(label).append(" table");
}
else {
for(Iterator i=routingTable.keySet().iterator(); i.hasNext();) {
String gname=(String)i.next();
sb.append("GROUP: '" + gname + "'\n");
Map map=(Map)routingTable.get(gname);
if(map == null) {
sb.append("\tnull list of addresses\n");
}
else if(map.isEmpty()) {
sb.append("\tempty list of addresses\n");
}
else {
AddressEntry ae;
for(Iterator j=map.values().iterator(); j.hasNext();) {
ae=(AddressEntry)j.next();
sb.append('\t');
sb.append(ae);
sb.append('\n');
}
}
}
}
return sb.toString();
}
/**
* The main server loop. Runs on the JGroups Router Main Thread.
*/
private void mainLoop() {
Socket sock=null;
DataInputStream input=null;
DataOutputStream output=null;
Address peer_addr=null, mbr, logical_addr;
if(bindAddress == null) {
bindAddress=srvSock.getInetAddress();
}
printStartupInfo();
GossipData req;
String group;
while(up && srvSock != null) {
try {
sock=srvSock.accept();
if(linger_timeout > 0) {
int linger=Math.max(1, (int)(linger_timeout / 1000));
sock.setSoLinger(true, linger);
}
if(sock_read_timeout > 0) {
sock.setSoTimeout((int)sock_read_timeout);
}
input=new DataInputStream(sock.getInputStream());
// if(log.isTraceEnabled())
// log.trace("accepted connection from " + sock);
req=new GossipData();
req.readFrom(input);
switch(req.getType()) {
case GossipRouter.REGISTER:
mbr=req.getAddress();
group=req.getGroup();
if(log.isTraceEnabled())
log.trace("REGISTER(" + group + ", " + mbr + ")");
if(group == null || mbr == null) {
if(log.isErrorEnabled()) log.error("group or member is null, cannot register member");
}
else
addGossipEntry(group, mbr, new AddressEntry(mbr));
Util.close(input);
Util.close(sock);
break;
case GossipRouter.UNREGISTER:
mbr=req.getAddress();
group=req.getGroup();
if(log.isTraceEnabled())
log.trace("UNREGISTER(" + group + ", " + mbr + ")");
if(group == null || mbr == null) {
if(log.isErrorEnabled()) log.error("group or member is null, cannot unregister member");
}
else
removeGossipEntry(group, mbr);
Util.close(input);
Util.close(output);
Util.close(sock);
break;
case GossipRouter.GOSSIP_GET:
group=req.getGroup();
List mbrs=null;
Map map;
map=(Map)routingTable.get(group);
if(map != null) {
mbrs=new LinkedList(map.keySet());
}
if(log.isTraceEnabled())
log.trace("GOSSIP_GET(" + group + ") --> " + mbrs);
output=new DataOutputStream(sock.getOutputStream());
GossipData rsp=new GossipData(GossipRouter.GET_RSP, group, null, mbrs);
rsp.writeTo(output);
Util.close(input);
Util.close(output);
Util.close(sock);
break;
case GossipRouter.ROUTER_GET:
group=req.getGroup();
output=new DataOutputStream(sock.getOutputStream());
List ret=null;
map=(Map)routingTable.get(group);
if(map != null) {
ret=new LinkedList(map.keySet());
}
else
ret=new LinkedList();
if(log.isTraceEnabled())
log.trace("ROUTER_GET(" + group + ") --> " + ret);
rsp=new GossipData(GossipRouter.GET_RSP, group, null, ret);
rsp.writeTo(output);
Util.close(input);
Util.close(output);
Util.close(sock);
break;
case GossipRouter.DUMP:
output=new DataOutputStream(sock.getOutputStream());
output.writeUTF(dumpRoutingTable());
Util.close(input);
Util.close(output);
Util.close(sock);
break;
case GossipRouter.CONNECT:
sock.setSoTimeout(0); // we have to disable this here
output=new DataOutputStream(sock.getOutputStream());
peer_addr=new IpAddress(sock.getInetAddress(), sock.getPort());
logical_addr=req.getAddress();
String group_name=req.getGroup();
if(log.isTraceEnabled())
log.trace("CONNECT(" + group_name + ", " + logical_addr + ")");
SocketThread st=new SocketThread(sock, input, group_name, logical_addr);
addEntry(group_name, logical_addr, new AddressEntry(logical_addr, peer_addr, sock, st, output));
st.start();
break;
case GossipRouter.DISCONNECT:
Address addr=req.getAddress();
group_name=req.getGroup();
removeEntry(group_name, addr);
if(log.isTraceEnabled())
log.trace("DISCONNECT(" + group_name + ", " + addr + ")");
Util.close(input);
Util.close(output);
Util.close(sock);
break;
case GossipRouter.SHUTDOWN:
if(log.isInfoEnabled()) log.info("router shutting down");
Util.close(input);
Util.close(output);
Util.close(sock);
up=false;
break;
default:
if(log.isWarnEnabled())
log.warn("received unkown gossip request (gossip=" + req + ')');
break;
}
}
catch(Exception e) {
if(up)
if(log.isErrorEnabled()) log.error("failure handling a client request", e);
Util.close(input);
Util.close(output);
Util.close(sock);
}
}
}
/**
* Cleans the routing tables while the Router is going down.
*/
private void cleanup() {
// shutdown the routing threads and cleanup the tables
Map map;
for(Iterator i=routingTable.values().iterator(); i.hasNext();) {
map=(Map)i.next();
if(map != null) {
for(Iterator j=map.values().iterator(); j.hasNext();) {
AddressEntry e=(AddressEntry)j.next();
e.destroy();
}
}
}
routingTable.clear();
}
/**
* Connects to the ServerSocket and sends the shutdown header.
*/
private void shutdown() {
Socket s=null;
DataOutputStream dos=null;
try {
s=new Socket(srvSock.getInetAddress(),srvSock.getLocalPort());
dos=new DataOutputStream(s.getOutputStream());
dos.writeInt(SHUTDOWN);
dos.writeUTF("");
}
catch(Exception e) {
if(log.isErrorEnabled()) log.error("shutdown failed: " + e);
}
finally {
Util.close(s);
Util.close(dos);
}
}
/**
* Removes expired gossip entries (entries older than EXPIRY_TIME msec).
* @since 2.2.1
*/
private void sweep() {
long diff, currentTime=System.currentTimeMillis();
int num_entries_removed=0;
Map.Entry entry, entry2;
Map map;
AddressEntry ae;
for(Iterator it=routingTable.entrySet().iterator(); it.hasNext();) {
entry=(Map.Entry)it.next();
map=(Map)entry.getValue();
if(map == null || map.isEmpty()) {
it.remove();
continue;
}
for(Iterator it2=map.entrySet().iterator(); it2.hasNext();) {
entry2=(Map.Entry)it2.next();
ae=(GossipRouter.AddressEntry)entry2.getValue();
diff=currentTime - ae.timestamp;
if(diff > expiryTime) {
it2.remove();
if(log.isTraceEnabled())
log.trace("removed " + ae.logical_addr + " (" + diff + " msecs old)");
num_entries_removed++;
}
}
}
if(num_entries_removed > 0) {
if(log.isTraceEnabled()) log.trace("done (removed " + num_entries_removed + " entries)");
}
}
private void route(Address dest, String dest_group, byte[] msg, Address sender) {
//if(log.isTraceEnabled()) {
// int len=msg != null? msg.length : 0;
//log.trace("routing request from " + sender + " for " + dest_group + " to " +
// (dest == null? "ALL" : dest.toString()) + ", " + len + " bytes");
//}
if(dest == null) { // send to all members in group dest.getChannelName()
if(dest_group == null) {
if(log.isErrorEnabled()) log.error("both dest address and group are null");
}
else {
sendToAllMembersInGroup(dest_group, msg, sender);
}
}
else {
// send to destination address
AddressEntry ae=findAddressEntry(dest_group, dest);
if(ae == null) {
if(log.isTraceEnabled())
log.trace("cannot find " + dest + " in the routing table, \nrouting table=\n" + dumpRoutingTable());
return;
}
if(ae.output == null) {
if(log.isErrorEnabled()) log.error(dest + " is associated with a null output stream");
return;
}
try {
sendToMember(dest, ae.output, msg, sender);
}
catch(Exception e) {
if(log.isErrorEnabled()) log.error("failed sending message to " + dest + ": " + e.getMessage());
removeEntry(dest_group, dest); // will close socket
}
}
}
private void addEntry(String groupname, Address logical_addr, AddressEntry entry) {
addEntry(groupname, logical_addr, entry, false);
}
/**
* Adds a new member to the routing group.
*/
private void addEntry(String groupname, Address logical_addr, AddressEntry entry, boolean update_only) {
if(groupname == null || logical_addr == null) {
if(log.isErrorEnabled()) log.error("groupname or logical_addr was null, entry was not added");
return;
}
synchronized(routingTable) {
Map mbrs=(Map)routingTable.get(groupname);
if(mbrs == null) {
mbrs=new ConcurrentHashMap();
mbrs.put(logical_addr, entry);
routingTable.put(groupname, mbrs);
}
else {
AddressEntry tmp=(AddressEntry)mbrs.get(logical_addr);
if(tmp != null) { // already present
if(update_only) {
tmp.update();
return;
}
tmp.destroy();
}
mbrs.put(logical_addr, entry);
}
}
}
private void removeEntry(String groupname, Address logical_addr) {
Map val;
val=(Map)routingTable.get(groupname);
if(val == null)
return;
synchronized(val) {
AddressEntry entry=(AddressEntry)val.get(logical_addr);
if(entry != null) {
entry.destroy();
val.remove(logical_addr);
}
}
}
/**
* @return null if not found
*/
private AddressEntry findAddressEntry(String group_name, Address logical_addr) {
if(group_name == null || logical_addr == null)
return null;
Map val=(Map)routingTable.get(group_name);
if(val == null)
return null;
return (AddressEntry)val.get(logical_addr);
}
/**
* Adds a new member to the group in the gossip table or renews the
* membership where is the case.
* @since 2.2.1
*/
private void addGossipEntry(String groupname, Address logical_addr, AddressEntry e) {
addEntry(groupname, logical_addr, e, true);
}
private void removeGossipEntry(String groupname, Address mbr) {
removeEntry(groupname, mbr);
}
private void sendToAllMembersInGroup(String groupname, byte[] msg, Address sender) {
Map val;
val=(Map)routingTable.get(groupname);
if(val == null || val.isEmpty())
return;
Map.Entry tmp;
AddressEntry entry;
synchronized(val) {
for(Iterator i=val.entrySet().iterator(); i.hasNext();) {
tmp=(Map.Entry)i.next();
entry=(GossipRouter.AddressEntry)tmp.getValue();
DataOutputStream dos=entry.output;
if(dos != null) {
// send only to 'connected' members
try {
sendToMember(null, dos, msg, sender);
}
catch(Exception e) {
if(log.isWarnEnabled()) log.warn("cannot send to " + entry.logical_addr + ": " + e.getMessage());
entry.destroy(); // this closes the socket
i.remove();
}
}
}
}
}
/**
* @throws IOException
*/
private void sendToMember(Address dest, DataOutputStream out, byte[] msg, Address sender) throws IOException {
if(out == null)
return;
if(discard_loopbacks && dest != null && dest.equals(sender)) {
return;
}
synchronized(out) {
Util.writeAddress(dest, out);
out.writeInt(msg.length);
out.write(msg, 0, msg.length);
}
}
/**
* Prints startup information.
*/
private void printStartupInfo() {
System.out.println("GossipRouter started at " + new Date());
System.out.print("Listening on port " + port);
System.out.println(" bound on address " + bindAddress);
System.out.print("Backlog is " + backlog);
System.out.print(", linger timeout is " + linger_timeout);
System.out.println(", and read timeout is " + sock_read_timeout);
}
/**
* Class used to store Addresses in both routing and gossip tables.
* If it is used for routing, sock and output have valid values, otherwise
* they're null and only the timestamp counts.
*/
class AddressEntry {
Address logical_addr=null, physical_addr=null;
Socket sock=null;
DataOutputStream output=null;
long timestamp=0;
final SocketThread thread;
/**
* AddressEntry for a 'gossip' membership.
*/
public AddressEntry(Address addr) {
this(addr, null, null, null, null);
}
public AddressEntry(Address logical_addr, Address physical_addr, Socket sock, SocketThread thread, DataOutputStream output) {
this.logical_addr=logical_addr;
this.physical_addr=physical_addr;
this.sock=sock;
this.thread=thread;
this.output=output;
this.timestamp=System.currentTimeMillis();
}
void destroy() {
if(thread != null) {
thread.finish();
}
Util.close(output);
output=null;
Util.close(sock);
sock=null;
timestamp=0;
}
public void update() {
timestamp=System.currentTimeMillis();
}
public boolean equals(Object other) {
return logical_addr.equals(((AddressEntry)other).logical_addr);
}
public String toString() {
StringBuffer sb=new StringBuffer("logical addr=");
sb.append(logical_addr).append(" (").append(physical_addr).append(")");
//if(sock != null) {
// sb.append(", sock=");
//sb.append(sock);
//}
if(timestamp > 0) {
long diff=System.currentTimeMillis() - timestamp;
sb.append(", ").append(diff).append(" ms old");
}
return sb.toString();
}
}
private static int threadCounter=0;
/**
* A SocketThread manages one connection to a client. Its main task is message routing.
*/
class SocketThread extends Thread {
private volatile boolean active=true;
Socket sock=null;
DataInputStream input=null;
Address logical_addr=null;
String group_name=null;
public SocketThread(Socket sock, DataInputStream ois, String group_name, Address logical_addr) {
super(Util.getGlobalThreadGroup(), "SocketThread " + (threadCounter++));
this.sock=sock;
input=ois;
this.group_name=group_name;
this.logical_addr=logical_addr;
}
void closeSocket() {
Util.close(input);
Util.close(sock);
}
void finish() {
active=false;
}
public void run() {
byte[] buf;
int len;
Address dst_addr=null;
String gname;
while(active) {
try {
// 1. Group name is first
gname=input.readUTF();
// 2. Second is the destination address
dst_addr=Util.readAddress(input);
// 3. Then the length of the byte buffer representing the message
len=input.readInt();
if(len == 0) {
if(log.isWarnEnabled()) log.warn("received null message");
continue;
}
// 4. Finally the message itself, as a byte buffer
buf=new byte[len];
input.readFully(buf, 0, buf.length); // message
}
catch(Exception io_ex) {
if(log.isTraceEnabled())
log.trace(sock.getInetAddress().getHostName() + ':' + sock.getPort() +
" closed connection; removing it from routing table");
removeEntry(group_name, logical_addr); // will close socket
return;
}
try {
route(dst_addr, gname, buf, logical_addr);
}
catch(Exception e) {
if(log.isErrorEnabled()) log.error("failed routing request to " + dst_addr, e);
break;
}
}
closeSocket();
}
}
public static void main(String[] args) throws Exception {
String arg;
int port=12001;
long expiry=GossipRouter.EXPIRY_TIME;
long timeout=GossipRouter.GOSSIP_REQUEST_TIMEOUT;
long routingTimeout=GossipRouter.ROUTING_CLIENT_REPLY_TIMEOUT;
int backlog = 0;
long soLinger = -1;
long soTimeout = -1;
GossipRouter router=null;
String bind_addr=null;
for(int i=0; i < args.length; i++) {
arg=args[i];
if("-port".equals(arg)) {
port=Integer.parseInt(args[++i]);
continue;
}
if("-bindaddress".equals(arg) || "-bind_addr".equals(arg)) {
bind_addr=args[++i];
continue;
}
if ("-backlog".equals(arg)) {
backlog=Integer.parseInt(args[++i]);
continue;
}
if("-expiry".equals(arg)) {
expiry=Long.parseLong(args[++i]);
continue;
}
// this option is not used and should be deprecated/removed
// in a future release
if("-timeout".equals(arg)) {
timeout=Long.parseLong(args[++i]);
continue;
}
// this option is not used and should be deprecated/removed
// in a future release
if("-rtimeout".equals(arg)) {
routingTimeout=Long.parseLong(args[++i]);
continue;
}
if ("-solinger".equals(arg)) {
soLinger=Long.parseLong(args[++i]);
continue;
}
if ("-sotimeout".equals(arg)) {
soTimeout=Long.parseLong(args[++i]);
continue;
}
help();
return;
}
System.out.println("GossipRouter is starting...");
try {
ClassConfigurator.getInstance(true);
router=new GossipRouter(port, bind_addr, expiry, timeout, routingTimeout);
if (backlog > 0)
router.setBacklog(backlog);
if (soTimeout >= 0)
router.setSocketReadTimeout(soTimeout);
if (soLinger >= 0)
router.setLingerTimeout(soLinger);
router.start();
}
catch(Exception e) {
System.err.println(e);
}
}
static void help() {
System.out.println();
System.out.println("GossipRouter [-port <port>] [-bind_addr <address>] [options]");
System.out.println();
System.out.println("Options:");
System.out.println();
// -timeout isn't used and should be deprecated/removed
// in a future release. It's left in this code for backwards
// compatability, but it is no longer advertized.
//System.out.println(" -timeout <msecs> - Number of millisecs the router waits to receive");
//System.out.println(" a gossip request after connection was established;");
//System.out.println(" upon expiration, the router initiates the routing");
//System.out.println(" protocol on the connection.");
System.out.println(" -backlog <backlog> - Max queue size of backlogged connections. Must be");
System.out.println(" greater than zero or the default of 1000 will be");
System.out.println(" used.");
System.out.println();
System.out.println(" -expiry <msecs> - Time until a gossip cache entry expires. 30000 is");
System.out.println(" the default value.");
System.out.println();
System.out.println(" -solinger <msecs> - Time for setting SO_LINGER on connections. 0");
System.out.println(" means do not set SO_LINGER. Must be greater than");
System.out.println(" or equal to zero or the default of 2000 will be");
System.out.println(" used.");
System.out.println();
System.out.println(" -sotimeout <msecs> - Time for setting SO_TIMEOUT on connections. 0");
System.out.println(" means don't set SO_TIMEOUT. Must be greater than");
System.out.println(" or equal to zero or the default of 3000 will be");
System.out.println(" used.");
System.out.println();
}
}