/protocols/jain-mgcp/stack/src/main/java/org/mobicents/protocols/mgcp/stack/JainMgcpStackImpl.java

http://mobicents.googlecode.com/ · Java · 457 lines · 301 code · 87 blank · 69 comment · 33 complexity · 8608c45632d60f5f47d3c9edbf07f402 MD5 · raw file

  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. /*
  23. * File Name : JainMgcpStackImpl.java
  24. *
  25. * The JAIN MGCP API implementaion.
  26. *
  27. * The source code contained in this file is in in the public domain.
  28. * It can be used in any project or product without prior permission,
  29. * license or royalty payments. There is NO WARRANTY OF ANY KIND,
  30. * EXPRESS, IMPLIED OR STATUTORY, INCLUDING, WITHOUT LIMITATION,
  31. * THE IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
  32. * AND DATA ACCURACY. We do not warrant or make any representations
  33. * regarding the use of the software or the results thereof, including
  34. * but not limited to the correctness, accuracy, reliability or
  35. * usefulness of the software.
  36. */
  37. package org.mobicents.protocols.mgcp.stack;
  38. import jain.protocol.ip.mgcp.CreateProviderException;
  39. import jain.protocol.ip.mgcp.DeleteProviderException;
  40. import jain.protocol.ip.mgcp.JainMgcpProvider;
  41. import jain.protocol.ip.mgcp.JainMgcpStack;
  42. import jain.protocol.ip.mgcp.OAM_IF;
  43. import java.io.IOException;
  44. import java.io.InputStream;
  45. import java.net.DatagramSocket;
  46. import java.net.InetAddress;
  47. import java.net.InetSocketAddress;
  48. import java.net.SocketAddress;
  49. import java.net.SocketException;
  50. import java.nio.ByteBuffer;
  51. import java.nio.channels.DatagramChannel;
  52. import java.util.Map;
  53. import java.util.Properties;
  54. import java.util.concurrent.ConcurrentHashMap;
  55. import java.util.concurrent.ThreadFactory;
  56. import java.util.concurrent.atomic.AtomicInteger;
  57. import org.apache.log4j.Level;
  58. import org.apache.log4j.Logger;
  59. import org.mobicents.protocols.mgcp.parser.UtilsFactory;
  60. import org.mobicents.protocols.mgcp.utils.PacketRepresentation;
  61. import org.mobicents.protocols.mgcp.utils.PacketRepresentationFactory;
  62. /**
  63. *
  64. * @author Oleg Kulikov
  65. * @author Pavel Mitrenko
  66. */
  67. public class JainMgcpStackImpl extends Thread implements JainMgcpStack, OAM_IF {
  68. // Static variables from properties files
  69. /**
  70. * Defines how many executors will work on event delivery
  71. */
  72. public static final String _EXECUTOR_TABLE_SIZE = "executorTableSize";
  73. /**
  74. * Defines how many message can be stored in queue before new ones are discarded.
  75. */
  76. public static final String _EXECUTOR_QUEUE_SIZE = "executorQueueSize";
  77. public static final String _MESSAGE_READER_THREAD_PRIORITY = "messageReaderThreadPriority";
  78. private static final Logger logger = Logger.getLogger(JainMgcpStackImpl.class);
  79. private static final String propertiesFileName = "mgcp-stack.properties";
  80. private String protocolVersion = "1.0";
  81. protected int port = 2727;
  82. private InetAddress localAddress = null;
  83. private boolean stopped = true;
  84. private int messageReaderThreadPriority = Thread.MIN_PRIORITY;
  85. private UtilsFactory utilsFactory = null;
  86. private PacketRepresentationFactory prFactory = null;
  87. // Should we ever get data more than 5000 bytes?
  88. private static final int BUFFER_SIZE = 5000;
  89. private DatagramChannel channel;
  90. // private Selector selector;
  91. ByteBuffer receiveBuffer = ByteBuffer.allocateDirect(BUFFER_SIZE);
  92. ByteBuffer sendBuffer = ByteBuffer.allocateDirect(BUFFER_SIZE);
  93. byte[] b = new byte[BUFFER_SIZE];
  94. // For now we have only one provider/delete prvider method wont work.
  95. public JainMgcpStackProviderImpl provider = null;
  96. private InetSocketAddress address = null;
  97. /**
  98. * holds current active transactions (RFC 3435 [$3.2.1.2]: for tx sent & received).
  99. *
  100. */
  101. private ConcurrentHashMap<Integer, TransactionHandler> localTransactions = new ConcurrentHashMap<Integer, TransactionHandler>();
  102. private ConcurrentHashMap<Integer, Integer> remoteTxToLocalTxMap = new ConcurrentHashMap<Integer, Integer>();
  103. private ConcurrentHashMap<Integer, TransactionHandler> completedTransactions = new ConcurrentHashMap<Integer, TransactionHandler>();
  104. protected MessageHandler messageHandler = null;
  105. private DatagramSocket socket;
  106. private long delay = 20;
  107. public void printStats() {
  108. System.out.println("localTransactions size = " + localTransactions.size());
  109. System.out.println("remoteTxToLocalTxMap size = " + remoteTxToLocalTxMap.size());
  110. System.out.println("completedTransactions size = " + completedTransactions.size());
  111. }
  112. // Defualt constructor for TCK
  113. public JainMgcpStackImpl() {
  114. }
  115. /** Creates a new instance of JainMgcpStackImpl */
  116. public JainMgcpStackImpl(InetAddress localAddress, int port) {
  117. this.localAddress = localAddress;
  118. this.port = port;
  119. }
  120. private void init() throws IOException {
  121. readProperties();
  122. // initExecutors();
  123. if (channel == null) {
  124. try {
  125. InetSocketAddress bindAddress = new InetSocketAddress(this.localAddress, this.port);
  126. this.channel = DatagramChannel.open();
  127. socket = this.channel.socket();
  128. socket.bind(bindAddress);
  129. this.channel.configureBlocking(false);
  130. this.localAddress = socket.getLocalAddress();
  131. logger.info("Jain Mgcp stack bound to IP " + this.localAddress + " and UDP port " + this.port);
  132. // This is for TCK don't remove
  133. System.out.println("Jain Mgcp stack bound to IP " + this.localAddress + " and UDP port " + this.port);
  134. } catch (SocketException e) {
  135. logger.error(e);
  136. throw new RuntimeException("Failed to find a local port " + this.port + " to bound stack");
  137. }
  138. }
  139. stopped = false;
  140. if (logger.isDebugEnabled()) {
  141. logger.debug("Starting main thread " + this);
  142. }
  143. this.provider = new JainMgcpStackProviderImpl(this);
  144. this.utilsFactory = new UtilsFactory(25);
  145. this.prFactory = new PacketRepresentationFactory(50, BUFFER_SIZE);
  146. this.messageHandler = new MessageHandler(this);
  147. this.setPriority(this.messageReaderThreadPriority);
  148. // So stack does not die
  149. this.setDaemon(false);
  150. start();
  151. }
  152. private void readProperties() {
  153. try {
  154. Properties props = new Properties();
  155. InputStream is = this.getClass().getResourceAsStream(this.propertiesFileName);
  156. if (is == null) {
  157. logger.warn("Failed to locate properties file, using default values");
  158. return;
  159. }
  160. props.load(is);
  161. String val = null;
  162. val = props.getProperty(_MESSAGE_READER_THREAD_PRIORITY, "" + this.messageReaderThreadPriority);
  163. this.messageReaderThreadPriority = Integer.parseInt(val);
  164. val = null;
  165. logger.info(this.propertiesFileName + " read successfully! \nmessageReaderThreadPriority = "
  166. + this.messageReaderThreadPriority);
  167. } catch (Exception e) {
  168. logger.warn("Failed to read properties file due to some error \"" + e.getMessage() + "\", using defualt values!!!!");
  169. }
  170. }
  171. /**
  172. * Closes the stack and it's underlying resources.
  173. */
  174. public void close() {
  175. stopped = true;
  176. try {
  177. if (logger.isDebugEnabled()) {
  178. logger.debug("Closing socket");
  179. }
  180. // selector.close();
  181. socket.close();
  182. if (this.channel != null) {
  183. this.channel.close();
  184. }
  185. } catch (Exception e) {
  186. if(logger.isEnabledFor(Level.ERROR))
  187. {
  188. logger.error("Could not gracefully close socket", e);
  189. }
  190. }
  191. }
  192. public JainMgcpProvider createProvider() throws CreateProviderException {
  193. if (this.provider != null) {
  194. throw new CreateProviderException(
  195. "Provider already created. Only 1 provider can be created. Delete the first and then re-create");
  196. }
  197. try {
  198. init();
  199. } catch (IOException e) {
  200. if(logger.isEnabledFor(Level.ERROR))
  201. {
  202. logger.error("Failed to open Socket ", e);
  203. }
  204. throw new CreateProviderException(e.getMessage());
  205. }
  206. return this.provider;
  207. }
  208. public void deleteProvider(JainMgcpProvider provider) throws DeleteProviderException {
  209. if (this.provider == null) {
  210. throw new DeleteProviderException("No Provider exist.");
  211. }
  212. if (this.provider != provider) {
  213. throw new DeleteProviderException("Passed provider is not current one.");
  214. }
  215. this.close();
  216. this.provider = null;
  217. }
  218. public void setPort(int port) {
  219. this.port = port;
  220. }
  221. public int getPort() {
  222. return port;
  223. }
  224. public UtilsFactory getUtilsFactory() {
  225. return this.utilsFactory;
  226. }
  227. public void setUtilsFactory(UtilsFactory utilsFactory) {
  228. this.utilsFactory = utilsFactory;
  229. }
  230. public InetAddress getAddress() {
  231. if (this.localAddress != null) {
  232. return this.localAddress;
  233. } else {
  234. return null;
  235. }
  236. }
  237. public String getProtocolVersion() {
  238. return protocolVersion;
  239. }
  240. public void setProtocolVersion(String protocolVersion) {
  241. this.protocolVersion = protocolVersion;
  242. }
  243. protected synchronized void send(byte[] data, SocketAddress address) {
  244. try {
  245. this.sendBuffer.clear();
  246. this.sendBuffer.put(data);
  247. this.sendBuffer.flip();
  248. this.channel.send(this.sendBuffer, address);
  249. } catch (IOException e) {
  250. if(logger.isEnabledFor(Level.ERROR))
  251. {
  252. logger.error("I/O Exception uccured, caused by", e);
  253. }
  254. }
  255. }
  256. public boolean isRequest(String header) {
  257. return header.matches("[\\w]{4}(\\s|\\S)*");
  258. }
  259. @Override
  260. public void run() {
  261. if (logger.isDebugEnabled()) {
  262. logger.debug("MGCP stack started successfully on " + this.localAddress + ":" + this.port);
  263. }
  264. int length = 0;
  265. long start = 0;
  266. long finish = 0;
  267. long drift = 0;
  268. long latency = 0;
  269. while (!stopped) {
  270. start = System.currentTimeMillis();
  271. try {
  272. do {
  273. this.receiveBuffer.clear();
  274. address = (InetSocketAddress) this.channel.receive(this.receiveBuffer);
  275. this.receiveBuffer.flip();
  276. length = this.receiveBuffer.limit();
  277. if (length != 0) {
  278. receiveBuffer.get(b, 0, length);
  279. PacketRepresentation pr = this.prFactory.allocate();
  280. pr.setRawData(b);
  281. pr.setLength(length);
  282. pr.setRemoteAddress(address.getAddress());
  283. pr.setRemotePort(address.getPort());
  284. messageHandler.scheduleMessages(pr);
  285. }
  286. } while (this.address != null);
  287. //this is for async send
  288. this.provider.flush();
  289. finish = System.currentTimeMillis();
  290. drift = (finish - start);
  291. latency = delay - drift;
  292. if (latency >= 5) {
  293. try {
  294. Thread.currentThread().sleep(latency);
  295. } catch (InterruptedException e) {
  296. return;
  297. }
  298. }
  299. } catch (IOException e) {
  300. if (stopped) {
  301. break;
  302. }
  303. if(logger.isEnabledFor(Level.ERROR))
  304. {
  305. logger.error("I/O exception occured:", e);
  306. }
  307. continue;
  308. }catch(Exception e)
  309. {
  310. //catch everything, so worker wont die.
  311. if (stopped) {
  312. break;
  313. }
  314. if(logger.isEnabledFor(Level.ERROR))
  315. {
  316. logger.error("Unexpected exception occured:", e);
  317. }
  318. continue;
  319. }
  320. }
  321. if (logger.isDebugEnabled()) {
  322. logger.debug("MGCP stack stopped gracefully on" + this.localAddress + ":" + this.port);
  323. }
  324. }
  325. public Map<Integer, TransactionHandler> getLocalTransactions() {
  326. return localTransactions;
  327. }
  328. public Map<Integer, Integer> getRemoteTxToLocalTxMap() {
  329. return remoteTxToLocalTxMap;
  330. }
  331. public Map<Integer, TransactionHandler> getCompletedTransactions() {
  332. return completedTransactions;
  333. }
  334. static class ThreadFactoryImpl implements ThreadFactory {
  335. final ThreadGroup group;
  336. final AtomicInteger threadNumber = new AtomicInteger(1);
  337. final String namePrefix;
  338. protected int priority = Thread.NORM_PRIORITY;
  339. protected boolean isDaemonFactory = false;
  340. ThreadFactoryImpl() {
  341. SecurityManager s = System.getSecurityManager();
  342. group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
  343. namePrefix = "JainMgcpStackImpl-FixedThreadPool-" + "thread-";
  344. }
  345. public Thread newThread(Runnable r) {
  346. Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 5);
  347. t.setDaemon(this.isDaemonFactory);
  348. // if (t.getPriority() != Thread.NORM_PRIORITY)
  349. // t.setPriority(Thread.NORM_PRIORITY);
  350. t.setPriority(priority);
  351. return t;
  352. }
  353. public int getPriority() {
  354. return priority;
  355. }
  356. public void setPriority(int priority) {
  357. this.priority = priority;
  358. }
  359. public boolean isDaemonFactory() {
  360. return isDaemonFactory;
  361. }
  362. public void setDaemonFactory(boolean isDaemonFactory) {
  363. this.isDaemonFactory = isDaemonFactory;
  364. }
  365. }
  366. // Set the number of Transactions per second
  367. public void setTransactionRate(int rate) {
  368. delay = (1000 / rate);
  369. }
  370. }