FileDocCategorySizeDatePackage
SipClientConnectionImpl.javaAPI DocphoneME MR2 API (J2ME)89605Wed May 02 18:00:42 BST 2007gov.nist.microedition.sip

SipClientConnectionImpl

public class SipClientConnectionImpl extends Object implements javax.microedition.sip.SipClientConnection
Client SIP connection implementation. This code is in the public domain.

Fields Summary
private com.sun.midp.security.SecurityToken
classSecurityToken
Security token for SIP/SIPS protocol class
public static final int
TERMINATED
Terminated, the final state, in which the SIP connection has been terminated by error or closed
public static final int
CREATED
Created, SipClientConnection created from Connector or SipDialog
public static final int
INITIALIZED
Initialized, request has been initialized with initRequest(...) or initAck() or initCancel()
public static final int
STREAM_OPEN
Stream Open, OutputStream opened with openContentOutputStream(). Opening InputStream for received response does not trigger state transition.
public static final int
PROCEEDING
Proceeding, request has been sent, waiting for the response, or provisional 1xx response received. initCancel() can be called, which will spawn a new SipClientConnection which is in Initialized state
public static final int
UNAUTHORIZED
Unauthorized, transaction completed with response 401 (Unauthorized) or 407 (Proxy Authentication Required). The application can re-originate the request with proper credentials by calling setCredentials() method. After this the SipClientConnection is back in Proceeding state.
public static final int
COMPLETED
Completed, transaction completed with final response (2xx, 3xx, 4xx, 5xx, 6xx) in this state the ACK can be initialized. Multiple 200 OK responses can be received. Note different state transition for responses 401 and 407.
private static final int
MAX_NUM_RESPONSES
Max length of queue of incoming responses.
private javax.microedition.sip.SipDialog
sipDialog
the sip dialog this client transaction belongs to
private javax.microedition.sip.SipClientConnectionListener
sipClientConnectionListener
Listener to notify when a response will be received
private javax.microedition.sip.SipConnectionNotifier
sipConnectionNotifier
The Sip Connection notifier associated with this client connection
private javax.microedition.sip.SipRefreshListener
sipRefreshListener
Callback interface for refresh events
private String
refreshID
The refresh ID of the refresh task associated with this client connection if there is any
protected int
state
current state of this client transaction
private boolean
connectionOpen
flag to know the state of the connection (open or close)
private Vector
credentials
list of credentials that can be used for authorization
private Request
request
The request for this client transaction.
private Request
requestSavedBeforeACK
The initial request saved before ACK is sent.
private Response
response
the response to the actual request
private Vector
responses
The queue of responses for processing.
private Response
responseReceived
the last received response
private SDPOutputStream
contentOutputStream
content of the response body
private InputStream
contentInputStream
content from the request body
private gov.nist.siplite.address.URI
requestURI
The request URI created from the user, host, port and parameters attributes
private String
scheme
Scheme name
private String
user
the user part of the SIP URI
private String
host
the host part of the SIP URI
private int
port
the port Number on which to send the request, part of the SIP URI
private gov.nist.core.NameValueList
parameters
the parameters of the SIP URI
private String
userSipURI
the sip uri of the user
private gov.nist.siplite.stack.ClientTransaction
clientTransaction
the client Transaction for an INVITE request
private Thread
listeningThread
Handle for asynchronous listening thread.
private StackConnector
stackConnector
Current stack of connectors.
private boolean
isNotifierCreated
Flag of creating internal notifier.
private boolean
useRequest
Flag indicating which SIP message (request or response) should be used in getHeader()/getHeaders().
protected boolean
enableInitCancel
Permission of generating CANCEL request.
private int
countReoriginateRequest
Count of authorization requests (RFC 2617, 3.2.2).
Constructors Summary
protected SipClientConnectionImpl(gov.nist.siplite.address.SipURI inputURI, com.sun.midp.security.SecurityToken classSecurityToken)
Creates a sip Client Connection to send a request to the following SIP URI user@host:portNumber;parameters

param
inputURI input SIP URI
param
classSecurityToken Security token for SIP/SIPS protocol class


                                     
     
         
            
        this.user = inputURI.getUser();
        this.host = inputURI.getHost();
        this.port = inputURI.getPort();
        this.parameters = inputURI.getUriParms();
        this.classSecurityToken = classSecurityToken;

        this.scheme = inputURI.getScheme();

        connectionOpen = true;
        credentials = new Vector();

        try {
            stackConnector = StackConnector.getInstance(classSecurityToken);
        } catch (IOException ioe) {
        }

        // Create the REQUEST URI of the request
        try {
            requestURI = StackConnector.addressFactory.createURI(scheme + ":" +
                ((user == null) ? "" : (user + "@")) + host);

            if (port != -1) {
                ((SipURI) requestURI).setPort(port);
            }

            // handle the parameters
            if (parameters != null) {
                Enumeration parNames = parameters.getKeys();
                while (parNames.hasMoreElements()) {
                    String name = (String) parNames.nextElement();
                    String value = (String) parameters.getValue(name);
                    ((SipURI) requestURI).setParameter(name, value);
                }
            }
        } catch (ParseException pe) {
            throw new
                IllegalArgumentException("The request URI can not be" +
                                         " created, check the URI syntax");
        }

        state = CREATED;
        useRequest = true;
    
protected SipClientConnectionImpl(gov.nist.siplite.address.URI requestURI, javax.microedition.sip.SipDialog sipDialog)
Constructs the client connection implementation.

param
requestURI the target SIP session URI
param
sipDialog the current transaction state

        if (!requestURI.isSipURI()) {
            throw new IllegalArgumentException("URI is not correct");
        }

        SipURI sipURI = (SipURI) requestURI;
        SipDialogImpl sipDialogImpl = (SipDialogImpl) sipDialog;

        user = sipURI.getUser();
        host = sipURI.getHost();
        port = sipURI.getPort();
        parameters = sipURI.getUriParms();
        classSecurityToken = sipDialogImpl.getSecurityToken();
        scheme = requestURI.getScheme();
        connectionOpen = true;
        credentials = new Vector();

        // Create the REQUEST URI of the request
        this.requestURI = requestURI;
        this.sipDialog = sipDialog;
        this.refreshID = sipDialogImpl.getRefreshID();

        // this.sipClientConnectionListener =
        //    ((SipDialogImpl)sipDialog).getSipClientConnectionListener();

        try {
            stackConnector = StackConnector.getInstance(classSecurityToken);
        } catch (IOException ioe) {
            if (Logging.REPORT_LEVEL <= Logging.ERROR) {
                Logging.report(Logging.ERROR, LogChannels.LC_JSR180,
                               "Could not create SipClientConnectionImpl: " +
                               ioe);
            }
        }

        sipDialogImpl.dialog.setStack(stackConnector.getSipStack());

        state = CREATED;
        useRequest = true;
    
private SipClientConnectionImpl(Request request, javax.microedition.sip.SipConnectionNotifier sipConnectionNotifier, String sipUserURI)
Constructs the client connection implementation.

param
request the current transaction
param
sipConnectionNotifier the state transition notifier
param
sipUserURI the user session information

        connectionOpen = true;
        credentials = new Vector();
        // Create the REQUEST of the connection
        this.request = request;
        this.userSipURI = sipUserURI;
        // Create the REQUEST URI of the request
        this.requestURI = request.getRequestURI();
        this.sipConnectionNotifier = sipConnectionNotifier;

        try {
            stackConnector = StackConnector.getInstance(classSecurityToken);
        } catch (IOException ioe) {
            if (Logging.REPORT_LEVEL <= Logging.ERROR) {
                Logging.report(Logging.ERROR, LogChannels.LC_JSR180,
                               "Could not create SipClientConnectionImpl: " +
                               ioe);
            }
        }

        if (request.getMethod().equals(Request.CANCEL)) {
            state = INITIALIZED;
        } else {
            state = CREATED;
        }

        useRequest = true;
    
Methods Summary
public voidaddHeader(java.lang.String name, java.lang.String value)
Adds a header to the SIP message. If multiple header field values exist the header value is added topmost of this type of headers. The implementations MAY restrict the access to some headers according to RFC 3261.

param
name - name of the header, either in full or compact form. RFC 3261 p.32
param
value - the header value
throws
SipException - INVALID_STATE if header can not be added in this state.
INVALID_OPERATION if the system does not allow to add this header.
throws
IllegalArgumentException - MAY be thrown if the header or value is invalid

        if (state != INITIALIZED) {
            throw new SipException("the Header can not be add,"
                                   + " because of wrong state.",
                                   SipException.INVALID_STATE);
        }
        if (name == null) {
            throw new
                IllegalArgumentException("The header name can not be null");
        }
        if (value == null) {
            throw new
                IllegalArgumentException("The header value can not be null");
        }
        Header header = null;
        try {
            header = StackConnector.headerFactory.createHeader(name, value);
        } catch (ParseException pe) {
            throw new IllegalArgumentException("The header can not be created,"
                                               + " check if it is correct");
        }
        request.addHeader(header);
    
private voidchangeClientConnectionState()
Change the state of this Client Connection due to an incoming response.

        // Change the Client Connection state
        // If it's a trying, the state is PROCEEDING
        if (responseReceived.getStatusCode() / 100 == 1
            && state == PROCEEDING) {
            state = PROCEEDING;
        }
        // If it's a 401 or 407, the state is UNAUTHORIZED
        else if (state == PROCEEDING &&
                 (responseReceived.getStatusCode() == Response.UNAUTHORIZED ||
                  responseReceived.getStatusCode() ==
                  Response.PROXY_AUTHENTICATION_REQUIRED)) {
            state = UNAUTHORIZED;
        }
        // Otherwise this is COMPLETED
        else {
            state = COMPLETED;
        }
    
private voidchangeDialogState()
Change the state of the dialog due to an incoming response.

        // Change the dialog state

        // REGISTER method doesn't establish a dialog, so sipDialog
        // should be null in this case.
        // IMPL_NOTE: check if it is really null as supposed to be.
        if (sipDialog == null) {
            return;
        }

        SipDialogImpl sipDialogImpl = (SipDialogImpl) sipDialog;
        String method = responseReceived.getCSeqHeader().getMethod();
        if (!stackConnector.getSipStack().allowDialogStateChange(method)) {
            return;
        }
        int statusCode = responseReceived.getStatusCode();

        if (statusCode / 100 == 2) {
            // RFC 3261, section 13.2.2.4:
            // If the dialog identifier in the 2xx response matches the dialog
            // identifier of an existing dialog, the dialog MUST be transitioned
            // to the "confirmed" state.

            // sipDialog cannot be null here - this check was done
            // at the beginning of the function
            sipDialogImpl.setState(SipDialog.CONFIRMED);
            sipDialogImpl.setDialogID(responseReceived.getDialogId(false));

            if (statusCode == 200) {
                if (method.equals(Request.NOTIFY)) {
                    sipDialogImpl.handleNotify(request, null,
                                           responseReceived.getDialogId(false));
                    return;
                }

                // handle the un-Subscribe state
                if (method.equals(Request.SUBSCRIBE)) {
                    ExpiresHeader expiresHeader = (ExpiresHeader)
                        responseReceived.getHeader(ExpiresHeader.NAME);

                    // According to RFC3265, p. 6:
                    // "200-class responses to SUBSCRIBE requests also
                    // MUST contain an "Expires" header."
                    //
                    // But we have to handle a situation when it is missing.
                    // RFC 3265, p. 8:
                    // "200-class responses indicate that the subscription
                    // has been accepted".
                    //
                    // So, the dialog state is set to CONFIRMED even if
                    // no Expires header is present.
                    if (expiresHeader != null &&
                        expiresHeader.getExpires() == 0) { // unsubscribe
                        sipDialogImpl.terminateIfNoSubscriptions();
                    } else { // subscribe confirmation
                        sipDialogImpl.setState(SipDialog.CONFIRMED);
                        sipDialogImpl.setDialogID(
                            responseReceived.getDialogId(false));
                    }
                } else if (method.equals(Request.BYE)) {
                    // IMPL_NOTE: check the RFC 3261. Probably we have to terminate
                    // the dialog for the responses other than 200 OK.
                    sipDialogImpl.setWaitForBye(false);
                    sipDialogImpl.terminateIfNoSubscriptions();
                }
            }
        } else if (statusCode / 100 == 1) {
            // provisional response
            if (statusCode != 100) {
                if (sipDialog.getState() == SipDialogImpl.INITIALIZED) {
                    // switch to EARLY state
                    sipDialogImpl.setState(SipDialog.EARLY);
                }
                /*
                 * Add a check to verify if it is reliable provisional
                 * response.
                 */
                Header requireHeader =
                    (ParameterLessHeader) responseReceived.getHeader(
                        Header.REQUIRE);
                if (requireHeader != null) {
                    sipDialogImpl.isReliableProvReceived =
                        Header.isReliableTagPresent(
                            requireHeader.getHeaderValue());
                }
                // RFC 3261, 12.1:
                // Within this specification, only 2xx and 101-199
                // responses with a To tag ... will establish a dialog.
                // set the dialog ID
                sipDialogImpl.setDialogID(responseReceived.getDialogId(false));
            }

        } else { // another response code - switch to TERMINATED state
            // Remove the subscription if any
            sipDialogImpl.removeSubscription(response);

            // JSR180 - not from CONFIRMED state
            if (sipDialog.getState() != SipDialog.CONFIRMED) {
                if (method.equals(Request.INVITE)) {
                    sipDialogImpl.setWaitForBye(false);
                }
                sipDialogImpl.terminateIfNoSubscriptions();
            }
        }
    
protected voidclearResponse()
Clears the current response.

        this.response = null;
    
public voidclose()
Close the clientconnection.

see
javax.microedition.io.Connection#close()

        responses.removeAllElements(); // clear queue
        // cleanup
        if (isNotifierCreated && (sipConnectionNotifier != null)) {
            // try {
            sipConnectionNotifier.close();
            // } catch (IOException exc) { // ignore
            // }
        }

        // Removing the connection from the connection list held by
        // the stackConnector

        // StackConnector.getInstance().
        // clientConnectionList.removeElement(this);
        connectionOpen = false;
        listeningThread = null;
        state = TERMINATED;
    
public intenableRefresh(javax.microedition.sip.SipRefreshListener srl)
Enables the refresh on for the request to be sent. The method return a refresh ID, which can be used to update or stop the refresh.

param
srl - callback interface for refresh events, if this is null the method returns 0 and refresh is not enabled.
return
refresh ID. If the request is not refreshable returns 0.
throws
SipException - INVALID_STATE if the refresh can not be enabled in this state.

        if (state != INITIALIZED) {
            throw new SipException("can not enable the refresh,"
                                   + " because of wrong state.",
                                   SipException.INVALID_STATE);
        }

        if (srl == null) {
            return 0;
        }

        String method = request.getMethod();
        if (!method.equals(Request.REGISTER) &&
            !method.equals(Request.SUBSCRIBE) &&
            !method.equals(Request.PUBLISH)) {
            return 0;
        }

        // understand the refresh listener thing
        sipRefreshListener = srl;
        int taskID = RefreshManager
            .getInstance()
            .createRefreshTask(
                request,
                sipConnectionNotifier,
                sipRefreshListener,
                this);
        refreshID = String.valueOf(taskID);
        return taskID;
    
protected java.lang.StringgetCallIdentifier()
Return the Call Identifier of this client connection If there is no call id yet, this method return an empty String

return
the call Identifier

        if (request == null) {
            return "";
        }

        return request.getCallIdentifier();
    
protected gov.nist.siplite.stack.ClientTransactiongetClientTransaction()
Gets the current client transaction.

return
the current client transaction

        return clientTransaction;
    
public javax.microedition.sip.SipDialoggetDialog()
Returns the current SIP dialog. This is available when the SipConnection belongs to a created SipDialog and the system has received (or sent) provisional (101-199) or final response (200).

return
SipDialog object if this connection belongs to a dialog, otherwise returns null.

        if (sipDialog != null) {
            byte dialogState = sipDialog.getState();
            if ((dialogState != SipDialog.EARLY) &&
                (dialogState != SipDialog.CONFIRMED)) {
                return null;
            }
        }

        return sipDialog;
    
public java.lang.StringgetHeader(java.lang.String name)
Gets the header field value of specified header type.

param
name - name of the header type, either in full or compact form. RFC 3261 p.32
return
topmost header field value, or null if the current message does not have such a header or the header is for other reason not available (e.g. message not initialized). Javadoc is not clear on whether this method should be applied to request or response. The NIST implementation uses response; but that seems to be wrong.

        Message currentMessage = useRequest ? (Message) request :
            (Message) responseReceived;

        if (currentMessage == null) {
            // There 'request' may absent in the CREATED state
            return null;
        }

        Header nameHeader = currentMessage.getHeader(name);

        if (nameHeader == null) {
            return null;
        }

        return nameHeader.getHeaderValue();
    
public java.lang.String[]getHeaders(java.lang.String name)
Gets the header field value(s) of specified header type

param
name - name of the header, either in full or compact form. RFC 3261 p.32
return
array of header field values (topmost first), or null if the current message does not have such a header or the header is for other reason not available (e.g. message not initialized). Javadoc is not clear on whether this method should be applied to request or response. The NIST implementation uses response only to calculate the size; but that seems to be wrong.

        Message currentMessage = useRequest ? (Message) request :
            (Message) responseReceived;

        if (currentMessage == null) {
            // There 'request' may absent in the CREATED state
            return null;
        }

        HeaderList nameList = currentMessage.getHeaderList(name);

        if (nameList == null) {
            return null;
        }

        int size = nameList.size();

        if (size < 1) {
            return null;
        }

        String[] headerValues = new String[size];

        for (int count = 0; count < size; count++) {
            headerValues[count] =
                ((Header) nameList.elementAt(count)).getHeaderValue();
        }

        return headerValues;
    
public java.lang.StringgetMethod()
Gets the SIP method. Applicable when a message has been initialized or received.

return
SIP method name REGISTER, INVITE, NOTIFY, etc. Returns null if the method is not available.

        // another implementation returns null in terminated state; so do we
        if (null == request || TERMINATED == state) {
            return null;
        } else {
            return request.getMethod();
        }
    
public java.lang.StringgetReasonPhrase()
Gets SIP response reason phrase. Available when SipClientConnection is in Proceeding or Completed state or when SipServerConnection is in Initialized state.

return
reason phrase. Returns null if the reason phrase is not available.

        if (state != PROCEEDING && state != COMPLETED && state != UNAUTHORIZED
            || responseReceived == null) {
            return null;
        } else {
            return responseReceived.getReasonPhrase();
        }
    
public RequestgetRequest()
Gets the current request.

return
the current request

        return request;
    
public java.lang.StringgetRequestURI()
Gets Request-URI. Available when SipClientConnection is in Initialized state or when SipServerConnection is in Request Received state. Built from the original URI given in Connector.open(). See RFC 3261 p.35 (8.1.1.1 Request-URI)

return
Request-URI of the message. Returns null if the Request-URI is not available.

        if (state != INITIALIZED || request == null) {
            return null;
        } else {
            return request.getRequestURI().toString();
        }
    
protected ResponsegetResponse()
Gets the response.

return
the response instance

        return response;
    
protected javax.microedition.sip.SipConnectionNotifiergetSipConnectionNotifier()
Gets the assigned SipConnectionNotifier.

return
the current SipConnectionNotifier

        return sipConnectionNotifier;
    
protected gov.nist.siplite.SipStackgetSipStack()
Gets the current sip stack.

return
the current sip stack

        return stackConnector.getSipStack();
    
public intgetState()
Gets the current state of connection.

return
the current state

        return state;
    
public intgetStatusCode()
Gets SIP response status code. Available when SipClientConnection is in Proceeding or Completed state or when SipServerConnection is in Initialized state.

return
status code 1xx, 2xx, 3xx, 4xx, ... Returns 0 if the status code is not available.

        if (responseReceived == null) {
            return 0;
        } else {
            return responseReceived.getStatusCode();
        }
    
public voidhandleMatchingNotify(Request notifyRequest)
The stack connector notifies this class when it receive NOTIFY request matching the previous SUBSCRIBE or REFER request.

param
notifyRequest NOTIFY request to process

        SipDialogImpl sipDialogImpl = (SipDialogImpl) sipDialog;
        int state = sipDialogImpl.getState();
        SubscriptionStateHeader ssh = (SubscriptionStateHeader)
            notifyRequest.getHeader(Header.SUBSCRIPTION_STATE);
        boolean isUnsubscribe = (ssh != null && ssh.isTerminated());

        if (state == SipDialogImpl.INITIALIZED ||
            (state == SipDialogImpl.CONFIRMED && isUnsubscribe)) {
            String dialogId = notifyRequest.getDialogId(false);
            // sipDialogImpl.setState(isUnsubscribe ?
            //       SipDialog.TERMINATED : SipDialog.CONFIRMED);
            sipDialogImpl.setDialogID(dialogId);
            sipDialogImpl.handleNotify(notifyRequest, null, dialogId);
        }
    
public voidinitAck()
Convenience method to initialize SipClientConnection with SIP request method ACK. ACK can be applied only to INVITE request.

see
JSR180 spec, v 1.0.1, p 27

        if (state != COMPLETED) {
            throw new SipException("the ACK request can not be initialized,"
                                   + " because of wrong state.",
                                   SipException.INVALID_STATE);
        }

        // restore first request
        if (requestSavedBeforeACK != null) {
            request = requestSavedBeforeACK;
        }

        if (!request.getMethod().equals(Request.INVITE)) {
            // original request is non-INVITE
            throw new SipException("Original request is non-INVITE",
                                   SipException.INVALID_OPERATION);
        }

        // JSR180: For error responses (3xx-6xx) the ACK is sent
        // automatically by the system in transaction level.
        // If user initializes an ACK which has already been
        // sent an Exception will be thrown.
        int statusCode = 0;

        if (response != null) {
            statusCode = response.getStatusCode() / 100;
        } else if (responseReceived != null) {
            statusCode = responseReceived.getStatusCode() / 100;
        }

        if (responseReceived.getStatusCode() / 100 > 2) {
            throw new SipException("ACK request was already sent",
                                   SipException.INVALID_OPERATION);
        }

        requestSavedBeforeACK = request; // save request
        // This may throw SipException.
        request = clientTransaction.createAck();

        state = INITIALIZED;
        useRequest = true;
    
protected voidinitBye()
Initialize the session termination transaction.

        // Generate Request
        SipDialogImpl sipDialogImpl = (SipDialogImpl) sipDialog;
        try {
            request = sipDialogImpl.dialog.createRequest(Request.BYE);
            // handle the parameters
            if (parameters != null) {
                Enumeration parNames = parameters.getKeys();
                while (parNames.hasMoreElements()) {
                    String name = (String) parNames.nextElement();
                    String value = (String) parameters.getValue(name);
                    ((SipURI) requestURI).setParameter(name, value);
                }
            }
        } catch (SipException ex) {
            if (Logging.REPORT_LEVEL <= Logging.ERROR) {
                Logging.report(Logging.ERROR, LogChannels.LC_JSR180,
                               "Could not create BYE request! " + ex);
            }
        } catch (ParseException pe) {
            // intentionally ignored
            // setParameter() is used with verified parameters
        }
    
public javax.microedition.sip.SipClientConnectioninitCancel()
Convenience method to initialize SipClientConnection with SIP request method CANCEL.

return
A new SipClientConnection with preinitialized CANCEL request.
throws
SipException - INVALID_STATE if the request can not be set, because of wrong state (in SipClientConnection) or the system has already got the 200 OK response (even if not read with receive() method). INVALID_OPERATION if CANCEL method can not be applied to the current request method.
see
javax.microedition.sip.SipClientConnection#initCancel()

        if ((state != PROCEEDING) || !enableInitCancel) {
            throw new SipException("the CANCEL request can not be initialized,"
                                   + " because of wrong state.",
                                   SipException.INVALID_STATE);
        }

        // JSR180: The CANCEL request will be built according to
        // the original INVITE request within this connection.
        // Therefore building CANCEL request from not-INVITE
        // original request is not allowed.
        if (!request.getMethod().equals(Request.INVITE)) {
            throw new SipException("The method of original request " +
                                   "is not INVITE",
                                   SipException.INVALID_OPERATION);
        }

        // init the cancel request
        Request cancelRequest = clientTransaction.createCancel();
        SipClientConnection sipClientConnectionCancel =
            new SipClientConnectionImpl(
                cancelRequest,
                sipConnectionNotifier,
                userSipURI);
        // stackConnector.clientConnectionList.addElement(
        //     sipClientConnectionCancel);
        return sipClientConnectionCancel;
    
protected voidinitNotify()
Convenience method to initialize SipClientConnection with SIP request method NOTIFY. This method is copied from latest updates to NIST workspace

        // don't call this method out of dialog
        if (sipDialog == null) {
            throw new IllegalArgumentException(
                "Initialization NOTIFY request out of dialog");
        }

        // Generate Request
        SipDialogImpl sipDialogImpl = (SipDialogImpl) sipDialog;
        try {
            request = sipDialogImpl.dialog.createRequest(Request.NOTIFY);

            // handle the parameters
            if (parameters != null) {
                Enumeration parNames = parameters.getKeys();
                while (parNames.hasMoreElements()) {
                    String name = (String) parNames.nextElement();
                    String value = (String) parameters.getValue(name);
                    ((SipURI) requestURI).setParameter(name, value);
                }
            }
        } catch (SipException ex) {
            // IMPL_NOTE : cleanup
            if (Logging.REPORT_LEVEL <= Logging.ERROR) {
                Logging.report(Logging.ERROR, LogChannels.LC_JSR180,
                               "Could not create the notify request! " + ex);
            }
        } catch (ParseException pe) {
            // intentionally ignored
            // setParameter() is used with verified parameters
        }
    
public voidinitRequest(java.lang.String method, javax.microedition.sip.SipConnectionNotifier scn)
Initializes the connection.

param
method the operation to be performed
param
scn the state transition notifier
exception
IllegalArgumentException if the parameters are not valid
exception
SipException if a transition error occurs
see
SipClientConnection#initRequest


        if (method == null) {
            throw new IllegalArgumentException("The method can not be null");
        }

        if (state != CREATED) {
            throw new SipException("the request can not be initialized," +
                                   " because of wrong state.",
                                   SipException.INVALID_STATE);
        }

        if ((state == CREATED) &&
            ((method.equals(Request.ACK)) ||
             (method.equals(Request.CANCEL)))) {
            throw new SipException("the request can not be initialized," +
                                   " because of wrong state.",
                                   SipException.INVALID_STATE);
        }

        if (!Lexer.isValidName(method)) {
            throw new IllegalArgumentException("Invalid method: '" +
                                               method + "'");
        }

        // Affect the sip connection notifier
        if (scn != null) {
            sipConnectionNotifier = scn;
        } else {
                String transport = parameters.getValueDefault(
                  SIPConstants.GENERAL_TRANSPORT, SIPConstants.TRANSPORT_UDP);

                int localPort;

                // see RFC 3261, 18.1.1 - selecting port number
                if (transport.equalsIgnoreCase(SIPConstants.TRANSPORT_TLS)) {
                    localPort = SIPConstants.DEFAULT_TLS_PORT;
                } else { // other transport protocol
                    localPort = SIPConstants.DEFAULT_NONTLS_PORT;
                }

                // Check if sipConnectionNotifier already exists on the same
                // port. This is always true when UAC and UAS are in the same
                // application and the user has opened a connection (like
                // Connector.open("sip:5060");) before calling this function.
                Vector connectionNotifiersList =
                    stackConnector.getConnectionNotifiersList();

                try {
                    for (int i = 0; i < connectionNotifiersList.size(); i++) {
                        SipConnectionNotifier currNotifier =
                            (SipConnectionNotifier)
                            connectionNotifiersList.elementAt(i);

                        if ((currNotifier.getLocalPort() == localPort) &&
                            (((SipConnectionNotifierImpl) currNotifier).
                             getSipProvider().getListeningPoint().
                             getTransport().equalsIgnoreCase(transport))) {
                            sipConnectionNotifier = currNotifier;
                            break;
                        }
                    }
                } catch (IOException ioe) {
                    throw new SipException(ioe.getMessage(),
                                           SipException.GENERAL_ERROR);
                }

                if (sipConnectionNotifier == null) {
                    // Notifier was not found - create it.
                    try {
                        sipConnectionNotifier =
                            stackConnector.createSipConnectionNotifier(
                                localPort,
                                scheme.equals(SIPConstants.SCHEME_SIP),
                                transport, null);
                    } catch (IOException ioe) {
                        throw new SipException(ioe.getMessage(),
                                               SipException.GENERAL_ERROR);
                    }

                    isNotifierCreated = true;
                } // end if
        } // end else (scn == null)

        // redirect the methods ACK and CANCEL towards their helper
        // methods
        if (method.equals(Request.ACK)) {
            initAck();
        }

        if (method.equals(Request.BYE) && (sipDialog != null)) {
            initBye();
            state = INITIALIZED;
            useRequest = true;
            return;
        }

        if (method.equals(Request.NOTIFY)) {
            // if a dialog was not created, send NOTIFY out of dialog.
            if (sipDialog != null) {
                initNotify();
                state = INITIALIZED;
                useRequest = true;
                return;
            }
        }

        // Create request into dialog
        if (sipDialog != null) {
            byte dialogState = sipDialog.getState();
            if ((dialogState == SipDialog.EARLY) ||
                (dialogState == SipDialog.CONFIRMED)) {

                if (method.equals(Request.PRACK) &&
                    (! ((SipDialogImpl) sipDialog).isReliableProvReceived)) {
                    return;
                }

                // if (sipDialog.getState() == SipDialog.CONFIRMED) {
                // When SipDialog instance has CONFIRMED state, any new
                // request should be inside of dialog and have same
                // headers (To, From, Call-ID...) as original request.
                try {
                    request =
                        ((SipDialogImpl) sipDialog).dialog.createRequest(
                        method);
                } catch (SipException ex) {
                    throw ex;
                    // throw new IllegalArgumentException(
                    //     "Could not create the bye request! " + ex);
                }
                state = INITIALIZED;
                useRequest = true;
                return;
            }
        }

        // We lookup in a record store to see whether or not there is
        // the user sip uri
        String sipURI = null;
        try {
            RecordStore rs = RecordStore.openRecordStore("UserSipUri", false);
            RecordEnumeration re = rs.enumerateRecords(null, null, false);
            if (re.hasNextElement()) {
                int recordID = re.nextRecordId();
                sipURI = new String(rs.getRecord(recordID));
            }
        } catch (RecordStoreException rse) {
            // rse.printStackTrace();
        }

        // if the record store is null the sip uri for the user
        // it is an anonymous sip uri
        if (sipURI != null) {
            userSipURI = sipURI;
        }

        Address userAddress = null;
        try {
            userAddress = StackConnector.addressFactory
                .createAddress(userSipURI);
        } catch (ParseException pe) {
            throw new IllegalArgumentException("The system property UserSipUri"
                                         +
                                         "can not be parsed, check the syntax");
        }

        // Call ID
        CallIdHeader callIdHeader = null;
        String callId = SIPUtils.generateCallIdentifier
            (stackConnector.getSipStack().getIPAddress());
        callIdHeader = new CallIdHeader();
        callIdHeader.setCallId(callId);

        // CSeq
        CSeqHeader cSeqHeader = null;

        try {
            cSeqHeader = StackConnector.headerFactory.createCSeqHeader(1,
                method);
        } catch (ParseException pe) {
            throw new SipException("Problem during the creation" +
                                   " of the CSeqHeader",
                                   SipException.GENERAL_ERROR);
        }

        // From
        FromHeader fromHeader = null;
        try {
            fromHeader = StackConnector
                .headerFactory
                .createFromHeader(
                    userAddress,
                    StackConnector.generateTag());
        } catch (ParseException ex) {
            throw new SipException("Problem during the creation" +
                                   " of the FromHeader",
                                   SipException.GENERAL_ERROR);
        }

        // ToHeader
        Address toAddress = StackConnector
            .addressFactory.createAddress(
                requestURI);
        ToHeader toHeader = null;
        try {
            toHeader = StackConnector
                .headerFactory.createToHeader(
                    toAddress, null);
        } catch (ParseException ex) {
            throw new SipException("Problem during the creation" +
                                   " of the ToHeader",
                                   SipException.GENERAL_ERROR);
        }

        // ViaHeader
        Vector viaHeaders = new Vector();
        String viaLocalAddress;
        String viaTransport;
        int viaLocalPort;

        try {
            viaLocalAddress = sipConnectionNotifier.getLocalAddress();
            viaLocalPort = sipConnectionNotifier.getLocalPort();
            viaTransport =
                ((SipConnectionNotifierImpl) sipConnectionNotifier)
                .getSipProvider().getListeningPoint().getTransport();
        } catch (IOException ioe) {
            throw new SipException("Internal Error, cannot get " +
                                   "the local port or address",
                                   SipException.GENERAL_ERROR);
        }

        try {
            ViaHeader viaHeader = StackConnector
                .headerFactory
                .createViaHeader(
                    viaLocalAddress,
                    viaLocalPort,
                    viaTransport,
                    SIPUtils.generateBranchId());
            viaHeaders.addElement(viaHeader);
        } catch (ParseException ex) {
            throw new SipException("Problem during the creation" +
                                   " of the ViaHeaders",
                                   SipException.GENERAL_ERROR);
        }

        // Max Forward Header
        MaxForwardsHeader maxForwardsHeader =
            StackConnector.headerFactory.createMaxForwardsHeader(70);

        // generate the request
        try {
            request = StackConnector
                .messageFactory.createRequest(
                    requestURI,
                    method,
                    callIdHeader,
                    cSeqHeader,
                    fromHeader,
                    toHeader,
                    viaHeaders,
                    maxForwardsHeader);
        } catch (ParseException ex) {
            throw new SipException("Problem during the creation " +
                                   " of the Request " + method,
                                   SipException.GENERAL_ERROR);
        }

        /*
         * Contact header - not in MESSAGE request (RFC 3428, 4).
         * RFC 3903, p. 5:
         * The PUBLISH request MAY contain a Contact header field, but including
         * one in a PUBLISH request has no meaning in the event publication
         * context and will be ignored by the ESC (Event State Compositor).
         */
        if (!method.equals(Request.MESSAGE) &&
            !method.equals(Request.PUBLISH)) {
            ContactHeader contactHeader = null;

            try {
                if (isNotifierCreated) {
                    // Notifier was not passed as an argument to initRequest()
                    SipURI contactURI = StackConnector
                        .addressFactory
                        .createSipURI("anonymous", // name
                                      viaLocalAddress);
                    contactURI.setTransportParam(viaTransport);
                    contactURI.setPort(viaLocalPort);
                    contactHeader =
                        StackConnector
                        .headerFactory
                        .createContactHeader(
                            StackConnector
                            .addressFactory
                            .createAddress(contactURI));
                } else { // notifier is given
                    SipURI contactURI = StackConnector
                        .addressFactory
                        .createSipURI(
                            userSipURI
                            .substring(scheme.length() + 1,
                                       userSipURI.indexOf("@")),
                            sipConnectionNotifier.getLocalAddress());
                    contactURI
                        .setTransportParam(
                            ((SipConnectionNotifierImpl)
                             sipConnectionNotifier).
                            getSipProvider().getListeningPoint()
                            .getTransport());
                    contactHeader =
                        StackConnector
                        .headerFactory
                        .createContactHeader(
                            StackConnector
                            .addressFactory
                            .createAddress(contactURI));
                    contactURI.setPort(sipConnectionNotifier.getLocalPort());
                }
            } catch (IOException ioe) {
                throw new SipException("Internal Error, cannot get " +
                                       "the local port or address",
                                       SipException.GENERAL_ERROR);
            } catch (ParseException ex) {
                throw new SipException("Problem during the creation " +
                                       "of the Contact Header",
                                       SipException.GENERAL_ERROR);
            }

            // set the header
            request.addHeader(contactHeader);
        }

        state = INITIALIZED;
        useRequest = true;
    
protected voidnotifyResponseReceived(Response response, gov.nist.siplite.stack.ClientTransaction inputClientTransaction)
The stack connector notifies this class when it receive a new response.

param
response the repsonse event to be propagated
param
inputClientTransaction client transaction of this response

        // System.out.println(">>> Response received : \n" + response);
        int statusCode = response.getStatusCode();
        int statusGroup = statusCode / 100;

        boolean ignoreResponse = false;

        if (state == COMPLETED) { // 2xx responses only
            if (statusGroup != 2) {
                ignoreResponse = true;
            }
        } else if (state == PROCEEDING) {
            // If there is some credentials and the client connection is in an
            // UNAUTHORIZED state, the request is reoriginate automatically
            if (credentials.size() > 0 &&
                ((statusCode == Response.UNAUTHORIZED) ||
                 (statusCode == Response.PROXY_AUTHENTICATION_REQUIRED))) {
                this.responseReceived = response;
                if (sipDialog != null) {
                    ((SipDialogImpl) sipDialog).setState(SipDialog.TERMINATED);
                }
                reoriginateRequest();
                ignoreResponse = true;
            }
        } else { // not COMPLETED and PROCEEDING
            ignoreResponse = true;
        }

        // check the queue size
        if (responses.size() > MAX_NUM_RESPONSES) {
            if (Logging.REPORT_LEVEL <= Logging.ERROR) {
                Logging.report(Logging.ERROR, LogChannels.LC_JSR180,
                               "Queue of incoming SIP packets is overflow");
            }
            ignoreResponse = true;
        }

        if (ignoreResponse) { // ignore response
            return;
        }

        IncomingQueueElement incomingElement =
            new IncomingQueueElement(response, inputClientTransaction);
        responses.addElement(incomingElement); // put to queue

        this.response = response;

        if (state == PROCEEDING) {
            if (statusGroup == 1) {
                // provisional response
                // JSR180: SipClientConnection: initCancel: The method is
                // available when a provisional response
                // has been received.
                enableInitCancel = true;
            } else {
                // All responses from 200-699 are final
                // JSR180: SipClientConnection: initCancel:
                // Throws: SipException - INVALID_STATE if ... or the system
                // has already got the 200 OK response (even if not read with
                // receive() method).
                enableInitCancel = false;
            }
        }

        if (response.getCSeqHeaderNumber() == request.getCSeqHeaderNumber()) {
            synchronized (this) {
                notify();
            }
            // We notify the listener that a response has been received
            if (sipClientConnectionListener != null) {
                sipClientConnectionListener.notifyResponse(this);
            }
        }

        String method = response.getCSeqHeader().getMethod();

        if (method.equals(Request.PUBLISH)) {
            // RFC 3903, p. 6:
            // When updating previously published event state, PUBLISH
            // requests MUST contain a single SIP-If-Match header field
            // identifying the specific event state that the request is
            // refreshing, modifying or removing. This header field MUST
            // contain a single entity-tag that was returned by the ESC
            // in the SIP-ETag header field of the response to a previous
            // publication.
            Header hEtag = response.getHeader(Header.SIP_ETAG);

            if (hEtag != null) {
                Header hIfMatch = request.getHeader(Header.SIP_IF_MATCH);

                if (hIfMatch == null) {
                    Exception ex = null;
                    // Create SIP_IF_MATCH header
                    try {
                        hIfMatch = StackConnector.headerFactory.createHeader(
                            Header.SIP_IF_MATCH, hEtag.getHeaderValue());
                        request.addHeader(hIfMatch);
                    } catch (NullPointerException npe) {
                        ex = npe;
                    } catch (ParseException pe) {
                        ex = pe;
                    } catch (SipException se) {
                        ex = se;
                    }
                    if (ex != null) {
                        if (Logging.REPORT_LEVEL <= Logging.ERROR) {
                            Logging.report(Logging.ERROR,
                               LogChannels.LC_JSR180,
                               "scc.notifyResponseReceived(): can't create " +
                               "SIP-If-Match header:" + ex);
                            ex.printStackTrace();
                        }
                    }
                } else {
                    hIfMatch.setHeaderValue(hEtag.getHeaderValue());
                }

                request.removeHeader(Header.SIP_ETAG);
            } else {
                if (Logging.REPORT_LEVEL <= Logging.WARNING) {
                    Logging.report(Logging.WARNING, LogChannels.LC_JSR180,
                         "scc.notifyResponseReceived(): response to PUBLISH " +
                         "doesn't contain SIP-Etag header!");
                }
            }
        }

        // Schedule the refresh if required (i.e., if the method is
        // refreshable). Refresh time is taken from the response,
        // either from Expires header or from "expires" parameter
        // of Contact header as described in RFCs 3261, section 10.2.4
        // and RFC 3265, section 3.1.1.
        // If a listener for refresh event has been set, it is notified.
        if (statusCode == Response.OK) {
            scheduleRefresh(method);
        }

        // Notify the listener
        if (refreshID != null) {
            sipRefreshListener.refreshEvent(Integer.parseInt(refreshID),
                                            statusCode,
                                            response.getReasonPhrase());
        }
    
public java.io.InputStreamopenContentInputStream()
Returns InputStream to read SIP message body content.

return
InputStream to read body content
throws
java.io.IOException - if the InputStream can not be opened, because of an I/O error occurred.
throws
SipException - INVALID_STATE the InputStream can not be opened in this state (e.g. no message received).

        String errStateMsg = "the content input stream can not be open, " +
            "because of wrong state.";

        if ((state != COMPLETED) && (state != PROCEEDING)) {
            throw new SipException(errStateMsg, SipException.INVALID_STATE);
        }

        if (!connectionOpen) {
            throw new IOException("The Connection has been closed!");
        }

        if (responseReceived == null) {
            // Although openContentInputStream() is called in the correct
            // user-level state, we may have a situation when this method
            // is called before the response was received. In this case
            // the internal state of SCC is invalid and the proper exception
            // to throw is SipException.INVALID_STATE.
            throw new SipException(errStateMsg, SipException.INVALID_STATE);
        }

        ContentLengthHeader contentLengthHeader =
            responseReceived.getContentLengthHeader();
        if (contentLengthHeader == null) {
            throw new
                IOException("Response contains no content length header.");
        }

        int bodyLength = contentLengthHeader.getContentLength();

        if (bodyLength == 0) {
            throw new IOException("Response's body has zero length.");
        }

        byte[] buf = responseReceived.getRawContent();
        if (buf == null) { // body is empty
            throw new IOException("Body of SIP response is empty.");
        }
        contentInputStream = new ByteArrayInputStream(buf);
        return contentInputStream;
    
public java.io.OutputStreamopenContentOutputStream()
Returns OutputStream to fill the SIP message body content. When calling close() on OutputStream the message will be sent to the network. So it is equivalent to call send(). Again send() must not be called after closing the OutputStream, since it will throw Exception because of calling the method in wrong state. Before opening OutputStream the Content-Length and Content-Type headers has to se set. If not SipException.UNKNOWN_LENGTH or SipException.UNKNOWN_TYPE will be thrown respectively.

return
OutputStream to write body content
throws
IOException if the OutputStream can not be opened, because of an I/O error occurred.
throws
SipException INVALID_STATE the OutputStream can not be opened in this state (e.g. no message initialized). UNKNOWN_LENGTH Content-Length header not set. UNKNOWN_TYPE Content-Type header not set.

        if (state != INITIALIZED) {
            throw new SipException("the content output stream can not be open,"
                                   + " because of wrong state.",
                                   SipException.INVALID_STATE);
        }
        if (request.getHeader(Header.CONTENT_TYPE) == null) {
            throw new SipException(
                "Content-Type unknown, set the content-type "
                + "header first",
                SipException.UNKNOWN_TYPE);
        }
        if (request.getHeader(Header.CONTENT_LENGTH) == null) {
            throw new SipException("Content-Length unknown, set the "
                                   + "content-length header first",
                                   SipException.UNKNOWN_LENGTH);
        }
        if (!connectionOpen) {
            throw new IOException("The Connection has been closed!");
        }
        contentOutputStream = new SDPOutputStream(this);
        state = STREAM_OPEN;
        return contentOutputStream;
    
public booleanreceive(long timeout)
Receives SIP response message. The receive method will update the SipClientConnection with the last new received response. If no message is received the method will block until something is received or specified amount of time has elapsed.

param
timeout - the maximum time to wait in milliseconds. 0 = do not wait, just poll
return
Returns true if response was received. Returns false if the given timeout elapsed and no response was received.
throws
SipException - INVALID_STATE if the receive can not be called because of wrong state.
throws
IOException - if the message could not be received or because of network failure

        if ((state != PROCEEDING) && (state != COMPLETED)) {
            throw new SipException(SipException.INVALID_STATE);
        }

        // check for a response
        if (responses.isEmpty()) {
            // wait for a response during the time specified by the timeout
            if (timeout != 0) {
                synchronized (this) {
                    try {
                        // listeningThread.sleep(timeout);
                        wait(timeout);
                    } catch (InterruptedException ie) {
                    }
                }
            }
        }

        if (responses.isEmpty()) {
            return false; // queue is empty
        }

        // get first response from queue
        IncomingQueueElement incomingElement =
            (IncomingQueueElement) responses.firstElement();
        responseReceived = incomingElement.getResponse();
        responses.removeElementAt(0); // remove from queue
        useRequest = false;

        // change client transaction if need
        if (incomingElement.containsClientTransaction()) {
            clientTransaction = incomingElement.getClientTransaction();
        }

        if ((responseReceived.getStatusCode() / 100 == 2) &&
            (state == COMPLETED)) { // multiple responses
            // change dialog
            sipDialog = new SipDialogImpl(clientTransaction.getDialog(),
                                          sipConnectionNotifier,
                                          classSecurityToken);
            // transaction is INVITE, checked in
            // ClientTransaction.isMessageTransOrMult()
            ((SipDialogImpl) sipDialog).setWaitForBye(true);
            ((SipDialogImpl) sipDialog).setState(SipDialog.CONFIRMED);
        }

        changeDialogState();
        changeClientConnectionState();

        return true;
    
public voidremoveHeader(java.lang.String name)
Reomves header from the SIP message. If multiple header field values exist the topmost is removed. The implementations MAY restrict the access to some headers according to RFC 3261. If the named header is not found this method does nothing.

param
name - name of the header to be removed, either int full or compact form RFC 3261 p.32.
throws
SipException - INVALID_STATE if header can not be removed in this state.
INVALID_OPERATION if the system does not allow to remove this header.

        if (state != INITIALIZED) {
            throw new SipException("the Header can not be removed,"
                                   + " because of wrong state.",
                                   SipException.INVALID_STATE);
        }
        if (name == null) {
            throw new
                IllegalArgumentException("The header name can not be null");
        }
        request.removeHeader(name, true);
    
private voidreoriginateRequest()
Reoriginate the request with the proper credentials


        // clear dialog
        if (sipDialog != null) {
            if (sipDialog.getState() == SipDialog.TERMINATED) {
                sipDialog = null;
            }
        }

        DigestClientAuthentication authentication =
            new DigestClientAuthentication(credentials);

        // Reoriginate the request with the proper credentials
        Request newRequest = authentication.createNewRequest(
            stackConnector.sipStack,
            this.request,
            this.responseReceived,
            this.countReoriginateRequest);

        if (newRequest != null) {
            this.countReoriginateRequest++;
            this.request = newRequest;

            // The request has been reinitialized...
            state = INITIALIZED;
            useRequest = true;

            // ...so it is sent out
            try {
                this.send();
            } catch (IOException ioe) {
                // ioe.printStackTrace();
            }
        }
    
private voidscheduleRefresh(java.lang.String method)
Schedules refreshing of the request if required.

param
method SIP method of the message

        if (sipRefreshListener == null) {
            return;
        }

        if (! (method.equals(Request.REGISTER) ||
               method.equals(Request.SUBSCRIBE) ||
               method.equals(Request.PUBLISH))) {
            return;
        }

        // Remove the body of the message in case if the request is PUBLISH,
        // see RFC 3903, p. 7 (section 4.1):
        // +-----------+-------+---------------+---------------+
        // | Operation | Body? | SIP-If-Match? | Expires Value |
        // +-----------+-------+---------------+---------------+
        // | Initial   | yes   | no            | > 0           |
        // | Refresh   | no    | yes           | > 0           |
        // | Modify    | yes   | yes           | > 0           |
        // | Remove    | no    | yes           | 0             |
        // +-----------+-------+---------------+---------------+
        if (method.equals(Request.PUBLISH)) {
            request.removeContent();
        }

        // If the expires is set, the refresh is scheduled for the
        // duration of the expires
        int expires, minExpires = Integer.MAX_VALUE;

        // RFC 3265, p. 6:
        // An "expires" parameter on the "Contact" header has no semantics for
        // SUBSCRIBE and is explicitly not equivalent to an "Expires" header in
        // a SUBSCRIBE request or response.
        if (!method.equals(Request.SUBSCRIBE)) {
            ContactList cl = response.getContactHeaders();

            if (cl != null) {
                // Take a minimal expiration time from Contact headers.
                Enumeration en = cl.getElements();

                while (en.hasMoreElements()) {
                    ContactHeader contactHeader =
                        (ContactHeader) en.nextElement();

                    if (contactHeader != null) {
                        try {
                            expires = Integer.parseInt(
                                contactHeader.getExpires());
                            if ((expires > 0) && (expires < minExpires)) {
                                minExpires = expires;
                            }
                        } catch (NumberFormatException e) {
                            // intentionally ignored
                            // in the worst case 
                            // minExpires = Integer.MAX_VALUE
                        }
                    }
                }
            }
        } // end if (not SUBSCRIBE)

        // Take an expiration time from the Expires header.
        ExpiresHeader expiresHeader =
            (ExpiresHeader) response.getHeader(ExpiresHeader.NAME);

        if (expiresHeader != null) {
            expires = expiresHeader.getExpires();

            if ((expires > 0) && (expires < minExpires)) {
                minExpires = expires;
            }
        }

        if (minExpires == Integer.MAX_VALUE || minExpires < 0) {
            // Apply defaults.
            minExpires = 3600;
        }

        // System.out.println(">>> Refresh time: " + minExpires);

        /*
                 if (expiresHeader != null) {
            expires = expiresHeader.getExpires();
            System.out.println(">>> From header: " + expires);
                 }
         */

        if (minExpires != 0) {
            RefreshManager.getInstance().scheduleTask(refreshID, minExpires);
        }
    
public voidsend()
Sends the SIP message. Send must also close the OutputStream if it was opened.

throws
IOException if the message could not be sent or because of network failure
throws
InterruptedIOException if a timeout occurs while either trying to send the message or if this Connection object is closed during this send operation
throws
SipException INVALID_STATE if the message cannot be sent in this state.
INVALID_MESSAGE there was an error in message format

        sendRequestImpl(false);
    
private voidsendRequestImpl(boolean isRefreshRequest)
This function is an implementation for send(). It sends the SIP message. Send must also close the OutputStream if it was opened.

param
isRefreshRequest a flag indicating if the request to be sent is a refreshing request (isRefreshRequest is true) or it is a regular request (isRefreshRequest is false).
throws
IOException if the message could not be sent or because of network failure
throws
InterruptedIOException if a timeout occurs while either trying to send the message or if this Connection object is closed during this send operation
throws
SipException INVALID_STATE if the message cannot be sent in this state.
INVALID_MESSAGE there was an error in message format


        if (state != STREAM_OPEN && state != INITIALIZED) {
            throw new SipException("can not send the request, " +
                                   "because of wrong state.",
                                   SipException.INVALID_STATE);
        }

        if (!connectionOpen) {
            throw new IOException("The Connection has been closed!");
        }

        if (contentOutputStream != null) {
            contentOutputStream.setOpen(false);
            request.setContent(
                contentOutputStream.getByteArrayOutputStream().toByteArray(),
                (ContentTypeHeader) request.getHeader(Header.CONTENT_TYPE));

            contentOutputStream = null;
        }

        // Check mandatory headers (RFC3261, 8.1.1)
        String[] mandatoryHeaders = {
            Header.TO, Header.FROM, Header.CSEQ,
            Header.CALL_ID, Header.MAX_FORWARDS, Header.VIA};
        Vector mandatoryList = new Vector();

        // add header names for all types of requests
        for (int i = 0; i < mandatoryHeaders.length; i++) {
            mandatoryList.addElement(mandatoryHeaders[i]);
        }

        String method = request.getMethod();

        // RFC 3515, p. 6:
        // A REFER request MUST contain exactly one Refer-To header field value.
        if (method.equals(Request.REFER)) {
            mandatoryList.addElement(Header.REFER_TO);
        }

        // RFC3265, p. 15:
        // NOTIFY requests MUST contain a "Subscription-State" header with
        // a value of "active", "pending", or "terminated".
        if (method.equals(Request.NOTIFY)) {
            mandatoryList.addElement(Header.SUBSCRIPTION_STATE);
        }

        for (int i = 0; i < mandatoryList.size(); i++) {
            if (request.getHeader(
                (String) mandatoryList.elementAt(i)) == null) {
                throw new SipException("Header " +
                                     (String) mandatoryList.elementAt(i) +
                                     " is missed", SipException.INVALID_STATE);
            }
        }

        // add "tag" parameter to "From" header if necessary
        FromHeader fromHeader = (FromHeader) request.getHeader(Header.FROM);

        // it is not null - please see above
        if (!fromHeader.hasTag()) { // no "tag" parameter
            fromHeader.setTag(StackConnector.generateTag());
        }

        // Request-URI
        // Fix added per NIST cvs digest dated July 3, 2005
        // RFC 3261, 10.2:
        // Request-URI: ... The "userinfo" and "@" components of the
        // SIP URI MUST NOT be present.
        if (method.equals(Request.REGISTER)) {
            Address reqUriAddress = null;
            try {
                reqUriAddress =
                    StackConnector.addressFactory.createAddress(
                        requestURI.toString());
                if (reqUriAddress.isSIPAddress()) {
                    ((SipURI) reqUriAddress.getURI()).removeUser();
                    requestURI = reqUriAddress.getURI();
                }
                request.setRequestURI(requestURI);
            } catch (ParseException pe) {
                throw new SipException(
                    "The system property UserSipUri can not be " +
                    "parsed, check the syntax",
                    SipException.INVALID_OPERATION);
            }
        }

        // Check that the parameters specified in Via header match
        // those which were set in sipConnectionNotifier.
        ViaHeader requestViaHeader =
            (ViaHeader) request.getViaHeaders().getFirst();
        int viaPort = requestViaHeader.getPort();
        int localPort = sipConnectionNotifier.getLocalPort();
        String transport = requestViaHeader.getTransport();

        if (localPort != viaPort) {
            throw new IOException("Via port (" + viaPort + ") doesn't " +
                                  "match the listener's port (" + localPort +
                                  ")!");
        }

        SipConnectionNotifierImpl notifierImpl =
            (SipConnectionNotifierImpl) sipConnectionNotifier;

        if (!notifierImpl.getSipProvider().getListeningPoint().
            getTransport().equalsIgnoreCase(transport)) {
            throw new IOException("Via transport doesn't match " +
                                  "the listener's transport!");
        }

        // RFC 3903 (PUBLISH method), p. 5:
        // The Record-Route header field has no meaning in PUBLISH
        // requests or responses, and MUST be ignored if present.
        if (method.equals(Request.PUBLISH)) {
            request.removeHeader(Header.RECORD_ROUTE);
        }

        if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
            Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                           "Request to be sent : " + request);
        }

        // System.out.println(">>> Request to be sent: \n" + request);

        // Creates the Nist-Siplite client Transaction for this
        // request
        try {
            clientTransaction =
                ((SipConnectionNotifierImpl) sipConnectionNotifier).
                getSipProvider().getNewClientTransaction(request);
        } catch (TransactionUnavailableException tue) {
            throw new SipException("Cannot create a new Client " +
                                   " Transaction for this request",
                                   SipException.TRANSACTION_UNAVAILABLE);
        } catch (IllegalArgumentException iae) {
            throw new SipException("SCC.send(): IAE occured (1): " +
                               iae.getMessage(), SipException.GENERAL_ERROR);
        } catch (NullPointerException npe) {
            throw new SipException("SCC.send(): NPE occured (1): " +
                               npe.getMessage(), SipException.GENERAL_ERROR);
        }

        // Set the application data so that when the response comes in,
        // it will retrieve this SipClientConnection
        clientTransaction.setApplicationData(this);

        // Send the request

        if (method.equals(Request.ACK)) {
            Dialog dlg = clientTransaction.getDialog();
            if (dlg.isServer()) {
              // Switch from server side to client side for re-invite
              dlg.addTransaction(clientTransaction);
            }
            try {
                dlg.sendAck(request);
            } catch (IllegalArgumentException iae) {
                throw new SipException("SCC.send(): can't send ACK: " + iae,
                                       SipException.GENERAL_ERROR);
            }
            state = COMPLETED;
            return;
        } else if (sipDialog != null && !isRefreshRequest) {
            // if (method.equals(Request.BYE) ||
            //   method.equals(Request.NOTIFY)) {
            // If the request is a BYE, we must send it with the dialog

            // If the dialog is established, all further requests should
            // be sent within it.
            SipDialogImpl sipDialogImpl = (SipDialogImpl) sipDialog;

            if (method.equals(Request.SUBSCRIBE) ||
                method.equals(Request.REFER)) {
                // Add a subscription
                sipDialogImpl.addSubscription(
                    new Subscription(sipDialogImpl.getDialog(), request));
            } else if (method.equals(Request.INVITE)) {
                sipDialogImpl.setWaitForBye(true);
            }

            sipDialogImpl.dialog.sendRequest(clientTransaction);
            state = PROCEEDING;
            return;
        } else {
            clientTransaction.sendRequest();
        }

        // An INVITE, SUBSCRIBE or REFER has been sent, so a dialog need to
        // be created.
        if (stackConnector.getSipStack().isDialogCreated(method) &&
            !isRefreshRequest) {
            sipDialog = new SipDialogImpl(clientTransaction.getDialog(),
                                          sipConnectionNotifier,
                                          classSecurityToken);

            SipDialogImpl sipDialogImpl = (SipDialogImpl) sipDialog;
            sipDialogImpl.setRefreshID(refreshID);
            sipDialogImpl.setSipClientConnectionListener(
                sipClientConnectionListener);
            stackConnector.sipDialogList.addElement(sipDialog);

            // Add a subscription
            if (!method.equals(Request.INVITE)) {
                sipDialogImpl.addSubscription(
                    new Subscription(sipDialogImpl.getDialog(), request));
            } else {
                sipDialogImpl.setWaitForBye(true);
            }
        }

        // If the method is a REGISTER it means that we are using a
        // proxy so we put put the route of the proxy in the router
        if (request.getMethod().equals(Request.REGISTER)) {
            SipURI sipURI = (SipURI) request.getRequestURI();

            int requestPort = sipURI.getPort();
            if (requestPort == -1) { // get port from sipConnectionNotifier
                requestPort = sipConnectionNotifier.getLocalPort();
            }

            String requestTransport = sipURI.getTransportParam();

            if ((requestTransport == null) ||
                (requestTransport.length() < 1)) {
                // get transport from sipConnectionNotifier
                requestTransport =
                    ((SipConnectionNotifierImpl) sipConnectionNotifier).
                    getSipProvider().getListeningPoint().getTransport();
            }

            stackConnector.sipStack.getRouter().setOutboundProxy(
                sipURI.getHost()
                + ":" + requestPort
                + "/"
                + requestTransport);
            // outboundProxy = true;
        }

        // Refresh must be scheduled after receiving a response,
        // refer the comments at the end of notifyResponseReceived().
        // scheduleRefresh(request.getMethod(), request, false);

        state = PROCEEDING;
    
public voidsetCredentials(java.lang.String username, java.lang.String password, java.lang.String realm)
Sets credentials for possible digest authentication. The application can set multiple credential triplets (username, password, realm) for one SipClientConnection. The username and password are specified for certain protection domain, which is defined by the realm parameter. The credentials can be set: before sending the original request in Initialized state. The API implementation caches the credentials for later use. when 401 (Unauthorized) or 407 (Proxy Authentication Required) response is received in the Unauthorized state. The API implementation uses the given credentials to re-originate the request with proper authorization header. After that the SipClientConnection will be in Proceeding state.

param
username username (for this protection domain)
param
password user password (for this protection domain)
param
realm defines the protection domain
throws
SipException INVALID_STATE if the credentials can not be set in this state.
throws
NullPointerException - if the username, password or realm is null

        if (state != INITIALIZED && state != UNAUTHORIZED) {
            throw new SipException("can not set the credentials, " +
                                   "because of wrong state.",
                                   SipException.INVALID_STATE);
        }

        if (username == null || password == null || realm == null) {
            throw new NullPointerException();
        }

        Credentials credential = new Credentials(username, password, realm);
        credentials.addElement(credential);

        // reoriginate the requests with the proper credentials
        if (state == UNAUTHORIZED) {
            reoriginateRequest();
        }
    
public voidsetHeader(java.lang.String name, java.lang.String value)
Sets header value in SIP message. If the header does not exist it will be added to the message, otherwise the existing header is overwritten. If multiple header field values exist the topmost is overwritten. The implementations MAY restrict the access to some headers according to RFC 3261.

param
name - name of the header, either in full or compact form. RFC 3261 p.32
param
value - the header value
throws
SipException - INVALID_STATE if header can not be set in this state.
INVALID_OPERATION if the system does not allow to set this header.
throws
IllegalArgumentException - MAY be thrown if the header or value is invalid

        if (state != INITIALIZED) {
            throw new SipException("the Header can not be set,"
                                   + " because of wrong state.",
                                   SipException.INVALID_STATE);
        }

        if (name == null) {
            throw new
                IllegalArgumentException("The header name can not be null");
        }

        if (value == null) {
            throw new
                IllegalArgumentException("The header value can not be null");
        }

        Header header = null;

        try {
            header = StackConnector.headerFactory.createHeader(name, value);
            // name = header.getName(); // The source name might be expanded
        } catch (ParseException pe) {
            throw new IllegalArgumentException(pe.getMessage());
        }

        if (header == null) {
            throw new IllegalArgumentException("null header!");
        }

        // Response doesn't exist in the INITIALIZED state, so here
        // it's clear which message (request or response) to use.
        request.attachHeader(header, true, true);

        /*
                 if (request.getHeader(name) == null) {
            request.addHeader(header);
                 } else {
            request.attachHeader(header, true, true);
                 }
         */
    
public voidsetListener(javax.microedition.sip.SipClientConnectionListener sccl)
Sets the listener for incoming responses. If a listener is already set it will be overwritten. Setting listener to null will remove the current listener.

param
sccl - reference to the listener object. Value null will remove the existing listener.
throws
IOException - if the connection is closed.

        if (!connectionOpen) {
            throw new IOException("The Connection has been closed!");
        }
        this.sipClientConnectionListener = sccl;
    
public voidsetRequestURI(java.lang.String newUri)
Sets Request-URI explicitly. Request-URI can be set only in Initialized state.

param
newUri Request-URI
throws
IllegalArgumentException MAY be thrown if the URI is invalid
throws
SipException INVALID_STATE if the Request-URI can not be set, because of wrong state. INVALID_OPERATION if the Request-URI is not allowed to be set.

        if (state != INITIALIZED) {
            throw new SipException("the request URI can not be set, " +
                                   " because of wrong state.",
                                   SipException.INVALID_STATE);
        }

        if (newUri == null) {
            throw new IllegalArgumentException("Invalid URI");
        }

        URI uri = null;
        try {
            uri = StackConnector.addressFactory.createURI(newUri);
        } catch (ParseException pe) {
            throw new IllegalArgumentException("Invalid URI");
        }

        request.setRequestURI(uri);
        requestURI = uri;
    
protected voidupdateAndSendRequestFromRefresh(Request updatedRequest)
Updates and sends the request from the refresh.

param
updatedRequest the updated request
throws
IOException if the message could not be sent or because of network failure
throws
InterruptedIOException if a timeout occurs while either trying to send the message or if this Connection object is closed during this send operation
throws
SipException INVALID_STATE if the message cannot be sent in this state.
INVALID_MESSAGE there was an error in message format

        request = updatedRequest;
        state = INITIALIZED;

        // If the request to be refreshed creates a dialog (i.e. SUBSCRIBE),
        // the next request will be sent within a dialog using the rules for
        // sending in-dialog requests. To avoid it, isRefreshRequest parameter
        // is used.
        sendRequestImpl(true);
    
protected java.io.OutputStreamupdateRequestAndOpenOutputStream(Request updatedRequest)
Updates the request and calls openContentOutputStream() to fill the new message body.

param
updatedRequest the updated request
return
OutputStream to write body content
throws
IOException if the OutputStream can not be opened, because of an I/O error occurred.
throws
SipException INVALID_STATE the OutputStream can not be opened in this state (e.g. no message initialized). UNKNOWN_LENGTH Content-Length header not set. UNKNOWN_TYPE Content-Type header not set.

        request = updatedRequest;
        state = INITIALIZED;
        return openContentOutputStream();