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

/scala/src/main/scala/com.idyria.osi.wsb.core/network.connectors.tcp/TCPConnector.scala

https://bitbucket.org/osi/wsb-core
Scala | 731 lines | 294 code | 196 blank | 241 comment | 37 complexity | fc29c4f422a49dde46e712d6fd77a63d MD5 | raw file
  1. package com.idyria.osi.wsb.core.network.connectors.tcp
  2. import scala.collection.JavaConversions._
  3. import com.idyria.osi.wsb.core.message._
  4. import com.idyria.osi.wsb.core.network.protocols.ProtocolHandler
  5. import com.idyria.osi.wsb.core.broker.MessageBroker
  6. import com.idyria.osi.wsb.core.network._
  7. import com.idyria.osi.tea.listeners.ListeningSupport
  8. import scala.io.Source
  9. import javax.net.ServerSocketFactory
  10. import java.net.InetSocketAddress
  11. import java.io.PrintStream
  12. import java.nio.channels._
  13. import java.nio._
  14. import java.net.Inet4Address
  15. import java.net.InetAddress
  16. import com.idyria.osi.wsb.core.network.connectors.AbstractConnector
  17. import java.net.SocketOption
  18. /**
  19. *
  20. * TCP Connector is a base connector for TCP connections.
  21. * It provides a high level data send/receive interface for the user, and manages connections with the Connector interface
  22. *
  23. *
  24. *
  25. */
  26. abstract class TCPConnector() extends AbstractConnector[TCPNetworkContext] with ListeningSupport {
  27. /**
  28. * Connection address
  29. */
  30. var address = "0.0.0.0"
  31. /**
  32. * Connection Port
  33. */
  34. var port = 8083
  35. // Server Runtime Fields
  36. //-----------
  37. /**
  38. * Server Socket used for server connection
  39. *
  40. * @group server
  41. */
  42. var serverSocket: ServerSocketChannel = null
  43. /**
  44. * Server Socket Selector
  45. *
  46. * @group server
  47. */
  48. var serverSocketSelector: Selector = null
  49. /**
  50. *
  51. * Maps a string to te client handler, for backpath matching
  52. *
  53. * @group server
  54. */
  55. var clientsContextsMap = Map[String, TCPNetworkContext]()
  56. //var onAcceptConnection = { }
  57. // Client Runtime Fields
  58. //-----------------
  59. /**
  60. * Client Socket to selected Server
  61. */
  62. var clientSocket: SocketChannel = null
  63. /**
  64. * Client Network Context to selected server
  65. */
  66. var clientNetworkContext: TCPNetworkContext = null
  67. // Send Interface
  68. //------------------------
  69. /**
  70. * Send for server side to client side
  71. */
  72. def send(buffer: ByteBuffer, context: TCPNetworkContext) = {
  73. this.direction match {
  74. case AbstractConnector.Direction.Client =>
  75. require(this.clientNetworkContext != null)
  76. //-- Pass to protocol implementation
  77. var resBuffer = protocolSendData(buffer, this.clientNetworkContext)
  78. //-- Send
  79. this.clientNetworkContext.socket.write(resBuffer)
  80. case AbstractConnector.Direction.Server =>
  81. //-- Pass to protocol implementation
  82. var resBuffer = protocolSendData(buffer, context)
  83. //-- Send
  84. while (resBuffer.remaining != 0)
  85. context.socket.write(resBuffer)
  86. }
  87. // println("Sending byte buffer to context")
  88. }
  89. /**
  90. * FIXME
  91. *
  92. * Send a message:
  93. *
  94. * - Send back to already existing connection
  95. * - Send through new opening connection
  96. */
  97. def send(msg: Message): Boolean = {
  98. (this.direction, this.clientNetworkContext) match {
  99. // Server
  100. //------------------
  101. case (AbstractConnector.Direction.Server, _) =>
  102. // Set Message to network context
  103. //--------
  104. msg.networkContext("message" -> msg)
  105. this.send(msg.toBytes, msg.networkContext.asInstanceOf[TCPNetworkContext])
  106. true
  107. // Client
  108. //--------------
  109. //-- No Client network context, cannot send
  110. case (AbstractConnector.Direction.Client, null) =>
  111. throw new RuntimeException("Client is not connected to Host, maybe something happened at connection")
  112. //-- Client network connection is there, do it
  113. case (AbstractConnector.Direction.Client, ctx) =>
  114. this.send(msg.toBytes, ctx)
  115. true
  116. }
  117. }
  118. /**
  119. * This connector can handle the message, if the network context is of the right type, and available
  120. */
  121. def canHandle(message: Message): Boolean = {
  122. message.networkContext match {
  123. // No context
  124. case ctx if (ctx == null) => false
  125. // Server, context must be in clients map
  126. case ctx if (this.direction == AbstractConnector.Direction.Server) => clientsContextsMap.contains(ctx.toString)
  127. // Client, host and port must match this one
  128. case ctx if (this.direction == AbstractConnector.Direction.Client) =>
  129. ctx.qualifier match {
  130. case NetworkContext.NetworkString(protocol, message, connectionString) if (protocol == this.protocolType && message == messageType) => true
  131. case _ => false
  132. }
  133. case _ => false
  134. }
  135. }
  136. // Protocol Implementation
  137. //----------------
  138. def protocolReceiveData(buffer: ByteBuffer, context: TCPNetworkContext): Option[Iterable[Any]]
  139. /**
  140. * Calls send Data through protocol implementation
  141. *
  142. * @return A Resulting ByteBuffer to be send over socket
  143. */
  144. def protocolSendData(buffer: ByteBuffer, context: TCPNetworkContext): ByteBuffer
  145. // LifeCycle
  146. //-------------------
  147. /**
  148. * Prepare Socket
  149. *
  150. */
  151. override def lInit = {
  152. logInfo("Creating Socket")
  153. // Create Server Socket
  154. //----------------------------
  155. /*if (this.direction == AbstractConnector.Direction.Server) {
  156. this.serverSocket = ServerSocketChannel.open();
  157. }*/
  158. }
  159. /**
  160. * Start Server Socket Thread
  161. */
  162. /* override def lStart = {
  163. this.start
  164. /*
  165. // Start Server Thread
  166. //--------------
  167. if (this.direction == AbstractConnector.Direction.Server) {
  168. this.start
  169. }
  170. // Connect to server as client
  171. //---------------------
  172. else {
  173. logInfo(s"Starting TCP Connector as client on $address:$port")
  174. // Connect
  175. this.clientSocket = SocketChannel.open(new InetSocketAddress(this.address,this.port))
  176. // Record Network Context
  177. this.clientNetworkContext = new TCPNetworkContext(this.clientSocket)
  178. logInfo(s"Client Started")
  179. @->("client.started")
  180. }*/
  181. }*/
  182. override def lStop = {
  183. // Stop all threads
  184. this.stopThread = true
  185. // Stop Server socket
  186. //------------
  187. if (this.direction == AbstractConnector.Direction.Server) {
  188. // Close Selector to stop operations on thread
  189. if (this.serverSocketSelector != null && this.serverSocketSelector.isOpen) {
  190. try {
  191. this.serverSocketSelector.close
  192. } catch {
  193. case e: Throwable =>
  194. }
  195. }
  196. } else {
  197. this.stopThread = true
  198. try {
  199. this.clientSocket.close
  200. } catch {
  201. case e: Throwable =>
  202. }
  203. }
  204. }
  205. // Connector Run Method
  206. //------------------------
  207. //-- React on common started to signal ready to go
  208. on("common.started") {
  209. started.release(Integer.MAX_VALUE)
  210. }
  211. //-- React on common failed to signal to make sure no thread is blocking
  212. on("common.start.failed") {
  213. started.release(Integer.MAX_VALUE)
  214. }
  215. /**
  216. * Start this connector in Listening mode if in SERVER direction,
  217. * or tries to connect to target address if in CLIENT direction
  218. *
  219. */
  220. override def run = {
  221. // Common
  222. //---------------
  223. //-- Prepare Read Buffer
  224. //----------------
  225. // Server Mode
  226. //-------------------
  227. if (this.direction == AbstractConnector.Direction.Server) {
  228. @->("server.start")
  229. logInfo[TCPConnector](s"Starting TCP Connector on $address:$port (${this.messageType})")
  230. // Bind
  231. //--------------
  232. this.serverSocket = ServerSocketChannel.open();
  233. this.serverSocket.bind(new InetSocketAddress(address, port))
  234. // Register Selector for all operations
  235. // !! Selector only works on non blocking sockets
  236. //----------------------
  237. this.serverSocketSelector = Selector.open()
  238. this.serverSocket.configureBlocking(false);
  239. this.serverSocket.register(this.serverSocketSelector, SelectionKey.OP_ACCEPT)
  240. //this.serverSocket.setOption(java.net.StandardSocketOptions.SO_RCVBUF, 4096)
  241. // Loop on Selection and handle actions
  242. //------------------
  243. @->("server.started")
  244. @->("common.started")
  245. while (!this.stopThread) {
  246. try {
  247. // Select blocking, will throw an exception if socket is closed
  248. logFine[TCPConnector](s"-- Waiting for something to happen on selector thread")
  249. var selected = this.serverSocketSelector.select
  250. var keyIterator = this.serverSocketSelector.selectedKeys.iterator;
  251. while (keyIterator.hasNext) {
  252. var key = keyIterator.next();
  253. key match {
  254. // Accept
  255. //--------------------
  256. case key if (key.isValid && key.isAcceptable) => {
  257. logFine[TCPConnector]("-- Accepting Connection")
  258. var clientSocket = serverSocket.accept
  259. // Prepare Network Context
  260. //----------------------------
  261. var networkContext = new TCPNetworkContext(clientSocket)
  262. networkContext.qualifier = s"client@${networkContext.hashCode}"
  263. clientsContextsMap += (networkContext.toString -> networkContext)
  264. @->("server.accepted")
  265. @->("server.accepted", networkContext)
  266. // Register Socket Channel to selector
  267. // !! Selector only works on non blocking sockets
  268. //-----------------
  269. clientSocket.configureBlocking(false);
  270. var clientSocketKey = clientSocket.register(this.serverSocketSelector, SelectionKey.OP_READ, SelectionKey.OP_WRITE)
  271. //-- Register NetworkContext with key
  272. clientSocketKey.attach(networkContext)
  273. keyIterator.remove
  274. }
  275. // Read: Pass Read datas to underlying implementation
  276. //-----------------
  277. case key if (key.isValid && key.isReadable) => {
  278. @->("server.read")
  279. keyIterator.remove
  280. logFine[TCPConnector]("-- Got Read key")
  281. //-- Take Channel
  282. var networkContext = key.attachment().asInstanceOf[TCPNetworkContext]
  283. var socketChannel = networkContext.socket
  284. var readBuffer = ByteBuffer.allocate(4096) // (Buffer of a page size per default)
  285. try {
  286. // Read Until 0, or < 0 closes the channel
  287. //----------
  288. //var continue = true;
  289. //while (continue) {
  290. socketChannel.read(readBuffer) match {
  291. // Continue Reading
  292. //------------
  293. case readbytes if (readbytes > 0) => {
  294. @->("server.read.datas")
  295. // Pass Datas to underlying protocol
  296. readBuffer.flip
  297. /*var passedBuffer = ByteBuffer.allocate(readbytes)
  298. passedBuffer.put(readBuffer)
  299. passedBuffer.flip*/
  300. //println("-> Din")
  301. var passedBuffer = readBuffer
  302. //readBuffer.clear
  303. protocolReceiveData(passedBuffer, networkContext) match {
  304. case Some(messages) =>
  305. //-- Get Message Factory
  306. var factory = (Message(this.messageType), networkContext[String]("message.type")) match {
  307. //-- Context Switched factory
  308. case (_, Some(contextFactoryName)) =>
  309. Message(contextFactoryName) match {
  310. case Some(factory) => factory
  311. case None =>
  312. throw new RuntimeException(s"TCP network context is configured with ${contextFactoryName} message type which has no registered factory")
  313. }
  314. //-- Normal Connector default factory
  315. case (Some(factory), None) => factory
  316. case _ =>
  317. throw new RuntimeException(s"TCP Connector is configured with ${this.messageType} message type which has no registered factory")
  318. }
  319. //-- Create Messages
  320. messages.foreach {
  321. m =>
  322. //println("[Server] Got message: "+new String(m.asInstanceOf[ByteBuffer].array()))
  323. logInfo[TCPConnector]("[Server] Got message: "+new String(m.asInstanceOf[ByteBuffer].array()))
  324. // Create Message
  325. var message = factory(m)
  326. // Append context
  327. message.networkContext = networkContext
  328. // Send
  329. this.network ! message
  330. }
  331. // Protocol not ready
  332. case None =>
  333. }
  334. // Clear Buffer for next read
  335. readBuffer.flip()
  336. // continue = false
  337. //readBuffer.clear();
  338. }
  339. // Nothing to read
  340. //----------------
  341. case readbytes if (readbytes == 0) => {
  342. @->("server.read.nodatas")
  343. readBuffer.clear();
  344. // continue = false
  345. }
  346. // Close Client Connection
  347. //------------
  348. case readbytes if (readbytes < 0) => {
  349. @->("server.read.close")
  350. logFine[TCPConnector]("-- Closing Connection")
  351. networkContext.@->("close")
  352. this.clientsContextsMap -= networkContext.toString
  353. socketChannel.close();
  354. //continue = false
  355. }
  356. }
  357. //}
  358. } catch {
  359. // IN case of IO error, forget about this connection
  360. case e: java.io.IOException =>
  361. this.clientsContextsMap -= networkContext.toString
  362. logFine[TCPConnector]("-- Closing Connection for: "+networkContext.toString)
  363. networkContext.@->("close")
  364. case e: Throwable => throw e
  365. } finally {
  366. // keyIterator.remove
  367. }
  368. }
  369. // Fall back
  370. //----------------
  371. case key => {
  372. keyIterator.remove
  373. }
  374. }
  375. // EOF Key matched
  376. logFine("-- EOF Keys Looop")
  377. } // EOF While has keys
  378. } catch {
  379. // Selector has been closed (connector close for example)
  380. case e: java.nio.channels.ClosedSelectorException =>
  381. //e.printStackTrace()
  382. case e: Throwable => throw e
  383. }
  384. } // EOF Server thread loop
  385. // Clean
  386. //----------------
  387. this.serverSocketSelector = null
  388. this.serverSocket.close
  389. this.serverSocket = null
  390. @->("server.end")
  391. } // Client Mode
  392. //------------------------
  393. else {
  394. logInfo[TCPConnector](s"Starting TCP Connector as client on $address:$port")
  395. // Connect
  396. var addr = new InetSocketAddress(InetAddress.getByName(this.address), this.port)
  397. try {
  398. this.clientSocket = SocketChannel.open(addr)
  399. } catch {
  400. case e: Throwable =>
  401. logError[TCPConnector](s"Failed connection to ${addr} and port $port, not resolved: ${addr.isUnresolved()}")
  402. // Make sure started signal has been given
  403. @->("common.start.failed")
  404. throw e
  405. }
  406. // Record Network Context
  407. this.clientNetworkContext = new TCPNetworkContext(this.clientSocket)
  408. // this.clientNetworkContext.qualifier = s"server@${this.clientNetworkContext.hashCode}"
  409. logInfo[TCPConnector](s"Client Started")
  410. @->("client.started")
  411. @->("common.started")
  412. // Data Loop
  413. //------------------------
  414. var readBuffer = ByteBuffer.allocate(4096) // (Buffer of a page size per default)
  415. var continue = true;
  416. while (continue && !this.stopThread)
  417. try {
  418. this.clientSocket.read(readBuffer) match {
  419. // Continue Reading
  420. //------------
  421. case readbytes if (readbytes > 0) => {
  422. // Pass Datas to underlying protocol
  423. readBuffer.flip
  424. /* var passedBuffer = ByteBuffer.allocate(readbytes)
  425. passedBuffer.put(readBuffer)
  426. passedBuffer.rewind*/
  427. protocolReceiveData(readBuffer, clientNetworkContext) match {
  428. case Some(messages) =>
  429. // Get Message Factory
  430. Message(this.messageType) match {
  431. case Some(factory) =>
  432. messages.foreach {
  433. m =>
  434. logInfo[TCPConnector]("[Client] Got message: " + new String(m.asInstanceOf[ByteBuffer].array()))
  435. // Create Message
  436. var message = factory(m)
  437. // Append context
  438. message.networkContext = clientNetworkContext
  439. // Send
  440. this.network ! message
  441. }
  442. case None =>
  443. throw new RuntimeException(s"TCP Connector is configured with ${this.messageType} message type which has no registered factory")
  444. }
  445. // Protocol not ready
  446. case None =>
  447. }
  448. // protocolReceiveData(passedBuffer, this.clientNetworkContext)
  449. // Clear Buffer for next read
  450. readBuffer.flip();
  451. }
  452. // Close Client Connection
  453. //------------
  454. case readbytes if (readbytes < 0) => {
  455. this.clientSocket.close();
  456. continue = false
  457. }
  458. }
  459. } catch {
  460. // In case of I/O Exception, stop
  461. case e: java.io.IOException =>
  462. continue = false
  463. // Otherwise let live
  464. case e: Throwable =>
  465. continue = false
  466. }
  467. // EOF Data -> Clean
  468. //------------------
  469. this.clientNetworkContext = null
  470. this.clientSocket = null
  471. }
  472. }
  473. }
  474. /**
  475. * This class is an implementation of TCPConnector, handing out application protocol management to a Protocolhandler class
  476. *
  477. */
  478. abstract class TCPProtocolHandlerConnector[T](var protocolHandlerFactory: (TCPNetworkContext => ProtocolHandler[T])) extends TCPConnector {
  479. // Protocol Implementation
  480. //----------------
  481. def protocolReceiveData(buffer: ByteBuffer, context: TCPNetworkContext): Option[Iterable[Any]] = {
  482. // Receive through Protocol handler
  483. //---------------
  484. var handler = ProtocolHandler(context, protocolHandlerFactory)
  485. handler.receive(buffer)
  486. handler.availableDatas.size match {
  487. case size if (size > 0) =>
  488. var res = List[T]()
  489. handler.availableDatas.foreach {
  490. data => res = res :+ data
  491. }
  492. @->("protocol.receive.endOfData")
  493. handler.availableDatas.clear
  494. Option(res)
  495. case _ =>
  496. None
  497. }
  498. }
  499. def protocolSendData(buffer: ByteBuffer, context: TCPNetworkContext): ByteBuffer = {
  500. // Send though protocol handler
  501. //-------------------------
  502. var sendBuffer = ProtocolHandler(context, protocolHandlerFactory).send(buffer, context)
  503. // Ensure next user can read from content
  504. //---------
  505. // println(s"""In protocol handler send, result is ${sendBuffer.remaining} of remaining""")
  506. sendBuffer.remaining match {
  507. case 0 =>
  508. // println("...so now fliping")
  509. sendBuffer.flip.asInstanceOf[ByteBuffer]
  510. case _ =>
  511. sendBuffer
  512. }
  513. }
  514. }
  515. // Network Context
  516. //---------------------
  517. class TCPNetworkContext(q: String) extends NetworkContext {
  518. this.qualifier = q
  519. var socket: SocketChannel = null
  520. def this(so: SocketChannel) = {
  521. this(so.getRemoteAddress().toString())
  522. socket = so
  523. so.getRemoteAddress() match {
  524. case sa: java.net.InetSocketAddress => this.qualifier = s"${sa.getHostString()}:${sa.getPort()}"
  525. case _ =>
  526. }
  527. }
  528. }