PageRenderTime 45ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/codebase/src/java/portico/org/portico/bindings/ptalk/transport/UdpTransport.java

https://bitbucket.org/serkancng/serkancng
Java | 332 lines | 206 code | 38 blank | 88 comment | 12 complexity | e55e3f23f91dce403426062460048fc5 MD5 | raw file
  1. /*
  2. * Copyright 2010 The Portico Project
  3. *
  4. * This file is part of portico.
  5. *
  6. * portico is free software; you can redistribute it and/or modify
  7. * it under the terms of the Common Developer and Distribution License (CDDL)
  8. * as published by Sun Microsystems. For more information see the LICENSE file.
  9. *
  10. * Use of this software is strictly AT YOUR OWN RISK!!!
  11. * If something bad happens you do not have permission to come crying to me.
  12. * (that goes for your lawyer as well)
  13. *
  14. */
  15. package org.portico.bindings.ptalk.transport;
  16. import java.io.IOException;
  17. import java.net.DatagramPacket;
  18. import java.net.DatagramSocket;
  19. import java.net.InetAddress;
  20. import java.net.InetSocketAddress;
  21. import java.net.MulticastSocket;
  22. import java.net.SocketException;
  23. import java.net.UnknownHostException;
  24. import java.util.Map;
  25. import org.apache.log4j.Logger;
  26. import org.portico.bindings.ptalk.Common;
  27. import org.portico.bindings.ptalk.channel.Channel;
  28. import org.portico.bindings.ptalk.channel.Packet;
  29. import org.portico.lrc.compat.JConfigurationException;
  30. import org.portico.lrc.compat.JRTIinternalError;
  31. public class UdpTransport implements ITransport
  32. {
  33. //----------------------------------------------------------
  34. // STATIC VARIABLES
  35. //----------------------------------------------------------
  36. ////////////////////////////////////////////////////////////
  37. //////// System properties for configurable values /////////
  38. ////////////////////////////////////////////////////////////
  39. /** Multicast address to bind to */
  40. public static final String PROP_MULTICAST_ADDRESS = "portico.ptalk.udp.address";
  41. /** Multicast socket to bind to - this is ADMIN port, federation port increment starting here */
  42. public static final String PROP_MULTICAST_PORT = "portico.ptalk.udp.port";
  43. /** Size of systems UDP send buffer */
  44. public static final String PROP_UDP_SEND_BUFFER_SIZE = "portico.ptalk.udp.sendBuffer";
  45. /** Size of systems UDP receive buffer */
  46. public static final String PROP_UDP_RECV_BUFFER_SIZE = "portico.ptalk.udp.receiveBuffer";
  47. ////////////////////////////////////////////////////////////
  48. //////// Default values for configurable properties ////////
  49. ////////////////////////////////////////////////////////////
  50. /** The default multicast address to send traffic on */
  51. public static final String DEFAULT_MULTICAST_ADDRESS = "228.10.10.11";
  52. /** The default multicast port */
  53. public static final String DEFAULT_MULTICAST_PORT = "20913";
  54. /** Used by the multicast packet receiver to create buffers to receive packets into */
  55. private static final int UDP_RECEIVE_BUFFER_SIZE = 65536;
  56. //----------------------------------------------------------
  57. // INSTANCE VARIABLES
  58. //----------------------------------------------------------
  59. private Logger logger;
  60. private Channel channel;
  61. // sending socket information
  62. // we use a datagram socket for sending because it'll stamp specific host/port information
  63. // on all outgoing packets, allowing us to properly identify where they came from (even for
  64. // multiple instances on the same host). If we just use a multicast
  65. private DatagramSocket sendingSocket;
  66. private InetSocketAddress sendingAddress;
  67. // multicast socket information
  68. private MulticastSocket receivingSocket;
  69. private InetAddress multicastAddress;
  70. private int multicastPort;
  71. private int sendBufferSize;
  72. private int recvBufferSize;
  73. // incoming message processing
  74. private MessageReceiver messageReceiver;
  75. //----------------------------------------------------------
  76. // CONSTRUCTORS
  77. //----------------------------------------------------------
  78. public UdpTransport()
  79. {
  80. this.sendBufferSize = 65507*10; // default value for Mac OS X * 100 messages
  81. this.recvBufferSize = 65507*10; // default value for Mac OS X * 100 messages
  82. }
  83. //----------------------------------------------------------
  84. // INSTANCE METHODS
  85. //----------------------------------------------------------
  86. ///////////////////////////////////////////////////////////////////////////////////
  87. //////////////////////////////// Lifecycle Methods ////////////////////////////////
  88. ///////////////////////////////////////////////////////////////////////////////////
  89. public void configure( Channel channel, Map<String,Object> configurationProperties )
  90. throws JConfigurationException
  91. {
  92. this.logger = Common.getLogger();
  93. this.channel = channel;
  94. logger.debug( "Configuring new instance of UDP Transport" );
  95. // multicast address configuration
  96. try
  97. {
  98. String addressString = (String)configurationProperties.get( PROP_MULTICAST_ADDRESS );
  99. if( addressString == null ) addressString = DEFAULT_MULTICAST_ADDRESS;
  100. logger.trace( "...multicast address: " + addressString );
  101. this.multicastAddress = InetAddress.getByName( addressString );
  102. }
  103. catch( UnknownHostException uhe )
  104. {
  105. logger.error( uhe );
  106. throw new JConfigurationException( uhe );
  107. }
  108. // multicast port configuration
  109. String portString = (String)configurationProperties.get( PROP_MULTICAST_PORT );
  110. if( portString == null ) portString = DEFAULT_MULTICAST_PORT;
  111. logger.trace( "...multicast port: "+portString );
  112. this.multicastPort = Integer.parseInt( portString );
  113. // send and receive buffer sizes
  114. String sendBufferString = System.getProperty( PROP_UDP_SEND_BUFFER_SIZE );
  115. if( sendBufferString != null )
  116. this.sendBufferSize = Integer.parseInt( sendBufferString );
  117. String recvBufferString = System.getProperty( PROP_UDP_RECV_BUFFER_SIZE );
  118. if( recvBufferString != null )
  119. this.recvBufferSize = Integer.parseInt( recvBufferString );
  120. }
  121. public void connect() throws JRTIinternalError
  122. {
  123. //////////////////////////////////////////
  124. // create the socket and join the group //
  125. //////////////////////////////////////////
  126. try
  127. {
  128. logger.debug( "ATTEMPT Connect to multicast group "+multicastAddress+":"+multicastPort );
  129. // set up the sending socket - we use a Datagram socket for sending to ensure that
  130. // a unique ip/port combination is stamped on each datagram (rather than that of the
  131. // generic multicast address)
  132. this.sendingSocket = new DatagramSocket();
  133. this.sendingAddress = new InetSocketAddress( InetAddress.getLocalHost(),
  134. sendingSocket.getLocalPort() );
  135. this.sendingSocket.setSendBufferSize( this.sendBufferSize );
  136. this.sendingSocket.setBroadcast( true );
  137. // set up the receiving socket (multicast)
  138. this.receivingSocket = new MulticastSocket( multicastPort );
  139. this.receivingSocket.joinGroup( multicastAddress );
  140. this.receivingSocket.setBroadcast( true );
  141. this.receivingSocket.setLoopbackMode( false ); // we loop back manually
  142. // SKIPPING soTimeout - controls how long receive waits for a callback
  143. // SKIPPING setTrafficClass
  144. this.receivingSocket.setTimeToLive( 8 ); // give it a little room to breath, 4 too small
  145. //this.receivingSocket.setSendBufferSize( this.sendBufferSize );
  146. this.receivingSocket.setReceiveBufferSize( this.recvBufferSize );
  147. // log out the values that are in use
  148. logger.trace( " Socket properties:" );
  149. logger.trace( " -> ------ Sending Socket (DatagramSocket) ------" );
  150. logger.trace( " -> (send) Address = "+sendingAddress );
  151. logger.trace( " -> (send) Broadcast = "+sendingSocket.getBroadcast() );
  152. logger.trace( " -> (send) SendBuffer = "+sendingSocket.getSendBufferSize() );
  153. logger.trace( " -> (send) RecvBuffer = "+sendingSocket.getReceiveBufferSize() );
  154. logger.trace( " -> ------ Receiving Socket (MulticastSocket) ------" );
  155. logger.trace( " -> (recv) Address = "+multicastAddress+":"+multicastPort );
  156. logger.trace( " -> (recv) Broadcast = "+receivingSocket.getBroadcast() );
  157. logger.trace( " -> (recv) Loopback = "+receivingSocket.getLoopbackMode() );
  158. logger.trace( " -> (recv) TTL = "+receivingSocket.getTimeToLive() );
  159. logger.trace( " -> (recv) TrafficClass = "+receivingSocket.getTrafficClass() );
  160. logger.trace( " -> (recv) SendBuffer = "+receivingSocket.getSendBufferSize() );
  161. logger.trace( " -> (recv) RecvBuffer = "+receivingSocket.getReceiveBufferSize() );
  162. }
  163. catch( IOException ioex )
  164. {
  165. logger.error( ioex );
  166. throw new JRTIinternalError( "Couldn't connect to multicast group: "+ioex.getMessage(),
  167. ioex );
  168. }
  169. logger.debug( "SUCCESS Connected to multicast group" );
  170. ///////////////////////////////////////////
  171. // start listening for incoming messages //
  172. ///////////////////////////////////////////
  173. this.messageReceiver = new MessageReceiver();
  174. this.messageReceiver.start();
  175. }
  176. public void disconnect() throws JRTIinternalError
  177. {
  178. // disconnect from the multicast group
  179. try
  180. {
  181. logger.debug( "ATTEMPT Disconnecting from multicast group" );
  182. this.sendingSocket.close();
  183. this.receivingSocket.leaveGroup( multicastAddress );
  184. this.receivingSocket.disconnect();
  185. logger.debug( "SUCCESS Disconnected from multicast group" );
  186. }
  187. catch( IOException ioex )
  188. {
  189. logger.error( ioex );
  190. throw new JRTIinternalError( "Problem leaving multicast group"+ioex.getMessage(), ioex );
  191. }
  192. }
  193. public InetSocketAddress getAddress()
  194. {
  195. return sendingAddress;
  196. }
  197. /**
  198. * Returns the socket address of the multicast group that this transport is connected to
  199. */
  200. public InetSocketAddress getGroupAddress()
  201. {
  202. return new InetSocketAddress( multicastAddress, multicastPort );
  203. }
  204. ///////////////////////////////////////////////////////////////////////////////////
  205. ///////////////////////////// Message Sending Methods /////////////////////////////
  206. ///////////////////////////////////////////////////////////////////////////////////
  207. public void sendToNetwork( Packet packet ) throws RuntimeException
  208. {
  209. // toss this packet out onto the network
  210. byte[] payload = packet.marshal();
  211. DatagramPacket datagram = new DatagramPacket( payload,
  212. payload.length,
  213. multicastAddress,
  214. multicastPort );
  215. try
  216. {
  217. if( logger.isTraceEnabled() )
  218. {
  219. logger.trace( "Sending packet from /"+sendingAddress.getAddress().getHostAddress()+":"+
  220. sendingAddress.getPort()+" ["+datagram.getLength()+" bytes, headers="+
  221. packet.getHeaders()+"] to ["+multicastAddress+":"+multicastPort+"]" );
  222. }
  223. // do the send
  224. sendingSocket.send( datagram );
  225. }
  226. catch( IOException ioex )
  227. {
  228. logger.error( ioex );
  229. throw new RuntimeException( ioex );
  230. }
  231. // loop it back around to the local federate
  232. channel.queueForReceiving( packet );
  233. }
  234. //----------------------------------------------------------
  235. // STATIC METHODS
  236. //----------------------------------------------------------
  237. ///////////////////////////////////////////////////////////////////////////////////
  238. ///////////////////////// Private Class: Message Receiver /////////////////////////
  239. ///////////////////////////////////////////////////////////////////////////////////
  240. private class MessageReceiver extends Thread
  241. {
  242. public MessageReceiver()
  243. {
  244. super( "MessageReceiver("+sendingSocket.getLocalPort()+")" );
  245. super.setDaemon( true );
  246. }
  247. public void run()
  248. {
  249. // This thread will pretty much run forever. The receive() method does not pay any
  250. // attention to interruptions, so the chances of Thread.interrupted() ever returning
  251. // true are pretty slim. To kill this thread, we wait for some external component to
  252. // close the socket on us, causing a SocketException to be thrown.
  253. while( Thread.interrupted() == false )
  254. {
  255. final byte[] buffer = new byte[UDP_RECEIVE_BUFFER_SIZE];
  256. final DatagramPacket datagram = new DatagramPacket( buffer, buffer.length );
  257. try
  258. {
  259. receivingSocket.receive( datagram );
  260. // skip packets from us
  261. if( datagram.getSocketAddress().equals(sendingAddress) )
  262. continue;
  263. byte[] payload = new byte[datagram.getLength()];
  264. System.arraycopy( datagram.getData(),
  265. datagram.getOffset(),
  266. payload,
  267. 0,
  268. datagram.getLength() );
  269. Packet ptalkPacket = new Packet();
  270. ptalkPacket.setSender( (InetSocketAddress)datagram.getSocketAddress() );
  271. ptalkPacket.unmarshal( payload, 0 );
  272. if( logger.isTraceEnabled() )
  273. {
  274. logger.trace( "Received packet from "+datagram.getSocketAddress()+" ["+
  275. datagram.getLength()+" bytes, headers="+ptalkPacket.getHeaders()+"]" );
  276. }
  277. channel.queueForReceiving( ptalkPacket );
  278. }
  279. catch( SocketException se )
  280. {
  281. // Socket has closed off. Most probable cause of this is that we're
  282. // shutting down. Log it and exit.
  283. logger.info( "Multicast socket closed. Exiting. No more messages will be received" );
  284. return;
  285. }
  286. catch( IOException ioex )
  287. {
  288. logger.error( "Exception in UDP MessageReceiver: "+ ioex.getMessage(), ioex );
  289. return;
  290. }
  291. }
  292. }
  293. }
  294. }