PageRenderTime 36ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/servers/diameter/core/jdiameter/impl/src/main/java/org/jdiameter/client/impl/transport/tcp/TCPTransportClient.java

http://mobicents.googlecode.com/
Java | 366 lines | 268 code | 40 blank | 58 comment | 46 complexity | acb4b7336ffc00b4b931bb8df5105b89 MD5 | raw file
Possible License(s): LGPL-3.0, GPL-3.0, LGPL-2.1, GPL-2.0, CC-BY-SA-3.0, CC0-1.0, Apache-2.0, BSD-3-Clause
  1. /*
  2. * JBoss, Home of Professional Open Source
  3. * Copyright 2011, Red Hat, Inc. and individual contributors
  4. * by the @authors tag. See the copyright.txt in the distribution for a
  5. * full listing of individual contributors.
  6. *
  7. * This is free software; you can redistribute it and/or modify it
  8. * under the terms of the GNU Lesser General Public License as
  9. * published by the Free Software Foundation; either version 2.1 of
  10. * the License, or (at your option) any later version.
  11. *
  12. * This software is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this software; if not, write to the Free
  19. * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  20. * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
  21. */
  22. package org.jdiameter.client.impl.transport.tcp;
  23. import org.jdiameter.api.AvpDataException;
  24. import org.jdiameter.client.api.io.NotInitializedException;
  25. import org.jdiameter.common.api.concurrent.IConcurrentFactory;
  26. import org.slf4j.Logger;
  27. import org.slf4j.LoggerFactory;
  28. import java.io.IOException;
  29. import java.net.InetSocketAddress;
  30. import java.net.Socket;
  31. import java.nio.BufferOverflowException;
  32. import java.nio.BufferUnderflowException;
  33. import java.nio.ByteBuffer;
  34. import java.nio.channels.AsynchronousCloseException;
  35. import java.nio.channels.ClosedByInterruptException;
  36. import java.nio.channels.SocketChannel;
  37. import java.nio.channels.spi.SelectorProvider;
  38. import java.util.concurrent.locks.Lock;
  39. import java.util.concurrent.locks.ReentrantLock;
  40. /**
  41. *
  42. * @author erick.svenson@yahoo.com
  43. * @author <a href="mailto:baranowb@gmail.com"> Bartosz Baranowski </a>
  44. * @author <a href="mailto:brainslog@gmail.com"> Alexandre Mendonca </a>
  45. */
  46. public class TCPTransportClient implements Runnable {
  47. private TCPClientConnection parentConnection;
  48. private IConcurrentFactory concurrentFactory;
  49. public static final int DEFAULT_BUFFER_SIZE = 1024;
  50. public static final int DEFAULT_STORAGE_SIZE = 2048;
  51. protected boolean stop = false;
  52. protected Thread selfThread;
  53. protected int bufferSize = DEFAULT_BUFFER_SIZE;
  54. protected ByteBuffer buffer = ByteBuffer.allocate(this.bufferSize);
  55. protected InetSocketAddress destAddress;
  56. protected InetSocketAddress origAddress;
  57. protected SocketChannel socketChannel;
  58. protected Lock lock = new ReentrantLock();
  59. protected int storageSize = DEFAULT_STORAGE_SIZE;
  60. protected ByteBuffer storage = ByteBuffer.allocate(storageSize);
  61. private String socketDescription = null;
  62. private static final Logger logger = LoggerFactory.getLogger(TCPTransportClient.class);
  63. public TCPTransportClient() {
  64. }
  65. /**
  66. * Default constructor
  67. *
  68. * @param concurrentFactory factory for create threads
  69. * @param parenConnection connection created this transport
  70. */
  71. TCPTransportClient(IConcurrentFactory concurrentFactory, TCPClientConnection parenConnection) {
  72. this.parentConnection = parenConnection;
  73. this.concurrentFactory = concurrentFactory;
  74. }
  75. /**
  76. * Network init socket
  77. */
  78. public void initialize() throws IOException, NotInitializedException {
  79. logger.debug("Initialising TCPTransportClient. Origin address is [{}] and destination address is [{}]", origAddress, destAddress);
  80. if (destAddress == null) {
  81. throw new NotInitializedException("Destination address is not set");
  82. }
  83. socketChannel = SelectorProvider.provider().openSocketChannel();
  84. if (origAddress != null) {
  85. socketChannel.socket().bind(origAddress);
  86. }
  87. socketChannel.connect(destAddress);
  88. socketChannel.configureBlocking(true);
  89. getParent().onConnected();
  90. }
  91. public TCPClientConnection getParent() {
  92. return parentConnection;
  93. }
  94. public void initialize(Socket socket) throws IOException, NotInitializedException {
  95. logger.debug("Initialising TCPTransportClient for a socket on [{}]", socket);
  96. socketDescription = socket.toString();
  97. socketChannel = socket.getChannel();
  98. socketChannel.configureBlocking(true);
  99. destAddress = new InetSocketAddress(socket.getInetAddress(), socket.getPort());
  100. }
  101. public void start() throws NotInitializedException {
  102. // for client
  103. if(socketDescription == null && socketChannel != null) {
  104. socketDescription = socketChannel.socket().toString();
  105. }
  106. logger.debug("Starting transport. Socket is {}", socketDescription);
  107. if (socketChannel == null) {
  108. throw new NotInitializedException("Transport is not initialized");
  109. }
  110. if (!socketChannel.isConnected()) {
  111. throw new NotInitializedException("Socket channel is not connected");
  112. }
  113. if (getParent() == null) {
  114. throw new NotInitializedException("No parent connection is set is set");
  115. }
  116. if (selfThread == null || !selfThread.isAlive()) {
  117. selfThread = concurrentFactory.getThread("TCPReader", this);
  118. }
  119. if (!selfThread.isAlive()) {
  120. selfThread.setDaemon(true);
  121. selfThread.start();
  122. }
  123. }
  124. public void run() {
  125. logger.debug("Transport is started. Socket is [{}]", socketDescription);
  126. try {
  127. while (!stop) {
  128. int dataLength = socketChannel.read(buffer);
  129. logger.debug("Just read [{}] bytes on [{}]", dataLength, socketDescription);
  130. if (dataLength == -1) {
  131. break;
  132. }
  133. buffer.flip();
  134. byte[] data = new byte[buffer.limit()];
  135. buffer.get(data);
  136. append(data);
  137. buffer.clear();
  138. }
  139. }
  140. catch (ClosedByInterruptException e) {
  141. logger.debug("Transport exception ", e);
  142. }
  143. catch (AsynchronousCloseException e) {
  144. logger.debug("Transport exception ", e);
  145. }
  146. catch (Throwable e) {
  147. logger.debug("Transport exception ", e);
  148. }
  149. finally {
  150. try {
  151. clearBuffer();
  152. if (socketChannel != null && socketChannel.isOpen()) {
  153. socketChannel.close();
  154. }
  155. getParent().onDisconnect();
  156. }
  157. catch (Exception e) {
  158. logger.debug("Error", e);
  159. }
  160. stop = false;
  161. logger.info("Read thread is stopped for socket [{}]", socketDescription);
  162. }
  163. }
  164. public void stop() throws Exception {
  165. logger.debug("Stopping transport. Socket is [{}]", socketDescription);
  166. stop = true;
  167. if (socketChannel != null && socketChannel.isOpen()) {
  168. socketChannel.close();
  169. }
  170. if (selfThread != null) {
  171. selfThread.join(100);
  172. }
  173. clearBuffer();
  174. logger.debug("Transport is stopped. Socket is [{}]", socketDescription);
  175. }
  176. public void release() throws Exception {
  177. stop();
  178. destAddress = null;
  179. }
  180. private void clearBuffer() throws IOException {
  181. bufferSize = DEFAULT_BUFFER_SIZE;
  182. buffer = ByteBuffer.allocate(bufferSize);
  183. }
  184. public InetSocketAddress getDestAddress() {
  185. return this.destAddress;
  186. }
  187. public void setDestAddress(InetSocketAddress address) {
  188. this.destAddress = address;
  189. if(logger.isDebugEnabled()) {
  190. logger.debug("Destination address is set to [{}] : [{}]", destAddress.getHostName(), destAddress.getPort());
  191. }
  192. }
  193. public void setOrigAddress(InetSocketAddress address) {
  194. this.origAddress = address;
  195. if(logger.isDebugEnabled()) {
  196. logger.debug("Origin address is set to [{}] : [{}]", origAddress.getHostName(), origAddress.getPort());
  197. }
  198. }
  199. public InetSocketAddress getOrigAddress()
  200. {
  201. return this.origAddress;
  202. }
  203. public void sendMessage(ByteBuffer bytes) throws IOException {
  204. if (logger.isDebugEnabled()) {
  205. logger.debug("About to send a byte buffer of size [{}] over the TCP nio socket [{}]", bytes.array().length, socketDescription);
  206. }
  207. int rc;
  208. lock.lock();
  209. try {
  210. rc = socketChannel.write(bytes);
  211. }
  212. catch (Exception e) {
  213. logger.debug("Unable to send message", e);
  214. throw new IOException("Error while sending message: " + e);
  215. }
  216. finally {
  217. lock.unlock();
  218. }
  219. if (rc == -1) {
  220. throw new IOException("Connection closed");
  221. }
  222. if (logger.isDebugEnabled()) {
  223. logger.debug("Sent a byte buffer of size [{}] over the TCP nio socket [{}]", bytes.array().length, socketDescription);
  224. }
  225. }
  226. public String toString() {
  227. StringBuffer buffer = new StringBuffer();
  228. buffer.append("Transport to ");
  229. if (this.destAddress != null) {
  230. buffer.append(this.destAddress.getHostName());
  231. buffer.append(":");
  232. buffer.append(this.destAddress.getPort());
  233. }
  234. else {
  235. buffer.append("null");
  236. }
  237. buffer.append("@");
  238. buffer.append(super.toString());
  239. return buffer.toString();
  240. }
  241. boolean isConnected() {
  242. return socketChannel != null && socketChannel.isConnected();
  243. }
  244. /**
  245. * Adds data to storage
  246. *
  247. * @param data data to add
  248. */
  249. private void append(byte[] data) {
  250. if (storage.position() + data.length >= storage.capacity()) {
  251. ByteBuffer tmp = ByteBuffer.allocate(storage.limit() + data.length * 2);
  252. byte[] tmpData = new byte[storage.position()];
  253. storage.flip();
  254. storage.get(tmpData);
  255. tmp.put(tmpData);
  256. storage = tmp;
  257. logger.warn("Increase storage size. Current size is {}", storage.array().length);
  258. }
  259. try {
  260. storage.put(data);
  261. }
  262. catch (BufferOverflowException boe) {
  263. logger.error("Buffer overflow occured", boe);
  264. }
  265. boolean messageReceived;
  266. do {
  267. messageReceived = seekMessage();
  268. } while (messageReceived);
  269. }
  270. private boolean seekMessage() {
  271. // make sure there's actual data written on the buffer
  272. if (storage.position() == 0) {
  273. return false;
  274. }
  275. storage.flip();
  276. try {
  277. // get first four bytes for version and message length
  278. // 0 1 2 3
  279. // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  280. // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  281. // | Version | Message Length |
  282. // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  283. int tmp = storage.getInt();
  284. // reset position so we can now read whole message
  285. logger.debug("No data received ?");
  286. storage.position(0);
  287. // check that version is 1, as per RFC 3588 - Section 3:
  288. // This Version field MUST be set to 1 to indicate Diameter Version 1
  289. byte vers = (byte) (tmp >> 24);
  290. if (vers != 1) {
  291. return false;
  292. }
  293. // extract the message length, so we know how much to read
  294. int messageLength = (tmp & 0xFFFFFF);
  295. // verify that we do have the whole message in the storage
  296. if (storage.limit() < messageLength) {
  297. // we don't have it all.. let's restore buffer to receive more
  298. storage.position(storage.limit());
  299. storage.limit(storage.capacity());
  300. logger.debug("Received partial message, waiting for remaining (expected: {} bytes, got {} bytes).", messageLength, storage.position());
  301. return false;
  302. }
  303. // read the complete message
  304. byte[] data = new byte[messageLength];
  305. storage.get(data);
  306. storage.compact();
  307. try {
  308. // make a message out of data and process it
  309. getParent().onMessageReceived(ByteBuffer.wrap(data));
  310. }
  311. catch (AvpDataException e) {
  312. logger.debug("Garbage was received. Discarding.");
  313. storage.clear();
  314. getParent().onAvpDataException(e);
  315. }
  316. }
  317. catch(BufferUnderflowException bue) {
  318. // we don't have enough data to read message length.. wait for more
  319. storage.position(storage.limit());
  320. storage.limit(storage.capacity());
  321. logger.debug("Buffer underflow occured, waiting for more data.", bue);
  322. return false;
  323. }
  324. return true;
  325. }
  326. }