PageRenderTime 26ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

/projects/jgroups-2.10.0/src/org/jgroups/stack/GossipRouter.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 1002 lines | 807 code | 132 blank | 63 comment | 164 complexity | 056c92ae030d3704be07e05c8673812c MD5 | raw file
  1. package org.jgroups.stack;
  2. import org.jgroups.Address;
  3. import org.jgroups.PhysicalAddress;
  4. import org.jgroups.protocols.PingData;
  5. import org.jgroups.annotations.ManagedAttribute;
  6. import org.jgroups.annotations.ManagedOperation;
  7. import org.jgroups.annotations.Property;
  8. import org.jgroups.jmx.JmxConfigurator;
  9. import org.jgroups.logging.Log;
  10. import org.jgroups.logging.LogFactory;
  11. import org.jgroups.util.*;
  12. import org.jgroups.util.UUID;
  13. import javax.management.MBeanServer;
  14. import java.io.DataInputStream;
  15. import java.io.DataOutputStream;
  16. import java.io.EOFException;
  17. import java.io.IOException;
  18. import java.net.InetAddress;
  19. import java.net.ServerSocket;
  20. import java.net.Socket;
  21. import java.net.SocketTimeoutException;
  22. import java.util.*;
  23. import java.util.Map.Entry;
  24. import java.util.concurrent.ConcurrentHashMap;
  25. import java.util.concurrent.ConcurrentMap;
  26. import java.util.concurrent.CopyOnWriteArrayList;
  27. import java.util.concurrent.atomic.AtomicBoolean;
  28. /**
  29. * Router for TCP based group comunication (using layer TCP instead of UDP). Instead of the TCP
  30. * layer sending packets point-to-point to each other member, it sends the packet to the router
  31. * which - depending on the target address - multicasts or unicasts it to the group / or single member.
  32. * <p/>
  33. * This class is especially interesting for applets which cannot directly make connections (neither
  34. * UDP nor TCP) to a host different from the one they were loaded from. Therefore, an applet would
  35. * create a normal channel plus protocol stack, but the bottom layer would have to be the TCP layer
  36. * which sends all packets point-to-point (over a TCP connection) to the router, which in turn
  37. * forwards them to their end location(s) (also over TCP). A centralized router would therefore have
  38. * to be running on the host the applet was loaded from.
  39. * <p/>
  40. * An alternative for running JGroups in an applet (IP multicast is not allows in applets as of
  41. * 1.2), is to use point-to-point UDP communication via the gossip server. However, then the appplet
  42. * has to be signed which involves additional administrative effort on the part of the user.
  43. * <p/>
  44. * Note that a GossipRouter is also a good way of running JGroups in Amazon's EC2 environment which (as of summer 09)
  45. * doesn't support IP multicasting.
  46. * @author Bela Ban
  47. * @author Vladimir Blagojevic
  48. * @author Ovidiu Feodorov <ovidiuf@users.sourceforge.net>
  49. * @version $Id: GossipRouter.java,v 1.76 2010/06/17 09:20:45 belaban Exp $
  50. * @since 2.1.1
  51. */
  52. public class GossipRouter {
  53. public static final byte CONNECT=1; // CONNECT(group, addr) --> local address
  54. public static final byte DISCONNECT=2; // DISCONNECT(group, addr)
  55. public static final byte GOSSIP_GET=4; // GET(group) --> List<addr> (members)
  56. public static final byte MESSAGE=10;
  57. public static final byte SUSPECT=11;
  58. public static final byte PING=12;
  59. public static final byte CLOSE=13;
  60. public static final byte CONNECT_OK=14;
  61. public static final byte OP_FAIL=15;
  62. public static final byte DISCONNECT_OK=16;
  63. public static final int PORT=12001;
  64. @ManagedAttribute(description="server port on which the GossipRouter accepts client connections", writable=true)
  65. private int port;
  66. @ManagedAttribute(description="address to which the GossipRouter should bind", writable=true, name="bindAddress")
  67. private String bindAddressString;
  68. @ManagedAttribute(description="time (in msecs) until gossip entry expires", writable=true)
  69. private long expiryTime=0;
  70. // Maintains associations between groups and their members
  71. private final ConcurrentMap<String, ConcurrentMap<Address, ConnectionHandler>> routingTable=new ConcurrentHashMap<String, ConcurrentMap<Address, ConnectionHandler>>();
  72. /**
  73. * Store physical address(es) associated with a logical address. Used mainly by TCPGOSSIP
  74. */
  75. private final Map<Address, Set<PhysicalAddress>> address_mappings=new ConcurrentHashMap<Address,Set<PhysicalAddress>>();
  76. private ServerSocket srvSock=null;
  77. private InetAddress bindAddress=null;
  78. @Property(description="Time (in ms) for setting SO_LINGER on sockets returned from accept(). 0 means do not set SO_LINGER")
  79. private long linger_timeout=2000L;
  80. @Property(description="Time (in ms) for SO_TIMEOUT on sockets returned from accept(). 0 means don't set SO_TIMEOUT")
  81. private long sock_read_timeout=0L;
  82. @Property(description="The max queue size of backlogged connections")
  83. private int backlog=1000;
  84. private final AtomicBoolean running = new AtomicBoolean(false);
  85. @ManagedAttribute(description="whether to discard message sent to self", writable=true)
  86. private boolean discard_loopbacks=false;
  87. protected List<ConnectionTearListener> connectionTearListeners=new CopyOnWriteArrayList<ConnectionTearListener>();
  88. protected ThreadFactory default_thread_factory=new DefaultThreadFactory(Util.getGlobalThreadGroup(), "gossip-handlers", true, true);
  89. protected Timer timer=null;
  90. protected final Log log=LogFactory.getLog(this.getClass());
  91. private boolean jmx=false;
  92. private boolean registered=false;
  93. public GossipRouter() {
  94. this(PORT);
  95. }
  96. public GossipRouter(int port) {
  97. this(port, null);
  98. }
  99. public GossipRouter(int port, String bindAddressString) {
  100. this(port,bindAddressString,false,0);
  101. }
  102. public GossipRouter(int port, String bindAddressString, boolean jmx) {
  103. this(port, bindAddressString,jmx,0);
  104. }
  105. public GossipRouter(int port, String bindAddressString, boolean jmx, long expiryTime) {
  106. this.port = port;
  107. this.bindAddressString = bindAddressString;
  108. this.jmx = jmx;
  109. this.expiryTime = expiryTime;
  110. this.connectionTearListeners.add(new FailureDetectionListener());
  111. }
  112. public void setPort(int port) {
  113. this.port=port;
  114. }
  115. public int getPort() {
  116. return port;
  117. }
  118. public void setBindAddress(String bindAddress) {
  119. bindAddressString=bindAddress;
  120. }
  121. public String getBindAddress() {
  122. return bindAddressString;
  123. }
  124. public int getBacklog() {
  125. return backlog;
  126. }
  127. public void setBacklog(int backlog) {
  128. this.backlog=backlog;
  129. }
  130. public void setExpiryTime(long expiryTime) {
  131. this.expiryTime = expiryTime;
  132. }
  133. public long getExpiryTime() {
  134. return expiryTime;
  135. }
  136. @Deprecated
  137. public void setGossipRequestTimeout(long gossipRequestTimeout) {
  138. }
  139. @Deprecated
  140. public static long getGossipRequestTimeout() {
  141. return 0;
  142. }
  143. @Deprecated
  144. public void setRoutingClientReplyTimeout(long routingClientReplyTimeout) {
  145. }
  146. @Deprecated
  147. public static long getRoutingClientReplyTimeout() {
  148. return 0;
  149. }
  150. @ManagedAttribute(description="status")
  151. public boolean isStarted() {
  152. return isRunning();
  153. }
  154. public boolean isDiscardLoopbacks() {
  155. return discard_loopbacks;
  156. }
  157. public void setDiscardLoopbacks(boolean discard_loopbacks) {
  158. this.discard_loopbacks=discard_loopbacks;
  159. }
  160. public long getLingerTimeout() {
  161. return linger_timeout;
  162. }
  163. public void setLingerTimeout(long linger_timeout) {
  164. this.linger_timeout=linger_timeout;
  165. }
  166. public long getSocketReadTimeout() {
  167. return sock_read_timeout;
  168. }
  169. public void setSocketReadTimeout(long sock_read_timeout) {
  170. this.sock_read_timeout=sock_read_timeout;
  171. }
  172. public ThreadFactory getDefaultThreadPoolThreadFactory() {
  173. return default_thread_factory;
  174. }
  175. public static String type2String(int type) {
  176. switch (type) {
  177. case CONNECT:
  178. return "CONNECT";
  179. case DISCONNECT:
  180. return "DISCONNECT";
  181. case GOSSIP_GET:
  182. return "GOSSIP_GET";
  183. case MESSAGE:
  184. return "MESSAGE";
  185. case SUSPECT:
  186. return "SUSPECT";
  187. case PING:
  188. return "PING";
  189. case CLOSE:
  190. return "CLOSE";
  191. case CONNECT_OK:
  192. return "CONNECT_OK";
  193. case DISCONNECT_OK:
  194. return "DISCONNECT_OK";
  195. case OP_FAIL:
  196. return "OP_FAIL";
  197. default:
  198. return "unknown (" + type + ")";
  199. }
  200. }
  201. /**
  202. * Lifecycle operation. Called after create(). When this method is called, the managed attributes
  203. * have already been set.<br>
  204. * Brings the Router into a fully functional state.
  205. */
  206. @ManagedOperation(description="Lifecycle operation. Called after create(). When this method is called, "
  207. + "the managed attributes have already been set. Brings the Router into a fully functional state.")
  208. public void start() throws Exception {
  209. if(running.compareAndSet(false, true)) {
  210. if(jmx && !registered) {
  211. MBeanServer server=Util.getMBeanServer();
  212. JmxConfigurator.register(this, server, "jgroups:name=GossipRouter");
  213. registered=true;
  214. }
  215. if(bindAddressString != null) {
  216. bindAddress=InetAddress.getByName(bindAddressString);
  217. srvSock=new ServerSocket(port, backlog, bindAddress);
  218. }
  219. else {
  220. srvSock=new ServerSocket(port, backlog);
  221. }
  222. Runtime.getRuntime().addShutdownHook(new Thread() {
  223. public void run() {
  224. GossipRouter.this.stop();
  225. }
  226. });
  227. // start the main server thread
  228. new Thread(new Runnable() {
  229. public void run() {
  230. mainLoop();
  231. }
  232. }, "GossipRouter").start();
  233. long expiryTime = getExpiryTime();
  234. if (expiryTime > 0) {
  235. timer = new Timer(true);
  236. timer.schedule(new TimerTask() {
  237. public void run() {
  238. sweep();
  239. }
  240. }, expiryTime, expiryTime);
  241. }
  242. } else {
  243. throw new Exception("Router already started.");
  244. }
  245. }
  246. /**
  247. * Always called before destroy(). Close connections and frees resources.
  248. */
  249. @ManagedOperation(description="Always called before destroy(). Closes connections and frees resources")
  250. public void stop() {
  251. if(running.compareAndSet(true, false)){
  252. Util.close(srvSock);
  253. clear();
  254. if(log.isInfoEnabled())
  255. log.info("router stopped");
  256. }
  257. }
  258. @ManagedOperation(description="Closes all connections and clears routing table (leave the server socket open)")
  259. public void clear() {
  260. if(running.get()) {
  261. for(ConcurrentMap<Address,ConnectionHandler> map: routingTable.values()) {
  262. for(ConnectionHandler ce: map.values())
  263. ce.close();
  264. }
  265. routingTable.clear();
  266. }
  267. }
  268. public void destroy() {
  269. }
  270. @ManagedAttribute(description="operational status", name="running")
  271. public boolean isRunning() {
  272. return running.get();
  273. }
  274. @ManagedOperation(description="dumps the contents of the routing table")
  275. public String dumpRoutingTable() {
  276. String label="routing";
  277. StringBuilder sb=new StringBuilder();
  278. if(routingTable.isEmpty()) {
  279. sb.append("empty ").append(label).append(" table");
  280. }
  281. else {
  282. boolean first=true;
  283. for(Map.Entry<String, ConcurrentMap<Address, ConnectionHandler>> entry : routingTable.entrySet()) {
  284. String gname=entry.getKey();
  285. if(!first)
  286. sb.append("\n");
  287. else
  288. first=false;
  289. sb.append(gname + ": ");
  290. Map<Address,ConnectionHandler> map=entry.getValue();
  291. if(map == null || map.isEmpty()) {
  292. sb.append("null");
  293. }
  294. else {
  295. sb.append(Util.printListWithDelimiter(map.keySet(), ", "));
  296. }
  297. }
  298. }
  299. return sb.toString();
  300. }
  301. @ManagedOperation(description="dumps the contents of the routing table")
  302. public String dumpRoutingTableDetailed() {
  303. String label="routing";
  304. StringBuilder sb=new StringBuilder();
  305. if(routingTable.isEmpty()) {
  306. sb.append("empty ").append(label).append(" table");
  307. }
  308. else {
  309. boolean first=true;
  310. for(Map.Entry<String, ConcurrentMap<Address, ConnectionHandler>> entry : routingTable.entrySet()) {
  311. String gname=entry.getKey();
  312. if(!first)
  313. sb.append("\n");
  314. else
  315. first=false;
  316. sb.append(gname + ":\n");
  317. Map<Address,ConnectionHandler> map=entry.getValue();
  318. if(map == null || map.isEmpty()) {
  319. sb.append("null");
  320. }
  321. else {
  322. for(Map.Entry<Address,ConnectionHandler> en: map.entrySet()) {
  323. sb.append(en.getKey() + ": ");
  324. ConnectionHandler handler=en.getValue();
  325. sb.append("sock=" +handler.sock).append("\n");
  326. }
  327. }
  328. sb.append("\n");
  329. }
  330. }
  331. return sb.toString();
  332. }
  333. @ManagedOperation(description="dumps the mappings between logical and physical addresses")
  334. public String dumpAddresssMappings() {
  335. StringBuilder sb=new StringBuilder();
  336. for(Map.Entry<Address,Set<PhysicalAddress>> entry: address_mappings.entrySet()) {
  337. sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n");
  338. }
  339. return sb.toString();
  340. }
  341. private void mainLoop() {
  342. if(bindAddress == null)
  343. bindAddress=srvSock.getInetAddress();
  344. printStartupInfo();
  345. while(isRunning()) {
  346. Socket sock=null;
  347. try {
  348. sock=srvSock.accept();
  349. if(linger_timeout > 0) {
  350. int linger=Math.max(1, (int)(linger_timeout / 1000));
  351. sock.setSoLinger(true, linger);
  352. }
  353. if(sock_read_timeout > 0)
  354. sock.setSoTimeout((int)sock_read_timeout);
  355. if(log.isDebugEnabled())
  356. log.debug("Accepted connection, socket is " + sock);
  357. ConnectionHandler ch=new ConnectionHandler(sock);
  358. getDefaultThreadPoolThreadFactory().newThread(ch).start();
  359. }
  360. catch(IOException e) {
  361. //only consider this exception if GR is not shutdown
  362. if(isRunning()) {
  363. log.error("failure handling connection from " + sock, e);
  364. Util.close(sock);
  365. }
  366. }
  367. }
  368. }
  369. /**
  370. * Removes expired gossip entries (entries older than EXPIRY_TIME msec).
  371. * @since 2.2.1
  372. */
  373. private void sweep() {
  374. long diff, currentTime = System.currentTimeMillis();
  375. List <ConnectionHandler> victims = new ArrayList<ConnectionHandler>();
  376. for (Iterator<Entry<String, ConcurrentMap<Address, ConnectionHandler>>> it = routingTable.entrySet().iterator(); it.hasNext();) {
  377. Map<Address, ConnectionHandler> map = it.next().getValue();
  378. if (map == null || map.isEmpty()) {
  379. it.remove();
  380. continue;
  381. }
  382. for (Iterator<Entry<Address, ConnectionHandler>> it2 = map.entrySet().iterator(); it2.hasNext();) {
  383. ConnectionHandler ch = it2.next().getValue();
  384. diff = currentTime - ch.timestamp;
  385. if (diff > expiryTime) {
  386. victims.add(ch);
  387. }
  388. }
  389. }
  390. for (ConnectionHandler v : victims) {
  391. v.close();
  392. }
  393. }
  394. private void route(Address dest, String group, byte[] msg) {
  395. if(dest == null) { // send to all members in group
  396. if(group == null) {
  397. if(log.isErrorEnabled())
  398. log.error("group is null");
  399. }
  400. else {
  401. sendToAllMembersInGroup(group, msg);
  402. }
  403. }
  404. else { // send unicast
  405. ConnectionHandler handler=findAddressEntry(group, dest);
  406. if(handler == null) {
  407. if(log.isTraceEnabled())
  408. log.trace("cannot find " + dest + " in the routing table, \nrouting table=\n" + dumpRoutingTable());
  409. return;
  410. }
  411. if(handler.output == null) {
  412. if(log.isErrorEnabled())
  413. log.error(dest + " is associated with a null output stream");
  414. return;
  415. }
  416. try {
  417. sendToMember(dest, handler.output, msg);
  418. }
  419. catch(Exception e) {
  420. if(log.isErrorEnabled())
  421. log.error("failed sending message to " + dest + ": " + e.getMessage());
  422. removeEntry(group, dest); // will close socket
  423. }
  424. }
  425. }
  426. private void removeEntry(String group, Address addr) {
  427. // Remove from routing table
  428. ConcurrentMap<Address, ConnectionHandler> map;
  429. if(group != null) {
  430. map=routingTable.get(group);
  431. if(map != null && map.remove(addr) != null) {
  432. if(log.isTraceEnabled())
  433. log.trace("Removed " +addr + " from group " + group);
  434. if(map.isEmpty()) {
  435. routingTable.remove(group);
  436. if(log.isTraceEnabled())
  437. log.trace("Removed group " + group);
  438. }
  439. }
  440. }
  441. else {
  442. for(Map.Entry<String,ConcurrentMap<Address,ConnectionHandler>> entry: routingTable.entrySet()) {
  443. map=entry.getValue();
  444. if(map != null && map.remove(addr) != null && map.isEmpty()) {
  445. routingTable.remove(entry.getKey());
  446. if(log.isTraceEnabled())
  447. log.trace("Removed " + entry.getKey() + " from group " + group);
  448. }
  449. }
  450. }
  451. address_mappings.remove(addr);
  452. if(addr instanceof UUID)
  453. UUID.remove((UUID)addr);
  454. }
  455. /**
  456. * @return null if not found
  457. */
  458. private ConnectionHandler findAddressEntry(String group, Address addr) {
  459. if(group == null || addr == null)
  460. return null;
  461. ConcurrentMap<Address,ConnectionHandler> map=routingTable.get(group);
  462. if(map == null)
  463. return null;
  464. return map.get(addr);
  465. }
  466. private void sendToAllMembersInGroup(String group, byte[] msg) {
  467. final ConcurrentMap<Address,ConnectionHandler> map=routingTable.get(group);
  468. if(map == null || map.isEmpty()) {
  469. if(log.isWarnEnabled())
  470. log.warn("didn't find any members for group " + group);
  471. return;
  472. }
  473. synchronized(map) {
  474. for(Map.Entry<Address,ConnectionHandler> entry: map.entrySet()) {
  475. ConnectionHandler handler=entry.getValue();
  476. DataOutputStream dos=handler.output;
  477. if(dos != null) {
  478. try {
  479. sendToMember(null, dos, msg);
  480. }
  481. catch(Exception e) {
  482. if(log.isWarnEnabled())
  483. log.warn("cannot send to " + entry.getKey() + ": " + e.getMessage());
  484. }
  485. }
  486. }
  487. }
  488. }
  489. private static void sendToMember(Address dest, final DataOutputStream out, byte[] msg) throws IOException {
  490. if(out == null)
  491. return;
  492. synchronized(out) {
  493. GossipData request=new GossipData(GossipRouter.MESSAGE, null, dest, msg);
  494. request.writeTo(out);
  495. out.flush();
  496. }
  497. }
  498. private void notifyAbnormalConnectionTear(final ConnectionHandler ch, final Exception e) {
  499. for (ConnectionTearListener l : connectionTearListeners) {
  500. l.connectionTorn(ch, e);
  501. }
  502. }
  503. public interface ConnectionTearListener {
  504. public void connectionTorn(ConnectionHandler ch, Exception e);
  505. }
  506. /*
  507. * https://jira.jboss.org/jira/browse/JGRP-902
  508. */
  509. class FailureDetectionListener implements ConnectionTearListener {
  510. public void connectionTorn(ConnectionHandler ch, Exception e) {
  511. Set<String> groups = ch.known_groups;
  512. for (String group : groups) {
  513. if(group == null)
  514. continue;
  515. Map<Address, ConnectionHandler> map = routingTable.get(group);
  516. if (map != null && !map.isEmpty()) {
  517. for (Iterator<Entry<Address, ConnectionHandler>> i = map.entrySet().iterator(); i.hasNext();) {
  518. ConnectionHandler entry = i.next().getValue();
  519. DataOutputStream stream = entry.output;
  520. try {
  521. for (Address a : ch.logical_addrs) {
  522. GossipData suspect = new GossipData(GossipRouter.SUSPECT);
  523. suspect.writeTo(stream);
  524. Util.writeAddress(a, stream);
  525. stream.flush();
  526. }
  527. } catch (Exception ioe) {
  528. // intentionally ignored
  529. }
  530. }
  531. }
  532. }
  533. }
  534. }
  535. /**
  536. * Prints startup information.
  537. */
  538. private void printStartupInfo() {
  539. System.out.println("GossipRouter started at " + new Date());
  540. System.out.print("Listening on port " + port);
  541. System.out.println(" bound on address " + bindAddress);
  542. System.out.print("Backlog is " + backlog);
  543. System.out.print(", linger timeout is " + linger_timeout);
  544. System.out.println(", and read timeout is " + sock_read_timeout);
  545. }
  546. /**
  547. * Handles the requests from a client (RouterStub)
  548. */
  549. class ConnectionHandler implements Runnable {
  550. private final AtomicBoolean active = new AtomicBoolean(false);
  551. private final Socket sock;
  552. private final DataOutputStream output;
  553. private final DataInputStream input;
  554. private final List<Address> logical_addrs=new ArrayList<Address>();
  555. Set<String> known_groups = new HashSet<String>();
  556. private long timestamp;
  557. public ConnectionHandler(Socket sock) throws IOException {
  558. this.sock=sock;
  559. this.input=new DataInputStream(sock.getInputStream());
  560. this.output=new DataOutputStream(sock.getOutputStream());
  561. }
  562. void close() {
  563. if(active.compareAndSet(true, false)) {
  564. if(log.isDebugEnabled())
  565. log.debug(this + " is being closed");
  566. Util.close(input);
  567. Util.close(output);
  568. Util.close(sock);
  569. for(Address addr: logical_addrs) {
  570. removeEntry(null, addr);
  571. }
  572. }
  573. }
  574. public void run() {
  575. if(active.compareAndSet(false, true)) {
  576. try {
  577. if(log.isDebugEnabled())
  578. log.debug(this + " entering receive loop");
  579. readLoop();
  580. }
  581. finally {
  582. close();
  583. }
  584. }
  585. }
  586. public boolean isRunning() {
  587. return active.get();
  588. }
  589. private void readLoop() {
  590. while(isRunning()) {
  591. GossipData request;
  592. Address addr;
  593. String group;
  594. try {
  595. request=new GossipData();
  596. request.readFrom(input);
  597. byte command=request.getType();
  598. addr=request.getAddress();
  599. group=request.getGroup();
  600. known_groups.add(group);
  601. timestamp = System.currentTimeMillis();
  602. if(log.isTraceEnabled())
  603. log.trace(this + " received " + request);
  604. switch(command) {
  605. case GossipRouter.CONNECT:
  606. handleConnect(request, addr, group);
  607. break;
  608. case GossipRouter.PING:
  609. // do nothing here - client doesn't expect response data
  610. break;
  611. case GossipRouter.MESSAGE:
  612. if(request.buffer == null || request.buffer.length == 0) {
  613. if(log.isWarnEnabled())
  614. log.warn(this +" received null message");
  615. break;
  616. }
  617. try {
  618. route(addr, request.getGroup(), request.getBuffer());
  619. }
  620. catch(Exception e) {
  621. if(log.isErrorEnabled())
  622. log.error(this +" failed in routing request to " + addr, e);
  623. }
  624. break;
  625. case GossipRouter.GOSSIP_GET:
  626. Set<PhysicalAddress> physical_addrs;
  627. List<PingData> mbrs=new ArrayList<PingData>();
  628. ConcurrentMap<Address,ConnectionHandler> map=routingTable.get(group);
  629. if(map != null) {
  630. for(Address logical_addr: map.keySet()) {
  631. physical_addrs=address_mappings.get(logical_addr);
  632. PingData rsp=new PingData(logical_addr, null, true, UUID.get(logical_addr),
  633. physical_addrs != null? new ArrayList<PhysicalAddress>(physical_addrs) : null);
  634. mbrs.add(rsp);
  635. }
  636. }
  637. output.writeShort(mbrs.size());
  638. for(PingData data: mbrs)
  639. data.writeTo(output);
  640. output.flush();
  641. if(log.isDebugEnabled())
  642. log.debug(this + " responded to GOSSIP_GET with " + mbrs);
  643. break;
  644. case GossipRouter.DISCONNECT:
  645. try {
  646. removeEntry(group, addr);
  647. sendData(new GossipData(DISCONNECT_OK));
  648. if(log.isDebugEnabled())
  649. log.debug(this + " disconnect completed");
  650. }
  651. catch(Exception e) {
  652. sendData(new GossipData(OP_FAIL));
  653. }
  654. break;
  655. case GossipRouter.CLOSE:
  656. close();
  657. break;
  658. case -1: // EOF
  659. notifyAbnormalConnectionTear(this, new EOFException("Connection broken"));
  660. break;
  661. }
  662. if(log.isTraceEnabled())
  663. log.trace(this + " processed " + request);
  664. }
  665. catch(SocketTimeoutException ste) {
  666. }
  667. catch(IOException ioex) {
  668. notifyAbnormalConnectionTear(this, ioex);
  669. break;
  670. }
  671. catch(Exception ex) {
  672. if (active.get()) {
  673. if (log.isWarnEnabled())
  674. log.warn("Exception in ConnectionHandler thread", ex);
  675. }
  676. break;
  677. }
  678. }
  679. }
  680. private void handleConnect(GossipData request, Address addr, String group) throws Exception {
  681. ConcurrentMap<Address, ConnectionHandler> map = null;
  682. try {
  683. checkExistingConnection(addr,group);
  684. String logical_name = request.getLogicalName();
  685. if (logical_name != null && addr instanceof org.jgroups.util.UUID)
  686. org.jgroups.util.UUID.add((org.jgroups.util.UUID) addr, logical_name);
  687. // group name, logical address, logical name, physical addresses (could be null)
  688. logical_addrs.add(addr); // allows us to remove the entries for this connection on
  689. // socket close
  690. map = routingTable.get(group);
  691. if (map == null) {
  692. map = new ConcurrentHashMap<Address, ConnectionHandler>();
  693. routingTable.put(group, map); // no concurrent requests on the same connection
  694. }
  695. map.put(addr, this);
  696. Set<PhysicalAddress> physical_addrs;
  697. if (request.getPhysicalAddresses() != null) {
  698. physical_addrs = address_mappings.get(addr);
  699. if (physical_addrs == null) {
  700. physical_addrs = new HashSet<PhysicalAddress>();
  701. address_mappings.put(addr, physical_addrs);
  702. }
  703. physical_addrs.addAll(request.getPhysicalAddresses());
  704. }
  705. sendStatus(CONNECT_OK);
  706. if(log.isDebugEnabled())
  707. log.debug(this + " connection handshake completed, added " +addr + " to group "+ group);
  708. } catch (Exception e) {
  709. removeEntry(group, addr);
  710. sendStatus(OP_FAIL);
  711. throw new Exception("Unsuccessful connection setup handshake for " + this);
  712. }
  713. }
  714. private boolean checkExistingConnection(Address addr, String group) throws Exception {
  715. boolean isOldExists = false;
  716. if (address_mappings.containsKey(addr)) {
  717. ConcurrentMap<Address, ConnectionHandler> map = null;
  718. ConnectionHandler oldConnectionH = null;
  719. if (group != null) {
  720. map = routingTable.get(group);
  721. if (map != null) {
  722. oldConnectionH = map.get(addr);
  723. }
  724. } else {
  725. for (Map.Entry<String, ConcurrentMap<Address, ConnectionHandler>> entry : routingTable
  726. .entrySet()) {
  727. map = entry.getValue();
  728. if (map != null) {
  729. oldConnectionH = map.get(addr);
  730. }
  731. }
  732. }
  733. if (oldConnectionH != null) {
  734. isOldExists = true;
  735. if (log.isDebugEnabled()) {
  736. log.debug("Found old connection[" + oldConnectionH + "] for addr[" + addr
  737. + "]. Closing old connection ...");
  738. }
  739. oldConnectionH.close();
  740. } else {
  741. if (log.isDebugEnabled()) {
  742. log.debug("No old connection for addr[" + addr + "] exists");
  743. }
  744. }
  745. }
  746. return isOldExists;
  747. }
  748. private void sendStatus(byte status) {
  749. try {
  750. output.writeByte(status);
  751. output.flush();
  752. } catch (IOException e1) {
  753. //ignored
  754. }
  755. }
  756. private void sendData(GossipData data) {
  757. try {
  758. data.writeTo(output);
  759. output.flush();
  760. } catch (IOException e1) {
  761. //ignored
  762. }
  763. }
  764. public String toString() {
  765. StringBuilder sb=new StringBuilder();
  766. sb.append("ConnectionHandler[peer: " + sock.getInetAddress());
  767. if(!logical_addrs.isEmpty())
  768. sb.append(", logical_addrs: " + Util.printListWithDelimiter(logical_addrs, ", "));
  769. sb.append("]");
  770. return sb.toString();
  771. }
  772. }
  773. public static void main(String[] args) throws Exception {
  774. int port=12001;
  775. int backlog=0;
  776. long soLinger=-1;
  777. long soTimeout=-1;
  778. long expiry_time=0;
  779. GossipRouter router=null;
  780. String bind_addr=null;
  781. boolean jmx=false;
  782. for(int i=0; i < args.length; i++) {
  783. String arg=args[i];
  784. if("-port".equals(arg)) {
  785. port=Integer.parseInt(args[++i]);
  786. continue;
  787. }
  788. if("-bindaddress".equals(arg) || "-bind_addr".equals(arg)) {
  789. bind_addr=args[++i];
  790. continue;
  791. }
  792. if("-backlog".equals(arg)) {
  793. backlog=Integer.parseInt(args[++i]);
  794. continue;
  795. }
  796. if("-expiry".equals(arg)) {
  797. expiry_time=Long.parseLong(args[++i]);
  798. continue;
  799. }
  800. if("-jmx".equals(arg)) {
  801. jmx=true;
  802. continue;
  803. }
  804. // this option is not used and should be deprecated/removed in a future release
  805. if("-timeout".equals(arg)) {
  806. System.out.println(" -timeout is deprecated and will be ignored");
  807. ++i;
  808. continue;
  809. }
  810. // this option is not used and should be deprecated/removed in a future release
  811. if("-rtimeout".equals(arg)) {
  812. System.out.println(" -rtimeout is deprecated and will be ignored");
  813. ++i;
  814. continue;
  815. }
  816. if("-solinger".equals(arg)) {
  817. soLinger=Long.parseLong(args[++i]);
  818. continue;
  819. }
  820. if("-sotimeout".equals(arg)) {
  821. soTimeout=Long.parseLong(args[++i]);
  822. continue;
  823. }
  824. help();
  825. return;
  826. }
  827. System.out.println("GossipRouter is starting. CTRL-C to exit JVM");
  828. try {
  829. router=new GossipRouter(port, bind_addr, jmx);
  830. if(backlog > 0)
  831. router.setBacklog(backlog);
  832. if(soTimeout >= 0)
  833. router.setSocketReadTimeout(soTimeout);
  834. if(soLinger >= 0)
  835. router.setLingerTimeout(soLinger);
  836. if(expiry_time > 0)
  837. router.setExpiryTime(expiry_time);
  838. router.start();
  839. }
  840. catch(Exception e) {
  841. System.err.println(e);
  842. }
  843. }
  844. static void help() {
  845. System.out.println();
  846. System.out.println("GossipRouter [-port <port>] [-bind_addr <address>] [options]");
  847. System.out.println();
  848. System.out.println("Options:");
  849. System.out.println();
  850. System.out.println(" -backlog <backlog> - Max queue size of backlogged connections. Must be");
  851. System.out.println(" greater than zero or the default of 1000 will be");
  852. System.out.println(" used.");
  853. System.out.println();
  854. System.out.println(" -jmx - Expose attributes and operations via JMX.");
  855. System.out.println();
  856. System.out.println(" -solinger <msecs> - Time for setting SO_LINGER on connections. 0");
  857. System.out.println(" means do not set SO_LINGER. Must be greater than");
  858. System.out.println(" or equal to zero or the default of 2000 will be");
  859. System.out.println(" used.");
  860. System.out.println();
  861. System.out.println(" -sotimeout <msecs> - Time for setting SO_TIMEOUT on connections. 0");
  862. System.out.println(" means don't set SO_TIMEOUT. Must be greater than");
  863. System.out.println(" or equal to zero or the default of 3000 will be");
  864. System.out.println(" used.");
  865. System.out.println();
  866. System.out.println(" -expiry <msecs> - Time for closing idle connections. 0");
  867. System.out.println(" means don't expire.");
  868. System.out.println();
  869. }
  870. }