Package com.ericsson.ssa.sip

Source Code of com.ericsson.ssa.sip.DialogFragment$BackwardListIterator

/*
* 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;

import com.ericsson.ssa.sip.PathNode.Type;
import com.ericsson.ssa.sip.dialog.DialogLifeCycle;

import org.apache.catalina.session.SessionLock;

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

import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.Externalizable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.sip.Address;
import javax.servlet.sip.SipSession;




/**
* The DialogFragment has information for subsequent requests of the internal
* route of a dialog. A dialog may consist of many fragments because of spirals.
*
* @author ehsroha
*/
public class DialogFragment implements Externalizable {
    private static final long serialVersionUID = -7081403938667526477L;

    /** The serialized format versioning. 2 = second version. */
    private static final short serializedFormVersion  = 2;
    // should be lower case
    public static final String DEFAULT_FRAGMENT_ID = "default_fragment_id";
    public static final String BAD_FRAGMENT_ID = "BAD_FRAGMENT_ID";
    private static final Logger m_Logger = LogUtil.SIP_LOGGER.getLogger();
    private LinkedList<PathNode> m_Nodes = new LinkedList<PathNode>();
    private Ascii7String m_ToTag = null;
    private String m_FragmentId = null; //DEFAULT_FRAGMENT_ID;
    private DialogSet m_DialogSet;
    private boolean m_IsValid = true;
    private AtomicInteger m_SessionCounter = new AtomicInteger(0);
    private long m__DialogStartedTimestamp = 0;
    protected Object validLock = new Object();
    private long invalidationTimeStamp;
    private String invalidationThreadInfo;
    private boolean isReplicable = (DialogFragmentManager.getInstance() instanceof PersistentDialogFragmentManager);
    private Ascii7String m_callId = null;
    private Address m_from = null;
    private String m_beKey = null;
    private transient boolean isMixedPersistence = false;
    private transient SessionLock sessionLock = new SessionLock();
    private boolean isConfirmed;
    private transient DialogLifeCycle dialogLifeCycle;
    private boolean isInitial = false;
    private boolean registered = false;
    private ReentrantLock messageLock = new ReentrantLock();
    private boolean isInviteDialog = false;

    public void obtainLockForIncomingMessage() {
        messageLock.lock();
    }

    public void releaseLockForIncomingMessage() {
        try {
            messageLock.unlock();
        } catch(IllegalMonitorStateException ex) {
            // ignore.
        }
    }

    /**
     * Use DialogFragmentManager as a factory to create DialogFragments.
     * However a dialogFragment can be cloned directly.
     *
     */
    protected DialogFragment(DialogSet set) {
        m_DialogSet = set;
        m_callId = set.getCallIdAscii7String(); // shared reference with ds
        m_from = set.getFrom(); // shared reference with ds
        m_beKey = set.getBeKey();

        m_FragmentId = set.nextFragmentId();
        //m_DialogSet.addDialog(this);
        m__DialogStartedTimestamp = System.currentTimeMillis();
        isInitial = true;
    }
   
    /* If DialogLifeCycle is initialized in the constructor, other threads
     * are seeing partially constructed dialog fragment
     */
    protected void initDialogLifeCycle() {
        dialogLifeCycle = new DialogLifeCycle(this);
    }

    // Support For externalization
    public DialogFragment() {
    }

    public boolean isInitialDialog() {
        return isInitial;
    }

    public static String createKey(String callId, String fromTag, String toTag) {
        return createKey(callId, fromTag, toTag, DEFAULT_FRAGMENT_ID);
    }

    /**
     * Create a key from the given arguments. A null value for toTag causes the
     * corresponding part of the key to be suppressed. Likewise, a zero value for
     * fragmentId causes that part of the ket to be suppressed.
     *
     * @param callId
     * @param fromTag
     * @param toTag
     * @param fragmentId
     * @return
     */
    public static String createKey(String callId, String fromTag, String toTag,
        String fragmentId) {
        StringBuilder sb = new StringBuilder(callId);

        // Ensure that the order of from-tag and to-tag is determined
        // by the lexical order of the two strings, to make the order constant.
        if ((fromTag == null) && (toTag == null)) {
            // Should never happen, but just in case there will be nothing added
        } else if ((fromTag != null) && (toTag == null)) {
            sb.append('&');
            sb.append(fromTag);
        } else if ((fromTag == null) && (toTag != null)) {
            sb.append('&');
            sb.append(toTag);
        } else if (fromTag.compareTo(toTag) <= 0) {
            sb.append('&');
            sb.append(fromTag);
            sb.append('&');
            sb.append(toTag);
        } else {
            sb.append('&');
            sb.append(toTag);
            sb.append('&');
            sb.append(fromTag);
        }

        if (fragmentId != null) {
          sb.append(':');
          sb.append(fragmentId.toLowerCase());
        }

        return sb.toString();
    }

    /**
     * @serialData See serialized form version
     * #serializedFormVersion in #readExternal(ObjectInput in)
     * 
     * @param out the stream to write the object members
     *
     * @throws IOException
     */
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeShort(serializedFormVersion);
       
        out.writeInt(m_Nodes.size());

        Iterator<PathNode> pni = m_Nodes.iterator();

        while (pni.hasNext()) {
            out.writeObject(pni.next());
        }

        if (m_ToTag == null) {
            out.writeUTF("");
        } else {
            out.writeUTF(m_ToTag.toString());
        }

        out.writeObject(m_FragmentId);
        // m_DialogSet should not be serialized because it is confirmed
        out.writeUTF(m_callId.toString());
        out.writeObject(m_from);
        out.writeBoolean(m_IsValid);
        out.writeBoolean(isInitial);
        out.writeInt(m_SessionCounter.get());
        out.writeObject(m_beKey);
        out.writeBoolean(isInviteDialog);
    }

    /**
     * @serialData first field is an short and represents the serializedFormVersion.<br>
     * <h3>Data layout for serializedFormVersion = 1 follows</h3>
     *
     * <li>field is a <b>Integer</b> and represents the number of written pathnodes</li>
     * <li>0..n fields of type <b>PathNode</b> representing all pathnodes in this dialogfragment</li>
     * <li>field is a <b>UTF String</b> and represents the m_toTag field</li>
     * <li>field is a <b>UTF String</b> and represents the fragmentId</li>
     * <li>field is a <b>UTF String</b> and represents the callId</li>
     * <li>field is a <b>javax.servlet.sip.Address</b> and represents the from field</li>
     * <li>field is a <b>Boolean</b> and represents the isValid field</li>
     * <li>field is a <b>Boolean</b> and represents the isInitial field</li>
     * <li>field is a <b>Integer</b> and represents the session counter</li>
     *
     * <h3>Data layout for serializedFormVersion = 2 follows</h3>
     *
     * <li>field is a <b>Integer</b> and represents the number of written pathnodes</li>
     * <li>0..n fields of type <b>PathNode</b> representing all pathnodes in this dialogfragment</li>
     * <li>field is a <b>UTF String</b> and represents the m_toTag field</li>
     * <li>field is a <b>UTF String</b> and represents the fragmentId</li>
     * <li>field is a <b>UTF String</b> and represents the callId</li>
     * <li>field is a <b>javax.servlet.sip.Address</b> and represents the from field</li>
     * <li>field is a <b>Boolean</b> and represents the isValid field</li>
     * <li>field is a <b>Boolean</b> and represents the isInitial field</li>
     * <li>field is a <b>Integer</b> and represents the session counter</li>
     * <li>field is a <b>String</b> and represents the beKey field</li>
     * <li>field is a <b>Boolean</b> and represents the isInviteDialog field</li>     *
     * @param in the stream to read the object members
     *
     * @throws IOException is thrown when unsupported version is detected
     * @throws ClassNotFoundException
     */   
    public void readExternal(ObjectInput in)
        throws IOException, ClassNotFoundException {
        short readSerializedFormVersion = in.readShort();
        switch(readSerializedFormVersion) {
        case 1:
            readExternalFormVersion1(in);
            break;
        case 2:
            readExternalFormVersion2(in);
            break;
        default:
            throw new IOException("Unable to deserialize into "
                    + this.getClass().getName()
                    + " with serialVersionUID = " + serialVersionUID
                    + " due to unknown serializedFormVersion of "
                    + readSerializedFormVersion);
        }
    }

    private void readExternalFormVersion1(ObjectInput in)
        throws IOException, ClassNotFoundException {
        // NEVER change the body of this method. This method takes
        // care of reading the serialization stream of a released product.
        dialogLifeCycle = new DialogLifeCycle(this);
        m_Nodes = new LinkedList<PathNode>();

        int i = in.readInt();

        for (int c = 0; c < i; c++) {
            m_Nodes.add((PathNode) in.readObject());
        }

        m_ToTag = new Ascii7String(in.readUTF());

        if (m_ToTag.length() == 0) {
            // map empty string back to null value
            m_ToTag = null;
        } else {
            registered = true;
        }

        m_FragmentId = (String)in.readObject();

        m_callId = new Ascii7String(in.readUTF());
        m_from = (Address) in.readObject();
        // m_DialogSet is null not valid for confirmed dialogs
        m_IsValid = in.readBoolean();
        isInitial = in.readBoolean();
        m_SessionCounter = new AtomicInteger(in.readInt());
        validLock = new Object();
        sessionLock = new SessionLock();
        isConfirmed = true;
    }   

    private void readExternalFormVersion2(ObjectInput in)
        throws IOException, ClassNotFoundException {
        // If serialization fields of serializedFormVersion1 are removed/shuffed
        // in version2 then deserialization should be done independently without
        // calling readExternalFormVersion1(in).
        readExternalFormVersion1(in);
        m_beKey = (String)in.readObject();
        isInviteDialog = in.readBoolean();
    }
    public DialogSet getDialogSet() {
        if (m_DialogSet == null) {
            throw new IllegalStateException(
                "This DialogFragment is in confirmed state and has no longer a DialogSet reference");
        }

        return m_DialogSet;
    }

    public Object clone() {
        //DialogFragment d = new DialogFragment(m_DialogSet);
        DialogFragment d = m_DialogSet.createAdditionalDialogFragment();
        LinkedList<PathNode> nodes = new LinkedList<PathNode>();
        PathNode p = null;

        for (int i = 0; i < m_Nodes.size(); ++i) {
            p = (PathNode) m_Nodes.get(i).clone();
            nodes.add(p);

            //            if (!p.isReplicable()) {
            //                d.isReplicable = false;
            //            }
        }

        d.m_Nodes = nodes;
        d.m_FragmentId = m_FragmentId;
        d.isReplicable = isReplicable;

        return d;
    }

    /**
     * Will clone the dialog fragment starting from caller to callee until the
     * pathNode is found or all if pathNode is not found.
     *
     * @param pn
     *        the stop indication for cloning
     * @return cloned dialog fragment
     */
    public DialogFragment cloneFromCallerToCalleeUntil(PathNode pn,
        boolean excludePn) {
        //DialogFragment d = new DialogFragment(m_DialogSet, true);
        DialogFragment d = m_DialogSet.createAdditionalDialogFragment();
        LinkedList<PathNode> nodes = new LinkedList<PathNode>();
        PathNode p = null;
        PathNode clone = null;
        Iterator<PathNode> iter = getCaller2CalleePath();

        while (iter.hasNext()) {
            p = iter.next();

            // will stop cloning when pathNode is equal, exclude pn
            if (excludePn && pn.equals(p)) {
                break;
            }

            clone = (PathNode) p.clone();
            nodes.add(clone);

            if (!clone.isReplicable()) {
                d.isReplicable = false;
            }

            // will stop cloning when pathNode is equal, include pn
            if (!excludePn && pn.equals(p)) {
                break;
            }
        }

        d.m_Nodes = nodes;

        return d;
    }

    /**
     * The cluster-wide unique identity of the dialog fragment. Consists of
     * <ul>
     * <li>Call-Id
     * <li>Trom-tag
     * <li>To-tag
     * <li>Server instance name
     * <li>Sequence number (within the dialog set)
     * </ul>
     *
     * @return
     */
    public String getDialogId() {
        return createKey(m_callId.toString(),
            m_from.getParameter(AddressImpl.TAG_PARAM), getToTag(), m_FragmentId);
    }

    public String getToTag() {
        // Sometimes the object is not null but the bytes insisde it is. ???
        synchronized (this) {
            return (m_ToTag == null) ? null : m_ToTag.toString();
        }
    }

    public void registerSession(SipSession s) {
        m_SessionCounter.getAndIncrement();
    }

    /**
     * The session is registered with this dialog fragment and MUST invoke
     * invalidate once it is invalidated itself. Sets the to tag of this dialog.
     *
     * @param toTag
     * @param s
     */
    public void setToTagAndRegisterSession(String toTag, SipSession s) {
        registerSession(s);

        if (registered == false) {
            // doubled checked locking pattern
            synchronized (this) {
                if (registered == false) {
                    m_ToTag = new Ascii7String(toTag);
                    DialogFragmentManager.getInstance()
                                         .registerDialogFragment(this);
                    registered = true;
                }
            }
        }
    }

    /**
     * Will try to set the toTag of this DialogFragment and register it. If it
     * fails the toTag is not set because it already has another toTag.
     *
     * @param toTag
     *        the toTag to update this DialogFragment with
     * @param isDialogCreational
     *        If true the DialogFragment is registered for subsequent requests.
     * @return true if DialogFragment has same toTag or was able to set the toTag
     *         or false if another toTag was already assigned to this
     *         DialogFragment
     */
    public boolean tryToSetToTagAndRegisterDialog(String toTag,
        boolean isDialogCreational) {

        if (registered == false) {
            // doubled checked locking pattern
            synchronized (this) {
                if (registered == false) {
                    // FIXME use SessionManager.getInstance().putIfAbsent instead
                    m_ToTag = new Ascii7String(toTag);
                    if (isDialogCreational) {
                        DialogFragmentManager.getInstance()
                                             .registerDialogFragment(this);
                    }

                    registered = true;
                    return true;
                }
            }
        }

        return m_ToTag.equals(toTag);
    }


    public String getFragmentId() {
        return m_FragmentId;
    }

    /**
     * Returns the number of milliseconds since created Intended to be used by
     * the Performance Management functionality when the fragment with id 1 is
     * invalidated
     */
    public long getDialogLifetime() {
        return System.currentTimeMillis() - m__DialogStartedTimestamp;
    }

    /**
     * Removes the dialog fragment from the registry once all registered sessions
     * are invalidated. A session MUST call this invalidate method to aviod
     * memory leaks.
     */
    public void invalidate(boolean hasTimedOut) {
        getDialogLifeCycle().markRemoveWhenFinished();
       
        if (m_SessionCounter.decrementAndGet() == 0) {
            synchronized (validLock) {
                if (isValidForIncomingTraffic()) {
                    // NOT invalidating since the DF is still ready for incoming traffic
                    return;
                }
                if (m_DialogSet != null) {
                    m_DialogSet.removeDialog(this);
                    m_DialogSet = null;
                }

                this.m_IsValid = false;
                this.invalidationTimeStamp = System.currentTimeMillis();

                Thread t = Thread.currentThread();
                this.invalidationThreadInfo = "Id=" + t.getId() + ", name=" +
                    t.getName();
            }
        }
    }

    /**
     * Gets the valid state of the dialogfragment.<br>
     * If the {@link #invalidate(boolean)} method has been called for this
     * object.<br>
     * this method should return false.
     *
     * @return true if valid.
     */
    public boolean isValid() {
        synchronized (validLock) {
            return this.m_IsValid;
        }
    }

    public boolean isValidForIncomingTraffic() {
        for (PathNode pn : m_Nodes) {
            SipSessionImplBase ss;
            ss = (SipSessionImplBase) pn.getSipSession();

            // Df can be used for incoming traffic as long as any
            // pathnode ss is valid for sip traffic (isReadyToInvalidate()==false)
            if (ss != null && ss.isValid() && ss.isReadyToInvalidate()==false) {
                return true;
            }
        }
        return false;
    }
   
   
    /**
     * This method return the referenceCounter
     * This value is increased by a call to registerSession(ss)
     * and decreased by a call to invalidate()
     * @return
     */
    public int getSessionCount() {
      return m_SessionCounter.get();
    }

   
    /**
     * Gets the time when the {@link #invalidate(boolean)} method was called.
     *
     * @return The time in milliseconds.
     */
    public long getInvalidationTimeStamp() {
        synchronized (validLock) {
            return this.invalidationTimeStamp;
        }
    }

    public String getInvalidationThreadInfo() {
        synchronized (validLock) {
            return this.invalidationThreadInfo;
        }
    }

    /**
     * Builds the application path by adding one node starting from caller.
     *
     * @param node
     *        add one node to the internal record route.
     */
    public synchronized void addToPath(PathNode node) {
        m_Nodes.add(node);

        if (m_Logger.isLoggable(Level.WARNING)) {
            // it is a mismatch if we have a different replication value as what was set by the first pathnode
            if (!isMixedPersistence && (size() > 1) &&
                    (isReplicable != node.isReplicable())) {
                // we only report the first occurrence of a mismatch
                isMixedPersistence = true;
                m_Logger.log(Level.WARNING,
                    DialogFragment.class.getCanonicalName() +
                    ".mixed_replication_types",
                    new Object[] {
                        node.getApplicationSession().getName(),
                        m_Nodes.get(0).getApplicationSession().getName()
                    });
            }
        }

        // only replicate if all node are replicable       
        isReplicable = isReplicable && node.isReplicable();
    }

    public PathNode getFirst() {
        return m_Nodes.getFirst();
    }

    public PathNode getLast() {
        return m_Nodes.getLast();
    }

    /**
     * Return number of PathNodes
     *
     * @return number of PathNodes
     */
    public synchronized int size() {
        return m_Nodes.size();
    }

    /**
     * Returns the path of an application or a transaction, which is the internal
     * route for subsequent requests of a dialog respectively the internal via
     * for responses. Will iterate from caller to callee
     *
     * @return an iterator from caller to callee of the path objects representing
     *         one node of the application or the transaction path.
     */
    public synchronized Iterator<PathNode> getCaller2CalleePath() {
      List<PathNode> nodes = new ArrayList<PathNode>(m_Nodes);
        return nodes.iterator();
    }

    /**
     * Returns the path of an application or a transaction, which is the internal
     * route for subsequent requests of a dialog respectively the internal via
     * for responses. Will iterate from callee to caller
     *
     * @return an iterator from callee to caller of the path objects representing
     *         one node of the application or the transaction path.
     */
    public synchronized Iterator<PathNode> getCallee2CallerPath() {
      List<PathNode> nodes = new ArrayList<PathNode>(m_Nodes);
        return new BackwardListIterator<PathNode>(nodes);
    }

    /**
     * Returns the origin of this message. Returns true if it is sent from the
     * caller of the dialog.
     *
     * @param m
     *        the message from either the caller or the callee
     * @return true if it is sent from the caller of the dialog otherwise false.
     * @throws NotEqualDialogException
     */
    public boolean isMessageFromCaller(SipServletMessageImpl m)
        throws NotEqualDialogException {
        if (m.getCallId().equals(m_callId.toString())) {
            String fromTag = m.getFromImpl().getParameter(AddressImpl.TAG_PARAM);
            String toTag = m.getToImpl().getParameter(AddressImpl.TAG_PARAM);
            boolean isFromCaller = false;
            boolean isFromCallee = false;

            if (!m.hasToTag() || (getToTag() == null)) {
                // If no to-tag exist either the message
                // is an initial request or a response
                // to that initial request and the request
                // will orginate from the caller.
                // TODO A message without tags could also be a
                // valid message from a RFC 2543 compliant UA.
                // This is not detected here.
                isFromCaller = getFromTag().equals(fromTag);
            } else {
                isFromCaller = getFromTag().equals(fromTag) &&
                    getToTag().equals(toTag);
                isFromCallee = getFromTag().equals(toTag) &&
                    getToTag().equals(fromTag);
            }

            // It's not the same dialog if the direction
            // of the request doesn't match either the
            // caller or the callee
            if (!isFromCaller && !isFromCallee) {
                throw new NotEqualDialogException();
            }

            return isFromCaller;
        } else {
            throw new NotEqualDialogException();
        }
    }

    @Override
    public String toString() {
        StringBuilder str = new StringBuilder();
        String NEW_LINE = System.getProperty("line.separator");
        str.append("[ DialogFragment fragmentId=" + this.m_FragmentId);
        str.append(NEW_LINE);
        str.append(", dialogStartedTimestamp=" +
            this.m__DialogStartedTimestamp);
        str.append(", hashCode=" + this.hashCode());
        str.append(", dialogLifetime=" + this.getDialogLifetime());
        str.append(", isValid=" + this.m_IsValid);
        str.append(", sessionCounter=" + this.m_SessionCounter);
        str.append(", node size=" + this.m_Nodes.size());
        str.append(", m_ToTag=" + this.m_ToTag);

        SipApplicationSessionImpl sip = null;
        String s = "SipApplicationSessionImpl=";

        for (PathNode p : m_Nodes) {
            if (p == null) {
                continue;
            }

            str.append(NEW_LINE);
            sip = p.getApplicationSession();
            str.append(", PathNode: type=" + p.getType() + ", hashCode" +
                p.hashCode() + ", servlet=" + sip);

            if (sip != null) {
                s += (", ID=" + sip.getId() + ", name=" + sip.getName() +
                " hashCode=" + sip.hashCode());
            }

            str.append(sip);
        }

        str.append(NEW_LINE);
        str.append(" ]");

        return str.toString();
    }

    /**
     * Returns true if this DialogFragment is replicable, false otherwise.
     *
     * A DialogFragment is considered replicable if and only if all of its
     * PathNodes are replicable.
     *
     * @return true if this DialogFragment is replicable, false otherwise
     */
    public boolean isReplicable() {
        return isReplicable;
    }

    public String getCallId() {
        return m_callId.toString();
    }

    public Address getFrom() {
        return m_from;
    }
   
    public void setFrom(Address from) {
      this.m_from = from;
    }

    public String getBeKey() {
        return m_beKey;
    }
   
    /**
     * @return the from tag of this Dialog.
     */
    public String getFromTag() {
        return m_from.getParameter(AddressImpl.TAG_PARAM);
    }

    public boolean isConfirmed() {
        return isConfirmed;
    }

    public void setConfirmed() {
        if (!isConfirmed) {
            for (Iterator<PathNode> iterator = m_Nodes.iterator(); iterator.hasNext();) {
                PathNode pn = iterator.next();
                SipSessionImplBase session = (SipSessionImplBase) pn.getSipSession();
                if (session == null || !session.isValid()) {
                    iterator.remove();
                    if(session != null) {
                        session.doCleanup();
                    }
                    if (m_Logger.isLoggable(Level.FINEST)) {
                        m_Logger.log(Level.FINEST, "Removed pathnode since it is obsolete: "+pn + "(SS-ID: "+pn.getSipSessionId()+")");
                    }
                } else if (pn instanceof ProxyContext) {
                    ((ProxyContext) pn).setConfirmed();
                }
            }

            if (isReplicable) {
                for (PathNode pn : m_Nodes) {
                    SipSessionBase ss;
                    ss = pn.getSipSession();

                    if ((ss != null) && ss.isValid()) {
                        ss.setShouldBePersisted();
                    }
                }
            }

            isConfirmed = true;
        }

        // Mindnotes for DF/DS refactoring....
        // ZZZ m_DialogSet = null;
        // let this be done by the transaction on the initial request
        // that could remove the early dialog + remove the ds reference from df
        // JSR289 team should implement this?
        // beware of multiple 200's via for example b2bua helper
    }

    /**
     * get this session locked for foreground if the session is found to be
     * presently background locked; retry logic in a time-decay polling loop
     * waits for background lock to clear after 6 attempts (12.6 seconds) it
     * unlocks the session and acquires the foreground lock
     */
    public boolean lockForegroundWithRetry() {
        boolean result = false;

        long pollTime = 200L;
        int tryNumber = 0;
        int maxNumberOfRetries = 7;
        boolean keepTrying = true;

        // try to lock up to numTries (i.e. 7) times
        // poll and wait starting with 200 ms
        while (keepTrying) {
            boolean lockResult = lockForeground();
            if (lockResult) {
                keepTrying = false;
                result = true;
                break;
            }

            tryNumber++;

            if (tryNumber < maxNumberOfRetries) {
                pollTime = pollTime * 2L;
                try {
                    Thread.sleep(pollTime);
                } catch (InterruptedException e) {
                    ;
                }
            } else {
                // unlock the background so we can take over
                // FIXME: need to log warning for this situation
                unlockBackground();
            }
        }

        return result;
    }

    /**
     * return whether this session is currently foreground locked
     */
    public synchronized boolean isForegroundLocked() {
        return sessionLock.isForegroundLocked();
    }

    /**
     * lock the session for foreground returns true if successful; false if
     * unsuccessful
     */
    public synchronized boolean lockBackground() {
        return sessionLock.lockBackground();
    }

    /**
     * lock the session for background returns true if successful; false if
     * unsuccessful
     */
    public synchronized boolean lockForeground() {
        return sessionLock.lockForeground();
    }

    /**
     * unlock the session completely irregardless of whether it was foreground
     * or background locked
     */
    public synchronized void unlockForegroundCompletely() {
        sessionLock.unlockForegroundCompletely();
    }

    /**
     * unlock the session from foreground
     */
    public synchronized void unlockForeground() {
        sessionLock.unlockForeground();
    }

    /**
     * unlock the session from background
     */
    public synchronized void unlockBackground() {
        sessionLock.unlockBackground();
    }

    /**
     * return the Session lock
     */
    public SessionLock getSessionLock() {
        return sessionLock;
    }

    /**
     * Gets the CSEQ from the contained UA:s.
     * Will only contain a value if it there is a single UA in the application path.
     * In case there are two or no UA:s null is returned.
     * @return the CSEQ from the contained UA:s
     */
    public String getCSeq() {
        UA[] uas = getUas();

        if ((uas[0] != null) && (uas[1] == null)) {
            return "" + uas[0].getRemoteCSeq();
        } else {
            return null;
        }
    }

    /**
     * Sets the CSEQ on the contained UA, provided that it is only one UA in the application
     * path. In case there are two UA:s (i.e, the application path is terminated in both ends)
     * the CSEQ is taken crosswise from their respective SipSession.
     *
     *  (And, of course, if there are no UA:s, i.e. the path contains only proxies, CSEQ has no meaning)
     * @param cseqStr
     */
    public void setCSeq(String cseqStr)  {
        UA[] uas = getUas();

        if ((cseqStr != null) && (uas[0] != null) && (uas[1] == null)) {
            uas[0].setRemoteCSeq(Integer.parseInt(cseqStr));
        } else if ((uas[0] != null) && (uas[1] != null)) {
            uas[0].setRemoteCSeq(uas[1].getSipSession().getCSeq());
            uas[1].setRemoteCSeq(uas[0].getSipSession().getCSeq());

            if ((cseqStr != null) && m_Logger.isLoggable(Level.WARNING)) {
                m_Logger.log(Level.WARNING,
                    DialogFragment.class.getCanonicalName() + ".cseq_two_ua");
            }
        } else if ((cseqStr == null) && (uas[0] != null) && (uas[1] == null)) {
            throw new RuntimeException(
                "There was no CSEQ given although there exists a UA.");
        } else if ((cseqStr != null) && (uas[0] == null) && (uas[1] == null)) {
            if (m_Logger.isLoggable(Level.WARNING)) {
                m_Logger.log(Level.WARNING,
                    DialogFragment.class.getCanonicalName() + ".cseq_no_ua");
            }
        }
    }

    private UA[] getUas() {
        UA[] uas = new UA[2];
        int i = 0;

        for (PathNode pn : m_Nodes) {
            if (pn instanceof UA) {
                uas[i++] = ((UA) pn);

                if (i >= uas.length) {
                    break;
                }
            }
        }

        return uas;
    }
   
    public boolean isInviteDialog() {
        return isInviteDialog;
    }

    public void setInviteDialog(boolean inviteDialog) {
        this.isInviteDialog = inviteDialog;
    }

    /**
     * Helper class for backward iterations of a list
     *
     * @author ehsroha
     */
    public class BackwardListIterator<T> implements Iterator<T> {
        private List<T> m_List;
        private int m_Index = -1;

        public BackwardListIterator(List<T> list) {
            m_List = list;
            m_Index = list.size();
        }

        public boolean hasNext() {
            return m_Index > 0;
        }

        public T next() {
            if (!hasNext()) {
                throw new NoSuchElementException();
            }

            m_Index--;

            return m_List.get(m_Index);
        }

        public void remove() {
            // should not work, read only
            throw new IllegalStateException();
        }
    }
   
    public DialogLifeCycle getDialogLifeCycle() {
        return dialogLifeCycle;
    }

    /**
     * Checks if the dialog contains a UA
     * @return true if there is a UA in the application path
     */
    public boolean containsUA() {
        for (PathNode pn : m_Nodes) {
            if (pn.getType() == Type.Callee || pn.getType() == Type.Caller) return true;
        }
        return false;
    }

    /**
     * Removes the dialog set and clears the reference to it.
     * @param recurse
     */
    public synchronized void clearDialogSet(boolean recurse) {
        if (m_DialogSet != null) {
            if (recurse) {
                m_DialogSet.remove();
            }
            m_DialogSet = null;
        }
    }

    /**
     * Remove the caller from the pathnodes.
     * <br>
     * The caller is expected to be the first element in the list
     * of pathnodes.
     *
     * @param id The sessionId of the requesting sipsession. When logging
     * is enabled, a sanity check will be done to see if the requestor is
     * indeed the caller and the first entry in the list.
     */
    public void removeCallerFromPath(String id) {
        PathNode removedPN = m_Nodes.removeFirst();
        // do some sanity checks if logging is enabled
        if (m_Logger.isLoggable(Level.FINE)) {
            if (removedPN.getType() != Type.Caller) {
                m_Logger.log(Level.FINE, "type of first pathnode is NOT Caller");
            }
            if (removedPN.getSipSessionId() != id) {
                m_Logger.log(Level.FINE, "id of first pathnode does not match");
            }           
        }
    }
}
TOP

Related Classes of com.ericsson.ssa.sip.DialogFragment$BackwardListIterator

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.