org.asteriskjava.live.internal.QueueManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of asterisk-java Show documentation
Show all versions of asterisk-java Show documentation
The free Java library for Asterisk PBX integration.
The newest version!
/*
* Copyright 2004-2006 Stefan Reuter
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.asteriskjava.live.internal;
import org.asteriskjava.live.AsteriskQueue;
import org.asteriskjava.live.ManagerCommunicationException;
import org.asteriskjava.live.QueueMemberState;
import org.asteriskjava.lock.LockableMap;
import org.asteriskjava.lock.Locker.LockCloser;
import org.asteriskjava.manager.EventTimeoutException;
import org.asteriskjava.manager.ResponseEvents;
import org.asteriskjava.manager.action.QueueStatusAction;
import org.asteriskjava.manager.event.*;
import org.asteriskjava.util.Log;
import org.asteriskjava.util.LogFactory;
import java.util.*;
import java.util.Map.Entry;
/**
* Manages queue events on behalf of an AsteriskServer.
*
* @author srt
* @version $Id$
*/
class QueueManager {
private final Log logger = LogFactory.getLog(this.getClass());
private final AsteriskServerImpl server;
private final ChannelManager channelManager;
private boolean queuesMonitorForced = false;
private long queueMonitorLastTimeReloaded;
private long queuesMonitorLeaseTime = 1000;
/**
* A map of ACD queues by there name. 101119 OLB: Modified to act as a LRU
* Cache to optimize updates
*/
private final LockableMap queuesLRU = new LockableMap<>(new LinkedHashMap<>());
QueueManager(AsteriskServerImpl server, ChannelManager channelManager) {
this.server = server;
this.channelManager = channelManager;
}
void initialize() throws ManagerCommunicationException {
ResponseEvents re;
try {
re = server.sendEventGeneratingAction(new QueueStatusAction());
} catch (ManagerCommunicationException e) {
final Throwable cause = e.getCause();
if (cause instanceof EventTimeoutException) {
// this happens with Asterisk 1.0.x as it doesn't send a
// QueueStatusCompleteEvent
re = ((EventTimeoutException) cause).getPartialResult();
} else {
throw e;
}
}
for (ManagerEvent event : re.getEvents()) {
if (event instanceof QueueParamsEvent) {
handleQueueParamsEvent((QueueParamsEvent) event);
} else if (event instanceof QueueMemberEvent) {
handleQueueMemberEvent((QueueMemberEvent) event);
} else if (event instanceof QueueEntryEvent) {
handleQueueEntryEvent((QueueEntryEvent) event);
}
}
}
/**
* Method to ask for a Queue data update
*
* @param queue
* @throws ManagerCommunicationException
* @author Octavio Luna
*/
void updateQueue(String queue) throws ManagerCommunicationException {
ResponseEvents re;
try {
QueueStatusAction queueStatusAction = new QueueStatusAction();
queueStatusAction.setQueue(queue);
re = server.sendEventGeneratingAction(queueStatusAction);
} catch (ManagerCommunicationException e) {
final Throwable cause = e.getCause();
if (cause instanceof EventTimeoutException) {
// this happens with Asterisk 1.0.x as it doesn't send a
// QueueStatusCompleteEvent
re = ((EventTimeoutException) cause).getPartialResult();
} else {
throw e;
}
}
for (ManagerEvent event : re.getEvents()) {
// 101119 OLB: solo actualizamos el QUEUE por ahora
if (event instanceof QueueParamsEvent) {
handleQueueParamsEvent((QueueParamsEvent) event);
} else if (event instanceof QueueMemberEvent) {
handleQueueMemberEvent((QueueMemberEvent) event);
} else if (event instanceof QueueEntryEvent) {
handleQueueEntryEvent((QueueEntryEvent) event);
}
}
}
void disconnected() {
try (LockCloser closer = queuesLRU.withLock()) {
for (AsteriskQueueImpl queue : queuesLRU.values()) {
queue.cancelServiceLevelTimer();
}
queuesLRU.clear();
}
}
/**
* Gets (a copy of) the list of the queues.
*
* @return a copy of the list of the queues.
*/
Collection getQueues() {
refreshQueuesIfForced();
Collection copy;
try (LockCloser closer = queuesLRU.withLock()) {
copy = new ArrayList(queuesLRU.values());
}
return copy;
}
public List getQueuesUpdatedAfter(Date date) {
refreshQueuesIfForced();
List copy = new ArrayList<>();
try (LockCloser closer = queuesLRU.withLock()) {
List> list = new ArrayList<>(queuesLRU.entrySet());
ListIterator> iter = list.listIterator(list.size());
Entry entry;
while (iter.hasPrevious()) {
entry = iter.previous();
AsteriskQueueImpl astQueue = entry.getValue();
if (astQueue.getLastUpdateMillis() <= date.getTime()) {
break;
}
copy.add(astQueue);
}
}
return copy;
}
/**
* Adds a queue to the internal map, keyed by name.
*
* @param queue the AsteriskQueueImpl to be added
*/
private void addQueue(AsteriskQueueImpl queue) {
try (LockCloser closer = queuesLRU.withLock()) {
queuesLRU.put(queue.getName(), queue);
}
}
/**
* Called during initialization to populate the list of queues.
*
* @param event the event received
*/
private void handleQueueParamsEvent(QueueParamsEvent event) {
AsteriskQueueImpl queue;
final String name = event.getQueue();
final Integer max = event.getMax();
final String strategy = event.getStrategy();
final Integer serviceLevel = event.getServiceLevel();
final Integer weight = event.getWeight();
final Integer calls = event.getCalls();
final Integer holdTime = event.getHoldTime();
final Integer talkTime = event.getTalkTime();
final Integer completed = event.getCompleted();
final Integer abandoned = event.getAbandoned();
final Double serviceLevelPerf = event.getServiceLevelPerf();
queue = getInternalQueueByName(name);
if (queue == null) {
queue = new AsteriskQueueImpl(server, name, max, strategy, serviceLevel, weight, calls, holdTime, talkTime,
completed, abandoned, serviceLevelPerf);
logger.info("Adding new queue " + queue);
addQueue(queue);
} else {
// We should never reach that code as this method is only called for
// initialization
// So the queue should never be in the queues list
try (LockCloser closer = queue.withLock()) {
try (LockCloser closer2 = queuesLRU.withLock()) {
if (queue.setMax(max) | queue.setServiceLevel(serviceLevel) | queue.setWeight(weight)
| queue.setCalls(calls) | queue.setHoldTime(holdTime) | queue.setTalkTime(talkTime)
| queue.setCompleted(completed) | queue.setAbandoned(abandoned)
| queue.setServiceLevelPerf(serviceLevelPerf)) {
queuesLRU.remove(queue.getName());
queuesLRU.put(queue.getName(), queue);
}
}
}
}
}
/**
* Called during initialization to populate the members of the queues.
*
* @param event the QueueMemberEvent received
*/
private void handleQueueMemberEvent(QueueMemberEvent event) {
final AsteriskQueueImpl queue = getInternalQueueByName(event.getQueue());
if (queue == null) {
logger.error("Ignored QueueEntryEvent for unknown queue " + event.getQueue());
return;
}
AsteriskQueueMemberImpl member = queue.getMember(event.getInterface());
if (member == null) {
member = new AsteriskQueueMemberImpl(server, queue, event.getInterface(),
QueueMemberState.valueOf(event.getStatus()), event.getPaused(), event.getPenalty(),
event.getMembership(), event.getCallsTaken(), event.getLastCall());
queue.addMember(member);
} else {
manageQueueMemberChange(queue, member, event);
}
}
private void manageQueueMemberChange(AsteriskQueueImpl queue, AsteriskQueueMemberImpl member, QueueMemberEvent event) {
if (member.stateChanged(QueueMemberState.valueOf(event.getStatus())) | member.pausedChanged(event.getPaused())
| member.penaltyChanged(event.getPenalty()) | member.callsTakenChanged(event.getCallsTaken())
| member.lastCallChanged(event.getLastCall())) {
queue.stampLastUpdate();
}
}
/**
* Called during initialization to populate entries of the queues. Currently
* does the same as handleJoinEvent()
*
* @param event - the QueueEntryEvent received
*/
private void handleQueueEntryEvent(QueueEntryEvent event) {
final AsteriskQueueImpl queue = getInternalQueueByName(event.getQueue());
final AsteriskChannelImpl channel = channelManager.getChannelImplByName(event.getChannel());
if (queue == null) {
logger.error("Ignored QueueEntryEvent for unknown queue " + event.getQueue());
return;
}
if (channel == null) {
logger.error("Ignored QueueEntryEvent for unknown channel " + event.getChannel());
return;
}
// Asterisk gives us an initial position but doesn't tell us when he
// shifts the others
// We won't use this data for ordering until there is a appropriate
// event in AMI.
// (and refreshing the whole queue is too intensive and suffers
// incoherencies
// due to asynchronous shift that leaves holes if requested too fast)
int reportedPosition = event.getPosition();
queue.createNewEntry(channel, reportedPosition, event.getDateReceived());
}
/**
* + * Called from AsteriskServerImpl whenever a new entry appears in a
* queue. + * + * @param event the JoinEvent received +
*/
void handleJoinEvent(QueueCallerJoinEvent event) {
final AsteriskQueueImpl queue = getInternalQueueByName(event.getQueue());
final AsteriskChannelImpl channel = channelManager.getChannelImplByName(event.getChannel());
if (queue == null) {
logger.error("Ignored JoinEvent for unknown queue " + event.getQueue());
return;
}
if (channel == null) {
logger.error("Ignored JoinEvent for unknown channel " + event.getChannel());
return;
}
// Asterisk gives us an initial position but doesn't tell us when he
// shifts the others
// We won't use this data for ordering until there is a appropriate
// event in AMI.
// (and refreshing the whole queue is too intensive and suffers
// incoherencies
// due to asynchronous shift that leaves holes if requested too fast)
int reportedPosition = event.getPosition();
queue.createNewEntry(channel, reportedPosition, event.getDateReceived());
}
/**
* Called from AsteriskServerImpl whenever a new entry appears in a queue.
*
* @param event the JoinEvent received
*/
void handleJoinEvent(JoinEvent event) {
final AsteriskQueueImpl queue = getInternalQueueByName(event.getQueue());
final AsteriskChannelImpl channel = channelManager.getChannelImplByName(event.getChannel());
if (queue == null) {
logger.error("Ignored JoinEvent for unknown queue " + event.getQueue());
return;
}
if (channel == null) {
logger.error("Ignored JoinEvent for unknown channel " + event.getChannel());
return;
}
// Asterisk gives us an initial position but doesn't tell us when he
// shifts the others
// We won't use this data for ordering until there is a appropriate
// event in AMI.
// (and refreshing the whole queue is too intensive and suffers
// incoherencies
// due to asynchronous shift that leaves holes if requested too fast)
int reportedPosition = event.getPosition();
queue.createNewEntry(channel, reportedPosition, event.getDateReceived());
}
/**
* Called from AsteriskServerImpl whenever an enty leaves a queue.
*
* @param event - the LeaveEvent received
*/
void handleLeaveEvent(QueueCallerLeaveEvent event) {
final AsteriskQueueImpl queue = getInternalQueueByName(event.getQueue());
final AsteriskChannelImpl channel = channelManager.getChannelImplByName(event.getChannel());
if (queue == null) {
logger.error("Ignored LeaveEvent for unknown queue " + event.getQueue());
return;
}
if (channel == null) {
logger.error("Ignored LeaveEvent for unknown channel " + event.getChannel());
return;
}
final AsteriskQueueEntryImpl existingQueueEntry = queue.getEntry(event.getChannel());
if (existingQueueEntry == null) {
logger.error("Ignored leave event for non existing queue entry in queue " + event.getQueue() + " for channel "
+ event.getChannel());
return;
}
queue.removeEntry(existingQueueEntry, event.getDateReceived());
}
/**
* Called from AsteriskServerImpl whenever an enty leaves a queue.
*
* @param event - the LeaveEvent received
*/
void handleLeaveEvent(LeaveEvent event) {
final AsteriskQueueImpl queue = getInternalQueueByName(event.getQueue());
final AsteriskChannelImpl channel = channelManager.getChannelImplByName(event.getChannel());
if (queue == null) {
logger.error("Ignored LeaveEvent for unknown queue " + event.getQueue());
return;
}
if (channel == null) {
logger.error("Ignored LeaveEvent for unknown channel " + event.getChannel());
return;
}
final AsteriskQueueEntryImpl existingQueueEntry = queue.getEntry(event.getChannel());
if (existingQueueEntry == null) {
logger.error("Ignored leave event for non existing queue entry in queue " + event.getQueue() + " for channel "
+ event.getChannel());
return;
}
queue.removeEntry(existingQueueEntry, event.getDateReceived());
}
/**
* Challange a QueueMemberStatusEvent. Called from AsteriskServerImpl
* whenever a member state changes.
*
* @param event that was triggered by Asterisk server.
*/
void handleQueueMemberStatusEvent(QueueMemberStatusEvent event) {
AsteriskQueueImpl queue = getInternalQueueByName(event.getQueue());
if (queue == null) {
logger.error("Ignored QueueMemberStatusEvent for unknown queue " + event.getQueue());
return;
}
AsteriskQueueMemberImpl member = queue.getMemberByLocation(event.getInterface());
if (member == null) {
logger.error("Ignored QueueMemberStatusEvent for unknown member " + event.getInterface());
return;
}
manageQueueMemberChange(queue, member, event);
queue.fireMemberStateChanged(member);
}
void handleQueueMemberPausedEvent(QueueMemberPausedEvent event) {
AsteriskQueueImpl queue = getInternalQueueByName(event.getQueue());
if (queue == null) {
logger.error("Ignored QueueMemberPausedEvent for unknown queue " + event.getQueue());
return;
}
AsteriskQueueMemberImpl member = queue.getMemberByLocation(event.getInterface());
if (member == null) {
logger.error("Ignored QueueMemberPausedEvent for unknown member " + event.getInterface());
return;
}
member.pausedChanged(event.getPaused());
}
void handleQueueMemberPenaltyEvent(QueueMemberPenaltyEvent event) {
AsteriskQueueImpl queue = getInternalQueueByName(event.getQueue());
if (queue == null) {
logger.error("Ignored QueueMemberStatusEvent for unknown queue " + event.getQueue());
return;
}
AsteriskQueueMemberImpl member = queue.getMemberByLocation(event.getLocation());
if (member == null) {
logger.error("Ignored QueueMemberStatusEvent for unknown member " + event.getLocation());
return;
}
member.penaltyChanged(event.getPenalty());
}
/**
* Retrieves a queue by its name.
*
* @param queueName name of the queue.
* @return the requested queue or null
if there is no queue
* with the given name.
*/
AsteriskQueueImpl getQueueByName(String queueName) {
refreshQueueIfForced(queueName);
AsteriskQueueImpl queue = getInternalQueueByName(queueName);
if (queue == null) {
logger.error("Requested queue '" + queueName + "' not found!");
}
return queue;
}
private AsteriskQueueImpl getInternalQueueByName(String queueName) {
AsteriskQueueImpl queue;
try (LockCloser closer = queuesLRU.withLock()) {
queue = queuesLRU.get(queueName);
}
return queue;
}
private void refreshQueueIfForced(String queueName) {
if (queuesMonitorForced) {
updateQueue(queueName);
}
}
private void refreshQueuesIfForced() {
if (queuesMonitorForced && (System.currentTimeMillis() - queueMonitorLastTimeReloaded) > queuesMonitorLeaseTime) {
initialize();
queueMonitorLastTimeReloaded = System.currentTimeMillis();
}
}
/**
* Challange a QueueMemberAddedEvent.
*
* @param event - the generated QueueMemberAddedEvent.
*/
public void handleQueueMemberAddedEvent(QueueMemberAddedEvent event) {
final AsteriskQueueImpl queue = getInternalQueueByName(event.getQueue());
if (queue == null) {
logger.error("Ignored QueueMemberAddedEvent for unknown queue " + event.getQueue());
return;
}
AsteriskQueueMemberImpl member = queue.getMember(event.getInterface());
if (member == null) {
member = new AsteriskQueueMemberImpl(server, queue, event.getInterface(),
QueueMemberState.valueOf(event.getStatus()), event.getPaused(), event.getPenalty(),
event.getMembership(), event.getCallsTaken(), event.getLastCall());
}
queue.addMember(member);
}
/**
* Challange a QueueMemberRemovedEvent.
*
* @param event - the generated QueueMemberRemovedEvent.
*/
public void handleQueueMemberRemovedEvent(QueueMemberRemovedEvent event) {
final AsteriskQueueImpl queue = getInternalQueueByName(event.getQueue());
if (queue == null) {
logger.error("Ignored QueueMemberRemovedEvent for unknown queue " + event.getQueue());
return;
}
final AsteriskQueueMemberImpl member = queue.getMember(event.getInterface());
if (member == null) {
logger.error("Ignored QueueMemberRemovedEvent for unknown agent name: " + event.getMemberName() + " location: "
+ event.getInterface() + " queue: " + event.getQueue());
return;
}
queue.removeMember(member);
}
public void forceQueuesMonitor(boolean force) {
queuesMonitorForced = force;
}
public boolean isQueuesMonitorForced() {
return queuesMonitorForced;
}
}