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: /*
026: * SipConnectionNotifierImpl.java
027: *
028: * Created on Jan 29, 2004
029: *
030: */
031: package gov.nist.microedition.sip;
032:
033: import java.util.Vector;
034:
035: import java.io.IOException;
036: import java.io.InterruptedIOException;
037:
038: import javax.microedition.io.Connection;
039:
040: import javax.microedition.sip.SipConnectionNotifier;
041: import javax.microedition.sip.SipDialog;
042: import javax.microedition.sip.SipException;
043: import javax.microedition.sip.SipServerConnection;
044: import javax.microedition.sip.SipServerConnectionListener;
045:
046: import gov.nist.siplite.ListeningPoint;
047: import gov.nist.siplite.ObjectInUseException;
048: import gov.nist.siplite.SipProvider;
049: import gov.nist.siplite.SipStack;
050: import gov.nist.siplite.message.Request;
051: import gov.nist.siplite.stack.Dialog;
052: import gov.nist.siplite.stack.ServerTransaction;
053: import gov.nist.siplite.header.*;
054: import gov.nist.siplite.address.*;
055: import com.sun.midp.security.SecurityToken;
056: import com.sun.midp.log.Logging;
057: import com.sun.midp.log.LogChannels;
058:
059: /**
060: * SIP Connection notifier implementation.
061: *
062: * <a href="{@docRoot}/uncopyright.html">This code is in the public domain.</a>
063: */
064: public class SipConnectionNotifierImpl implements SipConnectionNotifier { // , Runnable {
065: /**
066: * Security token for SIP/SIPS protocol class
067: */
068: private SecurityToken classSecurityToken;
069: /**
070: * Listener interface for incoming SIP requests.
071: */
072: private SipServerConnectionListener sipServerConnectionListener = null;
073: /**
074: * Messages received held in this vector
075: */
076: private Vector messageQueue = null;
077: /**
078: * flag to know the state of the connection (open or close)
079: */
080: private boolean connectionOpen;
081: /**
082: * listen address
083: */
084: private String localHost = null;
085: /**
086: * port number
087: */
088: private int localPort;
089: /**
090: * The Sip Provider for this connection Notifier
091: */
092: private SipProvider sipProvider = null;
093: /**
094: * Asynchronous notifier thread handle.
095: */
096: private Thread listeningThread = null;
097: /**
098: * Stack of associtaed connectors.
099: */
100: private StackConnector stackConnector = null;
101: /**
102: * Type value from
103: * connector.open(...type="application/vnd.company.battleships"...)
104: */
105: private String mimeType = null;
106: /**
107: * Boolean flag to indicate that this object is in waiting state during
108: * acceptAndOpen()
109: */
110: private boolean waitingForAcceptAndOpen = false;
111:
112: /**
113: * Indicates whether this SipConnectionNotifier is opened in shared mode
114: * or not
115: */
116: boolean sharedMode = false;
117:
118: /**
119: * Constructor called by the Connector.open() method
120: * @param sipProvider the network service provider
121: * @param localAddress the local connection end point
122: * @param localPort the port number on which the listener will wait for
123: * incoming messages
124: * @param classSecurityToken Security Token from SIP/SIPS protocol class
125: * @param mimeType MIMEtype associated with SipConnectionNotifier; used for
126: * filtering incomming SIP packets; null if not available
127: * @param sharedMode Flag to indicate that SipConnectionNotifier is in
128: * shared Mode
129: * @throws IOException if an I/OO error is detected
130: */
131: protected SipConnectionNotifierImpl(SipProvider sipProvider,
132: String localAddress, int localPort,
133: SecurityToken classSecurityToken, String mimeType,
134: boolean sharedMode) {
135: this .classSecurityToken = classSecurityToken;
136: this .sipProvider = sipProvider;
137: this .localHost = localAddress;
138: this .localPort = localPort;
139: this .mimeType = mimeType;
140: this .sharedMode = sharedMode;
141:
142: // Setting default value to attributes
143: connectionOpen = true;
144: messageQueue = new Vector();
145: try {
146: stackConnector = StackConnector
147: .getInstance(classSecurityToken);
148: } catch (IOException ioe) {
149: if (Logging.REPORT_LEVEL <= Logging.WARNING) {
150: Logging.report(Logging.WARNING, LogChannels.LC_JSR180,
151: "can't create SipConnectionNotifier : " + ioe);
152: ioe.printStackTrace();
153: }
154: }
155: }
156:
157: /**
158: * Accept and open the client connection.
159: * @return connection handle
160: * @see javax.microedition.sip.SipConnectionNotifier#acceptAndOpen()
161: */
162: public SipServerConnection acceptAndOpen() throws IOException,
163: InterruptedIOException, SipException {
164: if (!isConnectionOpen()) {
165: throw new InterruptedIOException("Connection was closed!");
166: }
167:
168: waitingForAcceptAndOpen = true;
169:
170: // IMPL_NOTE : handle the two others exceptions
171:
172: // create the sipServerConnection when a request is received
173: // through the processRequest method the processRequest method
174: // will add the Request in the Queue and notify()
175: if (messageQueue == null || messageQueue.size() < 1) {
176: synchronized (this ) {
177: try {
178: wait();
179: } catch (InterruptedException ie) {
180: if (Logging.REPORT_LEVEL <= Logging.WARNING) {
181: Logging
182: .report(
183: Logging.WARNING,
184: LogChannels.LC_JSR180,
185: "SipConnectionNotifierImpl.acceptAndOpen() :"
186: + "InterruptedException during wait() : "
187: + ie);
188: ie.printStackTrace();
189: }
190: } catch (IllegalMonitorStateException imse) {
191: if (Logging.REPORT_LEVEL <= Logging.WARNING) {
192: Logging
193: .report(
194: Logging.WARNING,
195: LogChannels.LC_JSR180,
196: "SipConnectionNotifierImpl.acceptAndOpen() :"
197: + "Illgeal monitor state during wait(): "
198: + imse);
199: imse.printStackTrace();
200: }
201: }
202: }
203: }
204:
205: if (!isConnectionOpen()) {
206: throw new InterruptedIOException(
207: "Connection is interrupted!");
208: }
209:
210: waitingForAcceptAndOpen = false;
211:
212: // Get the request received from the message queue
213: Request request = (Request) messageQueue.firstElement();
214: // Set up the dialog
215: SipDialog sipDialog = null;
216: ServerTransaction serverTransaction = (ServerTransaction) request
217: .getTransaction();
218: // Get the nist-siplite dialog
219: Dialog dialog = serverTransaction.getDialog();
220:
221: // If the method is an INVITE, SUBSCRIBE or REFER, we create
222: // a new dialog and add it to the list of dialog we have.
223: String method = request.getMethod();
224:
225: if (stackConnector.getSipStack().isDialogCreated(method)) {
226: // request URI
227: URI reqURI = null;
228: ContactList cl = request.getContactHeaders();
229: if (cl != null) {
230: if (cl.size() > 0) {
231: ContactHeader ch = (ContactHeader) cl.getFirst();
232: Address addr = (Address) ch.getValue();
233: if (addr != null) {
234: reqURI = addr.getURI();
235: }
236: }
237: }
238:
239: if (reqURI == null) {
240: reqURI = request.getFromHeader().getAddress().getURI();
241: }
242: sipDialog = new SipDialogImpl(dialog, this ,
243: classSecurityToken);
244: stackConnector.sipDialogList.addElement(sipDialog);
245:
246: if (method.equals(Request.INVITE)) {
247: ((SipDialogImpl) sipDialog).setWaitForBye(true);
248: }
249: } else if ((dialog != null)
250: && (!request.getMethod().equals(Request.CANCEL))) {
251: sipDialog = stackConnector.findDialog(dialog.getDialogId());
252: }
253: // IMPL_NOTE : check security access before returning the connection
254: SipServerConnection sipServerConnection = new SipServerConnectionImpl(
255: request, sipDialog, this );
256: // We remove the request from the queue
257: messageQueue.removeElementAt(0);
258:
259: return sipServerConnection;
260: }
261:
262: /**
263: * Sets a listener for incoming SIP requests. If a listener is
264: * already set it
265: * will be overwritten. Setting listener to null will remove the current
266: * listener.
267: * @param sscl listener for incoming SIP requests
268: * @throws IOException if the connection was closed
269: */
270: public void setListener(SipServerConnectionListener sscl)
271: throws IOException {
272: if (!isConnectionOpen())
273: throw new IOException("Connection was closed!");
274: this .sipServerConnectionListener = sscl;
275:
276: }
277:
278: /**
279: * Gets the local IP address for this SIP connection.
280: * @return local IP address. Returns null if the address is not available.
281: * @throws IOException - if the connection was closed
282: */
283: public String getLocalAddress() throws IOException {
284: if (!isConnectionOpen())
285: throw new IOException("Connection was closed!");
286: return localHost;
287: }
288:
289: /**
290: * Gets the local port for this SIP connection.
291: * @return local port number, that the notifier is listening to.
292: * Returns 0 if the port is not available.
293: * @throws IOException - if the connection was closed
294: */
295: public int getLocalPort() throws IOException {
296: if (!isConnectionOpen())
297: throw new IOException("Connection was closed!");
298: return this .localPort;
299: }
300:
301: /**
302: * Closes the connection notifier handling.
303: * @exception IOException if an error occurs while terminating
304: * @see javax.microedition.io.Connection#close()
305: */
306: public void close() throws IOException {
307: if (!isConnectionOpen()) { // connection is already closed
308: return;
309: }
310: // stop the listening points and sipProvider
311: if (sharedMode) {
312: stackConnector.closeSharedSipConnectionNotifier(mimeType);
313: } else {
314: SipStack sipStack = sipProvider.getSipStack();
315: ListeningPoint listeningPoint = sipProvider
316: .getListeningPoint();
317:
318: try {
319: sipStack.deleteListeningPoint(listeningPoint);
320: sipStack.deleteSipProvider(sipProvider);
321: } catch (ObjectInUseException oiue) {
322: throw new IOException(oiue.getMessage());
323: }
324: /*
325: * sipStack should be closed only if there are no more
326: * associated listening points
327: */
328: if (!sipStack.getListeningPoints().hasMoreElements()) {
329: sipStack.stopStack();
330: }
331: }
332:
333: // Removing the connection from the connection list held by
334: // the stackConnector
335: stackConnector.connectionNotifiersList.removeElement(this );
336:
337: // Notification that the connection has been closed
338: connectionOpen = false;
339:
340: if (waitingForAcceptAndOpen) {
341: synchronized (this ) {
342: try {
343: notify();
344: } catch (IllegalMonitorStateException imse) {
345: if (Logging.REPORT_LEVEL <= Logging.WARNING) {
346: Logging
347: .report(
348: Logging.WARNING,
349: LogChannels.LC_JSR180,
350: "SipConnectionNotifierImpl.close() :"
351: + "Illgeal monitor state during wait(): "
352: + imse);
353: imse.printStackTrace();
354: }
355: }
356: }
357: }
358: // listeningThread = null;
359: }
360:
361: /**
362: *
363: */
364:
365: /**
366: * The stack connector notifies this class when it receive a new request
367: * @param request - the new received request
368: */
369: protected void notifyRequestReceived(Request request) {
370: messageQueue.addElement(request);
371: // We notify the listener that a request has been received
372: if (this .sipServerConnectionListener != null)
373: try { // The user code is called
374: sipServerConnectionListener.notifyRequest(this );
375: } catch (Throwable exc) { // Ignore any user exception
376: if (Logging.REPORT_LEVEL <= Logging.WARNING) {
377: Logging
378: .report(Logging.WARNING,
379: LogChannels.LC_JSR180,
380: "Exception in notifyRequest() method has been thrown");
381: }
382: }
383: synchronized (this ) {
384: try {
385: notify();
386: } catch (IllegalMonitorStateException imse) {
387: imse.printStackTrace();
388: }
389: }
390: }
391:
392: /**
393: * Gets the sip provider.
394: * @return the sip provider
395: */
396: protected SipProvider getSipProvider() {
397: return sipProvider;
398: }
399:
400: /**
401: * Gets the connection state.
402: * @return true when connection is opened
403: */
404: protected boolean isConnectionOpen() {
405: return connectionOpen;
406: }
407:
408: /**
409: * Get the MIME type for this SipConnectionNotifier
410: *
411: * @return MIMEtype of the SipConnectionNotifier
412: */
413: protected String getMIMEType() {
414: return mimeType;
415: }
416:
417: /**
418: * Gets the current stack connector.
419: * @return the current stack connector
420: */
421: protected StackConnector getStackConnector() {
422: return stackConnector;
423: }
424:
425: }
|