All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.lastbamboo.common.ice.transport.AbstractIceStunChecker Maven / Gradle / Ivy

package org.lastbamboo.common.ice.transport;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.id.uuid.UUID;
import org.littleshoot.mina.common.CloseFuture;
import org.littleshoot.mina.common.IoSession;
import org.lastbamboo.common.ice.IceStunChecker;
import org.littleshoot.stun.stack.message.BindingRequest;
import org.littleshoot.stun.stack.message.CanceledStunMessage;
import org.littleshoot.stun.stack.message.NullStunMessage;
import org.littleshoot.stun.stack.message.StunMessage;
import org.littleshoot.stun.stack.transaction.StunTransactionListener;
import org.littleshoot.stun.stack.transaction.StunTransactionTracker;
import org.littleshoot.util.RuntimeIoException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Abstract class for STUN connectivity checkers.  This performs STUN checks
 * and notifies the callers of responses.  Subclasses supply the transport.
 */
public abstract class AbstractIceStunChecker implements IceStunChecker,
    StunTransactionListener
    {

    private final Logger m_log = LoggerFactory.getLogger(getClass());
    
    protected final IoSession m_ioSession;

    protected volatile int m_writeCallsForChecker = 0;
    
    protected final Map m_idsToResponses =
        new ConcurrentHashMap();

    protected final StunTransactionTracker m_transactionTracker;

    protected final Object m_requestLock = new Object();
    
    /**
     * TODO: Review if this works!!
     */
    protected volatile boolean m_transactionCanceled = false;

    protected volatile boolean m_closed = false;
    
    public AbstractIceStunChecker(final IoSession ioSession,
        final StunTransactionTracker transactionTracker)
        {
        if (ioSession == null)
            {
            throw new NullPointerException("Null session!!");
            }
        m_transactionTracker = transactionTracker;
        m_ioSession = ioSession;
        }
    
    public StunMessage write(final BindingRequest bindingRequest, 
        final long rto)
        {
        m_log.debug("Writing Binding Request...");
        this.m_writeCallsForChecker++;
        if (this.m_writeCallsForChecker > 1)
            {
            m_log.debug("Second call to checker!!");
            throw new RuntimeIoException("Too many calls to checker: "+
                this.m_writeCallsForChecker);
            }
        
        // If the pair is requesting a write, *that* pair hasn't been canceled.
        // STUN checkers can be used for multiple pairs because, for example,
        // local peer reflexive candidates often have the same base as local
        // host candidates, so there could otherwise be bind collisions
        // (since the remote candidates can be the same).
        //this.m_transactionCanceled = false;
        
        if (this.m_transactionCanceled ||
            this.m_closed || 
            this.m_ioSession.isClosing())
            {
            m_log.debug("Already closed");
            return new CanceledStunMessage();
            }
        try
            {
            return writeInternal(bindingRequest, rto);
            }
        catch (final Throwable t)
            {
            m_log.error("Could not write Binding Request", t);
            return new NullStunMessage();
            }
        }
    
    protected abstract StunMessage writeInternal(BindingRequest bindingRequest, 
        long rto);
    
    protected final void waitIfNoResponse(final BindingRequest request, 
        final long waitTime)
        {
        if (waitTime == 0L) return;
        if (!m_idsToResponses.containsKey(request.getTransactionId()))
            {
            try
                {
                m_requestLock.wait(waitTime);
                }
            catch (final InterruptedException e)
                {
                m_log.error("Unexpected interrupt", e);
                }
            }
        }

    public void cancelTransaction()
        {
        m_log.debug("Cancelling transaction!!");
        this.m_transactionCanceled = true;
        synchronized (m_requestLock)
            {
            m_requestLock.notifyAll();
            }
        }
    
    public Object onTransactionFailed(final StunMessage request,
        final StunMessage response)
        {
        m_log.warn("Transaction failed");
        return notifyWaiters(request, response);
        }
    
    public Object onTransactionSucceeded(final StunMessage request, 
        final StunMessage response)
        {
        return notifyWaiters(request, response);
        }

    private Object notifyWaiters(final StunMessage request, 
        final StunMessage response)
        {
        synchronized (m_requestLock)
            {
            this.m_idsToResponses.put(request.getTransactionId(), response);
            m_requestLock.notifyAll();
            }
        return null;
        }
    
    public void close()
        {
        if (this.m_ioSession == null)
            {
            m_log.debug("Can't close null session");
            return;
            }
        final CloseFuture future = this.m_ioSession.close();
        future.join();
        this.m_closed = true;
        }
    }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy