001: /*
002: * Portions Copyright 2000-2007 Sun Microsystems, Inc. All Rights
003: * Reserved. Use is subject to license terms.
004: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
005: *
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU General Public License version
008: * 2 only, as published by the Free Software Foundation.
009: *
010: * This program is distributed in the hope that it will be useful, but
011: * WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * General Public License version 2 for more details (a copy is
014: * included at /legal/license.txt).
015: *
016: * You should have received a copy of the GNU General Public License
017: * version 2 along with this work; if not, write to the Free Software
018: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
019: * 02110-1301 USA
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
022: * Clara, CA 95054 or visit www.sun.com if you need additional
023: * information or have any questions.
024: */
025: package gov.nist.siplite;
026:
027: import java.util.*;
028: import gov.nist.siplite.stack.*;
029: import gov.nist.siplite.message.*;
030: import gov.nist.siplite.address.*;
031: import gov.nist.core.*;
032: import gov.nist.microedition.sip.*;
033: import com.sun.midp.security.SecurityToken;
034:
035: import com.sun.midp.log.Logging;
036: import com.sun.midp.log.LogChannels;
037: import gov.nist.siplite.SIPConstants;
038:
039: /**
040: * Implementation of SipStack.
041: *
042: * The JAIN-SIP stack is initialized by a set of properties (see the JAIN
043: * SIP documentation for an explanation of these properties).
044: * In addition to these, the following are meaningful properties for
045: * the NIST SIP stack (specify these in the property array when you create
046: * the JAIN-SIP statck).:
047: * <ul>
048: *
049: * <li><b>gov.nist.javax.sip.TRACE_LEVEL = integer </b><br>
050: * Currently only 16 and 32 is meaningful.
051: * If this is set to 16 or above, then incoming
052: * valid messages are logged in SERVER_LOG. If you set this to 32 and
053: * specify a DEBUG_LOG then vast amounts of trace information will be dumped
054: * in to the specified DEBUG_LOG. The server log accumulates the signaling
055: * trace.
056: * This can be viewed using the trace viewer tool .
057: * Please send us both the server log and debug log
058: * when reporting non-obvious problems.</li>
059: *
060: * @version JAIN-SIP-1.1
061: *
062: *
063: * <a href="{@docRoot}/uncopyright.html">This code is in the public domain.</a>
064: *
065: *
066: */
067: public class SipStack extends SIPTransactionStack {
068: /** Current listening points. */
069: private Hashtable listeningPoints;
070: /** Vector of SIP providers. */
071: private Vector sipProviders;
072: /** Flag indicating the provider has been initialized. */
073: protected boolean stackInitialized;
074: /** Pathto router. */
075: protected String routerPath;
076: /** Current eventscanner. */
077: protected EventScanner eventScanner;
078: /** Current SIP listener. */
079: protected SipListener sipListener;
080: /** Connector for SIP stack. */
081: protected StackConnector sipStackConnector;
082: /** Name of the logfile used to log messages */
083: private String logFilename = null;
084: /** Outbound proxy for this stack */
085: protected String outboundProxy = null;
086:
087: /** Outbound proxy port for this stack */
088: protected int outboundPort = -1;
089:
090: /**
091: * Constructor.
092: * @param stackConnector connection to use for SIP Stack
093: */
094: public void setStackConnector(StackConnector stackConnector) {
095: this .sipStackConnector = stackConnector;
096:
097: }
098:
099: /**
100: * Creates a new instance of SipStack.
101: */
102: protected SipStack() {
103: super ();
104: NistSipMessageFactoryImpl msgFactory = new NistSipMessageFactoryImpl(
105: this );
106: super .setMessageFactory(msgFactory);
107: this .listeningPoints = new Hashtable();
108: this .sipProviders = new Vector();
109: }
110:
111: /**
112: * Stops the SIP stack processing.
113: */
114: public void stopStack() {
115: super .stopStack();
116: this .eventScanner.stop();
117: this .sipStackConnector.releaseInstance();
118: }
119:
120: /**
121: * Construct a SIP Stack top match requested configuration.
122: * @param configurationProperties selectors for SIP Stack
123: * @param classSecurityToken security token for saving
124: */
125: public SipStack(ConfigurationProperties configurationProperties,
126: SecurityToken classSecurityToken)
127: throws PeerUnavailableException {
128: this ();
129: this .eventScanner = new EventScanner(this );
130: this .eventScanner.start();
131: String address = configurationProperties
132: .getProperty("javax.sip.IP_ADDRESS");
133:
134: /** Retrieve the stack IP address */
135: if (address == null)
136: throw new PeerUnavailableException("address not specified");
137: super .setHostAddress(address);
138:
139: /** Retrieve the stack name */
140: String name = configurationProperties
141: .getProperty("javax.sip.STACK_NAME");
142: if (name == null)
143: throw new PeerUnavailableException("stack name is missing");
144: super .setStackName(name);
145:
146: routerPath = "gov.nist.siplite.stack.DefaultRouter";
147: outboundProxy = configurationProperties
148: .getProperty("javax.sip.OUTBOUND_PROXY");
149:
150: Exception ex = null;
151: try {
152: Class routerClass = Class.forName(routerPath);
153: Router router = (Router) routerClass.newInstance();
154: if (outboundProxy != null)
155: router.setOutboundProxy(outboundProxy);
156: router.setSipStack(this );
157: super .setRouter(router);
158:
159: } catch (ClassNotFoundException cnfe) {
160: ex = cnfe;
161: } catch (InstantiationException ie) {
162: ex = ie;
163: } catch (IllegalAccessException iae) {
164: ex = iae;
165: }
166: if (ex != null) {
167: throw new PeerUnavailableException(
168: "Could not instantiate router");
169: }
170: if (outboundProxy != null) {
171: Hop hop = new Hop(outboundProxy);
172: this .outboundProxy = hop.getHost();
173: this .outboundPort = hop.getPort();
174: }
175:
176: /**
177: * Retrieve the EXTENSION Methods. These are used for instantiation
178: * of Dialogs.
179: */
180: String extensionMethods = configurationProperties
181: .getProperty("javax.sip.EXTENSION_METHODS");
182:
183: if (extensionMethods != null) {
184: gov.nist.core.StringTokenizer st = new gov.nist.core.StringTokenizer(
185: extensionMethods, ':');
186:
187: while (st.hasMoreChars()) {
188: String em = st.nextToken();
189: if (em.toUpperCase().equals(Request.BYE)
190: || em.toUpperCase().equals(Request.ACK)
191: || em.toUpperCase().equals(Request.OPTIONS))
192: throw new PeerUnavailableException(
193: "Bad extension method " + em);
194: else
195: this .addExtensionMethod(em.toUpperCase());
196: }
197: }
198:
199: /* Set the retransmission filter. For SIPLite this is always true */
200: this .retransmissionFilter = true;
201:
202: String maxConnections = configurationProperties
203: .getProperty("gov.nist.javax.sip.MAX_CONNECTIONS");
204: if (maxConnections != null) {
205: try {
206: this .maxConnections = Integer.parseInt(maxConnections);
207: } catch (NumberFormatException nfe) {
208: if (Logging.REPORT_LEVEL <= Logging.ERROR) {
209: Logging.report(Logging.ERROR,
210: LogChannels.LC_JSR180,
211: "max connections - bad value "
212: + nfe.getMessage());
213: }
214: }
215: }
216:
217: String threadPoolSize = configurationProperties
218: .getProperty("gov.nist.javax.sip.THREAD_POOL_SIZE");
219: if (threadPoolSize != null) {
220: try {
221: this .threadPoolSize = Integer.parseInt(threadPoolSize);
222: } catch (NumberFormatException nfe) {
223: if (Logging.REPORT_LEVEL <= Logging.ERROR) {
224: Logging.report(Logging.ERROR,
225: LogChannels.LC_JSR180,
226: "thread pool size - bad value "
227: + ex.getMessage());
228: }
229: }
230: }
231:
232: String transactionTableSize = configurationProperties
233: .getProperty("gov.nist.javax.sip.MAX_SERVER_TRANSACTIONS");
234: if (transactionTableSize != null) {
235: try {
236: this .transactionTableSize = Integer
237: .parseInt(transactionTableSize);
238: } catch (NumberFormatException nfe) {
239: if (Logging.REPORT_LEVEL <= Logging.ERROR) {
240: Logging.report(Logging.ERROR,
241: LogChannels.LC_JSR180,
242: "transaction table size - bad value "
243: + ex.getMessage());
244: }
245: }
246: }
247: this .setSecurityToken(classSecurityToken);
248: }
249:
250: /**
251: * Gets the sip listener for the stack.
252: * @return the SIP listener
253: */
254: public SipListener getSipListener() {
255: return this .sipListener;
256: }
257:
258: /**
259: * Creates a new peer ListeningPoint on this SipStack on a specified
260: * host, port and transport and returns a reference to the newly created
261: * ListeningPoint object. The newly created ListeningPoint is implicitly
262: * attached to this SipStack upon execution of this method, by adding the
263: * ListeningPoint to the {@link SipStack#getListeningPoints()} of this
264: * SipStack, once it has been successfully created.
265: *
266: * @param port the port of the new ListeningPoint.
267: * @param transport the transport of the new ListeningPoint.
268: * SipStack.
269: * @return The peer ListeningPoint attached to this SipStack.
270: */
271: public synchronized ListeningPoint createListeningPoint(int port,
272: String transport) throws TransportNotSupportedException,
273: IllegalArgumentException {
274: if (transport == null)
275: throw new NullPointerException("null transport");
276: if (port <= 0)
277: throw new IllegalArgumentException("bad port");
278: if (!Utils.equalsIgnoreCase(transport, "UDP")
279: && !Utils.equalsIgnoreCase(transport, "TCP"))
280: throw new TransportNotSupportedException("bad transport "
281: + transport);
282:
283: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
284: Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
285: "createListeningPoint " + transport + " / " + port);
286: }
287:
288: String key = ListeningPoint.makeKey(super .stackAddress, port,
289: transport);
290:
291: ListeningPoint lip = (ListeningPoint) listeningPoints.get(key);
292: if (lip != null) {
293: return lip;
294: } else {
295: try {
296: MessageProcessor messageProcessor = this
297: .createMessageProcessor(port, transport);
298: lip = new ListeningPoint(this , this .getIPAddress(),
299: port, transport);
300: lip.messageProcessor = messageProcessor;
301: messageProcessor.setListeningPoint(lip);
302: this .listeningPoints.put(key, lip);
303: return lip;
304: } catch (java.io.IOException ex) {
305: throw new IllegalArgumentException(ex.getMessage());
306: }
307: }
308: }
309:
310: /**
311: * Creates a new peer SipProvider on this SipStack on a specified
312: * ListeningPoint and returns a reference to the newly created SipProvider
313: * object. The newly created SipProvider is implicitly attached to this
314: * SipStack upon execution of this method, by adding the SipProvider to the
315: * {@link SipStack#getSipProviders()} of this SipStack, once it has been
316: * successfully created.
317: *
318: * @param listeningPoint the ListeningPoint the SipProvider is to
319: * be attached to in order to send and Receive messages.
320: * @return The peer SipProvider attached to this SipStack on the specified
321: * ListeningPoint.
322: * @throws ListeningPointUnavailableException thrown if another
323: * SipProvider is already using the ListeningPoint.
324: */
325: public SipProvider createSipProvider(ListeningPoint listeningPoint)
326: throws ObjectInUseException {
327: if (listeningPoint == null)
328: throw new NullPointerException("null listeningPoint");
329:
330: if (listeningPoint.sipProviderImpl != null) {
331: throw new ObjectInUseException("Provider already attached!");
332: }
333:
334: SipProvider provider = new SipProvider(this );
335: provider.setListeningPoint(listeningPoint);
336: this .sipProviders.addElement(provider);
337: return provider;
338: }
339:
340: /**
341: * Deletes the specified peer ListeningPoint attached to this SipStack. The
342: * specified ListeningPoint is implicitly detached from this SipStack upon
343: * execution of this method, by removing the ListeningPoint from the
344: * {@link SipStack#getListeningPoints()} of this SipStack.
345: *
346: * @param listeningPoint the peer SipProvider to be deleted from
347: * this SipStack.
348: * @exception ObjectInUseException thrown if the specified peer
349: * ListeningPoint cannot be deleted because the peer ListeningPoint is
350: * currently in use.
351: *
352: * @since v1.1
353: */
354: public void deleteListeningPoint(ListeningPoint listeningPoint)
355: throws ObjectInUseException {
356: if (listeningPoint == null)
357: throw new NullPointerException("null listeningPoint arg");
358: ListeningPoint lip = (ListeningPoint) listeningPoint;
359: // Stop the message processing thread in the listening point.
360: lip.messageProcessor.stop();
361: String key = lip.getKey();
362: this .listeningPoints.remove(key);
363: }
364:
365: /**
366: * Deletes the specified peer SipProvider attached to this SipStack. The
367: * specified SipProvider is implicitly detached from this SipStack upon
368: * execution of this method, by removing the SipProvider from the
369: * {@link SipStack#getSipProviders()} of this SipStack. Deletion of a
370: * SipProvider does not automatically delete the ListeningPoint from the
371: * SipStack.
372: *
373: * @param sipProvider the peer SipProvider to be deleted from
374: * this SipStack.
375: * @exception ObjectInUseException thrown if the specified peer
376: * SipProvider cannot be deleted because the peer SipProvider is currently
377: * in use.
378: *
379: */
380: public void deleteSipProvider(SipProvider sipProvider)
381: throws ObjectInUseException {
382:
383: if (sipProvider == null)
384: throw new NullPointerException("null provider arg");
385: SipProvider sipProviderImpl = (SipProvider) sipProvider;
386: if (sipProviderImpl.listeningPoint.messageProcessor.inUse()) {
387: throw new ObjectInUseException("Provider in use");
388: }
389: sipProviderImpl.sipListener = null;
390: sipProviders.removeElement(sipProvider);
391: }
392:
393: /**
394: * Gets the IP Address that identifies this SipStack instance. Every Sip
395: * Stack object must have an IP Address and only a single SipStack object
396: * can service a single IP Address. This value is set using the Properties
397: * object passed to the {@link
398: * SipFactory#createSipStack} method upon
399: * creation of the SIP Stack object.
400: *
401: * @return a string identifing the IP Address
402: * @since v1.1
403: */
404: public String getIPAddress() {
405: return super .getHostAddress();
406: }
407:
408: /**
409: * Returns an Iterator of existing ListeningPoints created by this
410: * SipStack. All of the peer SipProviders of this SipStack will be
411: * proprietary objects belonging to the same stack vendor.
412: *
413: * @return an Iterator containing all existing peer ListeningPoints created
414: * by this SipStack. Returns an empty Iterator if no ListeningPoints exist.
415: */
416: public java.util.Enumeration getListeningPoints() {
417: return this .listeningPoints.elements();
418: }
419:
420: /**
421: * Gets the listening point for a given transport and port.
422: * @param port the communication port
423: * @param transport the connection channel
424: * @return the listening port
425: */
426: public ListeningPoint getListeningPoint(int port, String transport) {
427: String key = ListeningPoint.makeKey(super .stackAddress, port,
428: transport);
429:
430: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
431: Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
432: "getListeningPoint " + port + "/" + transport);
433: }
434:
435: return (ListeningPoint) listeningPoints.get(key);
436: }
437:
438: /**
439: * Gets the outbound proxy specification. Return null if no outbound
440: * proxy is specified.
441: * @return the outbound proxy address
442: */
443: public String getOutboundProxy() {
444: return this .outboundProxy;
445: }
446:
447: /**
448: * This method returns the value of the retransmission filter helper
449: * function for User Agent Client and User Agent Server applications. This
450: * value is set using the Properties object passed to the
451: * {@link SipFactory#createSipStack} method upon
452: * creation of the SIP Stack
453: * object.
454: * <p>
455: * The default value of the retransmission filter boolean is
456: * <var>false</var>.
457: * When retransmissions are handled by the SipProvider the application will
458: * not receive {@link Timeout#RETRANSMIT} notifications encapsulated in
459: * {@link gov.nist.siplite.TimeoutEvent}'s. However an application will get
460: * notified when a the underlying transaction expired with
461: * {@link Timeout#TRANSACTION} notifications encapsulated in a
462: * {@link gov.nist.siplite.TimeoutEvent}.</p>
463: *
464: * @return the value of the retransmission filter, true if the filter
465: * is set false otherwise.
466: * @since v1.1
467: */
468: public boolean isRetransmissionFilterActive() {
469: return this .retransmissionFilter;
470: }
471:
472: /**
473: * Gets the Router object that identifies the default
474: * Routing policy of this
475: * SipStack. It also provides means to set an outbound proxy. This value is
476: * set using the Properties object passed to the
477: * {@link SipFactory#createSipStack} method upon
478: * creation of the SIP Stack object.
479: *
480: * @return a the Router object identifying the Router policy.
481: * @since v1.1
482: */
483: public Router getRouter() {
484: return super .getRouter();
485: }
486:
487: /**
488: * Returns an Iterator of existing peer SipProviders that have been
489: * created by this SipStack. All of the peer SipProviders of this
490: * SipStack will be proprietary objects belonging to the same stack vendor.
491: *
492: * @return an Iterator containing all existing peer SipProviders created
493: * by this SipStack. Returns an empty Iterator if no SipProviders exist.
494: */
495: public Enumeration getSipProviders() {
496: return this .sipProviders.elements();
497: }
498:
499: /**
500: * Gets the user friendly name that identifies this SipStack instance. This
501: * value is set using the Properties object passed to the
502: * {@link SipFactory#createSipStack} method upon
503: * creation of the SIP Stack object.
504: *
505: * @return a string identifing the stack instance
506: */
507: public String getStackName() {
508: return this .stackName;
509: }
510:
511: /**
512: * The default transport to use for via headers.
513: * @return the default transport
514: */
515: public String getDefaultTransport() {
516: if (isTransportEnabled("udp"))
517: return "udp";
518: else if (isTransportEnabled("tcp"))
519: return "tcp";
520: else
521: return null;
522: }
523:
524: /**
525: * Invoked when an error has ocurred with a transaction.
526: *
527: * @param transactionErrorEvent Error event.
528: */
529: public void transactionErrorEvent(
530: SIPTransactionErrorEvent transactionErrorEvent) {
531: Transaction transaction = (Transaction) transactionErrorEvent
532: .getSource();
533:
534: }
535: }
|