Package org.cafesip.jiplet.sip

Source Code of org.cafesip.jiplet.sip.SipCommunicator

/*
* Created on Jul 19, 2005
*
* Copyright 2005 CafeSip.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*  http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.cafesip.jiplet.sip;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.Vector;

import javax.sip.ClientTransaction;
import javax.sip.Dialog;
import javax.sip.InvalidArgumentException;
import javax.sip.ListeningPoint;
import javax.sip.RequestEvent;
import javax.sip.ResponseEvent;
import javax.sip.ServerTransaction;
import javax.sip.SipException;
import javax.sip.SipProvider;
import javax.sip.TimeoutEvent;
import javax.sip.Transaction;
import javax.sip.TransactionAlreadyExistsException;
import javax.sip.TransactionState;
import javax.sip.address.Address;
import javax.sip.address.SipURI;
import javax.sip.address.URI;
import javax.sip.header.ContactHeader;
import javax.sip.header.FromHeader;
import javax.sip.header.RouteHeader;
import javax.sip.header.ToHeader;
import javax.sip.header.ViaHeader;
import javax.sip.message.Request;
import javax.sip.message.Response;

import org.cafesip.jiplet.Jiplet;
import org.cafesip.jiplet.JipletDialog;
import org.cafesip.jiplet.JipletException;
import org.cafesip.jiplet.JipletLogger;

/**
* This class enables SIP-related operations from within a jiplet class. It has
* methods to proxy requests, proxy responses and handle proxy timeouts. In
* addition, it can cancel proxy requests and other operations.
*
* @author Amit Chatterjee (copied heavily from JAIN-SIP proxy code)
*
*/
public class SipCommunicator
{
    private Jiplet jiplet;

    private boolean reset = true;

    ServerTransaction serverTransaction;

    private RequestEvent request;

    private ResponseEvent response;

    private TimeoutEvent timeout;

    private boolean addRecordRoute;

    private boolean stateful;

    private boolean presenceServer;

    /**
     * A constructor for this class.
     *
     */
    public SipCommunicator(Jiplet jiplet, RequestEvent request)
    {
        this.jiplet = jiplet;
        this.request = request;
        initStackInfo(jiplet);
    }

    public SipCommunicator(Jiplet jiplet, ResponseEvent response)
    {
        this.jiplet = jiplet;
        this.response = response;
        initStackInfo(jiplet);
    }

    public SipCommunicator(Jiplet jiplet, TimeoutEvent timeout)
    {
        this.jiplet = jiplet;
        this.timeout = timeout;
        initStackInfo(jiplet);
    }

    private void initStackInfo(Jiplet jiplet)
    {
    }

    /**
     * Cancels a proxy request. For this to work, the proxy object must be
     * obtained by calling the method requestEvent.getProxy() from the
     * processRequest() method of the jiplet class (or a class that extends
     * jiplet). If stateless, this method does nothing. Otherwise, it cancels the
     * previous operation by sending a CANCEL request to all the dialogs (call
     * legs). If the parameter statusCode is greater than 0, it also sends a
     * response with the provided status code to the leg that sent the original
     * request.
     *
     * @param statusCode
     *            A value greater than zero will result in a response to the
     *            user agent that started the dialog. If this is not desired,
     *            use -1 as this parameter.
     * @param reason
     *            A reason phrase along with the status code. If null, the
     *            reason phrase is not added.
     *
     */
    public void cancelRequest(int statusCode, String reason)
    {
        reset = true;

        if (stateful == false)
        {
            // we do not need to do anything for stateless proxying
            return;
        }

        if (request == null)
        {
            // we only process the cancel for a proxied request
            JipletLogger
                    .warn("This proxy object was not obtained by calling the"
                            + " method requestEvent.getProxy() from the processRequest() method of the jiplet class."
                            + " Therefore this proxy object cannot perform the cancel operation");
            return;
        }

        Dialog dialog = serverTransaction.getDialog();
        if (dialog == null)
        {
            JipletLogger
                    .warn("Could not obtain the SIP dialog while canceling a proxy."
                            + " Cannot cancel the proxy");
            return;
        }

        JipletDialog jdialog = jiplet.getDialog(serverTransaction.getDialog(),
                false);
        if (jdialog == null)
        {
            JipletLogger
                    .warn("Could not obtain the SIP dialog while canceling a proxy."
                            + " Cannot cancel the proxy");
            return;
        }

        TransactionsMapping transactionsMapping = jdialog
                .getTransactionsMapping();
        if (transactionsMapping == null)
        {
            JipletLogger
                    .warn("Could not obtain the jiplet dialog mapping while canceling a proxy."
                            + " This should not happen");
            return;
        }

        Transaction t = (Transaction) jdialog.getAttribute("firstTransaction");
        if (t == null)
        {
            JipletLogger
                    .warn("Could not find the first transaction while canceling a proxy."
                            + " Cannot cancel the proxy");
            return;
        }
       
        if (t instanceof ClientTransaction)
        {
            JipletLogger
                    .warn("Found the first transaction in the dialog to be a client transaction while canceling a proxy."
                            + " Cannot cancel the proxy");
            return;
        }

        ServerTransaction firstServerTransaction = (ServerTransaction) t;
        Vector transactions = transactionsMapping
                .getClientTransactions(firstServerTransaction);
        if (transactions == null || transactions.isEmpty())
        {
            // this is an error condition and should not happen
            JipletLogger
                    .error("While canceling a proxy request, found empty client transaction in the transaction map for the dialog. ");
            return;
        }

        for (Enumeration en = transactions.elements(); en.hasMoreElements();)
        {
            ClientTransaction ct = (ClientTransaction) en.nextElement();

            // check if the client transaction can be canceled.
            if (ct.getState().equals(TransactionState.COMPLETED)
                    || ct.getState().equals(TransactionState.TERMINATED))
            {
                continue;
            }

            try
            {
                JipletDialog clientJipletDialog = (JipletDialog) ct.getDialog()
                        .getApplicationData();
                if (clientJipletDialog != null)
                {
                    ClientTransaction client = clientJipletDialog
                            .getSipProvider().getNewClientTransaction(
                                    ct.createCancel());
                    client.sendRequest();
                }
                else
                {
                    ListeningPoint defaultLP = jiplet
                            .getListeningPointDefault();
                    ClientTransaction client = jiplet.getSipProvider(defaultLP)
                            .getNewClientTransaction(ct.createCancel());
                    client.sendRequest();
                }
            }
            catch (Exception e)
            {
                jiplet.error("Couldn't cancel client transaction : Exception "
                        + e.getClass().getName() + " : " + e.getMessage()
                        + "\n" + JipletLogger.getStackTrace(e));
            }
        }

        transactions.clear();

        try
        {
            if (statusCode >= 0)
            {
                if (firstServerTransaction.getState().equals(
                        TransactionState.COMPLETED)
                        || firstServerTransaction.getState().equals(
                                TransactionState.TERMINATED))
                {
                    // nothing to do
                    return;
                }

                Response response = jiplet.getMessageFactory().createResponse(
                        statusCode, firstServerTransaction.getRequest());
                if (reason != null)
                {
                    response.setReasonPhrase(reason);
                }
                firstServerTransaction.sendResponse(response);
            }
        }
        catch (Exception e)
        {
            jiplet.error("Exception " + e.getClass().getName() + " : "
                    + e.getMessage() + "\n" + JipletLogger.getStackTrace(e));
            return;
        }
    }

    /**
     * This method is similar to the cancelRequest(int statusCode) method except
     * that no response is sent to the originating request.
     */
    public void cancelRequest()
    {
        cancelRequest(-1, null);
    }
   
    /**
     * Call this method to drop an outbound leg for the case where other
     * outbound legs may be present and the event on this leg shouldn't drop
     * everything.
     *
     * @param clientTransaction for this leg
     * @return true if this leg was dropped, false otherwise
     */
    public boolean dropOutboundLeg(ClientTransaction clientTransaction)
    {
        if (clientTransaction == null || clientTransaction.getDialog() == null)
        {
            return false;
        }

        JipletDialog jd = jiplet
                .getDialog(clientTransaction.getDialog(), false);
        if (jd == null)
        {
            return false;
        }

        TransactionsMapping transactionsMapping = jd.getTransactionsMapping();
        ServerTransaction serverTransaction = transactionsMapping
                .getServerTransaction(clientTransaction);

        if (serverTransaction != null)
        {
            // if other outbound legs present, simply drop this leg out
            Vector clientsTransactionList = transactionsMapping
                    .getClientTransactions(serverTransaction);
            if (clientsTransactionList != null
                    && clientsTransactionList.size() > 1)
            {
                transactionsMapping.removeMapping(clientTransaction);
                return true;
            }
        }

        return false;
    }
   
    /**
     * This method is used to proxy a received SIP request message.
     *
     * @param uris
     *            list of URIs to proxy a SIP request message to. For ACK, CANCEL
     *            and BYE messages for a stateful proxy, the contact list can be
     *            an empty array list. The proxy will automatically proxy the
     *            request based on the dialog
     * @param addRecordRoute
     *            true if record route is to be added.
     * @param true
     *            if stateful proxy is required.
     * @param true
     *            if the proxy server is a presence server
     * @throws SipException
     * @throws ParseException
     * @throws JipletException
     * @throws InvalidArgumentException
     *
     */
    public void proxyRequest(ArrayList uris, boolean addRecordRoute,
            boolean stateful, boolean presenceServer)
            throws InvalidArgumentException, JipletException, ParseException,
            SipException
    {
        try
        {
            if (request == null)
            {
                throw new JipletException(
                        "The request cannot be proxied from a non-request event");
            }

            this.addRecordRoute = addRecordRoute;
            this.stateful = stateful;
            this.presenceServer = presenceServer;

            SipProvider provider = (SipProvider) request.getSource();
            Request msg = request.getRequest();

            if (serverTransaction == null)
            {
                if (jiplet.isDebugEnabled() == true)
                {
                    jiplet
                            .debug("The server transaction is being obtained first time for this proxy object");
                }
                serverTransaction = request.getServerTransaction();
            }
            else
            {
                if (jiplet.isDebugEnabled() == true)
                {
                    jiplet
                            .debug("The server transaction already exists. This proxy object is being re-used to send another request");
                }           
            }

            // PROXY BEHAVIOR

            /*
             * RFC 3261: 16.2: For all new requests, including any with unknown
             * methods, an element intending to proxy the request MUST:
             *
             * 1. Validate the request (Section 16.3)
             *
             * 2. Preprocess routing information (Section 16.4)
             *
             * 3. Determine target(s) for the request (Section 16.5)
             *
             * 4. Forward the request to each target (Section 16.6)
             *
             * 5. Process all responses (Section 16.7)
             */

            // 1. Validate the request (Section 16.3)
            RequestValidation valid = new RequestValidation(jiplet, presenceServer);
            if (valid.validateRequest(provider, request.getRequest(),
                    serverTransaction) == false)
            {
                jiplet
                        .warn("A request message that is to be proxied failed validation."
                                + " Not going to proxy it. An appropriate response has been sent");
                return;
            }

            // Let's check if the ACK is for the proxy: if there is no Route
            // header: it is mandatory for the ACK to be forwarded

            if (msg.getMethod().equals(Request.ACK))
            {
                ListIterator routes = msg.getHeaders(RouteHeader.NAME);
                if (routes == null || !routes.hasNext())
                {
                    if (jiplet.isDebugEnabled() == true)
                    {
                        jiplet.debug("Proxying an ACK "
                                + " targeted for the proxy, we ignore it");
                    }
                    return;
                }
            }

            if (stateful == true)
            {
                String method = msg.getMethod();
                // Methods that creates dialogs, so that can
                // generate transactions
                if (method.equals(Request.INVITE)
                        || method.equals(Request.SUBSCRIBE))
                {
                    try
                    {
                        if (serverTransaction == null)
                        {
                            serverTransaction = provider
                                    .getNewServerTransaction(msg);
                        }

                        TransactionsMapping transactionsMapping = jiplet
                                .getDialog(serverTransaction.getDialog(), true)
                                .getTransactionsMapping();
                        if (transactionsMapping == null)
                        {
                            transactionsMapping = new TransactionsMapping(
                                    jiplet, serverTransaction);
                           
                            // save server transaction side SipProvider in JipletDialog
                            Dialog serverDialog = serverTransaction.getDialog();
                            JipletDialog jd = jiplet.getDialog(serverDialog, true);
                            jd.setSipProvider((SipProvider) request.getSource());
                        }
                    }
                    catch (TransactionAlreadyExistsException e)
                    {
                        if (jiplet.isDebugEnabled() == true)
                        {
                            jiplet.debug("The request message to be proxied"
                                    + " is a retransmission, we drop it!");
                        }
                    }
                }
            }

            // 2. Preprocess routing information (Section 16.4)

            /*
             * The proxy MUST inspect the Request-URI of the request. If the
             * Request-URI of the request contains a value this proxy previously
             * placed into a Record-Route header field (see Section 16.6 item
             * 4), the proxy MUST replace the Request-URI in the request with
             * the last value from the Route header field, and remove that value
             * from the Route header field. The proxy MUST then proceed as if it
             * received this modified request. ..... (idem to below:) 16.12. The
             * proxy will inspect the URI in the topmost Route header field
             * value. If it indicates this proxy, the proxy removes it from the
             * Route header field (this route node has been reached).
             */

            ListIterator routes = msg.getHeaders(RouteHeader.NAME);
            if (routes != null)
            {
                if (routes.hasNext())
                {
                    RouteHeader routeHeader = (RouteHeader) routes.next();
                    Address routeAddress = routeHeader.getAddress();
                    SipURI routeSipURI = (SipURI) routeAddress.getURI();

                    String h = routeSipURI.getHost();
                    int port = routeSipURI.getPort();

                    if (jiplet.hasAddress(h, port) == true)
                    {
                        if (jiplet.isDebugEnabled() == true)
                        {
                            jiplet
                                    .debug("A request message to be proxied has this proxy in the route header. "
                                            + " We are going to remove the first route from "
                                            + " the RouteHeader");
                        }
                        routes.remove();
                    }
                }
            }

            // 3. Determine target(s) for the request (Section 16.5)

            /*
             * The set of targets will either be predetermined by the contents
             * of the request or will be obtained from an abstract location
             * service. Each target in the set is represented as a URI.
             */

            /*
             * If the Request-URI of the request contains an maddr parameter,
             * the Request-URI MUST be placed into the target set as the only
             * target URI, and the proxy MUST proceed to Section 16.6.
             */

            URI requestURI = msg.getRequestURI();
            if (requestURI.isSipURI())
            {
                SipURI requestSipURI = (SipURI) requestURI;
                if (requestSipURI.getMAddrParam() != null)
                {
                    uris.clear();
                    uris.add(requestURI);
                    if (jiplet.isDebugEnabled() == true)
                        jiplet
                                .debug("While proxying a request, "
                                        + " the only target is the Request-URI (mAddr parameter)");

                    // 4. Forward the request
                    RequestForwarding forwarder = new RequestForwarding(jiplet,
                            this, request,
                            serverTransaction, stateful, addRecordRoute);
                    forwarder.forwardRequest(uris);
                    return;
                }
            }

            if (stateful == true)
            {
                // Forward to next hop but dont reply OK right away for the
                // BYE. Bye is end-to-end not hop by hop!
                if (msg.getMethod().equals(Request.BYE))
                {
                    if (serverTransaction == null)
                    {
                        if (jiplet.isDebugEnabled() == true)
                            jiplet
                                    .debug("While proxying a request, null server transaction for BYE");
                        return;
                    }

                    Dialog d = serverTransaction.getDialog();
                    TransactionsMapping transactionsMapping = jiplet.getDialog(
                            d, true).getTransactionsMapping();
                    Dialog peerDialog = transactionsMapping
                            .getPeerDialog(serverTransaction);
                    Request clonedRequest = (Request) msg.clone();
                    FromHeader from = (FromHeader) clonedRequest
                            .getHeader(FromHeader.NAME);
                    from.removeParameter("tag");
                    ToHeader to = (ToHeader) clonedRequest
                            .getHeader(ToHeader.NAME);
                    to.removeParameter("tag");
                   
                    if (peerDialog.getState() != null)
                    {
                        JipletDialog clientDialog = jiplet.getDialog(peerDialog, false);
                        SipProvider clientProvider = clientDialog.getSipProvider();
                        ListeningPoint lp = clientProvider.getListeningPoints()[0];
                        // TODO, need to save the right transport - save LP instead of SipProvider?
                        // (in JipletDialog)
                        ViaHeader via = jiplet.getHeaderFactory().createViaHeader(lp.getIPAddress(),
                                    lp.getPort(), lp.getTransport(), null);
                        clonedRequest.addHeader(via);   
                       
                        ClientTransaction newct = clientProvider
                                .getNewClientTransaction(clonedRequest);
                        transactionsMapping
                                .addMapping(serverTransaction, newct);
                        peerDialog.sendRequest(newct);
                        jiplet.registerForResponse(clonedRequest, 60000L);
                        return;
                    }
                    else
                    {
                        // the peer dialog is not yet established so bail out.
                        // this is a client error - client is sending BYE
                        // before dialog establishment.
                        jiplet
                                .warn("While proxying a SIP request, bad dialog state - BYE dropped");
                        return;
                    }
                }
                // NEW CODE added by Amit to handle ACK and CANCEL
                else if (msg.getMethod().equals(Request.ACK))
                {
                    if (serverTransaction == null)
                    {
                        if (jiplet.isDebugEnabled() == true)
                            jiplet
                                    .debug("While proxying an ACK request, null server transaction");
                        return;
                    }

                    Dialog d = serverTransaction.getDialog();
                    TransactionsMapping transactionsMapping = jiplet.getDialog(
                            d, true).getTransactionsMapping();
                    Dialog peerDialog = transactionsMapping
                            .getPeerDialog(serverTransaction);
                    Request clonedRequest = (Request) msg.clone();
                    if (peerDialog.getState() != null)
                    {
                        peerDialog.sendAck(clonedRequest);
                    }
                    return;
                }
                else if (msg.getMethod().equals(Request.CANCEL))
                {
                    if (serverTransaction == null)
                    {
                        if (jiplet.isDebugEnabled() == true)
                            jiplet
                                    .debug("While proxying a CANCEL request, null server transaction for BYE");
                        return;
                    }

                    Dialog d = serverTransaction.getDialog();
                    JipletDialog jd = jiplet.getDialog(d, true);
                    TransactionsMapping transactionsMapping = jd
                            .getTransactionsMapping();

                    Vector transactions = transactionsMapping
                            .getClientTransactions((ServerTransaction) jd
                                    .getAttribute("firstTransaction"));
                   
                    if (transactions == null || transactions.isEmpty())
                    {
                        return;
                    }

                    for (Enumeration en = transactions.elements(); en
                            .hasMoreElements();)
                    {
                        ClientTransaction ct = (ClientTransaction) en
                                .nextElement();

                        // check if the client transaction can be canceled.
                        if (ct.getState().equals(TransactionState.COMPLETED)
                                || ct.getState().equals(
                                        TransactionState.TERMINATED))
                        {
                            continue;
                        }
                       
                        JipletDialog clientJipletDialog = (JipletDialog) ct.getDialog().getApplicationData();
                        if (clientJipletDialog != null)
                        {
                            ClientTransaction client = clientJipletDialog.getSipProvider().getNewClientTransaction(ct.createCancel());
                            client.sendRequest();
                        }
                    }
                }
                // END NEW CODE

            }

            /*
             * If the target set for the request has not been predetermined as
             * described above, this implies that the element is responsible for
             * the domain in the Request-URI, and the element MAY use whatever
             * mechanism it desires to determine where to send the request. ...
             * When accessing the location service constructed by a registrar,
             * the Request-URI MUST first be canonicalized as described in
             * Section 10.3 before being used as an index.
             */

            if (requestURI.isSipURI())
            {
                SipURI requestSipURI = (SipURI) requestURI;
                Iterator iterator = requestSipURI.getParameterNames();
                if (jiplet.isDebugEnabled() == true)
                    jiplet.debug("While proxying a request, we canonicalized"
                            + " the request-URI");
                while (iterator != null && iterator.hasNext())
                {
                    String name = (String) iterator.next();
                    requestSipURI.removeParameter(name);
                }
            }

            // We fork only INVITE
            if (uris.size() > 1 && !msg.getMethod().equals(Request.INVITE))
            {
                if (jiplet.isDebugEnabled() == true)
                    jiplet.debug("While proxying a request, the request "
                            + " to fork is not an INVITE, so we will process"
                            + " it with the first target as the only target.");

                URI uri = (URI) uris.get(0);
                uris.clear();
                uris.add(uri);

                // 4. Forward the request to the target:
                RequestForwarding forwarder = new RequestForwarding(jiplet,
                        this, request,
                        serverTransaction, stateful, addRecordRoute);
                forwarder.forwardRequest(uris);
                return;
            }

            if (!uris.isEmpty())
            {
                if (jiplet.isDebugEnabled() == true)
                    jiplet.debug("While proxying a request, the target set"
                            + " is the set of the contacts URI from the "
                            + " location service");

                // 4. Forward the request statefully to each target Section
                // 16.6.:

                RequestForwarding forwarder = new RequestForwarding(jiplet,
                        this, request,
                        serverTransaction, stateful, addRecordRoute);
                forwarder.forwardRequest(uris);
                return;
            }

            /*
             * If the target set remains empty after applying all of the above,
             * the proxy MUST return an error response, which SHOULD be the 480
             * (Temporarily Unavailable) response.
             */
            Response response = jiplet.getMessageFactory().createResponse(
                    Response.TEMPORARILY_UNAVAILABLE, msg);
            if (serverTransaction != null)
                serverTransaction.sendResponse(response);
            else
                provider.sendResponse(response);

            if (jiplet.isDebugEnabled() == true)
                jiplet
                        .debug("While proxying a request, unable to set "
                                + " the targets, 480 (Temporarily Unavailable) replied");
        }
        finally
        {
            reset = false;
        }
    }

    public ViaHeader getStackViaHeader() throws ParseException,
            InvalidArgumentException
    {
        ListeningPoint lp = jiplet.getListeningPointDefault();

        return jiplet.getHeaderFactory().createViaHeader(lp.getIPAddress(),
                lp.getPort(), lp.getTransport(), null);
    }

    public ContactHeader getStackContactHeader() throws ParseException
    {
        ListeningPoint lp = jiplet.getListeningPointDefault();

        SipURI sipURI = jiplet.getAddressFactory().createSipURI(null, lp.getIPAddress());
        sipURI.setPort(lp.getPort());
        sipURI.setTransportParam(lp.getTransport());
        Address contactAddress = jiplet.getAddressFactory().createAddress(
                sipURI);

        return jiplet.getHeaderFactory().createContactHeader(contactAddress);
    }

    /**
     * This method is used to proxy a received SIP request message. This method
     * is similar to the other proxyRequest method except that it proxies the
     * request to a single location.
     */
    public void proxyRequest(URI uri, boolean addRecordRoute, boolean stateful,
            boolean presenceServer) throws InvalidArgumentException,
            JipletException, ParseException, SipException
    {
        ArrayList list = new ArrayList();
        list.add(uri);
        proxyRequest(list, addRecordRoute, stateful, presenceServer);
    }

    /**
     * This method is used to proxy the received SIP response message.
     *
     * @param true
     *            if the proxy server is a presence server
     * @throws JipletException
     * @throws ParseException
     * @throws SipException
     */
    public void proxyResponse(boolean presenceServer) throws JipletException,
            SipException, ParseException
    {
        if (response == null)
        {
            throw new JipletException(
                    "The response cannot be proxied from a non-response event");
        }

        ResponseForwarding forwarder = new ResponseForwarding(jiplet,
                (SipProvider) response.getSource(), presenceServer);

        forwarder.forwardResponse(response.getResponse(), response
                .getClientTransaction());
    }

    /**
     * This method is used to handle proxy processing when a SIP timeout-occurs.
     * The jiplet is notified of timeout by the container by invoking the
     * processTimeout() method. The jiplet should call this method for handling
     * the timeout-related to proxy forwarding.
     *
     * @param stateful
     *            true if the proxy is stateful
     * @throws ParseException
     * @throws SipException
     * @throws JipletException
     */
    public void handleProxyTimeout(boolean stateful) throws ParseException,
            SipException, JipletException
    {
        if (timeout == null)
        {
            throw new JipletException(
                    "The timout cannot be handled from a non-timeout event");
        }

        if (stateful == false)
        {
            // TODO this needs to be looked into in some details. For now, just
            // return.
            return;
        }

        SipProvider provider = (SipProvider) timeout.getSource();
        TransactionsMapping transactionsMapping = null;
        if (timeout.isServerTransaction())
        {
            ServerTransaction serverTransaction = timeout
                    .getServerTransaction();
            Dialog dialog = serverTransaction.getDialog();
            if (dialog != null)
            {

                transactionsMapping = jiplet.getDialog(dialog, true)
                        .getTransactionsMapping();
                transactionsMapping.removeMapping(serverTransaction);
            }
        }
        else
        {
            ClientTransaction clientTransaction = timeout
                    .getClientTransaction();
            Dialog dialog = clientTransaction.getDialog();
            ServerTransaction st = null;
            if (dialog != null)
            {
                transactionsMapping = jiplet.getDialog(dialog, true)
                        .getTransactionsMapping();
                if (transactionsMapping != null)
                {
                    st = transactionsMapping
                            .getServerTransaction(clientTransaction);
                }

                if (st == null)
                {
                    jiplet.error("Unable to retrieve the server transaction,"
                            + " cannot process timeout!");
                    return;
                }
            }
            else
            {
                jiplet.error("Unable to retrieve the transaction Mapping,"
                        + " cannot process timeout!");
                return;
            }

            Request request = st.getRequest();

            // This removes the given mapping from the table but not
            // necessarily the whole thing.
            transactionsMapping.removeMapping(clientTransaction);
            if (!transactionsMapping.hasMapping(st))
            {
                // No more mappings left in the transaction table.
                Response response = jiplet.getMessageFactory().createResponse(
                        Response.REQUEST_TIMEOUT, request);
               
                try
                {
                    st.sendResponse(response);
                }
                catch (InvalidArgumentException e)
                {
                    // inapplicable - it only happens if the Response was created
                    // by Dialog.createReliableProvisionalResponse(int) and the
                    // application calls ServerTransaction.sendResponse() to send it
                    JipletLogger
                            .error("Timeout response sending failed  - invalid send method used. Failed response = \n"
                                   + response.toString());
                }

            }
        }
    }

    /**
     * @return the server transaction. Must be called after a proxyRequest() or
     *         a proxyResponse() has been called.
     */
    public ServerTransaction getServerTransaction()
    {
        return serverTransaction;
    }

    protected boolean isReset()
    {
        return reset;
    }

    protected void setReset(boolean reset)
    {
        this.reset = reset;
    }

    /**
     * @return returns the request event.
     */
    public RequestEvent getRequest()
    {
        return request;
    }

    /**
     * @return returns the response event
     */
    public ResponseEvent getResponse()
    {
        return response;
    }

    /**
     * @return the timeout event
     */
    public TimeoutEvent getTimeout()
    {
        return timeout;
    }
}
TOP

Related Classes of org.cafesip.jiplet.sip.SipCommunicator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.