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