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

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

The newest version!
package org.asteriskjava.pbx.internal.managerAPI;

import org.asteriskjava.live.ManagerCommunicationException;
import org.asteriskjava.lock.Locker.LockCloser;
import org.asteriskjava.pbx.*;
import org.asteriskjava.pbx.asterisk.wrap.actions.OriginateAction;
import org.asteriskjava.pbx.asterisk.wrap.events.*;
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;

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;

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.info("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.info("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.info("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.info("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);

                }
            }
        }

        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
    public void onManagerEvent(final ManagerEvent event) {
        try (LockCloser closer = this.withLock()) {
            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.info("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.info("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.info("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
                                .info("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.info("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.info("notifying success 362");
                originateLatch.countDown();
            }
        }

    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy