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

org.asteriskjava.pbx.internal.managerAPI.OriginateBaseClass Maven / Gradle / Ivy

There is a newer version: 3.41.0
Show newest version
package org.asteriskjava.pbx.internal.managerAPI;

import java.util.HashMap;
import java.util.HashSet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

import org.asteriskjava.live.ManagerCommunicationException;
import org.asteriskjava.pbx.AsteriskSettings;
import org.asteriskjava.pbx.CallerID;
import org.asteriskjava.pbx.Channel;
import org.asteriskjava.pbx.EndPoint;
import org.asteriskjava.pbx.NewChannelListener;
import org.asteriskjava.pbx.PBX;
import org.asteriskjava.pbx.PBXException;
import org.asteriskjava.pbx.PBXFactory;
import org.asteriskjava.pbx.asterisk.wrap.actions.OriginateAction;
import org.asteriskjava.pbx.asterisk.wrap.events.BridgeEvent;
import org.asteriskjava.pbx.asterisk.wrap.events.DialBeginEvent;
import org.asteriskjava.pbx.asterisk.wrap.events.DialEndEvent;
import org.asteriskjava.pbx.asterisk.wrap.events.HangupEvent;
import org.asteriskjava.pbx.asterisk.wrap.events.LinkEvent;
import org.asteriskjava.pbx.asterisk.wrap.events.ManagerEvent;
import org.asteriskjava.pbx.asterisk.wrap.events.NewChannelEvent;
import org.asteriskjava.pbx.asterisk.wrap.events.OriginateResponseEvent;
import org.asteriskjava.pbx.asterisk.wrap.events.UnlinkEvent;
import org.asteriskjava.pbx.asterisk.wrap.response.ManagerResponse;
import org.asteriskjava.pbx.internal.core.AsteriskPBX;
import org.asteriskjava.util.Log;
import org.asteriskjava.util.LogFactory;

public abstract class OriginateBaseClass extends EventListenerBaseClass
{

    /*
     * this class generates and issues ActionEvents to asterisk through the
     * manager. This is the asterisk coal face.
     */
    protected static final Log logger = LogFactory.getLog(OriginateBaseClass.class);

    private volatile String originateID;

    volatile private boolean originateSuccess;

    private final Channel monitorChannel1;

    private boolean hungup = false;

    private Channel newChannel;

    private final Channel monitorChannel2;

    private final OriginateResult result;

    Exception ex = new Exception("Created here");

    /**
     * The following two variables together are used to determine if the
     * originated channel has come up. This is to overcome the problem that the
     * OriginateResponseEvent and the NewChannelEvent can occur in any order
     * (although the NewChannelEvent will occur first in most circumstances).
     * Note: we get many NewChannelEvents but we are only interested in a very
     * specific one.
     */
    // Used to track if the originate event has been seen.
    private boolean originateSeen = false;

    // Used to track if the new (final) channel has been seen.
    private boolean channelSeen = false;

    private final NewChannelListener listener;

    private final CountDownLatch originateLatch = new CountDownLatch(1);

    private final AsteriskPBX pbx;

    private String channelId;

    private final AtomicInteger managerEventsSeen = new AtomicInteger();

    protected OriginateBaseClass(final NewChannelListener listener, final Channel monitor, final Channel monitor2)
    {
        super("NewOrginateClass", PBXFactory.getActivePBX());
        pbx = (AsteriskPBX) PBXFactory.getActivePBX();
        this.listener = listener;
        this.monitorChannel1 = monitor;
        this.monitorChannel2 = monitor2;
        this.result = new OriginateResult();

    }

    private static final AtomicLong originateSeed = new AtomicLong();

    /**
     * @param local
     * @param target
     * @param myVars
     * @param callerId the caller id to set when initiating this call.
     * @param hideCallerId
     * @param context
     * @return
     */
    OriginateResult originate(final EndPoint local, final EndPoint target, final HashMap myVars,
            final CallerID callerID, final Integer timeout, final boolean hideCallerId, final String context)
    {
        OriginateBaseClass.logger.warn("originate called");
        this.originateSeen = false;
        this.channelSeen = false;

        if (this.hungup)
        {
            // the monitored channel already hungup so just return false and
            // shutdown
            return null;
        }

        OriginateBaseClass.logger.warn("originate connection endPoint \n" + local + " to endPoint " + target //$NON-NLS-2$
                + " vars " + myVars);
        ManagerResponse response = null;

        final AsteriskSettings settings = PBXFactory.getActiveProfile();

        final OriginateAction originate = new OriginateAction();
        this.originateID = originate.getActionId();

        channelId = "" + (System.currentTimeMillis() / 1000) + ".AJ" + originateSeed.incrementAndGet();
        originate.setChannelId(channelId);

        Integer localTimeout = timeout;

        if (timeout == null)
        {
            localTimeout = 30000;
            try
            {
                localTimeout = settings.getDialTimeout() * 1000;
            }
            catch (final Exception e)
            {
                OriginateBaseClass.logger.error("Invalid dial timeout value");
            }
        }

        // Whilst the originate document says that it takes a channel it
        // actually takes an
        // end point. I haven't check but I'm skeptical that you can actually
        // originate to
        // a channel as the doco talks about 'dialing the channel'. I suspect
        // this
        // may be part of asterisk's sloppy terminology.
        if (local.isLocal())
        {
            originate.setEndPoint(local);
            originate.setOption("/n");
        }
        else
        {
            originate.setEndPoint(local);
        }

        originate.setContext(context);
        originate.setExten(target);
        originate.setPriority(1);

        // Set the caller id.
        if (hideCallerId)
        {
            // hide callerID
            originate.setCallingPres(32);
        }
        else
        {
            originate.setCallerId(callerID);
        }

        originate.setVariables(myVars);
        originate.setAsync(true);
        originate.setTimeout(localTimeout);

        try
        {
            // Just add us as an asterisk event listener.
            this.startListener();

            response = pbx.sendAction(originate, localTimeout);
            OriginateBaseClass.logger.warn("Originate.sendAction completed");
            if (response.getResponse().compareToIgnoreCase("Success") != 0)
            {
                OriginateBaseClass.logger
                        .error("Error Originating call" + originate.toString() + " : " + response.getMessage());//$NON-NLS-2$
                throw new ManagerCommunicationException(response.getMessage(), null);
            }

            // wait the set timeout +1 second to allow for
            // asterisk to start the originate
            if (!originateLatch.await(localTimeout + 1000, TimeUnit.MILLISECONDS))
            {
                logger.error("Originate Latch timed out");
            }
        }
        catch (final InterruptedException e)
        {
            OriginateBaseClass.logger.debug(e, e);
        }
        catch (final Exception e)
        {
            OriginateBaseClass.logger.error(e, e);
        }
        finally
        {
            this.close();
        }

        if (this.originateSuccess)
        {
            this.result.setSuccess(true);
            this.result.setChannelData(this.newChannel);
            OriginateBaseClass.logger.warn("new channel ok: " + this.newChannel);
        }
        else
        {
            OriginateBaseClass.logger.warn("originate failed to connect endPoint: " + local + " to ext " + target); //$NON-NLS-2$

            if (this.newChannel != null)
            {
                try
                {
                    logger.warn("Hanging up");
                    pbx.hangup(this.newChannel);
                }
                catch (IllegalArgumentException | IllegalStateException | PBXException e)
                {
                    logger.error(e, e);

                }
            }
        }

        logger.warn("Manager Events seen " + managerEventsSeen.get());

        return this.result;
    }

    void abort(final String reason)
    {
        OriginateBaseClass.logger.warn("Aborting originate ");
        this.originateSuccess = false;
        this.result.setAbortReason(reason);
        this.hungup = true;
        if (this.newChannel != null)
        {
            OriginateBaseClass.logger.warn("Aborted, Hangup up on the way out");
            this.result.setChannelHungup(true);

            PBX pbx = PBXFactory.getActivePBX();
            try
            {
                pbx.hangup(this.newChannel);
            }
            catch (IllegalArgumentException | IllegalStateException | PBXException e)
            {
                logger.error(e, e);

            }
        }
        originateLatch.countDown();

    }

    @Override
    public HashSet> requiredEvents()
    {
        HashSet> required = new HashSet<>();

        required.add(OriginateResponseEvent.class);
        required.add(BridgeEvent.class);
        // bridge event is a subclass of linkevent, so we need link & unlink in
        // our list of events to support Asterisk 1.4
        required.add(LinkEvent.class);
        required.add(UnlinkEvent.class);
        required.add(HangupEvent.class);
        required.add(NewChannelEvent.class);
        required.add(DialEndEvent.class);
        required.add(DialBeginEvent.class);

        return required;
    }

    /**
     * It is important that this method is synchronised as there is some
     * interaction between the events and we need to ensure we process one at a
     * time.
     */
    @Override
    synchronized public void onManagerEvent(final ManagerEvent event)
    {

        managerEventsSeen.getAndIncrement();
        if (event instanceof HangupEvent)
        {
            final HangupEvent hangupEvt = (HangupEvent) event;
            final Channel hangupChannel = hangupEvt.getChannel();

            if (channelId.equals(hangupEvt.getUniqueId()))
            {
                this.originateSuccess = false;
                OriginateBaseClass.logger.warn("Dest channel " + this.newChannel + " hungup");
                originateLatch.countDown();
            }
            if ((this.monitorChannel1 != null) && (hangupChannel.isSame(this.monitorChannel1)))
            {
                this.originateSuccess = false;
                this.hungup = true;
                if (this.newChannel != null)
                {
                    OriginateBaseClass.logger.warn("hanging up " + this.newChannel);
                    this.result.setChannelHungup(true);

                    PBX pbx = PBXFactory.getActivePBX();
                    try
                    {
                        logger.warn("Hanging up");
                        pbx.hangup(this.newChannel);
                    }
                    catch (IllegalArgumentException | IllegalStateException | PBXException e)
                    {
                        logger.error(e, e);

                    }
                }
                OriginateBaseClass.logger.warn("notify channel 1 hungup");
                originateLatch.countDown();
            }
            if ((this.monitorChannel2 != null) && (hangupChannel.isSame(this.monitorChannel2)))
            {
                this.originateSuccess = false;
                this.hungup = true;
                if (this.newChannel != null)
                {
                    OriginateBaseClass.logger.warn("Hanging up channel " + this.newChannel);
                    this.result.setChannelHungup(true);

                    PBX pbx = PBXFactory.getActivePBX();
                    try
                    {
                        pbx.hangup(this.newChannel);
                    }
                    catch (IllegalArgumentException | IllegalStateException | PBXException e)
                    {
                        logger.error(e, e);

                    }
                }
                OriginateBaseClass.logger.warn("Notify channel 2 (" + this.monitorChannel2 + ") hungup");//$NON-NLS-2$
                originateLatch.countDown();
            }

        }
        if (event instanceof OriginateResponseEvent)
        {
            OriginateBaseClass.logger.debug("response : " + this.newChannel);

            final OriginateResponseEvent response = (OriginateResponseEvent) event;
            OriginateBaseClass.logger.debug("OriginateResponseEvent: channel="
                    + (response.isChannel() ? response.getChannel() : response.getEndPoint()) + " originateID:"
                    + this.originateID);
            OriginateBaseClass.logger.debug("{" + response.getReason() + ":" + response.getResponse() + "}"); //$NON-NLS-2$ //$NON-NLS-3$
            if (this.originateID != null)
            {
                if (this.originateID.compareToIgnoreCase(response.getActionId()) == 0)
                {
                    this.originateSuccess = response.isSuccess();
                    OriginateBaseClass.logger.warn("OriginateResponse: matched actionId, success=" + this.originateSuccess
                            + " channelSeen=" + this.channelSeen);

                    this.originateSeen = true;

                    if (originateSuccess)
                    {
                        if (newChannel == null)
                        {
                            this.newChannel = response.getChannel();
                        }
                        channelSeen = newChannel != null;

                        // if we have also seen the channel then we can notify
                        // the
                        // originate() method
                        // that the call is up. Otherwise we will rely on the
                        // NewChannelEvent doing the
                        // notify.
                        if (this.channelSeen)
                        {
                            OriginateBaseClass.logger.warn("notify originate response event " + this.originateSuccess);
                            originateLatch.countDown();
                        }
                        else
                        {
                            logger.error("Originate Response didn't contain the channel");
                        }
                    }
                }
            }
            else
            {
                OriginateBaseClass.logger.warn("actionid is null");
            }
        }

        if (event instanceof NewChannelEvent)
        {
            final NewChannelEvent newState = (NewChannelEvent) event;
            if (channelId.equalsIgnoreCase(newState.getChannel().getUniqueId()))
            {
                final Channel channel = newState.getChannel();

                OriginateBaseClass.logger.warn("new channel event :" + channel + " context = " + newState.getContext() //$NON-NLS-2$
                        + " state =" + newState.getChannelStateDesc() + " state =" + newState.getChannelState()); //$NON-NLS-2$

                handleId(channel);
            }
        }

        if (event instanceof BridgeEvent)
        {
            if (((BridgeEvent) event).isLink())
            {
                final BridgeEvent bridgeEvent = (BridgeEvent) event;
                Channel channel = bridgeEvent.getChannel1();
                if (bridgeEvent.getChannel1().isLocal())
                {
                    channel = bridgeEvent.getChannel2();
                }
                if (channelId.equalsIgnoreCase(bridgeEvent.getChannel1().getUniqueId())
                        || channelId.equalsIgnoreCase(bridgeEvent.getChannel2().getUniqueId()))
                {

                    OriginateBaseClass.logger
                            .warn("new bridge event :" + channel + " channel1 = " + bridgeEvent.getChannel1() //$NON-NLS-2$
                                    + " channel2 =" + bridgeEvent.getChannel2());

                    handleId(channel);
                }
            }

        }

        if (event instanceof DialEndEvent)
        {
            if (channelId.equalsIgnoreCase(((DialEndEvent) event).getDestChannel().getUniqueId()))
            {
                String forward = ((DialEndEvent) event).getForward();
                if (forward != null && forward.length() > 0)
                {
                    logger.warn("DialEndEvent");
                    originateLatch.countDown();
                }
            }
        }
    }

    private void handleId(Channel channel)
    {

        if ((this.newChannel == null) && !channel.isLocal())
        {
            this.newChannel = channel;
            this.channelSeen = true;

            OriginateBaseClass.logger.warn("new channel name " + channel);
            if (this.listener != null)
            {
                /*
                 * Update the phone channel to allow the call to be cancelled
                 * before it's answered.
                 */
                this.listener.channelUpdate(channel);
            }

            if (this.originateSeen)
            {
                OriginateBaseClass.logger.warn("notifying success 362");
                originateLatch.countDown();
            }
        }

    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy