/** * * Copyright 2003-2007 Jive Software. * * 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.jivesoftware.smackx.workgroup.user; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.CopyOnWriteArraySet; import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.SmackException.NoResponseException; import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.StanzaCollector; import org.jivesoftware.smack.StanzaListener; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.XMPPException.XMPPErrorException; import org.jivesoftware.smack.filter.AndFilter; import org.jivesoftware.smack.filter.FromMatchesFilter; import org.jivesoftware.smack.filter.StanzaFilter; import org.jivesoftware.smack.filter.StanzaTypeFilter; import org.jivesoftware.smack.packet.ExtensionElement; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Presence; import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; import org.jivesoftware.smackx.disco.packet.DiscoverInfo; import org.jivesoftware.smackx.muc.MultiUserChatManager; import org.jivesoftware.smackx.muc.packet.MUCUser; import org.jivesoftware.smackx.workgroup.MetaData; import org.jivesoftware.smackx.workgroup.WorkgroupInvitation; import org.jivesoftware.smackx.workgroup.WorkgroupInvitationListener; import org.jivesoftware.smackx.workgroup.ext.forms.WorkgroupForm; import org.jivesoftware.smackx.workgroup.packet.DepartQueuePacket; import org.jivesoftware.smackx.workgroup.packet.QueueUpdate; import org.jivesoftware.smackx.workgroup.packet.SessionID; import org.jivesoftware.smackx.workgroup.packet.UserID; import org.jivesoftware.smackx.workgroup.settings.ChatSetting; import org.jivesoftware.smackx.workgroup.settings.ChatSettings; import org.jivesoftware.smackx.workgroup.settings.OfflineSettings; import org.jivesoftware.smackx.workgroup.settings.SoundSettings; import org.jivesoftware.smackx.workgroup.settings.WorkgroupProperties; import org.jivesoftware.smackx.xdata.Form; import org.jivesoftware.smackx.xdata.FormField; import org.jivesoftware.smackx.xdata.packet.DataForm; import org.jxmpp.jid.DomainBareJid; import org.jxmpp.jid.EntityBareJid; import org.jxmpp.jid.EntityJid; import org.jxmpp.jid.Jid; /** * Provides workgroup services for users. Users can join the workgroup queue, depart the * queue, find status information about their placement in the queue, and register to * be notified when they are routed to an agent.
*
* This class only provides a users perspective into a workgroup and is not intended
* for use by agents.
*
* @author Matt Tucker
* @author Derek DeMoro
*/
public class Workgroup {
private final EntityBareJid workgroupJID;
private final XMPPConnection connection;
private boolean inQueue;
private final CopyOnWriteArraySet
*
* Some servers may be configured to require certain meta-data in order to
* join the queue. In that case, the {@link #joinQueue(Form)} method should be
* used instead of this method so that meta-data may be passed in.
*
* The server tracks the conversations that a user has with agents over time. By
* default, that tracking is done using the user's JID. However, this is not always
* possible. For example, when the user is logged in anonymously using a web client.
* In that case the user ID might be a randomly generated value put into a persistent
* cookie or a username obtained via the session. A userID can be explicitly
* passed in by using the {@link #joinQueue(Form, Jid)} method. When specified,
* that userID will be used instead of the user's JID to track conversations. The
* server will ignore a manually specified userID if the user's connection to the server
* is not anonymous.
*
* @throws XMPPException if an error occurred joining the queue. An error may indicate
* that a connection failure occurred or that the server explicitly rejected the
* request to join the queue.
* @throws SmackException
* @throws InterruptedException
*/
public void joinQueue() throws XMPPException, SmackException, InterruptedException {
joinQueue(null);
}
/**
* Joins the workgroup queue to wait to be routed to an agent. After joining
* the queue, queue status events will be sent to indicate the user's position and
* estimated time left in the queue. Once joining the queue, there are three ways
* the user can leave the queue:
*
* Some servers may be configured to require certain meta-data in order to
* join the queue.
*
* The server tracks the conversations that a user has with agents over time. By
* default, that tracking is done using the user's JID. However, this is not always
* possible. For example, when the user is logged in anonymously using a web client.
* In that case the user ID might be a randomly generated value put into a persistent
* cookie or a username obtained via the session. A userID can be explicitly
* passed in by using the {@link #joinQueue(Form, Jid)} method. When specified,
* that userID will be used instead of the user's JID to track conversations. The
* server will ignore a manually specified userID if the user's connection to the server
* is not anonymous.
*
* @param answerForm the completed form the send for the join request.
* @throws XMPPException if an error occurred joining the queue. An error may indicate
* that a connection failure occurred or that the server explicitly rejected the
* request to join the queue.
* @throws SmackException
* @throws InterruptedException
*/
public void joinQueue(Form answerForm) throws XMPPException, SmackException, InterruptedException {
joinQueue(answerForm, null);
}
/**
* Joins the workgroup queue to wait to be routed to an agent. After joining
* the queue, queue status events will be sent to indicate the user's position and
* estimated time left in the queue. Once joining the queue, there are three ways
* the user can leave the queue:
*
* Some servers may be configured to require certain meta-data in order to
* join the queue.
*
* The server tracks the conversations that a user has with agents over time. By
* default, that tracking is done using the user's JID. However, this is not always
* possible. For example, when the user is logged in anonymously using a web client.
* In that case the user ID might be a randomly generated value put into a persistent
* cookie or a username obtained via the session. When specified, that userID will
* be used instead of the user's JID to track conversations. The server will ignore a
* manually specified userID if the user's connection to the server is not anonymous.
*
* @param answerForm the completed form associated with the join request.
* @param userID String that represents the ID of the user when using anonymous sessions
* or null if a userID should not be used.
* @throws XMPPErrorException if an error occurred joining the queue. An error may indicate
* that a connection failure occurred or that the server explicitly rejected the
* request to join the queue.
* @throws NoResponseException
* @throws NotConnectedException
* @throws InterruptedException
*/
public void joinQueue(Form answerForm, Jid userID) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
// If already in the queue ignore the join request.
if (inQueue) {
throw new IllegalStateException("Already in queue " + workgroupJID);
}
JoinQueuePacket joinPacket = new JoinQueuePacket(workgroupJID, answerForm, userID);
connection.createStanzaCollectorAndSend(joinPacket).nextResultOrThrow();
// Notify listeners that we've joined the queue.
fireQueueJoinedEvent();
}
/**
* Joins the workgroup queue to wait to be routed to an agent. After joining
* the queue, queue status events will be sent to indicate the user's position and
* estimated time left in the queue. Once joining the queue, there are three ways
* the user can leave the queue:
*
* Some servers may be configured to require certain meta-data in order to
* join the queue.
*
* The server tracks the conversations that a user has with agents over time. By
* default, that tracking is done using the user's JID. However, this is not always
* possible. For example, when the user is logged in anonymously using a web client.
* In that case the user ID might be a randomly generated value put into a persistent
* cookie or a username obtained via the session. When specified, that userID will
* be used instead of the user's JID to track conversations. The server will ignore a
* manually specified userID if the user's connection to the server is not anonymous.
*
* @param metadata metadata to create a dataform from.
* @param userID String that represents the ID of the user when using anonymous sessions
* or null if a userID should not be used.
* @throws XMPPException if an error occurred joining the queue. An error may indicate
* that a connection failure occurred or that the server explicitly rejected the
* request to join the queue.
* @throws SmackException
* @throws InterruptedException
*/
public void joinQueue(Map
*
* Normally, the user would not manually leave the queue. However, they may wish to
* under certain circumstances -- for example, if they no longer wish to be routed
* to an agent because they've been waiting too long.
*
* @throws XMPPErrorException if an error occurred trying to send the depart queue
* request to the server.
* @throws NoResponseException
* @throws NotConnectedException
* @throws InterruptedException
*/
public void departQueue() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
// If not in the queue ignore the depart request.
if (!inQueue) {
return;
}
DepartQueuePacket departPacket = new DepartQueuePacket(this.workgroupJID);
connection.createStanzaCollectorAndSend(departPacket).nextResultOrThrow();
// Notify listeners that we're no longer in the queue.
fireQueueDepartedEvent();
}
/**
* Adds a queue listener that will be notified of queue events for the user
* that created this Workgroup instance.
*
* @param queueListener the queue listener.
*/
public void addQueueListener(QueueListener queueListener) {
queueListeners.add(queueListener);
}
/**
* Removes a queue listener.
*
* @param queueListener the queue listener.
*/
public void removeQueueListener(QueueListener queueListener) {
queueListeners.remove(queueListener);
}
/**
* Adds an invitation listener that will be notified of groupchat invitations
* from the workgroup for the the user that created this Workgroup instance.
*
* @param invitationListener the invitation listener.
*/
public void addInvitationListener(WorkgroupInvitationListener invitationListener) {
invitationListeners.add(invitationListener);
}
/**
* Removes an invitation listener.
*
* @param invitationListener the invitation listener.
*/
public void removeQueueListener(WorkgroupInvitationListener invitationListener) {
invitationListeners.remove(invitationListener);
}
private void fireInvitationEvent(WorkgroupInvitation invitation) {
for (WorkgroupInvitationListener listener : invitationListeners) {
listener.invitationReceived(invitation);
}
}
private void fireQueueJoinedEvent() {
for (QueueListener listener : queueListeners) {
listener.joinedQueue();
}
}
private void fireQueueDepartedEvent() {
for (QueueListener listener : queueListeners) {
listener.departedQueue();
}
}
private void fireQueuePositionEvent(int currentPosition) {
for (QueueListener listener : queueListeners) {
listener.queuePositionUpdated(currentPosition);
}
}
private void fireQueueTimeEvent(int secondsRemaining) {
for (QueueListener listener : queueListeners) {
listener.queueWaitTimeUpdated(secondsRemaining);
}
}
// PacketListener Implementation.
private void handlePacket(Stanza packet) {
if (packet instanceof Message) {
Message msg = (Message) packet;
// Check to see if the user left the queue.
ExtensionElement pe = msg.getExtension("depart-queue", "http://jabber.org/protocol/workgroup");
ExtensionElement queueStatus = msg.getExtension("queue-status", "http://jabber.org/protocol/workgroup");
if (pe != null) {
fireQueueDepartedEvent();
}
else if (queueStatus != null) {
QueueUpdate queueUpdate = (QueueUpdate) queueStatus;
if (queueUpdate.getPosition() != -1) {
fireQueuePositionEvent(queueUpdate.getPosition());
}
if (queueUpdate.getRemaingTime() != -1) {
fireQueueTimeEvent(queueUpdate.getRemaingTime());
}
}
else {
// Check if a room invitation was sent and if the sender is the workgroup
MUCUser mucUser = msg.getExtension("x", "http://jabber.org/protocol/muc#user");
MUCUser.Invite invite = mucUser != null ? mucUser.getInvite() : null;
if (invite != null && workgroupJID.equals(invite.getFrom())) {
String sessionID = null;
Map
*
*
*
* A user cannot request to join the queue again if already in the queue. Therefore,
* this method will throw an IllegalStateException if the user is already in the queue.
*
*
*
* A user cannot request to join the queue again if already in the queue. Therefore,
* this method will throw an IllegalStateException if the user is already in the queue.
*
*
*
* A user cannot request to join the queue again if already in the queue. Therefore,
* this method will throw an IllegalStateException if the user is already in the queue.
*
*
*
* A user cannot request to join the queue again if already in the queue. Therefore,
* this method will throw an IllegalStateException if the user is already in the queue.