Package com.ericsson.ssa.sip.transaction

Source Code of com.ericsson.ssa.sip.transaction.TransactionManager

/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2009 Sun Microsystems, Inc. All rights reserved.
* Copyright (c) Ericsson AB, 2004-2008. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License").  You
* may not use this file except in compliance with the License. You can obtain
* a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
* or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code.  If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [year]
* [name of copyright owner]"
*
* Contributor(s):
*
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license."  If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above.  However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package com.ericsson.ssa.sip.transaction;

import com.ericsson.ssa.config.annotations.Configuration;
import com.ericsson.ssa.container.SipBindingCtx;
import com.ericsson.ssa.container.SipBindingResolver;
import com.ericsson.ssa.container.reporter.ReporterResolver;

// inserted by hockey (automatic)
import com.ericsson.ssa.container.reporter.Reporter;
import com.ericsson.ssa.sip.AddressImpl;
import com.ericsson.ssa.sip.DialogFragment;

import com.ericsson.ssa.sip.Dispatcher;
import com.ericsson.ssa.sip.Header;
import com.ericsson.ssa.sip.Layer;
import com.ericsson.ssa.sip.LayerHelper;
import com.ericsson.ssa.sip.MultiLineHeader;
import com.ericsson.ssa.sip.OutboundInterface;
import com.ericsson.ssa.sip.SipServletRequestImpl;
import com.ericsson.ssa.sip.SipServletResponseImpl;
import com.ericsson.ssa.sip.ViaImpl;
import com.ericsson.ssa.sip.dns.TargetTuple;

import org.jvnet.glassfish.comms.util.LogUtil;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import java.util.Iterator;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import java.util.logging.Logger;


/**
* @author ekrigro TransactionManader is a Layered Dispatcher TODO Clean up in
*         the transaction Map
*/
public class TransactionManager implements Layer {
    private static final long MAX_TRANSACTION_LIFETIME; // A transaction which is 1 hour old must really be stale!
    static {
        {
            long maxTrLifeTime = 3600; // In seconds; a transaction which is 1 hour old must really be stale!
            try {
                maxTrLifeTime = Integer.parseInt(System.getProperty("sipContainer.transaction.maxTransactionLifetime", "" + maxTrLifeTime));
            } catch (Throwable t) {
                // swallow...
            }
            MAX_TRANSACTION_LIFETIME  = maxTrLifeTime * 1000;
        }
    }
    private static TransactionManager _tm = null;
    private Logger log = LogUtil.SIP_LOGGER.getLogger();
    private Layer _nextLayer;
    private Map<String, ClientTransaction> ctMap = new ConcurrentHashMap<String, ClientTransaction>();
    private Map<String, ServerTransaction> stMap = new ConcurrentHashMap<String, ServerTransaction>();
    private ConcurrentHashMap<String, Object> stLockMap = new ConcurrentHashMap<String, Object>();
    private AtomicLong m_EasSipServerTransactions = new AtomicLong();
    private AtomicLong m_EasSipClientTransactions = new AtomicLong();
    private AtomicLong m_EasTotalSipTransactionTime = new AtomicLong();
    private AtomicLong m_EasTotalSipTransactionCount = new AtomicLong();
    private AtomicLong noMatchingTranCounter = new AtomicLong();
    private int tranErrOccurrences =
        Integer.getInteger("org.glassfish.sip.noMatchingTranOccurrences",1);

    /**
     * _timerT1 is defaulted to 500 here, but its actual runtime value is set
     * from user preferences via the setTimerT1 method.
     */
    long _timerT1 = 500;

    /**
     * _timerT2 is defaulted to 4000 here, but its actual runtime value is set
     * from user preferences via the setTimerT2 method.
     */
    long _timerT2 = 4000;

    /**
     * _timerT4 is defaulted to 4000 here, but its actual runtime value is set
     * from user preferences via the setTimerT4 method.
     */
    long _timerT4 = 5000;
    private MessageDigest md = null;
    private Reporter _reporter;
    private boolean shouldScavengeCTOnNextInvocation = true;

    private  static boolean quenchUDPRetransmission = Boolean.getBoolean(
            "org.jvnet.glassfish.comms.sip.transaction.quenchUDPInviteRetransmissions");

    public static boolean isQuenchUDPRetransmission() {
        return quenchUDPRetransmission;
    }
   
    private TransactionManager() {
        try {
            md = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException ignore) {
        }
    }

    public String getOngoingTransactions() {
        StringBuffer sb = new StringBuffer();
        for (ClientTransaction tr : ctMap.values()) {
            sb.append(tr.getTransactionId()).append('\n');
        }
        for (ServerTransaction tr : stMap.values()) {
            sb.append(tr.getTransactionId()).append('\n');
        }
        return sb.toString();
    }

    public void setReporters(String reporters) {
        _reporter = ReporterResolver.getInstance().getReporter(reporters);
    }

    public Reporter getReporter() {
        return _reporter;
    }

    private ServerTransaction getServerTransaction(String id) {
        ServerTransaction st = null;
        Object mutex = stLockMap.get(id);

        if (mutex != null) {
            synchronized (mutex) {
                st = stMap.get(id);
            }
        }

        return st;
    }

    private void invokeCreatedOrFetchedServerTransaction(
        SipServletRequestImpl req, String branchId) {
        boolean isTransactionCreated = false;
        ServerTransaction st = null;
        ServerTransaction stCancel = null;
        SipServletResponseImpl resp = null;      
        String branchIdCancel=null;      
       
        Object newMutex = new Object();
        Object mutex=null;
       
        if (req.getMethod().equals("CANCEL")) {
          // to distinguish cancel from original transaction add method to branch id...
          branchIdCancel=branchId + req.getMethod();
      mutex = stLockMap.putIfAbsent(branchIdCancel, newMutex);
    } else {
      mutex = stLockMap.putIfAbsent(branchId, newMutex);
    }

        if (mutex == null) {
            mutex = newMutex;
        }

        synchronized (mutex) {
            st = stMap.get(branchId);

            if (req.getMethod().equals("CANCEL")) {
                stCancel = stMap.get(branchIdCancel);
                if (st != null) {
                    if (stCancel == null || !NonInviteServerTransaction.class.isInstance(stCancel)) {
                         stCancel = new NonInviteServerTransaction(branchIdCancel, req);
                        putServerTransaction(stCancel);
                        req.pushTransactionDispatcher(stCancel);
                        req.pushApplicationDispatcher(this);
                        isTransactionCreated = true;
                    }
                } else if (stCancel == null) {
                     // orginal transaction to cancel is gone...
                     resp = req.createTerminatingResponse(481);
                     resp.setRemote(req.getRemote());
                }
            } else if (st == null) {
                st = req.getMethod().equals("INVITE")
                    ? new InviteServerTransaction(branchId, req)
                    : new NonInviteServerTransaction(branchId, req);
                req.pushTransactionDispatcher(st); // Push the new ST
                req.pushApplicationDispatcher(this);
                putServerTransaction(st);
                isTransactionCreated = true;
            }
        }

        // invoke outside synchronization block...
        if (resp != null) {
            // orginal transaction to cancel is gone, lets reply...
            resp.popDispatcher().dispatch(resp);
        } else if (stCancel != null) {
            // lets inform the original transaction
            // that a cancel request is pending...
            if (isTransactionCreated) {
                st.handleCancel(stCancel);
            } else {//retransmission.
                stCancel.handle(req);
            }
        } else if (isTransactionCreated) {
            LayerHelper.next(req, this, _nextLayer);
        } else {
            st.handle(req);
        }
    }

    /*
     * Initial method for incomming ST transactions
     */
    public void next(SipServletRequestImpl req) {
        // Find existing or new server transaction
        // TODO - make lazy creation for stateless
        String method = req.getMethod();
        ServerTransaction st = null;

       /* Commenting this code (471)for now because there is a
      problem when in the backend when this check happens twice,
      once in the SipLBManager and once here.
      The req transport will always be tcp in the backend
      because the clb uses tcp for frontend to backend traffic.
      But the top via is popped and removed by SipLBManager,
      so the actual VIA put by the client here is checked
      against TCP which may or may not be true. 
        if( req.isInitial() && !isRemoteSocketandViaTransportConsistent(req)) {
            SipServletResponseImpl resp = req.createTerminatingResponse(400);
            resp.popDispatcher().dispatch(resp);
            return;
        }
        */
        String id = getBranchId(req);
       
        // Get the branch from the top VIA
        if (id != null) {
            // Got the branchId now see if there is a match
            // TODO check also METHOD & sent by value
            //
            // Must separate ACK for 2xx and non-2xx:
            // ACK for non-2xx has same branch id as INVITE
            // and must be sent to transaction. If transaction
            // was not found forward it, then its assumed to be
            // ACK for 2xx...
            try {
                if (method.equals("ACK")) {
                    st = getServerTransaction(id);

                    if (st == null) {
                        req.pushApplicationDispatcher(this);
                        req.pushTransactionDispatcher(this);
                        LayerHelper.next(req, this, _nextLayer);
                    } else {
                        st.handle(req);
                    }
                } else {
                    invokeCreatedOrFetchedServerTransaction(req, id);
                }
            } catch (RuntimeException re) {
              if (getServerTransaction(id) != null) {
                removeServerTransaction(id);
              }
                throw (re);
            }
        } else {
            // No branch in via
            if (log.isLoggable(Level.FINE)) {
                log.log(Level.FINE,
                    "No Branch in Via header " + req.toString());
            }

            SipServletResponseImpl resp = (SipServletResponseImpl) req.createResponse(400,
                    "No Branch in Via header");
            Dispatcher d = resp.popDispatcher();

            if (d != null) {
                d.dispatch(resp);
            }
        }
    }

    /*
     * Initial method for incomming CT transactions
     */
    public void next(SipServletResponseImpl resp) {
        boolean stopLayer = false;

        // Pass it on to the client transaction if there is one
        String id = getBranchId(resp);

        if (resp.getMethod().equals("CANCEL")) {
            id = id + resp.getMethod();
        }

        ClientTransaction ct = (id != null) ? ctMap.get(id) : null;

        if (ct != null) {
            stopLayer = ct.handle(resp);
        } else {
            // logging all the transaction errors is worsening the situation
            //  when there are lot of transaction errors
            if (noMatchingTranCounter.incrementAndGet() % tranErrOccurrences == 0) {
                if (log.isLoggable(Level.SEVERE)) {
                    log.log(Level.SEVERE, "sip.stack.transaction.no_matching_client_transaction", new Object[] { resp.getCallId() });
                }
                if (log.isLoggable(Level.FINE)) {
                    log.log(Level.FINE, "Matching client transaction is not found for the response " + resp.toDebugString());
                }
            }

            return;
        }

        // POP via
        Header via = resp.getRawHeader(Header.VIA);
        via.setReadOnly(false);

        ListIterator<String> li = via.getValues();
        String topVia = li.next();

        if ((topVia != null) && resp.getMethod().equals("INVITE")) {
            // An INVITE response need to save the topVia, which is used
            // by CANCEL to find it's way to the correct transaction...
            Header viaOfCancel = new MultiLineHeader(Header.VIA, true);
            ViaImpl v = new ViaImpl(topVia);
            viaOfCancel.setValue(v.toString(), true);
            resp.setCancelVia(viaOfCancel);
        }

        // TODO could match to see that it's the right host'n port
        li.remove();
        via.setReadOnly(true);

        if (log.isLoggable(Level.FINE)) {
            log.log(Level.FINE, "Removing via = " + topVia);
        }

        if (!stopLayer) {
            try {
                LayerHelper.next(resp, this, _nextLayer);
            } catch (RuntimeException re) {
                removeClientTransaction(id);
                throw (re);
            }
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see com.ericsson.ssa.sip.Layer#registerNext(com.ericsson.ssa.sip.Layer)
     */
    public void registerNext(Layer layer) {
        _nextLayer = layer;
    }
   
   
    private TargetTuple selectTTToUse(SipServletRequestImpl msg) {
        // check for the JSR 289 outbound interface in the message
        // if not found use the external address in sip-container
        TargetTuple outtt = null;
       
        OutboundInterface oi = msg.getOutboundInterface();
       
        SipBindingCtx sipBindingCtx = SipBindingResolver.instance().
                getActiveExternalContext();
        for (TargetTuple tt : sipBindingCtx.getTargetTuples()) {
            if (msg.getTransport().equalsIgnoreCase(tt.getProtocol().name())) {
                outtt = tt;
                break;
            }
        }
        if (oi == null) {
            return outtt;
        } else {
            int port = oi.getPort() == 0 ? outtt.getPort() : oi.getPort();
            return new TargetTuple(outtt.getProtocol(), oi.getHost(), port);
        }
    }
   
    /*
     * (non-Javadoc)
     *
     * @see com.ericsson.ssa.sip.Dispatcher#dispatch(com.ericsson.ssa.sip.SipServletRequestImpl)
     */
    public void dispatch(SipServletRequestImpl req) {
        // Sending out request UAC or Proxy
        // Slightly special handling of ACK and CANCEL
        String method = req.getMethod();

        if (!method.equals("CANCEL")) {
            Header via = req.getRawHeader(Header.VIA);

            if (via == null) {
                via = new MultiLineHeader(Header.VIA, true);
            }

            TargetTuple ttToUse = selectTTToUse(req);           

            if (log.isLoggable(Level.FINEST)) {
                log.log(Level.FINEST, "Via header transport: " +
                        ttToUse.getIP()+":"+ttToUse.getPort()+" Transport="+
                        ttToUse.getProtocol().name());
            }

            ViaImpl v = new ViaImpl(req.getProtocol(),
                    req.getTransport().toUpperCase(), ttToUse.getIP(),
                    ttToUse.getPort());
            String id = Transaction.generateBranch();
            v.setParameter(ViaImpl.PARAM_BRANCH, id);

            if (log.isLoggable(Level.FINE)) {
                log.log(Level.FINE, "Adding via = " + v);
            }

            String string_via = v.toString();
            if (req.getHeader("VIA_NEXTHOP_PARAMETERS") != null) {
              // special handling for applications that wants to affect the future via
              String via_nexthop_parameters = req.getHeader("VIA_NEXTHOP_PARAMETERS");
              req.removeHeader("VIA_NEXTHOP_PARAMETERS"); // we should never keep this header when request is sent
              string_via += ';'+ via_nexthop_parameters;  // plain String concatenation, knowledge & responsiblity is of application
            }
           

            via.setValue(string_via, true);
            req.setHeader(via);

            if (!method.equals("ACK")) {
                ClientTransaction ct = method.equals("INVITE")
                    ? new InviteClientTransaction(id, req)
                    : new NonInviteClientTransaction(id, req);
                putClientTransaction(ct);
            }
        } else {
            // CANCEL
            String id = getBranchId(req) + req.getMethod();
            ClientTransaction ct = new NonInviteClientTransaction(id, req);
            putClientTransaction(ct);
        }

        Dispatcher d = req.popDispatcher();

        if (d != null) {
            d.dispatch(req);
        }
    }

    /*
     * Nothing todo here more then pop. Should only get her for stateless proxy
     * Responses should go on ServerTransaction
     */
    public void dispatch(SipServletResponseImpl resp) {
        Dispatcher d = resp.popDispatcher();

        if (d != null) {
            d.dispatch(resp);
        }
    }

    public synchronized static TransactionManager getInstance() {
        if (_tm != null) {
            return _tm;
        }

        return _tm = new TransactionManager();
    }

    private String getBranchId(SipServletResponseImpl resp) {
        ViaImpl via = null;

        // Get the branch from the top VIA
        String vstr = resp.getHeader(Header.VIA);

        if ((vstr != null) && ((via = new ViaImpl(vstr)) != null)) {
            String id = null;

            // TODO Add a check of the magic cockie to the parsing of ViaImpl
            if ((id = via.getParameter(ViaImpl.PARAM_BRANCH)) != null) {
                return id;
            }
        }

        return null;
    }

    private String getBranchId(SipServletRequestImpl req) {
        ViaImpl via = null;

        // Get the branch from the top VIA
        String vstr = req.getHeader(Header.VIA);

        if ((vstr != null) && ((via = new ViaImpl(vstr)) != null)) {
            String id = null;

            // TODO Add a check of the magic cockie to the parsing of ViaImpl
            if ((id = via.getParameter(ViaImpl.PARAM_BRANCH)) != null) {
                return id;
            } else // Calculate an id according to rules from rfc2543
            {
                return createBranchId(req, via);
            }
        }

        return null;
    }

    /**
     * Create a branch ID according to RFC3261 chapter 17.2.3
     * @param req the request
     * @param via the via to use for creating the branchId
     * @return the brancId
     */
    private String createBranchId(SipServletRequestImpl req, ViaImpl via) {
        try {
        // RFC3261 chapter 17.2.3
        StringBuilder sb = new StringBuilder();
        sb.append(req.getRequestURI().toString());
        sb.append(req.getFromImpl().getParameter(AddressImpl.TAG_PARAM));
        // sb.append(req.getTo().getParameter(AddressImpl.TAG_PARAM));
        sb.append(req.getCallId());
        sb.append(req.getCSeqNumber());
        sb.append(via.toString());

        if (log.isLoggable(Level.FINE)) {
            log.log(Level.FINE, "Before hash, input is : " + sb.toString());
        }

            MessageDigest localMD = (MessageDigest) md.clone();
            byte[] hash = localMD.digest(sb.toString().getBytes());

        sb = new StringBuilder();

        for (int i = 0; i < hash.length; i++) {
            String d = Integer.toHexString(new Byte(hash[i]).intValue() & 0xFF);

            if (d.length() == 1) {
                sb.append('0');
            }

            sb.append(d);
        }

        if (log.isLoggable(Level.FINE)) {
            log.log(Level.FINE, "Generated id, hash is : " + sb.toString());
        }

        if (log.isLoggable(Level.FINE)) {
            log.log(Level.FINE, "sip.stack.transaction.using_generated_tid_rfc2543", new Object[] { sb.toString(), via });
        }

        return sb.toString();
        } catch (CloneNotSupportedException ex) {
             log.log(Level.SEVERE, "sip.stack.error.occurred.for.digest.clone",ex);
        }
        return null;
    }

    private void putClientTransaction(ClientTransaction t) {
        ctMap.put(t.getTransactionId(), t);
        associateTransactionWithDialog(t);
        if (log.isLoggable(Level.FINEST)) {
            log.log(Level.FINEST, "Added client transaction: " + t.getTransactionId());
        }
    }

    public void remove(ClientTransaction t) {
        String id = t.getTransactionId();
        removeClientTransaction(id);
    }

    private void removeClientTransaction(String id) {
        if (id != null) {
            ClientTransaction t = ctMap.remove(id);
            if (log.isLoggable(Level.FINEST)) {
                log.log(Level.FINEST, "Removed client transaction: "+id);
            }

            if (t == null) {
                if (log.isLoggable(Level.WARNING)) {
                    log.log(Level.WARNING, "Transaction was null: " + id);
                }
            } else {
                removeTransactionFromDialog(t, true);
            }
        }
    }
   
    private void associateTransactionWithDialog(Transaction t) {
        SipServletRequestImpl req = t.getRequest();
        if (req == null) return;
        DialogFragment dialog = req.getDialog();
        if (dialog == null) return;
        dialog.getDialogLifeCycle().associateTransaction(t.getTransactionId());
    }
   
    private void removeTransactionFromDialog(Transaction t, boolean client) {
        DialogFragment dialog = t.getDialog();
        if (dialog == null) {
            if (log.isLoggable(Level.FINE)) {
                log.log(Level.FINE, "Transaction lacked dialog: " + t.getTransactionId() + " (the dialog has most likely been removed already)");
            }

            return;
        }
        dialog.getDialogLifeCycle().onTransactionRemoved(t.getTransactionId(), client);
    }

    private void putServerTransaction(ServerTransaction t) {
        stMap.put(t.getTransactionId(), t);
        associateTransactionWithDialog(t);
        if (log.isLoggable(Level.FINEST)) {
            log.log(Level.FINEST, "Added server transaction: " + t.getTransactionId());
        }
    }


    public void remove(ServerTransaction t) {
        String id = t.getTransactionId();
        removeServerTransaction(id);
    }

    private void removeServerTransaction(String id) {
        if (id != null) {
            Object mutex = stLockMap.get(id);

            if (mutex == null) {
                if (log.isLoggable(Level.WARNING) && stMap.get(id) != null) {
                    log.log(Level.WARNING, "sip.stack.transaction.failed_get_read_mutex");
                }
                ServerTransaction t = stMap.remove(id);
                if (t == null) {
                    if (log.isLoggable(Level.WARNING)) {
                        log.log(Level.WARNING, "Transaction was null: " + id);
                    }
                } else {
                    removeTransactionFromDialog(t, false);
                    if (log.isLoggable(Level.FINEST)) {
                        log.log(Level.FINEST, "Removed server transaction: " + id);
                    }
                }
            } else {
                synchronized (mutex) {
                    ServerTransaction t = stMap.remove(id);
                    if (t == null) {
                        if (log.isLoggable(Level.WARNING)) {
                            log.log(Level.WARNING, "Transaction was null: " + id);
                        }
                    } else {
                        removeTransactionFromDialog(t, false);
                        if (log.isLoggable(Level.FINEST)) {
                            log.log(Level.FINEST, "Removed server transaction: " + id);
                        }
                    }
                }
            }

            stLockMap.remove(id);
        }
    }
   
    public boolean transactionExists(String trId) {
        return ctMap.get(trId) != null || stMap.get(trId) != null;
    }

    /*
     * CLIENT TRANSACTION 1. If the response has the same value of the branch
     * parameter in the top Via header field as the branch parameter in the top
     * Via header field of the request that created the transaction. 2. If the
     * method parameter in the CSeq header field matches the method of the
     * request that created the transaction. The method is needed since a CANCEL
     * request constitutes a different transaction, but shares the same value of
     * the branch parameter.
     */

    /*
     * SERVER TRANSACTION 1. the branch parameter in the request is equal to the
     * one in the top Via header field of the request that created the
     * transaction, and 2. the sent-by value in the top Via of the request is
     * equal to the one in the request that created the transaction, and 3. the
     * method of the request matches the one that created the transaction, except
     * for ACK, where the method of the request that created the transaction is
     * INVITE.
     */
    @Configuration(key = "T1InMillis", node = "/SipService/SipProtocol/SipTimers")
    public void setTimerT1(Integer t) {
        if (log.isLoggable(Level.FINEST)) {
            log.log(Level.FINEST, "setTimerT1 from " + _timerT1 + " to " +
                t);
        }

        _timerT1 = t;
    }

    public long getTimerT1() {
        return _timerT1;
    }

    @Configuration(key = "T2InMillis", node = "/SipService/SipProtocol/SipTimers")
    public void setTimerT2(Integer t) {
        if (log.isLoggable(Level.FINEST)) {
            log.log(Level.FINEST, "setTimerT2 from " + _timerT2 + " to " +
                t);
        }

        _timerT2 = t;
    }

    public long getTimerT2() {
        return _timerT2;
    }

    @Configuration(key = "T4InMillis", node = "/SipService/SipProtocol/SipTimers")
    public void setTimerT4(Integer t) {
        if (log.isLoggable(Level.FINEST)) {
            log.log(Level.FINEST, "setTimerT4 from " + _timerT4 + " to " +
                t);
        }

        _timerT4 = t;
    }

    public long getTimerT4() {
        return _timerT4;
    }

    /* Performance Counters */
    public long getEasSipClientTransactions() {
        return m_EasSipClientTransactions.longValue();
    }

    public long getEasSipServerTransactions() {
        return m_EasSipServerTransactions.longValue();
    }

    public long getEasTotalSipTransactionTime() {
        return m_EasTotalSipTransactionTime.longValue();
    }

    public long getEasTotalSipTransactionCount() {
        return m_EasTotalSipTransactionCount.longValue();
    }

    // package access
    void recordTransactionTime(long transactionTime) {
        m_EasTotalSipTransactionTime.addAndGet(transactionTime);
        m_EasTotalSipTransactionCount.incrementAndGet();
    }

    // package access
    void incrEasSipClientTransactions() {
        m_EasSipClientTransactions.incrementAndGet();
    }

    // package access
    void incrEasSipServerTransactions() {
        m_EasSipServerTransactions.incrementAndGet();
    }

    public void doScavenge() {
        if (log.isLoggable(Level.FINE)) {
            log.log(Level.FINE, "Removing obsolete transactions.");
        }
       
        if (shouldScavengeCTOnNextInvocation) {
            scavengeCT();
        } else {
            scavengeST();
        }
       
        if (log.isLoggable(Level.FINEST)) {
            String s = "Ongoing server transactions:\n";
            for (ServerTransaction st : stMap.values()) {
                s += st.getTransactionId() + "; request:\n" + st.getRequest();
            }
            s += "\n\nOngoing client transactions:\n";
            for (ClientTransaction ct : ctMap.values()) {
                s += ct.getTransactionId() + "; request:\n" + ct.getRequest();
            }
            log.log(Level.FINEST, s);
        }
    }

    private void scavengeST() {
        Iterator<Entry<String, ServerTransaction>> iter = stMap.entrySet().iterator();
        log.log(Level.FINE, "Check for overdue ServerTransactions");

        long now = System.currentTimeMillis();
        int numRemoved = 0;

        while (iter.hasNext()) {
            Entry<String, ServerTransaction> entry = iter.next();
            ServerTransaction st = entry.getValue();

            if ((st != null) &&
                    (now > (st.getTransactionStartTime() +
                    MAX_TRANSACTION_LIFETIME))) {
                remove(st);
                numRemoved++;
            }
        }

        if (numRemoved > 0) {
            log.log(Level.INFO, "Number of overdue ServerTransactions removed:" + numRemoved);
        }
       
        shouldScavengeCTOnNextInvocation = true;
    }

    private void scavengeCT() {
        Iterator<Entry<String, ClientTransaction>> iter = ctMap.entrySet().iterator();;
        log.log(Level.FINE, "Check for overdue ClientTransactions");

        long now = System.currentTimeMillis();
        int numRemoved = 0;

        while (iter.hasNext()) {
            Entry<String, ClientTransaction> entry = iter.next();
            ClientTransaction ct = entry.getValue();

            if ((ct != null) &&
                    (now > (ct.getTransactionStartTime() +
                    MAX_TRANSACTION_LIFETIME))) {
                remove(ct);
                numRemoved++;
            }
        }

        if (numRemoved > 0) {
            log.log(Level.INFO, "Number of overdue ClientTransactions removed:" + numRemoved);
        }

        shouldScavengeCTOnNextInvocation = false;
    }

    private boolean isRemoteSocketandViaTransportConsistent(SipServletRequestImpl req) {

        String vstr = req.getHeader(Header.VIA);

        ViaImpl via;
        if ((vstr != null) && ((via = new ViaImpl(vstr)) != null)) {
           
            String viaTransport = via.getTransport();           
            String remoteTransport = req.getRemote().getProtocol().name();
           
            if ( viaTransport!= null && remoteTransport != null && !viaTransport.equalsIgnoreCase(remoteTransport)) {
                return false;
            }
        }

        return true;
    }

  public void invokeNextLayer(SipServletResponseImpl resp) {
    LayerHelper.next(resp, this, _nextLayer);
  }
}
TOP

Related Classes of com.ericsson.ssa.sip.transaction.TransactionManager

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.