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

org.asteriskjava.pbx.internal.asterisk.MeetmeRoomControl Maven / Gradle / Ivy

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

import org.asteriskjava.AsteriskVersion;
import org.asteriskjava.live.ManagerCommunicationException;
import org.asteriskjava.lock.Locker.LockCloser;
import org.asteriskjava.pbx.*;
import org.asteriskjava.pbx.asterisk.wrap.actions.CommandAction;
import org.asteriskjava.pbx.asterisk.wrap.actions.ConfbridgeListAction;
import org.asteriskjava.pbx.asterisk.wrap.events.*;
import org.asteriskjava.pbx.asterisk.wrap.response.CommandResponse;
import org.asteriskjava.pbx.asterisk.wrap.response.ManagerResponse;
import org.asteriskjava.pbx.internal.core.AsteriskPBX;
import org.asteriskjava.pbx.internal.core.CoherentManagerEventListener;
import org.asteriskjava.pbx.internal.managerAPI.EventListenerBaseClass;
import org.asteriskjava.util.Log;
import org.asteriskjava.util.LogFactory;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicReference;

public class MeetmeRoomControl extends EventListenerBaseClass implements CoherentManagerEventListener {
    /*
     * listens for a channel entering or leaving meetme rooms. when there is
     * only 1 channel left in a room it sets it as inactive .It will not set to
     * inactive unless okToKill is set to true. It is set to false by
     * default.Also manages the available room list.
     */

    private static final Log logger = LogFactory.getLog(MeetmeRoomControl.class);

    private Integer meetmeBaseAddress;

    private MeetmeRoom rooms[];

    private int roomCount;

    private boolean meetmeInstalled = false;

    private final static AtomicReference self = new AtomicReference<>();

    synchronized public static void init(PBX pbx, final int roomCount) throws NoMeetmeException {
        if (MeetmeRoomControl.self.get() != null) {
            logger.warn("The MeetmeRoomControl has already been initialised."); //$NON-NLS-1$
        } else {
            MeetmeRoomControl.self.set(new MeetmeRoomControl(pbx, roomCount));
        }
    }

    public static MeetmeRoomControl getInstance() {
        if (MeetmeRoomControl.self.get() == null) {
            throw new IllegalStateException(
                    "The MeetmeRoomControl has not been initialised. Please call MeetmeRoomControl.init()."); //$NON-NLS-1$
        }

        return MeetmeRoomControl.self.get();

    }

    private MeetmeRoomControl(PBX pbx, final int roomCount) throws NoMeetmeException {
        super("MeetmeRoomControl", pbx); //$NON-NLS-1$
        this.roomCount = roomCount;
        final AsteriskSettings settings = PBXFactory.getActiveProfile();
        this.meetmeBaseAddress = settings.getMeetmeBaseAddress();
        this.rooms = new MeetmeRoom[roomCount];
        this.configure((AsteriskPBX) pbx);

        this.startListener();
    }

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

        required.add(MeetMeJoinEvent.class);
        required.add(MeetMeLeaveEvent.class);

        return required;
    }

    /*
     * returns the next available meetme room, or null if no rooms are
     * available.
     */
    public MeetmeRoom findAvailableRoom(RoomOwner newOwner) {
        try (LockCloser closer = this.withLock()) {
            int count = 0;
            for (final MeetmeRoom room : this.rooms) {
                if (MeetmeRoomControl.logger.isDebugEnabled()) {
                    MeetmeRoomControl.logger.debug("room " + room.getRoomNumber() + " count " + count);
                }
                if (room.getOwner() == null || !room.getOwner().isRoomStillRequired()) {
                    /*
                     * new code to attempt to recover uncleared meetme rooms
                     * safely
                     */
                    try {
                        final Long lastUpdated = room.getLastUpdated();
                        final long now = System.currentTimeMillis();
                        if (lastUpdated != null) {
                            final long elapsedTime = now - lastUpdated;
                            MeetmeRoomControl.logger.error(
                                    "room: " + room.getRoomNumber() + " count: " + count + " elapsed: " + elapsedTime);
                            if ((elapsedTime > 1800000) && (room.getChannelCount() < 2)) {
                                MeetmeRoomControl.logger.error("clearing room"); //$NON-NLS-1$
                                room.setInactive();
                            }
                        }
                    } catch (final Exception e) {
                        /*
                         * attempt to make this new change safe
                         */
                        MeetmeRoomControl.logger.error(e, e);
                    }

                    if (room.getChannelCount() == 0) {
                        room.setInactive();
                        room.setOwner(newOwner);
                        MeetmeRoomControl.logger.info("Returning available room " + room.getRoomNumber());
                        return room;
                    }

                } else {
                    logger.warn("Meetme " + room.getRoomNumber() + " is still in use by " + room.getOwner());
                }
                count++;
            }
            MeetmeRoomControl.logger.error("no more available rooms");
            return null;
        }
    }

    /**
     * Returns the MeetmeRoom for the given room number. The room number will be
     * an integer value offset from the meetme base address.
     *
     * @param roomNumber the meetme room number
     * @return
     */
    private MeetmeRoom findMeetmeRoom(final String roomNumber) {
        try (LockCloser closer = this.withLock()) {
            MeetmeRoom foundRoom = null;
            for (final MeetmeRoom room : this.rooms) {
                if (room.getRoomNumber().compareToIgnoreCase(roomNumber) == 0) {
                    foundRoom = room;
                    break;
                }
            }
            return foundRoom;
        }

    }

    MeetmeRoom getRoom(final int room) {
        try (LockCloser closer = this.withLock()) {
            return this.rooms[room];
        }
    }

    @Override
    public void onManagerEvent(final ManagerEvent event) {
        MeetmeRoom room;
        if (event instanceof MeetMeJoinEvent) {
            final MeetMeJoinEvent evt = (MeetMeJoinEvent) event;
            room = this.findMeetmeRoom(evt.getMeetMe());
            final Channel channel = evt.getChannel();
            if (room != null) {
                if (room.addChannel(channel)) {
                    MeetmeRoomControl.logger.debug(channel + " has joined the conference " //$NON-NLS-1$
                            + room.getRoomNumber() + " channelCount " + (room.getChannelCount())); //$NON-NLS-1$
                    room.setLastUpdated();
                }
            }
        }
        if (event instanceof MeetMeLeaveEvent) {
            final MeetMeLeaveEvent evt = (MeetMeLeaveEvent) event;
            room = this.findMeetmeRoom(evt.getMeetMe());
            final Channel channel = evt.getChannel();
            if (room != null) {
                // ignore local dummy channels// &&
                // !channel.toUpperCase().startsWith("LOCAL/")) {

                if (MeetmeRoomControl.logger.isDebugEnabled()) {
                    MeetmeRoomControl.logger.debug(channel + " has left the conference " //$NON-NLS-1$
                            + room.getRoomNumber() + " channel count " + (room.getChannelCount())); //$NON-NLS-1$
                }
                room.removeChannel(channel);
                room.setLastUpdated();
                if (room.getChannelCount() < 2 && room.getForceClose()) {
                    this.hangupChannels(room);
                    room.setInactive();
                }

                if (room.getChannelCount() < 1) {
                    room.setInactive();
                }
            }
        }
    }

    public void hangupChannels(final MeetmeRoom room) {

        final Channel Channels[] = room.getChannels();
        if (room.isActive()) {
            PBX pbx = PBXFactory.getActivePBX();

            for (final Channel channel : Channels) {
                room.removeChannel(channel);

                try {
                    logger.warn("Hanging up");
                    pbx.hangup(channel);
                } catch (IllegalArgumentException | IllegalStateException | PBXException e) {
                    logger.error(e, e);

                }
            }
        }
    }

    private void configure(AsteriskPBX pbx) throws NoMeetmeException {
        final int base = this.meetmeBaseAddress;
        for (int r = 0; r < this.roomCount; r++) {
            this.rooms[r] = new MeetmeRoom(r + base);
        }

        try {
            String command;
            if (pbx.getVersion().isAtLeast(AsteriskVersion.ASTERISK_13)) {
                command = "ConfBridge list"; //$NON-NLS-1$
                ConfbridgeListAction action = new ConfbridgeListAction();
                final ResponseEvents response = pbx.sendEventGeneratingAction(action, 3000);
                Map roomChannelCount = new HashMap<>();
                for (ResponseEvent event : response.getEvents()) {

                    ConfbridgeListEvent e = (ConfbridgeListEvent) event;
                    Integer current = roomChannelCount.get(e.getConference());
                    if (current == null) {
                        roomChannelCount.put(e.getConference(), 1);
                    } else {
                        roomChannelCount.put(e.getConference(), current + 1);
                    }
                }
                for (Entry entry : roomChannelCount.entrySet()) {
                    setRoomCount(entry.getKey(), entry.getValue(), Integer.parseInt(entry.getKey()));

                }
                this.meetmeInstalled = true;

            } else {
                if (pbx.getVersion().isAtLeast(AsteriskVersion.ASTERISK_1_6)) {
                    command = "meetme list"; //$NON-NLS-1$
                } else {
                    command = "meetme"; //$NON-NLS-1$
                }
                final CommandAction commandAction = new CommandAction(command);
                final ManagerResponse response = pbx.sendAction(commandAction, 3000);
                if (!(response instanceof CommandResponse)) {
                    throw new ManagerCommunicationException(response.getMessage(), null);
                }

                final CommandResponse commandResponse = (CommandResponse) response;
                MeetmeRoomControl.logger.debug("parsing active meetme rooms"); //$NON-NLS-1$
                for (final String line : commandResponse.getResult()) {
                    this.parseMeetme(line);
                    this.meetmeInstalled = true;
                    MeetmeRoomControl.logger.debug(line);
                }
            }
        } catch (final NoMeetmeException e) {
            throw e;
        } catch (final Exception e) {
            MeetmeRoomControl.logger.error(e, e);
            throw new NoMeetmeException(e.getLocalizedMessage());
        }
    }

    private void parseMeetme(final String line) throws NoMeetmeException {
        try (LockCloser closer = this.withLock()) {
            if (line != null) {
                if (line.toLowerCase().startsWith("no such command 'meetme'")) //$NON-NLS-1$
                {
                    throw new NoMeetmeException("Asterisk is not configured correctly! Please enable the MeetMe app"); //$NON-NLS-1$
                }

                if ((!line.toLowerCase().startsWith("no active meetme conferences.")) //$NON-NLS-1$
                        && (!line.toLowerCase().startsWith("conf num")) //$NON-NLS-1$
                        && (!line.toLowerCase().startsWith("* total number")) //$NON-NLS-1$
                        && (!line.toLowerCase().startsWith("no such conference")) //$NON-NLS-1$
                        && (!line.toLowerCase().startsWith("no such command 'meetme")) //$NON-NLS-1$
                ) {
                    // Update the stats on each meetme
                    final String roomNumber = line.substring(0, 10).trim();
                    final String tmp = line.substring(11, 25).trim();
                    final int channelCount = Integer.parseInt(tmp);

                    final int roomNo = Integer.valueOf(roomNumber);
                    setRoomCount(roomNumber, channelCount, roomNo);
                }
            }
        }
    }

    private void setRoomCount(final String roomNumber, final int channelCount, final int roomNo) {
        Integer base = this.meetmeBaseAddress;
        // First check if its one of our rooms.
        if ((roomNo >= base) && (roomNo < (base + this.roomCount))) {
            final MeetmeRoom room = this.findMeetmeRoom(roomNumber);
            if (room != null) {
                if (room.getChannelCount() != channelCount) {
                    /*
                     * After a restart there may have been meetme rooms left up
                     * and running with live calls. We need to identify any
                     * active rooms so we don't accidentally re-use an active
                     * room which would result in a crossed channel.
                     */
                    MeetmeRoomControl.logger.warn("Room number: " + room.getRoomNumber() //$NON-NLS-1$
                            + " has a server side channel count = " + channelCount //$NON-NLS-1$
                            + " when the channel count for that room is: " + room.getChannelCount() //$NON-NLS-1$
                            + " the server side channel count will be reset."); //$NON-NLS-1$
                }
                room.resetChannelCount(channelCount);
                room.setActive();
            }
            // else "Found roomNumber:" + roomNumber + " but it was not
            // in the list of rooms managed by MeetmeRoomControl.");
            // //$NON-NLS-1$ //$NON-NLS-2$

        }
    }

    public void stop() {
        this.close();

    }

    @Override
    public ListenerPriority getPriority() {
        return ListenerPriority.NORMAL;
    }

    public boolean isMeetmeInstalled() {
        return this.meetmeInstalled;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy