PageRenderTime 71ms CodeModel.GetById 25ms app.highlight 40ms RepoModel.GetById 1ms app.codeStats 0ms

/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/*
 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}