FileDocCategorySizeDatePackage
SipProvider.javaAPI DocphoneME MR2 API (J2ME)29346Wed May 02 18:00:42 BST 2007gov.nist.siplite

SipProvider

public final class SipProvider extends Object implements SIPTransactionEventListener
Implementation of the JAIN-SIP provider interface.
version
JAIN-SIP-1.1 This code is in the public domain.

Fields Summary
protected SipListener
sipListener
Current SIP listener.
protected boolean
isActive
Flag to indicate the provider is active.
protected SipStack
sipStack
Current SIP Stack context.
protected ListeningPoint
listeningPoint
Current event listening filter.
protected ServerTransaction
currentTransaction
Curent server transactions.
private EventScanner
eventScanner
Curent event scanner processor.
Constructors Summary
protected SipProvider(SipStack sipStack)
Creates a new instance of SipProvider.

param
sipStack the current SIP stack context

        this.sipStack = sipStack;
        this.eventScanner = sipStack.eventScanner;
    
Methods Summary
public voidaddSipListener(SipListener sipListener)
This method registers the SipListener object to this SipProvider, once registered the SIP Listener can send events on the SipProvider and recieve events emitted from the SipProvider. As JAIN SIP resticts a unicast Listener special case, that is, that one and only one Listener may be registered on the SipProvider concurrently.

If an attempt is made to re-register the existing SipListener this method returns silently. A previous SipListener must be removed from the SipProvider before another SipListener can be registered to the SipProvider.

param
sipListener to be registered with the Provider.
throws
TooManyListenersException this exception is thrown when a new SipListener attempts to register with the SipProvider when another SipListener is already registered with this SipProvider.


        synchronized (sipStack) {
            Enumeration it = sipStack.getSipProviders();
            while (it.hasMoreElements()) {
                SipProvider provider = (SipProvider) it.nextElement();
                if (provider.sipListener  != null &&
                        provider.sipListener != sipListener)
                    throw new TooManyListenersException();
            }
        }

        if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
            Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                "add SipListener " + sipListener);
        }

        this.sipListener = sipListener;
        sipStack.sipListener = sipListener;

        synchronized (sipStack) {
            Enumeration it = sipStack.getSipProviders();
            while (it.hasMoreElements()) {
                SipProvider provider = (SipProvider) it.nextElement();
                provider.sipListener = sipListener;
            }
        }
    
public booleanequals(java.lang.Object obj)
Compares to this instance for equivalence.

param
obj object for comparison
return
true if the object is the same

        return super.equals(obj);
    
public voidequipADialogForTransaction(ServerTransaction transaction, Request sipRequest)
Find or create a dialog with the dialog-id obtained from the request. Initializing the dialog's route information. Bind the dialog and the transaction to each other.

param
transaction trasaction for the request
param
sipRequest request whose tags are the source of dialog-ID and route information

        // So I can handle timeouts.
        // IMPL_NOTE: do we need it here, or in the calling code?
        transaction.addEventListener(this);

        String dialogId = sipRequest.getDialogId(true);
        Dialog dialog = sipStack.getDialog(dialogId);

        if (dialog == null) {
            dialog = sipStack.createDialog(transaction);
        }

        dialog.setStack(this.sipStack);
        dialog.addRoute(sipRequest);

        if (dialog.getRemoteTag() != null &&
                dialog.getLocalTag() != null)  {
            this.sipStack.putDialog(dialog);
        }

        transaction.setDialog(dialog);
    
public ListeningPointgetListeningPoint()
Returns the ListeningPoint of this SipProvider. A SipProvider has a single Listening Point at any specific point in time.

see
ListeningPoint
return
the ListeningPoint of this SipProvider

        return this.listeningPoint;
    
public CallIdHeadergetNewCallId()
Returns a unique CallIdHeader for identifying dialogues between two SIP applications.

return
new CallId unique within the SIP Stack.

        String callId = SIPUtils.generateCallIdentifier
                (this.getSipStack().getIPAddress());
        CallIdHeader callid = new  CallIdHeader();
        callid.setCallId(callId);
        return callid;

    
public ClientTransactiongetNewClientTransaction(Request request)
Once an application wants to a send a new request it must first request a new client transaction identifier. This method is called by an application to create the client transaction befores it sends the Request via the SipProvider on that transaction. This methods returns a new unique client transaction identifier that can be passed to the stateful sendRequest method on the SipProvider and the sendAck/sendBye methods on the Dialog in order to send a request.

param
request the new Request message that is to handled statefully by the Provider.
return
a new unique client transation identifier
see
ClientTransaction
since
v1.1

        if (request == null) {
            throw new NullPointerException("null request");
        }

        if (request.getTransaction() != null) {
            throw new TransactionUnavailableException
                    ("Transaction already assigned to request");
        }

        if (request.getMethod().equals(Request.CANCEL)) {
            ClientTransaction ct = (ClientTransaction)
                sipStack.findCancelTransaction(request, false);

            if (ct != null) {
                ClientTransaction retval = sipStack.createClientTransaction
                        (ct.getMessageChannel());
                ((Transaction)retval).setOriginalRequest(request);
                ((Transaction)retval).addEventListener(this);
                sipStack.addTransaction((ClientTransaction)retval);
                ((ClientTransaction)retval).setDialog((Dialog)ct.getDialog());
                return retval;
            }

        }

        if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
            Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                "could not find existing transaction for "
                + request.getFirstLine());
        }

        String dialogId = request.getDialogId(false);
        Dialog dialog = sipStack.getDialog(dialogId);
        // isDialog - is request sends inside dialog
        boolean isDialog = false;
        if (dialog != null) {
            int dialogState = dialog.getState();
            if (dialogState == Dialog.EARLY_STATE ||
                dialogState == Dialog.CONFIRMED_STATE ||
                dialogState == Dialog.COMPLETED_STATE) {
                isDialog = true;
            }
        }
        Enumeration it = sipStack.getRouter().getNextHops(request, isDialog);

        if (it == null || !it.hasMoreElements())  {
            // could not route the request as out of dialog.
            // maybe the user has no router or the router cannot resolve
            // the route.
            // If this is part of a dialog then use the route from the dialog
            if (dialog != null)   {
                try {
                    Hop hop = dialog.getNextHop();

                    if (hop != null) {
                        ClientTransaction ct = null;

                        ct = (ClientTransaction) sipStack
                            .createMessageChannel(hop);
                        
                        String branchId = SIPUtils.generateBranchId();

                        if (request.getTopmostVia() != null) {
                            request.getTopmostVia().setBranch(branchId);
                        } else {
                            // Find a message processor to assign this
                            // transaction to.
                            MessageProcessor messageProcessor =
                                    this.listeningPoint.messageProcessor;
                            ViaHeader via = messageProcessor.getViaHeader();
                            request.addHeader(via);
                        }

                        ct.setOriginalRequest(request);
                        ct.setBranch(branchId);
                        ct.setDialog(dialog);
                        ct.addEventListener(this);

                        return ct;
                    } // end if
                } catch (Exception ex) {
                    throw new TransactionUnavailableException(ex.getMessage());
                }
            } else {
                throw new TransactionUnavailableException("no route!");
            }
        } else {
            try {
                // An out of dialog route was found. Assign this to the
                // client transaction.
                while (it.hasMoreElements()) {
                    Hop hop = (Hop) it.nextElement();

                    ClientTransaction ct = null;

                    ct = (ClientTransaction) sipStack
                        .createMessageChannel(hop);

                    // ClientTransaction ct =
                    //   (ClientTransaction) sipStack.createMessageChannel(hop);

                    if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                        Logging.report(Logging.INFORMATION,
                            LogChannels.LC_JSR180, "hop = " + hop + "ct " + ct);
                    }

                    if (ct == null) continue;

                    String branchId = SIPUtils.generateBranchId();

                    if (request.getTopmostVia() != null) {
                        request.getTopmostVia().setBranch(branchId);
                    } else {
                        // Find a message processor to assign
                        // this transaction to.
                        MessageProcessor messageProcessor =
                                listeningPoint.messageProcessor;
                        ViaHeader via = messageProcessor.getViaHeader();
                        request.addHeader(via);
                    }

                    ct.setOriginalRequest(request);
                    ct.setBranch(branchId);

                    if (sipStack.isDialogCreated(request.getMethod())) {
                        // create a new dialog to contain this transaction
                        // provided this is necessary.
                        // This could be a re-invite
                        // (noticed by Brad Templeton)

                        if (dialog != null) {
                            ct.setDialog(dialog);
                        } else {
                            sipStack.createDialog(ct);
                        }
                    } else {
                        ct.setDialog(dialog);
                    }

                    // The provider is the event listener for all transactions.
                    ct.addEventListener(this);
                    return (ClientTransaction) ct;
                } // end while()
            } catch (SipException ex) {
                throw new TransactionUnavailableException(ex.getMessage());
            }
        } // end else

        throw new TransactionUnavailableException
                ("Could not create transaction - could not resolve next hop! ");
    
public ServerTransactiongetNewServerTransaction(Request request)
An application has the responsibility of deciding to respond to a Request that does not match an existing server transaction. The method is called by an application that decides to respond to an unmatched Request statefully. This methods return a new unique server transaction identifier that can be passed to the stateful sendResponse methods in order to respond to the request.

param
request the initial Request message that the doesn't match an existing transaction that the application decides to handle statefully.
return
a new unique server transation identifier
throws
TransactionAlreadyExistsException if a transaction already exists that is already handling this Request. This may happen if the application gets retransmits of the same request before the initial transaction is allocated.
see
ServerTransaction
since
v1.1


        try {
            ServerTransaction transaction = null;
            Request sipRequest = (Request) request;
            if (sipStack.isDialogCreated(sipRequest.getMethod())) {
                if (sipStack.findTransaction((Request)request, true) != null)
                    throw new TransactionAlreadyExistsException
                            ("server transaction already exists!");
                transaction = (ServerTransaction) this.currentTransaction;
                if (transaction == null)
                    throw new  TransactionUnavailableException
                            ("Transaction not available");
                if (!transaction
                        .isMessagePartOfTransaction((Request) request)) {
                    throw new TransactionUnavailableException
                            ("Request Mismatch");
                }
                transaction.setOriginalRequest(sipRequest);
                try {
                    sipStack.addTransaction(transaction);
                } catch (IOException ex) {
                    throw new TransactionUnavailableException
                            ("Error sending provisional response");
                }
                equipADialogForTransaction(transaction, sipRequest);
            } else {
                transaction = (ServerTransaction)
                sipStack.findTransaction((Request) request, true);
                if (transaction != null)
                    throw new TransactionAlreadyExistsException
                            ("Transaction exists! ");
                transaction = (ServerTransaction) this.currentTransaction;
                if (transaction == null)
                    throw new TransactionUnavailableException
                            ("Transaction not available!");
                if (!transaction
                        .isMessagePartOfTransaction((Request) request))
                    throw new TransactionUnavailableException
                            ("Request Mismatch");
                transaction.setOriginalRequest(sipRequest);
                // Map the transaction.
                try {
                    sipStack.addTransaction(transaction);
                } catch (IOException ex) {
                    throw new TransactionUnavailableException
                            ("Could not send back provisional response!");
                }
                String dialogId =   sipRequest.getDialogId(true);
                Dialog dialog = sipStack.getDialog(dialogId);
                if (dialog != null) {
                    dialog.addTransaction(transaction);
                    dialog.addRoute(sipRequest);
                }
            }
            return transaction;
        } catch (RuntimeException ex) {
            ex.printStackTrace();
            throw ex;
        }
    
public SipStackgetSipStack()
Returns the SipStack that this SipProvider is attached to. A SipProvider can only be attached to a single SipStack object which belongs to the same SIP stack as the SipProvider.

see
SipStack
return
the attached SipStack.

        return  this.sipStack;
    
public voidhandleEvent(SipEvent sipEvent, Transaction transaction)
Handles the SIP event - because we have only one listener and we are already in the context of a separate thread, we dont need to enque the event and signal another thread.

param
sipEvent is the event to process.
param
transaction the current transaction


        if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
            Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                "handleEvent " + sipEvent +
                "currentTransaction = " + transaction +
                "this.sipListener = " + this.sipListener);
        }

        if (this.sipListener == null)
            return;

        EventWrapper eventWrapper = new EventWrapper();
        eventWrapper.sipEvent = sipEvent;
        eventWrapper.transaction = transaction;

        if (transaction != null &&
            transaction instanceof ClientTransaction) {
                ((ClientTransaction)transaction).setEventPending();
        }

        this.eventScanner.addEvent(eventWrapper);
    
public voidremoveSipListener(SipListener sipListener)
Removes the SipListener from this SipProvider. This method returns silently if the sipListener argument is not registered with the SipProvider.

param
sipListener - the SipListener to be removed from this SipProvider

        if (sipListener == this.sipListener) {
            this.sipListener = null;
        }
    
public voidsendRequest(Request request)
Sends specified Request and returns void i.e. no transaction record is associated with this action. This method implies that the application is functioning statelessly specific to this Request, hence the underlying SipProvider acts statelessly.

Once the Request message has been passed to this method, the SipProvider will forget about this Request. No transaction semantics will be associated with the Request and no retranmissions will occur on the Request by the SipProvider, if these semantics are required it is the responsibility of the application not the JAIN SIP Stack.

  • Stateless Proxy - A stateless proxy simply forwards every request it receives downstream and discards information about the request message once the message has been forwarded. A stateless proxy does not have any notion of a transaction.

since
v1.1
see
Request
param
request - the Request message to send statelessly
throws
SipException if implementation cannot send request for any reason

        // request sends out of dialog
        Enumeration it = sipStack.getRouter().getNextHops(request, false);
        if (it == null || !it.hasMoreElements()) {
            throw new SipException("could not determine next hop!",
                SipException.GENERAL_ERROR);
        }

        // Will slow down the implementation because it involves
        // a search to see if a transaction exists.
        // Just to double check adding some assertion
        // checking under debug.
        Transaction tr = sipStack.findTransaction(request, false);
        if (tr != null) {
            throw new SipException("Cannot send: stateless Transaction found!",
                SipException.GENERAL_ERROR);
        }

        while (it.hasMoreElements()) {
            Hop nextHop = (Hop) it.nextElement();

            Request sipRequest = request;
            String bid = sipRequest.getTransactionId();
            ViaHeader via = sipRequest.getTopmostVia();
            via.setBranch(bid);
            Request newRequest;

            // Do not create a transaction for this request. If it has
            // Mutliple route headers then take the first one off the
            // list and copy into the request URI.
            if (sipRequest.getHeader(Header.ROUTE) != null) {
                newRequest = (Request) sipRequest.clone();
                Enumeration rl =
                        newRequest.getHeaders(Header.ROUTE);
                RouteHeader route = (RouteHeader) rl.nextElement();
                newRequest.setRequestURI(route.getAddress().getURI());
                sipRequest.removeHeader(Header.ROUTE, true);
            } else {
                newRequest = sipRequest;
            }

            MessageChannel messageChannel =
                    sipStack.createRawMessageChannel(nextHop);
            try {
                if (messageChannel != null) {
                    messageChannel.sendMessage((Message)newRequest);
                    return;
                } else {
                    throw new SipException("Could not forward request.",
                        SipException.GENERAL_ERROR);
                }

            } catch (IOException ex) {
                continue;
            }
        }
    
public voidsendResponse(Response sipResponse)
Sends specified {@link Response} and returns void i.e. no transaction record is associated with this action. This method implies that the application is functioning as either a stateless proxy or a stateless User Agent Server.
  • Stateless proxy - A stateless proxy simply forwards every response it receives upstream and discards information about the response message once the message has been forwarded. A stateless proxy does not have any notion of a transaction.
  • Stateless User Agent Server - A stateless UAS does not maintain transaction state. It replies to requests normally, but discards any state that would ordinarily be retained by a UAS after a response has been sent. If a stateless UAS receives a retransmission of a request, it regenerates the response and resends it, just as if it were replying to the first instance of the request. A UAS cannot be stateless unless the request processing for that method would always result in the same response if the requests are identical. Stateless UASs do not use a transaction layer; they receive requests directly from the transport layer and send responses directly to the transport layer.

see
Response
param
sipResponse the Response to send statelessly.
throws
IOException if I/O error occured
throws
SipException if implementation cannot send response for any other reason
see
Response
since
v1.1

        ViaHeader via = sipResponse.getTopmostVia();
        if (via == null) {
            throw new SipException("No via header in response!",
                SipException.INVALID_MESSAGE);
        }

        int    port = via.getPort();
        String transport = via.getTransport();
        // check to see if Via has "received paramaeter". If so
        // set the host to the via parameter. Else set it to the
        // Via host.
        String host = via.getReceived();

        if (host == null) {
            host = via.getHost();
        }

        if (port == -1) {
            port = 5060; // IMPL_NOTE: move to SIPConstants
        }

        Hop hop = new Hop(host + ":" + port + "/" + transport);
        MessageChannel messageChannel = sipStack.createRawMessageChannel(hop);
        messageChannel.sendMessage(sipResponse);
    
public voidsetListeningPoint(ListeningPoint listeningPoint)
This method sets the listening point of the SipProvider. A SipProvider can only have a single listening point at any specific time. This method returns silently if the same listeningPoint argument is re-set on the SipProvider.

JAIN SIP supports recieving messages from any port and interface that a server listens on for UDP, on that same port and interface for TCP in case a message may need to be sent using TCP, rather than UDP, if it is too large. In order to satisfy this functionality an application must create two SipProviders and set identical listeningPoints except for transport on each SipProvder.

Multiple SipProviders are prohibited to listen on the same listening point.

param
listeningPoint of this SipProvider
see
ListeningPoint
since
v1.1

        if (listeningPoint == null)
            throw new NullPointerException("Null listening point");
        ListeningPoint lp = (ListeningPoint) listeningPoint;
        lp.sipProviderImpl = this;
        this.listeningPoint = (ListeningPoint) listeningPoint;

    
protected voidstop()
Stops processing messages for this provider. Post an empty message to our message processing queue that signals us to quit.

        // Put an empty event in the queue and post ourselves a message.
        if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
            Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                "Exiting provider");
        }

        synchronized (this) {
            listeningPoint.removeSipProvider();
        }

        this.eventScanner.stop();
    
public voidtransactionErrorEvent(SIPTransactionErrorEvent transactionErrorEvent)
Invoked when an error has ocurred with a transaction. Propagate up to the listeners.

param
transactionErrorEvent Error event.

        Transaction transaction =
                (Transaction) transactionErrorEvent.getSource();

        if (transactionErrorEvent.getErrorID() ==
                SIPTransactionErrorEvent.TRANSPORT_ERROR) {

            if (Logging.REPORT_LEVEL <= Logging.ERROR) {
                Logging.report(Logging.ERROR, LogChannels.LC_JSR180,
                    "TransportError occured on " + transaction);
            }

            //  handle Transport error as timeout.
            Object errorObject = transactionErrorEvent.getSource();
            Timeout timeout = Timeout.TRANSACTION;
            TimeoutEvent ev = null;

            if (errorObject instanceof ServerTransaction) {
                ev = new TimeoutEvent(this, (ServerTransaction)
                errorObject);
            } else {
                ev = new TimeoutEvent(this, (ClientTransaction)
                errorObject,
                        timeout);
            }
            this.handleEvent(ev, (Transaction) errorObject);

        } else {
            //  This is a timeout event.
            Object errorObject = transactionErrorEvent.getSource();
            Timeout timeout = Timeout.TRANSACTION;
            TimeoutEvent ev = null;

            if (errorObject instanceof ServerTransaction) {
                ev = new TimeoutEvent(this, (ServerTransaction)
                errorObject);
            } else {
                ev = new TimeoutEvent(this, (ClientTransaction)
                errorObject,
                        timeout);
            }
            this.handleEvent(ev, (Transaction) errorObject);

        }