mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-11-26 14:02:06 +01:00
Initial check-in.
git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/trunk@2369 b35dd754-fafc-0310-a699-88a17e54d16e
This commit is contained in:
parent
2aaccd0dec
commit
5d656558fd
26 changed files with 3599 additions and 0 deletions
125
source/org/jivesoftware/smackx/workgroup/Invitation.java
Normal file
125
source/org/jivesoftware/smackx/workgroup/Invitation.java
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
/**
|
||||||
|
* $RCSfile$
|
||||||
|
* $Revision$
|
||||||
|
* $Date$
|
||||||
|
*
|
||||||
|
* Copyright (C) 1999-2003 Jive Software. All rights reserved.
|
||||||
|
*
|
||||||
|
* This software is the proprietary information of Jive Software.
|
||||||
|
* Use is subject to license terms.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jivesoftware.smackx.workgroup;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An immutable class wrapping up the basic information which comprises a group chat invitation.
|
||||||
|
*
|
||||||
|
* @author loki der quaeler
|
||||||
|
*/
|
||||||
|
public class Invitation {
|
||||||
|
|
||||||
|
protected String uniqueID;
|
||||||
|
|
||||||
|
protected String sessionID;
|
||||||
|
|
||||||
|
protected String groupChatName;
|
||||||
|
protected String issuingWorkgroupName;
|
||||||
|
protected String messageBody;
|
||||||
|
protected String invitationSender;
|
||||||
|
protected Map metaData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This calls the 5-argument constructor with a null MetaData argument value
|
||||||
|
*
|
||||||
|
* @param jid the jid string with which the issuing AgentSession or Workgroup instance
|
||||||
|
* was created
|
||||||
|
* @param group the jid of the room to which the person is invited
|
||||||
|
* @param workgroup the jid of the workgroup issuing the invitation
|
||||||
|
* @param sessID the session id associated with the pending chat
|
||||||
|
* @param msgBody the body of the message which contained the invitation
|
||||||
|
* @param from the user jid who issued the invitation, if known, null otherwise
|
||||||
|
*/
|
||||||
|
public Invitation (String jid, String group, String workgroup,
|
||||||
|
String sessID, String msgBody, String from) {
|
||||||
|
this(jid, group, workgroup, sessID, msgBody, from, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param jid the jid string with which the issuing AgentSession or Workgroup instance
|
||||||
|
* was created
|
||||||
|
* @param group the jid of the room to which the person is invited
|
||||||
|
* @param workgroup the jid of the workgroup issuing the invitation
|
||||||
|
* @param sessID the session id associated with the pending chat
|
||||||
|
* @param msgBody the body of the message which contained the invitation
|
||||||
|
* @param from the user jid who issued the invitation, if known, null otherwise
|
||||||
|
* @param metaData the metadata sent with the invitation
|
||||||
|
*/
|
||||||
|
public Invitation (String jid, String group, String workgroup, String sessID, String msgBody,
|
||||||
|
String from, Map metaData) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.uniqueID = jid;
|
||||||
|
this.sessionID = sessID;
|
||||||
|
this.groupChatName = group;
|
||||||
|
this.issuingWorkgroupName = workgroup;
|
||||||
|
this.messageBody = msgBody;
|
||||||
|
this.invitationSender = from;
|
||||||
|
this.metaData = metaData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the jid string with which the issuing AgentSession or Workgroup instance
|
||||||
|
* was created.
|
||||||
|
*/
|
||||||
|
public String getUniqueID () {
|
||||||
|
return this.uniqueID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the session id associated with the pending chat; working backwards temporally,
|
||||||
|
* this session id should match the session id to the corresponding offer request
|
||||||
|
* which resulted in this invitation.
|
||||||
|
*/
|
||||||
|
public String getSessionID () {
|
||||||
|
return this.sessionID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the jid of the room to which the person is invited.
|
||||||
|
*/
|
||||||
|
public String getGroupChatName () {
|
||||||
|
return this.groupChatName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the name of the workgroup from which the invitation was issued.
|
||||||
|
*/
|
||||||
|
public String getWorkgroupName () {
|
||||||
|
return this.issuingWorkgroupName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the contents of the body-block of the message that housed this invitation.
|
||||||
|
*/
|
||||||
|
public String getMessageBody () {
|
||||||
|
return this.messageBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the user who issued the invitation, or null if it wasn't known.
|
||||||
|
*/
|
||||||
|
public String getInvitationSender () {
|
||||||
|
return this.invitationSender;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the meta data associated with the invitation, or null if this instance was
|
||||||
|
* constructed with none
|
||||||
|
*/
|
||||||
|
public Map getMetaData () {
|
||||||
|
return this.metaData;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
/**
|
||||||
|
* $RCSfile$
|
||||||
|
* $Revision$
|
||||||
|
* $Date$
|
||||||
|
*
|
||||||
|
* Copyright (C) 1999-2003 Jive Software. All rights reserved.
|
||||||
|
*
|
||||||
|
* This software is the proprietary information of Jive Software.
|
||||||
|
* Use is subject to license terms.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.workgroup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface which all classes interested in hearing about group chat invitations should
|
||||||
|
* implement.
|
||||||
|
*
|
||||||
|
* @author loki der quaeler
|
||||||
|
*/
|
||||||
|
public interface InvitationListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The implementing class instance will be notified via this method when an invitation
|
||||||
|
* to join a group chat has been received from the server.
|
||||||
|
*
|
||||||
|
* @param invitation an Invitation instance embodying the information pertaining to the
|
||||||
|
* invitation
|
||||||
|
*/
|
||||||
|
public void invitationReceived(Invitation invitation);
|
||||||
|
|
||||||
|
}
|
59
source/org/jivesoftware/smackx/workgroup/MetaData.java
Normal file
59
source/org/jivesoftware/smackx/workgroup/MetaData.java
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
/**
|
||||||
|
* $RCSfile$
|
||||||
|
* $Revision$
|
||||||
|
* $Date$
|
||||||
|
*
|
||||||
|
* Copyright (C) 1999-2003 Jive Software. All rights reserved.
|
||||||
|
*
|
||||||
|
* This software is the proprietary information of Jive Software.
|
||||||
|
* Use is subject to license terms.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jivesoftware.smackx.workgroup;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.workgroup.util.MetaDataUtils;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.PacketExtension;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MetaData packet extension.
|
||||||
|
*/
|
||||||
|
public class MetaData implements PacketExtension {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Element name of the packet extension.
|
||||||
|
*/
|
||||||
|
public static final String ELEMENT_NAME = "metadata";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Namespace of the packet extension.
|
||||||
|
*/
|
||||||
|
public static final String NAMESPACE = "http://www.jivesoftware.com/workgroup/metadata";
|
||||||
|
|
||||||
|
private Map metaData;
|
||||||
|
|
||||||
|
public MetaData(Map metaData) {
|
||||||
|
this.metaData = metaData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the Map of metadata contained by this instance
|
||||||
|
*/
|
||||||
|
public Map getMetaData() {
|
||||||
|
return metaData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getElementName() {
|
||||||
|
return ELEMENT_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNamespace() {
|
||||||
|
return NAMESPACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toXML() {
|
||||||
|
return MetaDataUtils.serializeMetaData(this.getMetaData());
|
||||||
|
}
|
||||||
|
}
|
77
source/org/jivesoftware/smackx/workgroup/QueueUser.java
Normal file
77
source/org/jivesoftware/smackx/workgroup/QueueUser.java
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
/**
|
||||||
|
* $RCSfile$
|
||||||
|
* $Revision$
|
||||||
|
* $Date$
|
||||||
|
*
|
||||||
|
* Copyright (C) 1999-2003 Jive Software. All rights reserved.
|
||||||
|
*
|
||||||
|
* This software is the proprietary information of Jive Software.
|
||||||
|
* Use is subject to license terms.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jivesoftware.smackx.workgroup;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An immutable class which wraps up customer-in-queue data return from the server; depending on
|
||||||
|
* the type of information dispatched from the server, not all information will be available in
|
||||||
|
* any given instance.
|
||||||
|
*
|
||||||
|
* @author loki der quaeler
|
||||||
|
*/
|
||||||
|
public class QueueUser {
|
||||||
|
|
||||||
|
private String userID;
|
||||||
|
|
||||||
|
private int queuePosition;
|
||||||
|
private int estimatedTime;
|
||||||
|
private Date joinDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param uid the user jid of the customer in the queue
|
||||||
|
* @param position the position customer sits in the queue
|
||||||
|
* @param time the estimate of how much longer the customer will be in the queue in seconds
|
||||||
|
* @param joinedAt the timestamp of when the customer entered the queue
|
||||||
|
*/
|
||||||
|
public QueueUser (String uid, int position, int time, Date joinedAt) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.userID = uid;
|
||||||
|
this.queuePosition = position;
|
||||||
|
this.estimatedTime = time;
|
||||||
|
this.joinDate = joinedAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the user jid of the customer in the queue
|
||||||
|
*/
|
||||||
|
public String getUserID () {
|
||||||
|
return this.userID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the position in the queue at which the customer sits, or -1 if the update which
|
||||||
|
* this instance embodies is only a time update instead
|
||||||
|
*/
|
||||||
|
public int getQueuePosition () {
|
||||||
|
return this.queuePosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the estimated time remaining of the customer in the queue in seconds, or -1 if
|
||||||
|
* if the update which this instance embodies is only a position update instead
|
||||||
|
*/
|
||||||
|
public int getEstimatedRemainingTime () {
|
||||||
|
return this.estimatedTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the timestamp of when this customer entered the queue, or null if the server did not
|
||||||
|
* provide this information
|
||||||
|
*/
|
||||||
|
public Date getQueueJoinTimestamp () {
|
||||||
|
return this.joinDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
60
source/org/jivesoftware/smackx/workgroup/agent/Agent.java
Normal file
60
source/org/jivesoftware/smackx/workgroup/agent/Agent.java
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
package org.jivesoftware.smackx.workgroup.agent;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.Presence;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An Agent represents the agent role in a Workgroup Queue.
|
||||||
|
*/
|
||||||
|
public class Agent {
|
||||||
|
|
||||||
|
private String user;
|
||||||
|
private int maxChats = -1;
|
||||||
|
private int currentChats = -1;
|
||||||
|
private Presence presence = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an Agent
|
||||||
|
* @param user - the current agents JID
|
||||||
|
* @param currentChats - the number of chats the agent is in.
|
||||||
|
* @param maxChats - the maximum number of chats the agent is allowed.
|
||||||
|
* @param presence - the agents presence
|
||||||
|
*/
|
||||||
|
public Agent( String user, int currentChats, int maxChats, Presence presence ) {
|
||||||
|
this.user = user;
|
||||||
|
this.currentChats = currentChats;
|
||||||
|
this.maxChats = maxChats;
|
||||||
|
this.presence = presence;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the agents JID
|
||||||
|
* @return - the agents JID.
|
||||||
|
*/
|
||||||
|
public String getUser() {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the maximum number of chats for this agent.
|
||||||
|
* @return - maximum number of chats allowed.
|
||||||
|
*/
|
||||||
|
public int getMaxChats() {
|
||||||
|
return maxChats;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the current chat count.
|
||||||
|
* @return - the current chat count.
|
||||||
|
*/
|
||||||
|
public int getCurrentChats() {
|
||||||
|
return currentChats;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the agents <code>Presence</code>
|
||||||
|
* @return - the agents presence.
|
||||||
|
*/
|
||||||
|
public Presence getPresence() {
|
||||||
|
return presence;
|
||||||
|
}
|
||||||
|
}
|
644
source/org/jivesoftware/smackx/workgroup/agent/AgentSession.java
Normal file
644
source/org/jivesoftware/smackx/workgroup/agent/AgentSession.java
Normal file
|
@ -0,0 +1,644 @@
|
||||||
|
/**
|
||||||
|
* $RCSfile$
|
||||||
|
* $Revision$
|
||||||
|
* $Date$
|
||||||
|
*
|
||||||
|
* Copyright (C) 1999-2003 Jive Software. All rights reserved.
|
||||||
|
*
|
||||||
|
* This software is the proprietary information of Jive Software.
|
||||||
|
* Use is subject to license terms.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jivesoftware.smackx.workgroup.agent;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.*;
|
||||||
|
import org.jivesoftware.smack.filter.*;
|
||||||
|
import org.jivesoftware.smack.packet.*;
|
||||||
|
import org.jivesoftware.smack.util.StringUtils;
|
||||||
|
import org.jivesoftware.smackx.GroupChatInvitation;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.workgroup.*;
|
||||||
|
import org.jivesoftware.smackx.workgroup.packet.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class embodies the agent's active presence within a given workgroup. The application
|
||||||
|
* should have N instances of this class, where N is the number of workgroups to which the
|
||||||
|
* owning agent of the application belongs. This class provides all functionality that a
|
||||||
|
* session within a given workgroup is expected to have from an agent's perspective -- setting
|
||||||
|
* the status, tracking the status of queues to which the agent belongs within the workgroup, and
|
||||||
|
* dequeuing customers.
|
||||||
|
*
|
||||||
|
* @author Matt Tucker
|
||||||
|
* @author loki der quaeler
|
||||||
|
*/
|
||||||
|
public class AgentSession {
|
||||||
|
|
||||||
|
private XMPPConnection connection;
|
||||||
|
|
||||||
|
private String workgroupName;
|
||||||
|
|
||||||
|
private boolean online = false;
|
||||||
|
private Presence.Mode presenceMode;
|
||||||
|
private int currentChats;
|
||||||
|
private int maxChats;
|
||||||
|
private Map metaData;
|
||||||
|
|
||||||
|
private Map queues;
|
||||||
|
|
||||||
|
private List offerListeners;
|
||||||
|
private List invitationListeners;
|
||||||
|
private List queueUsersListeners;
|
||||||
|
private List queueAgentsListeners;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new agent session instance.
|
||||||
|
*
|
||||||
|
* @param connection a connection instance which must have already gone through authentication.
|
||||||
|
* @param workgroupName the fully qualified name of the workgroup.
|
||||||
|
*/
|
||||||
|
public AgentSession(String workgroupName, XMPPConnection connection) {
|
||||||
|
// Login must have been done before passing in connection.
|
||||||
|
if (!connection.isAuthenticated()) {
|
||||||
|
throw new IllegalStateException("Must login to server before creating workgroup.");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.workgroupName = workgroupName;
|
||||||
|
this.connection = connection;
|
||||||
|
|
||||||
|
this.maxChats = -1;
|
||||||
|
|
||||||
|
this.metaData = new HashMap();
|
||||||
|
|
||||||
|
this.queues = new HashMap();
|
||||||
|
|
||||||
|
offerListeners = new ArrayList();
|
||||||
|
invitationListeners = new ArrayList();
|
||||||
|
queueUsersListeners = new ArrayList();
|
||||||
|
queueAgentsListeners = new ArrayList();
|
||||||
|
|
||||||
|
// Create a filter to listen for packets we're interested in.
|
||||||
|
OrFilter filter = new OrFilter();
|
||||||
|
filter.addFilter(new PacketTypeFilter(OfferRequestProvider.OfferRequestPacket.class));
|
||||||
|
filter.addFilter(new PacketTypeFilter(OfferRevokeProvider.OfferRevokePacket.class));
|
||||||
|
filter.addFilter(new PacketTypeFilter(Presence.class));
|
||||||
|
filter.addFilter(new PacketTypeFilter(Message.class));
|
||||||
|
|
||||||
|
// only interested in packets from the workgroup or from operators also running the
|
||||||
|
// operator client -- for example peer-to-peer invites wouldn't come from
|
||||||
|
// the workgroup, but would come from the operator client
|
||||||
|
//OrFilter froms = new OrFilter(new FromContainsFilter(this.workgroupName),
|
||||||
|
// new FromContainsFilter(clientResource));
|
||||||
|
|
||||||
|
connection.addPacketListener(new PacketListener() {
|
||||||
|
public void processPacket(Packet packet) {
|
||||||
|
handlePacket(packet);
|
||||||
|
}
|
||||||
|
}, filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the agent's current presence mode.
|
||||||
|
*
|
||||||
|
* @return the agent's current presence mode.
|
||||||
|
*/
|
||||||
|
public Presence.Mode getPresenceMode() {
|
||||||
|
return presenceMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current number of chats the agent is in.
|
||||||
|
*
|
||||||
|
* @return the current number of chats the agent is in.
|
||||||
|
*/
|
||||||
|
public int getCurrentChats() {
|
||||||
|
return currentChats;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the maximum number of chats the agent can participate in.
|
||||||
|
*
|
||||||
|
* @return the maximum number of chats the agent can participate in.
|
||||||
|
*/
|
||||||
|
public int getMaxChats() {
|
||||||
|
return maxChats;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the agent is online with the workgroup.
|
||||||
|
*
|
||||||
|
* @return true if the agent is online with the workgroup.
|
||||||
|
*/
|
||||||
|
public boolean isOnline() {
|
||||||
|
return online;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows the addition of a new key-value pair to the agent's meta data, if the value is
|
||||||
|
* new data, the revised meta data will be rebroadcast in an agent's presence broadcast.
|
||||||
|
*
|
||||||
|
* @param key the meta data key
|
||||||
|
* @param val the non-null meta data value
|
||||||
|
*/
|
||||||
|
public void setMetaData(String key, String val) throws XMPPException {
|
||||||
|
synchronized (this.metaData) {
|
||||||
|
String oldVal = (String)this.metaData.get(key);
|
||||||
|
|
||||||
|
if ((oldVal == null) || (! oldVal.equals(val))) {
|
||||||
|
metaData.put(key, val);
|
||||||
|
|
||||||
|
setStatus(presenceMode, currentChats, maxChats);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows the removal of data from the agent's meta data, if the key represents existing data,
|
||||||
|
* the revised meta data will be rebroadcast in an agent's presence broadcast.
|
||||||
|
*
|
||||||
|
* @param key the meta data key
|
||||||
|
*/
|
||||||
|
public void removeMetaData(String key)
|
||||||
|
throws XMPPException {
|
||||||
|
synchronized (this.metaData) {
|
||||||
|
String oldVal = (String)metaData.remove(key);
|
||||||
|
|
||||||
|
if (oldVal != null) {
|
||||||
|
setStatus(presenceMode, currentChats, maxChats);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows the retrieval of meta data for a specified key.
|
||||||
|
*
|
||||||
|
* @param key the meta data key
|
||||||
|
* @return the meta data value associated with the key or <tt>null</tt> if the meta-data
|
||||||
|
* doesn't exist..
|
||||||
|
*/
|
||||||
|
public String getMetaData(String key) {
|
||||||
|
return (String)metaData.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether the agent is online with the workgroup. If the user tries to go online with
|
||||||
|
* the workgroup but is not allowed to be an agent, an XMPPError with error code 401 will
|
||||||
|
* be thrown.
|
||||||
|
*
|
||||||
|
* @param online true to set the agent as online with the workgroup.
|
||||||
|
* @throws XMPPException if an error occurs setting the online status.
|
||||||
|
*/
|
||||||
|
public void setOnline( boolean online ) throws XMPPException {
|
||||||
|
// If the online status hasn't changed, do nothing.
|
||||||
|
if (this.online == online) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.online = online;
|
||||||
|
|
||||||
|
Presence presence = null;
|
||||||
|
|
||||||
|
// If the user is going online...
|
||||||
|
if (online) {
|
||||||
|
presence = new Presence(Presence.Type.AVAILABLE);
|
||||||
|
presence.setTo(workgroupName);
|
||||||
|
|
||||||
|
PacketCollector collector = this.connection.createPacketCollector(new AndFilter(
|
||||||
|
new PacketTypeFilter(Presence.class), new FromContainsFilter(workgroupName)));
|
||||||
|
|
||||||
|
connection.sendPacket(presence);
|
||||||
|
|
||||||
|
presence = (Presence)collector.nextResult(5000);
|
||||||
|
collector.cancel();
|
||||||
|
if (presence == null) {
|
||||||
|
throw new XMPPException("No response from server on status set.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (presence.getError() != null) {
|
||||||
|
throw new XMPPException(presence.getError());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Otherwise the user is going offline...
|
||||||
|
else {
|
||||||
|
presence = new Presence(Presence.Type.UNAVAILABLE);
|
||||||
|
presence.setTo(workgroupName);
|
||||||
|
connection.sendPacket(presence);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the agent's current status with the workgroup. The presence mode affects how offers
|
||||||
|
* are routed to the agent. The possible presence modes with their meanings are as follows:<ul>
|
||||||
|
*
|
||||||
|
* <li>Presence.Mode.AVAILABLE -- (Default) the agent is available for more chats
|
||||||
|
* (equivalent to Presence.Mode.CHAT).
|
||||||
|
* <li>Presence.Mode.DO_NOT_DISTURB -– the agent is busy and should not be disturbed.
|
||||||
|
* However, special case, or extreme urgency chats may still be offered to the agent.
|
||||||
|
* <li>Presence.Mode.AWAY -- the agent is not available and should not
|
||||||
|
* have a chat routed to them (equivalent to Presence.Mode.EXTENDED_AWAY).</ul>
|
||||||
|
*
|
||||||
|
* The current chats value indicates how many chats the agent is currently in. Because the agent
|
||||||
|
* is responsible for reporting the current chats value to the server, this value <b>must</b>
|
||||||
|
* be set every time it changes.<p>
|
||||||
|
*
|
||||||
|
* The max chats value is the maximum number of chats the agent is willing to have routed to
|
||||||
|
* them at once. Some servers may be configured to only accept max chat values in a certain
|
||||||
|
* range; for example, between two and five. In that case, the maxChats value the agent sends
|
||||||
|
* may be adjusted by the server to a value within that range.
|
||||||
|
*
|
||||||
|
* @param presenceMode the presence mode of the agent.
|
||||||
|
* @param currentChats the current number of chats the agent is in.
|
||||||
|
* @param maxChats the maximum number of chats the agent is willing to accept.
|
||||||
|
* @throws XMPPException if an error occurs setting the agent status.
|
||||||
|
* @throws IllegalStateException if the agent is not online with the workgroup.
|
||||||
|
*/
|
||||||
|
public void setStatus(Presence.Mode presenceMode, int currentChats, int maxChats )
|
||||||
|
throws XMPPException
|
||||||
|
{
|
||||||
|
setStatus( presenceMode, currentChats, maxChats, null );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the agent's current status with the workgroup. The presence mode affects how offers
|
||||||
|
* are routed to the agent. The possible presence modes with their meanings are as follows:<ul>
|
||||||
|
*
|
||||||
|
* <li>Presence.Mode.AVAILABLE -- (Default) the agent is available for more chats
|
||||||
|
* (equivalent to Presence.Mode.CHAT).
|
||||||
|
* <li>Presence.Mode.DO_NOT_DISTURB -– the agent is busy and should not be disturbed.
|
||||||
|
* However, special case, or extreme urgency chats may still be offered to the agent.
|
||||||
|
* <li>Presence.Mode.AWAY -- the agent is not available and should not
|
||||||
|
* have a chat routed to them (equivalent to Presence.Mode.EXTENDED_AWAY).</ul>
|
||||||
|
*
|
||||||
|
* The current chats value indicates how many chats the agent is currently in. Because the agent
|
||||||
|
* is responsible for reporting the current chats value to the server, this value <b>must</b>
|
||||||
|
* be set every time it changes.<p>
|
||||||
|
*
|
||||||
|
* The max chats value is the maximum number of chats the agent is willing to have routed to
|
||||||
|
* them at once. Some servers may be configured to only accept max chat values in a certain
|
||||||
|
* range; for example, between two and five. In that case, the maxChats value the agent sends
|
||||||
|
* may be adjusted by the server to a value within that range.
|
||||||
|
*
|
||||||
|
* @param presenceMode the presence mode of the agent.
|
||||||
|
* @param currentChats the current number of chats the agent is in.
|
||||||
|
* @param maxChats the maximum number of chats the agent is willing to accept.
|
||||||
|
* @param status sets the status message of the presence update.
|
||||||
|
* @throws XMPPException if an error occurs setting the agent status.
|
||||||
|
* @throws IllegalStateException if the agent is not online with the workgroup.
|
||||||
|
*/
|
||||||
|
public void setStatus(Presence.Mode presenceMode, int currentChats, int maxChats, String status )
|
||||||
|
throws XMPPException
|
||||||
|
{
|
||||||
|
if (!online) {
|
||||||
|
throw new IllegalStateException("Cannot set status when the agent is not online.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (presenceMode == null) {
|
||||||
|
presenceMode = Presence.Mode.AVAILABLE;
|
||||||
|
}
|
||||||
|
this.presenceMode = presenceMode;
|
||||||
|
this.currentChats = currentChats;
|
||||||
|
this.maxChats = maxChats;
|
||||||
|
|
||||||
|
Presence presence = new Presence(Presence.Type.AVAILABLE);
|
||||||
|
presence.setMode(presenceMode);
|
||||||
|
presence.setTo(this.getWorkgroupName());
|
||||||
|
|
||||||
|
if( status != null ) {
|
||||||
|
presence.setStatus( status );
|
||||||
|
}
|
||||||
|
// Send information about max chats and current chats as a packet extension.
|
||||||
|
DefaultPacketExtension agentStatus = new DefaultPacketExtension(AgentStatus.ELEMENT_NAME,
|
||||||
|
AgentStatus.NAMESPACE);
|
||||||
|
agentStatus.setValue("current-chats", ""+currentChats);
|
||||||
|
agentStatus.setValue("max-chats", ""+maxChats);
|
||||||
|
presence.addExtension(agentStatus);
|
||||||
|
presence.addExtension(new MetaData(this.metaData));
|
||||||
|
|
||||||
|
PacketCollector collector = this.connection.createPacketCollector(new AndFilter(
|
||||||
|
new PacketTypeFilter(Presence.class), new FromContainsFilter(workgroupName)));
|
||||||
|
|
||||||
|
this.connection.sendPacket(presence);
|
||||||
|
|
||||||
|
presence = (Presence)collector.nextResult(5000);
|
||||||
|
collector.cancel();
|
||||||
|
if (presence == null) {
|
||||||
|
throw new XMPPException("No response from server on status set.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (presence.getError() != null) {
|
||||||
|
throw new XMPPException(presence.getError());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a user from the workgroup queue. This is an administrative action that the
|
||||||
|
*
|
||||||
|
* The agent is not guaranteed of having privileges to perform this action; an exception
|
||||||
|
* denying the request may be thrown.
|
||||||
|
*/
|
||||||
|
public void dequeueUser(String userID) throws XMPPException {
|
||||||
|
// todo: this method simply won't work right now.
|
||||||
|
DepartQueuePacket departPacket = new DepartQueuePacket(this.workgroupName);
|
||||||
|
|
||||||
|
// PENDING
|
||||||
|
this.connection.sendPacket(departPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the fully-qualified name of the workgroup for which this session exists
|
||||||
|
*/
|
||||||
|
public String getWorkgroupName() {
|
||||||
|
return workgroupName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param queueName the name of the queue
|
||||||
|
* @return an instance of WorkgroupQueue for the argument queue name, or null if none exists
|
||||||
|
*/
|
||||||
|
public WorkgroupQueue getQueue(String queueName) {
|
||||||
|
return (WorkgroupQueue)queues.get(queueName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterator getQueues() {
|
||||||
|
return Collections.unmodifiableMap((new HashMap(queues))).values().iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addQueueUsersListener(QueueUsersListener listener) {
|
||||||
|
synchronized(queueUsersListeners) {
|
||||||
|
if (!queueUsersListeners.contains(listener)) {
|
||||||
|
queueUsersListeners.add(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeQueueUsersListener(QueueUsersListener listener) {
|
||||||
|
synchronized(queueUsersListeners) {
|
||||||
|
queueUsersListeners.remove(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addQueueAgentsListener(QueueAgentsListener listener) {
|
||||||
|
synchronized(queueAgentsListeners) {
|
||||||
|
if (!queueAgentsListeners.contains(listener)) {
|
||||||
|
queueAgentsListeners.add(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeQueueAgentsListener(QueueAgentsListener listener) {
|
||||||
|
synchronized(queueAgentsListeners) {
|
||||||
|
queueAgentsListeners.remove(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an offer listener.
|
||||||
|
*
|
||||||
|
* @param offerListener the offer listener.
|
||||||
|
*/
|
||||||
|
public void addOfferListener(OfferListener offerListener) {
|
||||||
|
synchronized(offerListeners) {
|
||||||
|
if (!offerListeners.contains(offerListener)) {
|
||||||
|
offerListeners.add(offerListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an offer listener.
|
||||||
|
*
|
||||||
|
* @param offerListener the offer listener.
|
||||||
|
*/
|
||||||
|
public void removeOfferListener(OfferListener offerListener) {
|
||||||
|
synchronized(offerListeners) {
|
||||||
|
offerListeners.remove(offerListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an invitation listener.
|
||||||
|
*
|
||||||
|
* @param invitationListener the invitation listener.
|
||||||
|
*/
|
||||||
|
public void addInvitationListener(InvitationListener invitationListener) {
|
||||||
|
synchronized(invitationListeners) {
|
||||||
|
if (!invitationListeners.contains(invitationListener)) {
|
||||||
|
invitationListeners.add(invitationListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an invitation listener.
|
||||||
|
*
|
||||||
|
* @param invitationListener the invitation listener.
|
||||||
|
*/
|
||||||
|
public void removeInvitationListener(InvitationListener invitationListener) {
|
||||||
|
synchronized(invitationListeners) {
|
||||||
|
offerListeners.remove(invitationListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fireOfferRequestEvent(OfferRequestProvider.OfferRequestPacket requestPacket) {
|
||||||
|
Offer offer = new Offer(this.connection, this, requestPacket.getUserID(),
|
||||||
|
this.getWorkgroupName(),
|
||||||
|
new Date((new Date()).getTime()
|
||||||
|
+ (requestPacket.getTimeout() * 1000)),
|
||||||
|
requestPacket.getSessionID(), requestPacket.getMetaData());
|
||||||
|
|
||||||
|
synchronized (offerListeners) {
|
||||||
|
for (Iterator i=offerListeners.iterator(); i.hasNext(); ) {
|
||||||
|
OfferListener listener = (OfferListener)i.next();
|
||||||
|
listener.offerReceived(offer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fireOfferRevokeEvent(OfferRevokeProvider.OfferRevokePacket orp) {
|
||||||
|
RevokedOffer revokedOffer = new RevokedOffer(orp.getUserID(), this.getWorkgroupName(),
|
||||||
|
orp.getSessionID(), orp.getReason(),
|
||||||
|
new Date());
|
||||||
|
|
||||||
|
synchronized (offerListeners) {
|
||||||
|
for (Iterator i=offerListeners.iterator(); i.hasNext(); ) {
|
||||||
|
OfferListener listener = (OfferListener)i.next();
|
||||||
|
listener.offerRevoked(revokedOffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fireInvitationEvent(String groupChatJID, String sessionID, String body,
|
||||||
|
String from, Map metaData)
|
||||||
|
{
|
||||||
|
Invitation invitation = new Invitation(connection.getUser(), groupChatJID,
|
||||||
|
workgroupName, sessionID, body, from, metaData);
|
||||||
|
|
||||||
|
synchronized(invitationListeners) {
|
||||||
|
for (Iterator i=invitationListeners.iterator(); i.hasNext(); ) {
|
||||||
|
InvitationListener listener = (InvitationListener)i.next();
|
||||||
|
listener.invitationReceived(invitation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fireQueueUsersEvent(WorkgroupQueue queue, WorkgroupQueue.Status status,
|
||||||
|
int averageWaitTime, Date oldestEntry, Set users)
|
||||||
|
{
|
||||||
|
synchronized(queueUsersListeners) {
|
||||||
|
for (Iterator i=queueUsersListeners.iterator(); i.hasNext(); ) {
|
||||||
|
QueueUsersListener listener = (QueueUsersListener)i.next();
|
||||||
|
if (status != null) {
|
||||||
|
listener.statusUpdated(queue, status);
|
||||||
|
}
|
||||||
|
if (averageWaitTime != -1) {
|
||||||
|
listener.averageWaitTimeUpdated(queue, averageWaitTime);
|
||||||
|
}
|
||||||
|
if (oldestEntry != null) {
|
||||||
|
listener.oldestEntryUpdated(queue, oldestEntry);
|
||||||
|
}
|
||||||
|
if (users != null) {
|
||||||
|
listener.usersUpdated(queue, users);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fireQueueAgentsEvent(WorkgroupQueue queue, int currentChats,
|
||||||
|
int maxChats, Set agents)
|
||||||
|
{
|
||||||
|
synchronized(queueAgentsListeners) {
|
||||||
|
for (Iterator i=queueAgentsListeners.iterator(); i.hasNext(); ) {
|
||||||
|
QueueAgentsListener listener = (QueueAgentsListener)i.next();
|
||||||
|
if (currentChats != -1) {
|
||||||
|
listener.currentChatsUpdated(queue, currentChats);
|
||||||
|
}
|
||||||
|
if (maxChats != -1) {
|
||||||
|
listener.maxChatsUpdated(queue, maxChats);
|
||||||
|
}
|
||||||
|
if (agents != null) {
|
||||||
|
listener.agentsUpdated(queue, agents);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PacketListener Implementation.
|
||||||
|
|
||||||
|
private void handlePacket(Packet packet) {
|
||||||
|
if (packet instanceof OfferRequestProvider.OfferRequestPacket) {
|
||||||
|
fireOfferRequestEvent((OfferRequestProvider.OfferRequestPacket)packet);
|
||||||
|
}
|
||||||
|
else if (packet instanceof Presence) {
|
||||||
|
Presence presence = (Presence)packet;
|
||||||
|
|
||||||
|
// The workgroup can send us a number of different presence packets. We
|
||||||
|
// check for different packet extensions to see what type of presence
|
||||||
|
// packet it is.
|
||||||
|
|
||||||
|
String queueName = StringUtils.parseResource(presence.getFrom());
|
||||||
|
WorkgroupQueue queue = (WorkgroupQueue)queues.get(queueName);
|
||||||
|
// If there isn't already an entry for the queue, create a new one.
|
||||||
|
if (queue == null) {
|
||||||
|
queue = new WorkgroupQueue(queueName);
|
||||||
|
queues.put(queueName, queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueueOverview packet extensions contain basic information about a queue.
|
||||||
|
QueueOverview queueOverview = (QueueOverview)presence.getExtension(
|
||||||
|
QueueOverview.ELEMENT_NAME, QueueOverview.NAMESPACE);
|
||||||
|
if (queueOverview != null) {
|
||||||
|
if (queueOverview.getStatus() == null) {
|
||||||
|
queue.setStatus(WorkgroupQueue.Status.CLOSED);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
queue.setStatus(queueOverview.getStatus());
|
||||||
|
}
|
||||||
|
queue.setAverageWaitTime(queueOverview.getAverageWaitTime());
|
||||||
|
queue.setOldestEntry(queueOverview.getOldestEntry());
|
||||||
|
// Fire event.
|
||||||
|
fireQueueUsersEvent(queue, queueOverview.getStatus(),
|
||||||
|
queueOverview.getAverageWaitTime(), queueOverview.getOldestEntry(),
|
||||||
|
null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueueDetails packet extensions contain information about the users in
|
||||||
|
// a queue.
|
||||||
|
QueueDetails queueDetails = (QueueDetails)packet.getExtension(
|
||||||
|
QueueDetails.ELEMENT_NAME, QueueDetails.NAMESPACE);
|
||||||
|
if (queueDetails != null) {
|
||||||
|
queue.setUsers(queueDetails.getUsers());
|
||||||
|
// Fire event.
|
||||||
|
fireQueueUsersEvent(queue, null, -1, null, queueDetails.getUsers());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify agent packets gives an overview of agent activity in a queue.
|
||||||
|
DefaultPacketExtension notifyAgents = (DefaultPacketExtension)presence.getExtension(
|
||||||
|
"notify-agents", "xmpp:workgroup");
|
||||||
|
if (notifyAgents != null) {
|
||||||
|
int currentChats = Integer.parseInt(notifyAgents.getValue("current-chats"));
|
||||||
|
int maxChats = Integer.parseInt(notifyAgents.getValue("max-chats"));
|
||||||
|
queue.setCurrentChats(currentChats);
|
||||||
|
queue.setMaxChats(maxChats);
|
||||||
|
// Fire event.
|
||||||
|
fireQueueAgentsEvent(queue, currentChats, maxChats, null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Agent status
|
||||||
|
AgentStatus agentStatus = (AgentStatus)presence.getExtension(AgentStatus.ELEMENT_NAME,
|
||||||
|
AgentStatus.NAMESPACE);
|
||||||
|
if (agentStatus != null) {
|
||||||
|
Set agents = agentStatus.getAgents();
|
||||||
|
// Look for information about the agent that created this session and
|
||||||
|
// update local status fields accordingly.
|
||||||
|
for (Iterator i=agents.iterator(); i.hasNext(); ) {
|
||||||
|
Agent agent = (Agent)i.next();
|
||||||
|
if (agent.getUser().equals(StringUtils.parseBareAddress(
|
||||||
|
connection.getUser())))
|
||||||
|
{
|
||||||
|
maxChats = agent.getMaxChats();
|
||||||
|
currentChats = agent.getCurrentChats();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Set the list of agents for the queue.
|
||||||
|
queue.setAgents(agents);
|
||||||
|
// Fire event.
|
||||||
|
fireQueueAgentsEvent(queue, -1, -1, agentStatus.getAgents());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (packet instanceof Message) {
|
||||||
|
Message message = (Message)packet;
|
||||||
|
|
||||||
|
GroupChatInvitation invitation = (GroupChatInvitation)message.getExtension(
|
||||||
|
GroupChatInvitation.ELEMENT_NAME, GroupChatInvitation.NAMESPACE);
|
||||||
|
|
||||||
|
if (invitation != null) {
|
||||||
|
String roomAddress = invitation.getRoomAddress();
|
||||||
|
String sessionID = null;
|
||||||
|
Map metaData = null;
|
||||||
|
|
||||||
|
SessionID sessionIDExt = (SessionID)message.getExtension(SessionID.ELEMENT_NAME,
|
||||||
|
SessionID.NAMESPACE);
|
||||||
|
if (sessionIDExt != null) {
|
||||||
|
sessionID = sessionIDExt.getSessionID();
|
||||||
|
}
|
||||||
|
|
||||||
|
MetaData metaDataExt = (MetaData)message.getExtension(MetaData.ELEMENT_NAME,
|
||||||
|
MetaData.NAMESPACE);
|
||||||
|
if (metaDataExt != null) {
|
||||||
|
metaData = metaDataExt.getMetaData();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.fireInvitationEvent(roomAddress, sessionID, message.getBody(),
|
||||||
|
message.getFrom(), metaData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (packet instanceof OfferRevokeProvider.OfferRevokePacket) {
|
||||||
|
fireOfferRevokeEvent((OfferRevokeProvider.OfferRevokePacket)packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
163
source/org/jivesoftware/smackx/workgroup/agent/Offer.java
Normal file
163
source/org/jivesoftware/smackx/workgroup/agent/Offer.java
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
/**
|
||||||
|
* $RCSfile$
|
||||||
|
* $Revision$
|
||||||
|
* $Date$
|
||||||
|
*
|
||||||
|
* Copyright (C) 1999-2003 Jive Software. All rights reserved.
|
||||||
|
*
|
||||||
|
* This software is the proprietary information of Jive Software.
|
||||||
|
* Use is subject to license terms.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jivesoftware.smackx.workgroup.agent;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
|
import org.jivesoftware.smack.packet.Packet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class embodying the semantic agent chat offer; specific instances allow the acceptance or
|
||||||
|
* rejecting of the offer.<br>
|
||||||
|
*
|
||||||
|
* @author Matt Tucker
|
||||||
|
* @author loki der quaeler
|
||||||
|
* @author Derek DeMoro
|
||||||
|
*/
|
||||||
|
public class Offer {
|
||||||
|
private XMPPConnection connection;
|
||||||
|
private AgentSession session;
|
||||||
|
|
||||||
|
private String sessionID;
|
||||||
|
private String userID;
|
||||||
|
private String workgroupName;
|
||||||
|
private Date expiresDate;
|
||||||
|
private Map metaData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new offer.
|
||||||
|
*
|
||||||
|
* @param conn the XMPP connection with which the issuing session was created.
|
||||||
|
* @param agentSession the agent session instance through which this offer was issued.
|
||||||
|
* @param userID the XMPP address of the user from which the offer originates.
|
||||||
|
* @param workgroupName the fully qualified name of the workgroup.
|
||||||
|
* @param expiresDate the date at which this offer expires.
|
||||||
|
* @param sessionID the session id associated with the offer.
|
||||||
|
* @param metaData the metadata associated with the offer.
|
||||||
|
*/
|
||||||
|
public Offer( XMPPConnection conn, AgentSession agentSession, String userID,
|
||||||
|
String workgroupName, Date expiresDate,
|
||||||
|
String sessionID, Map metaData ) {
|
||||||
|
this.connection = conn;
|
||||||
|
this.session = agentSession;
|
||||||
|
this.userID = userID;
|
||||||
|
this.workgroupName = workgroupName;
|
||||||
|
this.expiresDate = expiresDate;
|
||||||
|
this.sessionID = sessionID;
|
||||||
|
this.metaData = metaData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accepts the offer.
|
||||||
|
*/
|
||||||
|
public void accept() {
|
||||||
|
Packet acceptPacket = new AcceptPacket( this.session.getWorkgroupName() );
|
||||||
|
connection.sendPacket( acceptPacket );
|
||||||
|
// TODO: listen for a reply.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rejects the offer.
|
||||||
|
*/
|
||||||
|
public void reject() {
|
||||||
|
RejectPacket rejectPacket = new RejectPacket( this.session.getWorkgroupName() );
|
||||||
|
connection.sendPacket( rejectPacket );
|
||||||
|
// TODO: listen for a reply.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the XMPP address of the user from which the offer originates
|
||||||
|
* (eg jsmith@example.com/WebClient). For example, if the user jsmith initiates
|
||||||
|
* a support request by joining the workgroup queue, then this user ID will be
|
||||||
|
* jsmith's address.
|
||||||
|
*
|
||||||
|
* @return the XMPP address of the user from which the offer originates.
|
||||||
|
*/
|
||||||
|
public String getUserID() {
|
||||||
|
return userID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The fully qualified name of the workgroup (eg support@example.com).
|
||||||
|
*
|
||||||
|
* @return the name of the workgroup.
|
||||||
|
*/
|
||||||
|
public String getWorkgroupName() {
|
||||||
|
return this.workgroupName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The date when the offer will expire. The agent must {@link #accept()}
|
||||||
|
* the offer before the expiration date or the offer will lapse and be
|
||||||
|
* routed to another agent. Alternatively, the agent can {@link #reject()}
|
||||||
|
* the offer at any time if they don't wish to accept it..
|
||||||
|
*
|
||||||
|
* @return the date at which this offer expires.
|
||||||
|
*/
|
||||||
|
public Date getExpiresDate() {
|
||||||
|
return this.expiresDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The session ID associated with the offer.
|
||||||
|
*
|
||||||
|
* @return the session id associated with the offer.
|
||||||
|
*/
|
||||||
|
public String getSessionID() {
|
||||||
|
return this.sessionID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The meta-data associated with the offer.
|
||||||
|
*
|
||||||
|
* @return the offer meta-data.
|
||||||
|
*/
|
||||||
|
public Map getMetaData() {
|
||||||
|
return this.metaData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packet for rejecting offers.
|
||||||
|
*/
|
||||||
|
private class RejectPacket extends IQ {
|
||||||
|
|
||||||
|
RejectPacket( String workgroup ) {
|
||||||
|
this.setTo( workgroup );
|
||||||
|
this.setType( IQ.Type.SET );
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getChildElementXML() {
|
||||||
|
return "<offer-reject jid=\"" + Offer.this.getUserID() +
|
||||||
|
"\" xmlns=\"xmpp:workgroup" + "\"/>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packet for accepting an offer.
|
||||||
|
*/
|
||||||
|
private class AcceptPacket extends IQ {
|
||||||
|
|
||||||
|
AcceptPacket( String workgroup ) {
|
||||||
|
this.setTo( workgroup );
|
||||||
|
this.setType( IQ.Type.SET );
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getChildElementXML() {
|
||||||
|
return "<offer-accept jid=\"" + Offer.this.getUserID() +
|
||||||
|
"\" xmlns=\"xmpp:workgroup" + "\"/>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
/**
|
||||||
|
* $RCSfile$
|
||||||
|
* $Revision$
|
||||||
|
* $Date$
|
||||||
|
*
|
||||||
|
* Copyright (C) 1999-2003 Jive Software. All rights reserved.
|
||||||
|
*
|
||||||
|
* This software is the proprietary information of Jive Software.
|
||||||
|
* Use is subject to license terms.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jivesoftware.smackx.workgroup.agent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface which all classes interested in hearing about chat offers associated to a particular
|
||||||
|
* AgentSession instance should implement.<br>
|
||||||
|
*
|
||||||
|
* @author Matt Tucker
|
||||||
|
* @author loki der quaeler
|
||||||
|
* @see org.jivesoftware.smackx.workgroup.agent.AgentSession
|
||||||
|
*/
|
||||||
|
public interface OfferListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The implementing class instance will be notified via this when the AgentSession has received
|
||||||
|
* an offer for a chat. The instance will then have the ability to accept, reject, or ignore
|
||||||
|
* the request (resulting in a revocation-by-timeout).
|
||||||
|
*
|
||||||
|
* @param request the Offer instance embodying the details of the offer
|
||||||
|
*/
|
||||||
|
public void offerReceived (Offer request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The implementing class instance will be notified via this when the AgentSessino has received
|
||||||
|
* a revocation of a previously extended offer.
|
||||||
|
*
|
||||||
|
* @param revokedOffer the RevokedOffer instance embodying the details of the revoked offer
|
||||||
|
*/
|
||||||
|
public void offerRevoked (RevokedOffer revokedOffer);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package org.jivesoftware.smackx.workgroup.agent;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public interface QueueAgentsListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current number of chats the agents are handling was updated.
|
||||||
|
*
|
||||||
|
* @param queue the workgroup queue.
|
||||||
|
* @param currentChats the current number of chats the agents are handling.
|
||||||
|
*/
|
||||||
|
public void currentChatsUpdated(WorkgroupQueue queue, int currentChats);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum number of chats the agents can handle was updated.
|
||||||
|
*
|
||||||
|
* @param queue the workgroup queue.
|
||||||
|
* @param maxChats the maximum number of chats the agents can handle.
|
||||||
|
*/
|
||||||
|
public void maxChatsUpdated(WorkgroupQueue queue, int maxChats);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of available agents servicing the queue was updated.
|
||||||
|
*
|
||||||
|
* @param queue the workgroup queue.
|
||||||
|
* @param agents the available agents servicing the queue.
|
||||||
|
*/
|
||||||
|
public void agentsUpdated(WorkgroupQueue queue, Set agents);
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
package org.jivesoftware.smackx.workgroup.agent;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public interface QueueUsersListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The status of the queue was updated.
|
||||||
|
*
|
||||||
|
* @param queue the workgroup queue.
|
||||||
|
* @param status the status of queue.
|
||||||
|
*/
|
||||||
|
public void statusUpdated(WorkgroupQueue queue, WorkgroupQueue.Status status);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The average wait time of the queue was updated.
|
||||||
|
*
|
||||||
|
* @param queue the workgroup queue.
|
||||||
|
* @param averageWaitTime the average wait time of the queue.
|
||||||
|
*/
|
||||||
|
public void averageWaitTimeUpdated(WorkgroupQueue queue, int averageWaitTime);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The date of oldest entry waiting in the queue was updated.
|
||||||
|
*
|
||||||
|
* @param queue the workgroup queue.
|
||||||
|
* @param oldestEntry the date of the oldest entry waiting in the queue.
|
||||||
|
*/
|
||||||
|
public void oldestEntryUpdated(WorkgroupQueue queue, Date oldestEntry);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of users waiting in the queue was updated.
|
||||||
|
*
|
||||||
|
* @param queue the workgroup queue.
|
||||||
|
* @param users the list of users waiting in the queue.
|
||||||
|
*/
|
||||||
|
public void usersUpdated(WorkgroupQueue queue, Set users);
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
/**
|
||||||
|
* $RCSfile$
|
||||||
|
* $Revision$
|
||||||
|
* $Date$
|
||||||
|
*
|
||||||
|
* Copyright (C) 1999-2003 Jive Software. All rights reserved.
|
||||||
|
*
|
||||||
|
* This software is the proprietary information of Jive Software.
|
||||||
|
* Use is subject to license terms.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jivesoftware.smackx.workgroup.agent;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An immutable simple class to embody the information concerning a revoked offer, this is namely
|
||||||
|
* the reason, the workgroup, the userJID, and the timestamp which the message was received.<br>
|
||||||
|
*
|
||||||
|
* @author loki der quaeler
|
||||||
|
*/
|
||||||
|
public class RevokedOffer {
|
||||||
|
|
||||||
|
protected String userID;
|
||||||
|
protected String workgroupName;
|
||||||
|
protected String sessionID;
|
||||||
|
protected String reason;
|
||||||
|
protected Date timestamp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param uid the jid of the user for which this revocation was issued
|
||||||
|
* @param wg the fully qualified name of the workgroup
|
||||||
|
* @param sid the session id attributed to this chain of packets
|
||||||
|
* @param cause the server issued message as to why this revocation was issued
|
||||||
|
* @param ts the timestamp at which the revocation was issued
|
||||||
|
*/
|
||||||
|
public RevokedOffer (String uid, String wg, String sid, String cause, Date ts) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.userID = uid;
|
||||||
|
this.workgroupName = wg;
|
||||||
|
this.sessionID = sid;
|
||||||
|
this.reason = cause;
|
||||||
|
this.timestamp = ts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the jid of the user for which this revocation was issued
|
||||||
|
*/
|
||||||
|
public String getUserID () {
|
||||||
|
return this.userID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the fully qualified name of the workgroup
|
||||||
|
*/
|
||||||
|
public String getWorkgroupName () {
|
||||||
|
return this.workgroupName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the session id which will associate all packets for the pending chat
|
||||||
|
*/
|
||||||
|
public String getSessionID() {
|
||||||
|
return this.sessionID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the server issued message as to why this revocation was issued
|
||||||
|
*/
|
||||||
|
public String getReason () {
|
||||||
|
return this.reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the timestamp at which the revocation was issued
|
||||||
|
*/
|
||||||
|
public Date getTimestamp () {
|
||||||
|
return this.timestamp;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,239 @@
|
||||||
|
/**
|
||||||
|
* $RCSfile$
|
||||||
|
* $Revision$
|
||||||
|
* $Date$
|
||||||
|
*
|
||||||
|
* Copyright (C) 1999-2003 Jive Software. All rights reserved.
|
||||||
|
*
|
||||||
|
* This software is the proprietary information of Jive Software.
|
||||||
|
* Use is subject to license terms.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jivesoftware.smackx.workgroup.agent;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A queue in a workgroup, which is a pool of agents that are routed a specific type of
|
||||||
|
* chat request.
|
||||||
|
*/
|
||||||
|
public class WorkgroupQueue {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
private Status status = Status.CLOSED;
|
||||||
|
|
||||||
|
private int averageWaitTime = -1;
|
||||||
|
private Date oldestEntry = null;
|
||||||
|
private Set users = Collections.EMPTY_SET;
|
||||||
|
|
||||||
|
private Set agents = Collections.EMPTY_SET;
|
||||||
|
private int maxChats = 0;
|
||||||
|
private int currentChats = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new workgroup queue instance.
|
||||||
|
*
|
||||||
|
* @param name the name of the queue.
|
||||||
|
*/
|
||||||
|
WorkgroupQueue(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of the queue.
|
||||||
|
*
|
||||||
|
* @return the name of the queue.
|
||||||
|
*/
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the status of the queue.
|
||||||
|
*
|
||||||
|
* @return the status of the queue.
|
||||||
|
*/
|
||||||
|
public Status getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setStatus(Status status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of users waiting in the queue waiting to be routed to
|
||||||
|
* an agent.
|
||||||
|
*
|
||||||
|
* @return the number of users waiting in the queue.
|
||||||
|
*/
|
||||||
|
public int getUserCount() {
|
||||||
|
if (users == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return users.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an Iterator for the users in the queue waiting to be routed to
|
||||||
|
* an agent (QueueUser instances).
|
||||||
|
*
|
||||||
|
* @return an Iterator for the users waiting in the queue.
|
||||||
|
*/
|
||||||
|
public Iterator getUsers() {
|
||||||
|
if (users == null) {
|
||||||
|
return Collections.EMPTY_SET.iterator();
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableSet(users).iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setUsers(Set users) {
|
||||||
|
this.users = users;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the average amount of time users wait in the queue before being
|
||||||
|
* routed to an agent. If average wait time info isn't available, -1 will
|
||||||
|
* be returned.
|
||||||
|
*
|
||||||
|
* @return the average wait time
|
||||||
|
*/
|
||||||
|
public int getAverageWaitTime() {
|
||||||
|
return averageWaitTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAverageWaitTime(int averageTime) {
|
||||||
|
this.averageWaitTime = averageTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the date of the oldest request waiting in the queue. If there
|
||||||
|
* are no requests waiting to be routed, this method will return <tt>null</tt>.
|
||||||
|
*
|
||||||
|
* @return the date of the oldest request in the queue.
|
||||||
|
*/
|
||||||
|
public Date getOldestEntry() {
|
||||||
|
return oldestEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setOldestEntry(Date oldestEntry) {
|
||||||
|
this.oldestEntry = oldestEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the count of the currently available agents in the queue.
|
||||||
|
*
|
||||||
|
* @return the number of active agents in the queue.
|
||||||
|
*/
|
||||||
|
public int getAgentCount() {
|
||||||
|
synchronized (agents) {
|
||||||
|
return agents.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an Iterator the currently active agents (Agent instances).
|
||||||
|
*
|
||||||
|
* @return an Iterator for the active agents.
|
||||||
|
*/
|
||||||
|
public Iterator getAgents() {
|
||||||
|
return Collections.unmodifiableSet(agents).iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAgents(Set agents) {
|
||||||
|
this.agents = agents;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the maximum number of simultaneous chats the queue can handle.
|
||||||
|
*
|
||||||
|
* @return the max number of chats the queue can handle.
|
||||||
|
*/
|
||||||
|
public int getMaxChats() {
|
||||||
|
return maxChats;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setMaxChats(int maxChats) {
|
||||||
|
this.maxChats = maxChats;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current number of active chat sessions in the queue.
|
||||||
|
*
|
||||||
|
* @return the current number of active chat sessions in the queue.
|
||||||
|
*/
|
||||||
|
public int getCurrentChats() {
|
||||||
|
return currentChats;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setCurrentChats(int currentChats) {
|
||||||
|
this.currentChats = currentChats;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class to represent the status of the workgroup. The possible values are:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>WorkgroupQueue.Status.OPEN -- the queue is active and accepting new chat requests.
|
||||||
|
* <li>WorkgroupQueue.Status.ACTIVE -- the queue is active but NOT accepting new chat
|
||||||
|
* requests.
|
||||||
|
* <li>WorkgroupQueue.Status.CLOSED -- the queue is NOT active and NOT accepting new
|
||||||
|
* chat requests.
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
public static class Status {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The queue is active and accepting new chat requests.
|
||||||
|
*/
|
||||||
|
public static final Status OPEN = new Status("open");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The queue is active but NOT accepting new chat requests. This state might
|
||||||
|
* occur when the workgroup has closed because regular support hours have closed,
|
||||||
|
* but there are still several requests left in the queue.
|
||||||
|
*/
|
||||||
|
public static final Status ACTIVE = new Status("active");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The queue is NOT active and NOT accepting new chat requests.
|
||||||
|
*/
|
||||||
|
public static final Status CLOSED = new Status("closed");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a String into the corresponding status. Valid String values
|
||||||
|
* that can be converted to a status are: "open", "active", and "closed".
|
||||||
|
*
|
||||||
|
* @param type the String value to covert.
|
||||||
|
* @return the corresponding Type.
|
||||||
|
*/
|
||||||
|
public static Status fromString(String type) {
|
||||||
|
if (type == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
type = type.toLowerCase();
|
||||||
|
if (OPEN.toString().equals(type)) {
|
||||||
|
return OPEN;
|
||||||
|
}
|
||||||
|
else if (ACTIVE.toString().equals(type)) {
|
||||||
|
return ACTIVE;
|
||||||
|
}
|
||||||
|
else if (CLOSED.toString().equals(type)) {
|
||||||
|
return CLOSED;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
private Status(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
390
source/org/jivesoftware/smackx/workgroup/packet/AgentStatus.java
Normal file
390
source/org/jivesoftware/smackx/workgroup/packet/AgentStatus.java
Normal file
|
@ -0,0 +1,390 @@
|
||||||
|
/**
|
||||||
|
* $RCSfile$
|
||||||
|
* $Revision$
|
||||||
|
* $Date$
|
||||||
|
*
|
||||||
|
* Copyright (C) 1999-2003 Jive Software. All rights reserved.
|
||||||
|
*
|
||||||
|
* This software is the proprietary information of Jive Software.
|
||||||
|
* Use is subject to license terms.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.workgroup.packet;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.beans.PropertyDescriptor;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.*;
|
||||||
|
import org.jivesoftware.smack.provider.PacketExtensionProvider;
|
||||||
|
import org.jivesoftware.smack.provider.ProviderManager;
|
||||||
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
import org.jivesoftware.smackx.workgroup.agent.Agent;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An immutable container around the basic data known about a given agent, as well as a
|
||||||
|
* PacketExtension implementor which knows how to write the agent-status packet extension.
|
||||||
|
* The packet extension implementation doesn't transmit its presence information as, due to
|
||||||
|
* the protocol design, that is to be done by the presence packet which houses this extension.
|
||||||
|
* Similarly, the agent-status packet extension transmits nothing about the agent id, nor
|
||||||
|
* the metadata.<br>
|
||||||
|
*
|
||||||
|
* PENDING: feels hacky to carry around the presence packet, but it's an adequate container
|
||||||
|
* for the time being.<br>
|
||||||
|
*
|
||||||
|
* @author loki der quaeler
|
||||||
|
*/
|
||||||
|
public class AgentStatus implements PacketExtension {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Element name of the packet extension.
|
||||||
|
*/
|
||||||
|
public static final String ELEMENT_NAME = "agent-status";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Namespace of the packet extension.
|
||||||
|
*/
|
||||||
|
public static final String NAMESPACE = "xmpp:workgroup";
|
||||||
|
|
||||||
|
private Set agents;
|
||||||
|
|
||||||
|
AgentStatus() {
|
||||||
|
agents = new HashSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
void addAgent(Agent agent) {
|
||||||
|
synchronized (agents) {
|
||||||
|
agents.add(agent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAgentCount() {
|
||||||
|
synchronized (agents) {
|
||||||
|
return agents.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set getAgents() {
|
||||||
|
synchronized (agents) {
|
||||||
|
return Collections.unmodifiableSet(agents);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getElementName () {
|
||||||
|
return ELEMENT_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNamespace () {
|
||||||
|
return NAMESPACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toXML () {
|
||||||
|
StringBuffer buf = new StringBuffer();
|
||||||
|
|
||||||
|
buf.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(NAMESPACE).append("\">");
|
||||||
|
|
||||||
|
synchronized (agents) {
|
||||||
|
for (Iterator i=agents.iterator(); i.hasNext(); ) {
|
||||||
|
Agent agent = (Agent)i.next();
|
||||||
|
buf.append("<agent jid=\"").append(agent.getUser()).append("\">");
|
||||||
|
|
||||||
|
if (agent.getCurrentChats() != -1) {
|
||||||
|
buf.append("<current-chats>");
|
||||||
|
buf.append(agent.getCurrentChats());
|
||||||
|
buf.append("</current-chats>");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (agent.getMaxChats() != -1) {
|
||||||
|
buf.append("<max-chats>").append(agent.getMaxChats()).append("</max-chats>");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (agent.getPresence() != null) {
|
||||||
|
buf.append(agent.getPresence().toXML());
|
||||||
|
// TODO: ensure that presence.toXML method is ok, then delete code below.
|
||||||
|
/*Presence presence = agent.getPresence();
|
||||||
|
int priority = presence.getPriority();
|
||||||
|
Presence.Mode mode = presence.getMode();
|
||||||
|
String status = presence.getStatus();
|
||||||
|
|
||||||
|
buf.append("<presence>");
|
||||||
|
if (status != null) {
|
||||||
|
buf.append("<status>").append(status).append("</status>");
|
||||||
|
}
|
||||||
|
if (priority != -1) {
|
||||||
|
buf.append("<priority>").append(priority).append("</priority>");
|
||||||
|
}
|
||||||
|
if (mode != null && mode != Presence.Mode.AVAILABLE) {
|
||||||
|
buf.append("<show>").append(mode).append("</show>");
|
||||||
|
}
|
||||||
|
buf.append("</presence>");*/
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.append("</agent>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.append("</").append(this.getElementName()).append("> ");
|
||||||
|
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packet extension provider for AgentStatus packets.
|
||||||
|
*/
|
||||||
|
public static class Provider implements PacketExtensionProvider {
|
||||||
|
|
||||||
|
public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
|
||||||
|
AgentStatus agentStatus = new AgentStatus();
|
||||||
|
|
||||||
|
int eventType = parser.getEventType();
|
||||||
|
if (eventType != XmlPullParser.START_TAG) {
|
||||||
|
throw new IllegalStateException("Parser not in proper position, or bad XML.");
|
||||||
|
}
|
||||||
|
|
||||||
|
eventType = parser.next();
|
||||||
|
|
||||||
|
while ((eventType == XmlPullParser.START_TAG)
|
||||||
|
&& ("agent".equals(parser.getName()))) {
|
||||||
|
String jid = null;
|
||||||
|
int currentChats = -1;
|
||||||
|
int maxChats = -1;
|
||||||
|
Presence presence = null;
|
||||||
|
|
||||||
|
jid = parser.getAttributeValue("", "jid");
|
||||||
|
if (jid == null) {
|
||||||
|
// throw exception
|
||||||
|
}
|
||||||
|
|
||||||
|
eventType = parser.next();
|
||||||
|
String elementName = parser.getName();
|
||||||
|
while ((eventType != XmlPullParser.END_TAG) || (!"agent".equals(elementName))) {
|
||||||
|
if ("current-chats".equals(elementName)) {
|
||||||
|
currentChats = Integer.parseInt(parser.nextText());
|
||||||
|
parser.next();
|
||||||
|
}
|
||||||
|
else if ("max-chats".equals(elementName)) {
|
||||||
|
maxChats = Integer.parseInt(parser.nextText());
|
||||||
|
parser.next();
|
||||||
|
}
|
||||||
|
else if ("presence".equals(elementName)) {
|
||||||
|
presence = parsePresence(parser);
|
||||||
|
parser.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
eventType = parser.getEventType();
|
||||||
|
elementName = parser.getName();
|
||||||
|
|
||||||
|
if (eventType != XmlPullParser.END_TAG) {
|
||||||
|
// throw exception
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Agent agent = new Agent(jid, currentChats, maxChats, presence);
|
||||||
|
agentStatus.addAgent(agent);
|
||||||
|
|
||||||
|
eventType = parser.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eventType != XmlPullParser.END_TAG) {
|
||||||
|
// throw exception -- PENDING logic verify: useless case?
|
||||||
|
}
|
||||||
|
|
||||||
|
return agentStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Note: all methods below are copied directly from the Smack PacketReader class
|
||||||
|
// and represent all methods that are needed for presence packet parsing.
|
||||||
|
// Unfortunately, there is no elegant way to pass of presence packet parsing to
|
||||||
|
// Smack core when the presence packet context is a non-standard one such as in
|
||||||
|
// the agent-status protocol. Future Smack changes may change this situation,
|
||||||
|
// which would allow us to delete the code copy.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a presence packet.
|
||||||
|
*
|
||||||
|
* @param parser the XML parser, positioned at the start of a presence packet.
|
||||||
|
* @return an Presence object.
|
||||||
|
* @throws Exception if an exception occurs while parsing the packet.
|
||||||
|
*/
|
||||||
|
private Presence parsePresence(XmlPullParser parser) throws Exception {
|
||||||
|
Presence.Type type = Presence.Type.fromString(parser.getAttributeValue("", "type"));
|
||||||
|
|
||||||
|
Presence presence = new Presence(type);
|
||||||
|
presence.setTo(parser.getAttributeValue("", "to"));
|
||||||
|
presence.setFrom(parser.getAttributeValue("", "from"));
|
||||||
|
presence.setPacketID(parser.getAttributeValue("", "id"));
|
||||||
|
|
||||||
|
// Parse sub-elements
|
||||||
|
boolean done = false;
|
||||||
|
while (!done) {
|
||||||
|
int eventType = parser.next();
|
||||||
|
if (eventType == XmlPullParser.START_TAG) {
|
||||||
|
String elementName = parser.getName();
|
||||||
|
String namespace = parser.getNamespace();
|
||||||
|
if (elementName.equals("status")) {
|
||||||
|
presence.setStatus(parser.nextText());
|
||||||
|
}
|
||||||
|
else if (elementName.equals("priority")) {
|
||||||
|
try {
|
||||||
|
int priority = Integer.parseInt(parser.nextText());
|
||||||
|
presence.setPriority(priority);
|
||||||
|
}
|
||||||
|
catch (NumberFormatException nfe) { }
|
||||||
|
}
|
||||||
|
else if (elementName.equals("show")) {
|
||||||
|
presence.setMode(Presence.Mode.fromString(parser.nextText()));
|
||||||
|
}
|
||||||
|
else if (elementName.equals("error")) {
|
||||||
|
presence.setError(parseError(parser));
|
||||||
|
}
|
||||||
|
// Otherwise, it must be a packet extension.
|
||||||
|
else {
|
||||||
|
presence.addExtension(parsePacketExtension(elementName, namespace, parser));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (eventType == XmlPullParser.END_TAG) {
|
||||||
|
if (parser.getName().equals("presence")) {
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return presence;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a packet extension sub-packet.
|
||||||
|
*
|
||||||
|
* @param elementName the XML element name of the packet extension.
|
||||||
|
* @param namespace the XML namespace of the packet extension.
|
||||||
|
* @param parser the XML parser, positioned at the starting element of the extension.
|
||||||
|
* @return a PacketExtension.
|
||||||
|
* @throws Exception if a parsing error occurs.
|
||||||
|
*/
|
||||||
|
private PacketExtension parsePacketExtension(String elementName, String namespace,
|
||||||
|
XmlPullParser parser) throws Exception
|
||||||
|
{
|
||||||
|
// See if a provider is registered to handle the extension.
|
||||||
|
Object provider = ProviderManager.getExtensionProvider(elementName, namespace);
|
||||||
|
if (provider != null) {
|
||||||
|
if (provider instanceof PacketExtensionProvider) {
|
||||||
|
return ((PacketExtensionProvider)provider).parseExtension(parser);
|
||||||
|
}
|
||||||
|
else if (provider instanceof Class) {
|
||||||
|
return (PacketExtension)parseWithIntrospection(
|
||||||
|
elementName, (Class)provider, parser);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// No providers registered, so use a default extension.
|
||||||
|
DefaultPacketExtension extension = new DefaultPacketExtension(elementName, namespace);
|
||||||
|
boolean done = false;
|
||||||
|
while (!done) {
|
||||||
|
int eventType = parser.next();
|
||||||
|
if (eventType == XmlPullParser.START_TAG) {
|
||||||
|
String name = parser.getName();
|
||||||
|
// If an empty element, set the value with the empty string.
|
||||||
|
if (parser.isEmptyElementTag()) {
|
||||||
|
extension.setValue(name,"");
|
||||||
|
}
|
||||||
|
// Otherwise, get the the element text.
|
||||||
|
else {
|
||||||
|
eventType = parser.next();
|
||||||
|
if (eventType == XmlPullParser.TEXT) {
|
||||||
|
String value = parser.getText();
|
||||||
|
extension.setValue(name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (eventType == XmlPullParser.END_TAG) {
|
||||||
|
if (parser.getName().equals(elementName)) {
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return extension;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object parseWithIntrospection(String elementName,
|
||||||
|
Class objectClass, XmlPullParser parser) throws Exception
|
||||||
|
{
|
||||||
|
boolean done = false;
|
||||||
|
Object object = objectClass.newInstance();
|
||||||
|
while (!done) {
|
||||||
|
int eventType = parser.next();
|
||||||
|
if (eventType == XmlPullParser.START_TAG) {
|
||||||
|
String name = parser.getName();
|
||||||
|
String stringValue = parser.nextText();
|
||||||
|
PropertyDescriptor descriptor = new PropertyDescriptor(name, objectClass);
|
||||||
|
// Load the class type of the property.
|
||||||
|
Class propertyType = descriptor.getPropertyType();
|
||||||
|
// Get the value of the property by converting it from a
|
||||||
|
// String to the correct object type.
|
||||||
|
Object value = decode(propertyType, stringValue);
|
||||||
|
// Set the value of the bean.
|
||||||
|
descriptor.getWriteMethod().invoke(object, new Object[] { value });
|
||||||
|
}
|
||||||
|
else if (eventType == XmlPullParser.END_TAG) {
|
||||||
|
if (parser.getName().equals(elementName)) {
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes a String into an object of the specified type. If the object
|
||||||
|
* type is not supported, null will be returned.
|
||||||
|
*
|
||||||
|
* @param type the type of the property.
|
||||||
|
* @param value the encode String value to decode.
|
||||||
|
* @return the String value decoded into the specified type.
|
||||||
|
*/
|
||||||
|
private static Object decode(Class type, String value) throws Exception {
|
||||||
|
if (type.getName().equals("java.lang.String")) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
if (type.getName().equals("boolean")) {
|
||||||
|
return Boolean.valueOf(value);
|
||||||
|
}
|
||||||
|
if (type.getName().equals("int")) {
|
||||||
|
return Integer.valueOf(value);
|
||||||
|
}
|
||||||
|
if (type.getName().equals("long")) {
|
||||||
|
return Long.valueOf(value);
|
||||||
|
}
|
||||||
|
if (type.getName().equals("float")) {
|
||||||
|
return Float.valueOf(value);
|
||||||
|
}
|
||||||
|
if (type.getName().equals("double")) {
|
||||||
|
return Double.valueOf(value);
|
||||||
|
}
|
||||||
|
if (type.getName().equals("java.lang.Class")) {
|
||||||
|
return Class.forName(value);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses error sub-packets.
|
||||||
|
*
|
||||||
|
* @param parser the XML parser.
|
||||||
|
* @return an error sub-packet.
|
||||||
|
* @throws Exception if an exception occurs while parsing the packet.
|
||||||
|
*/
|
||||||
|
private XMPPError parseError(XmlPullParser parser) throws Exception {
|
||||||
|
String errorCode = null;
|
||||||
|
for (int i=0; i<parser.getAttributeCount(); i++) {
|
||||||
|
if (parser.getAttributeName(i).equals("code")) {
|
||||||
|
errorCode = parser.getAttributeValue("", "code");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String message = parser.nextText();
|
||||||
|
while (true) {
|
||||||
|
if (parser.getEventType() == XmlPullParser.END_TAG && parser.getName().equals("error")) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new XMPPError(Integer.parseInt(errorCode), message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
/**
|
||||||
|
* $RCSfile$
|
||||||
|
* $Revision$
|
||||||
|
* $Date$
|
||||||
|
*
|
||||||
|
* Copyright (C) 1999-2003 Jive Software. All rights reserved.
|
||||||
|
*
|
||||||
|
* This software is the proprietary information of Jive Software.
|
||||||
|
* Use is subject to license terms.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jivesoftware.smackx.workgroup.packet;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A IQ packet used to depart a workgroup queue. There are two cases for issuing a depart
|
||||||
|
* queue request:<ul>
|
||||||
|
* <li>The user wants to leave the queue. In this case, an instance of this class
|
||||||
|
* should be created without passing in a user address.
|
||||||
|
* <li>An administrator or the server removes wants to remove a user from the queue.
|
||||||
|
* In that case, the address of the user to remove from the queue should be
|
||||||
|
* used to create an instance of this class.</ul>
|
||||||
|
*
|
||||||
|
* @author loki der quaeler
|
||||||
|
*/
|
||||||
|
public class DepartQueuePacket extends IQ {
|
||||||
|
|
||||||
|
private String user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a depart queue request packet to the specified workgroup.
|
||||||
|
*
|
||||||
|
* @param workgroup the workgroup to depart.
|
||||||
|
*/
|
||||||
|
public DepartQueuePacket(String workgroup) {
|
||||||
|
this(workgroup, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a depart queue request to the specified workgroup and for the
|
||||||
|
* specified user.
|
||||||
|
*
|
||||||
|
* @param workgroup the workgroup to depart.
|
||||||
|
* @param user the user to make depart from the queue.
|
||||||
|
*/
|
||||||
|
public DepartQueuePacket(String workgroup, String user) {
|
||||||
|
this.user = user;
|
||||||
|
|
||||||
|
setTo(workgroup);
|
||||||
|
setType(IQ.Type.SET);
|
||||||
|
setFrom(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getChildElementXML() {
|
||||||
|
StringBuffer buf = new StringBuffer("<depart-queue xmlns=\"xmpp:workgroup\"");
|
||||||
|
|
||||||
|
if (this.user != null) {
|
||||||
|
buf.append("><jid>").append(this.user).append("</jid></depart-queue>");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
buf.append("/>");
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
/**
|
||||||
|
* $RCSfile$
|
||||||
|
* $Revision$
|
||||||
|
* $Date$
|
||||||
|
*
|
||||||
|
* Copyright (C) 1999-2003 Jive Software. All rights reserved.
|
||||||
|
*
|
||||||
|
* This software is the proprietary information of Jive Software.
|
||||||
|
* Use is subject to license terms.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jivesoftware.smackx.workgroup.packet;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.workgroup.MetaData;
|
||||||
|
import org.jivesoftware.smackx.workgroup.util.MetaDataUtils;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.PacketExtension;
|
||||||
|
import org.jivesoftware.smack.provider.PacketExtensionProvider;
|
||||||
|
|
||||||
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This provider parses meta data if it's not contained already in a larger extension provider.
|
||||||
|
*
|
||||||
|
* @author loki der quaeler
|
||||||
|
*/
|
||||||
|
public class MetaDataProvider
|
||||||
|
implements PacketExtensionProvider {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PacketExtensionProvider implementation
|
||||||
|
*/
|
||||||
|
public PacketExtension parseExtension (XmlPullParser parser)
|
||||||
|
throws Exception {
|
||||||
|
Map metaData = MetaDataUtils.parseMetaData(parser);
|
||||||
|
|
||||||
|
return new MetaData(metaData);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,154 @@
|
||||||
|
/**
|
||||||
|
* $RCSfile$
|
||||||
|
* $Revision$
|
||||||
|
* $Date$
|
||||||
|
*
|
||||||
|
* Copyright (C) 1999-2003 Jive Software. All rights reserved.
|
||||||
|
*
|
||||||
|
* This software is the proprietary information of Jive Software.
|
||||||
|
* Use is subject to license terms.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jivesoftware.smackx.workgroup.packet;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.workgroup.*;
|
||||||
|
import org.jivesoftware.smackx.workgroup.util.MetaDataUtils;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.*;
|
||||||
|
import org.jivesoftware.smack.provider.*;
|
||||||
|
|
||||||
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An IQProvider for agent offer requests.
|
||||||
|
*
|
||||||
|
* @author loki der quaeler
|
||||||
|
*/
|
||||||
|
public class OfferRequestProvider implements IQProvider {
|
||||||
|
|
||||||
|
public OfferRequestProvider () {
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQ parseIQ(XmlPullParser parser) throws Exception {
|
||||||
|
int eventType = parser.getEventType();
|
||||||
|
String uid = null;
|
||||||
|
String sessionID = null;
|
||||||
|
int timeout = -1;
|
||||||
|
boolean done = false;
|
||||||
|
Map metaData = new HashMap();
|
||||||
|
|
||||||
|
if (eventType != XmlPullParser.START_TAG) {
|
||||||
|
// throw exception
|
||||||
|
}
|
||||||
|
|
||||||
|
uid = parser.getAttributeValue("", "jid");
|
||||||
|
if (uid == null) {
|
||||||
|
// throw exception
|
||||||
|
}
|
||||||
|
|
||||||
|
parser.nextTag();
|
||||||
|
while (!done) {
|
||||||
|
eventType = parser.getEventType();
|
||||||
|
|
||||||
|
if (eventType == XmlPullParser.START_TAG) {
|
||||||
|
String elemName = parser.getName();
|
||||||
|
|
||||||
|
if ("timeout".equals(elemName)) {
|
||||||
|
timeout = Integer.parseInt(parser.nextText());
|
||||||
|
}
|
||||||
|
else if (MetaData.ELEMENT_NAME.equals(elemName)) {
|
||||||
|
metaData = MetaDataUtils.parseMetaData(parser);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (SessionID.ELEMENT_NAME.equals(elemName)) {
|
||||||
|
sessionID = parser.getAttributeValue("", "session");
|
||||||
|
|
||||||
|
parser.nextTag();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (eventType == XmlPullParser.END_TAG) {
|
||||||
|
if ("offer".equals(parser.getName())) {
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
parser.nextTag();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
parser.nextTag();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OfferRequestPacket offerRequest = new OfferRequestPacket(uid, timeout, metaData, sessionID);
|
||||||
|
offerRequest.setType(IQ.Type.SET);
|
||||||
|
|
||||||
|
return offerRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class OfferRequestPacket extends IQ {
|
||||||
|
|
||||||
|
private int timeout;
|
||||||
|
private String userID;
|
||||||
|
private Map metaData;
|
||||||
|
private String sessionID;
|
||||||
|
|
||||||
|
public OfferRequestPacket(String uid, int timeout, Map metaData, String sID) {
|
||||||
|
this.userID = uid;
|
||||||
|
this.timeout = timeout;
|
||||||
|
this.metaData = metaData;
|
||||||
|
this.sessionID = sID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserID() {
|
||||||
|
return userID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the session id which will be associated with the customer for whom this offer
|
||||||
|
* is extended, or null if the offer did not contain one.
|
||||||
|
*
|
||||||
|
* @return the session id associated to the customer
|
||||||
|
*/
|
||||||
|
public String getSessionID() {
|
||||||
|
return sessionID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of seconds the agent has to accept the offer before
|
||||||
|
* it times out.
|
||||||
|
*
|
||||||
|
* @return the offer timeout (in seconds).
|
||||||
|
*/
|
||||||
|
public int getTimeout() {
|
||||||
|
return this.timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map getMetaData() {
|
||||||
|
return this.metaData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getChildElementXML () {
|
||||||
|
StringBuffer buf = new StringBuffer();
|
||||||
|
|
||||||
|
buf.append("<offer xmlns=\"xmpp:workgroup\" jid=\"").append(userID).append("\">");
|
||||||
|
buf.append("<timeout>").append(timeout).append("</timeout>");
|
||||||
|
|
||||||
|
if (sessionID != null) {
|
||||||
|
buf.append('<').append(SessionID.ELEMENT_NAME);
|
||||||
|
buf.append(" session=\"");
|
||||||
|
buf.append(getSessionID()).append("\" xmlns=\"");
|
||||||
|
buf.append(SessionID.NAMESPACE).append("\"/>");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (metaData != null) {
|
||||||
|
buf.append(MetaDataUtils.serializeMetaData(metaData));
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.append("</offer>");
|
||||||
|
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
/**
|
||||||
|
* $RCSfile$
|
||||||
|
* $Revision$
|
||||||
|
* $Date$
|
||||||
|
*
|
||||||
|
* Copyright (C) 1999-2003 Jive Software. All rights reserved.
|
||||||
|
*
|
||||||
|
* This software is the proprietary information of Jive Software.
|
||||||
|
* Use is subject to license terms.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jivesoftware.smackx.workgroup.packet;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.provider.IQProvider;
|
||||||
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
|
|
||||||
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An IQProvider class which has savvy about the offer-revoke tag.<br>
|
||||||
|
*
|
||||||
|
* @author loki der quaeler
|
||||||
|
*/
|
||||||
|
public class OfferRevokeProvider implements IQProvider {
|
||||||
|
|
||||||
|
public IQ parseIQ (XmlPullParser parser) throws Exception {
|
||||||
|
// The parser will be positioned on the opening IQ tag, so get the JID attribute.
|
||||||
|
String uid = parser.getAttributeValue("", "jid");
|
||||||
|
String reason = null;
|
||||||
|
String sessionID = null;
|
||||||
|
boolean done = false;
|
||||||
|
|
||||||
|
while (!done) {
|
||||||
|
int eventType = parser.next();
|
||||||
|
|
||||||
|
if ((eventType == XmlPullParser.START_TAG) && parser.getName().equals("reason")) {
|
||||||
|
reason = parser.nextText();
|
||||||
|
}
|
||||||
|
else if ((eventType == XmlPullParser.START_TAG)
|
||||||
|
&& parser.getName().equals(SessionID.ELEMENT_NAME)) {
|
||||||
|
sessionID = parser.getAttributeValue("", "session");
|
||||||
|
}
|
||||||
|
else if ((eventType == XmlPullParser.END_TAG)
|
||||||
|
&& parser.getName().equals("offer-revoke")) {
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new OfferRevokePacket(uid, reason, sessionID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OfferRevokePacket extends IQ {
|
||||||
|
|
||||||
|
protected String userID;
|
||||||
|
protected String sessionID;
|
||||||
|
protected String reason;
|
||||||
|
|
||||||
|
public OfferRevokePacket (String uid, String cause, String sid) {
|
||||||
|
this.userID = uid;
|
||||||
|
this.reason = cause;
|
||||||
|
this.sessionID = sid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserID () {
|
||||||
|
return this.userID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getReason () {
|
||||||
|
return this.reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSessionID () {
|
||||||
|
return this.sessionID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getChildElementXML () {
|
||||||
|
StringBuffer buf = new StringBuffer();
|
||||||
|
buf.append("<offer-revoke xmlns=\"xmpp:workgroup\" jid=\"").append(userID).append("\">");
|
||||||
|
if (reason != null) {
|
||||||
|
buf.append("<reason>").append(reason).append("</reason>");
|
||||||
|
}
|
||||||
|
buf.append("</offer-revoke>");
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,179 @@
|
||||||
|
package org.jivesoftware.smackx.workgroup.packet;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.PacketExtension;
|
||||||
|
import org.jivesoftware.smack.provider.PacketExtensionProvider;
|
||||||
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.workgroup.QueueUser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queue details packet extension, which contains details about the users
|
||||||
|
* currently in a queue.
|
||||||
|
*/
|
||||||
|
public class QueueDetails implements PacketExtension {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Element name of the packet extension.
|
||||||
|
*/
|
||||||
|
public static final String ELEMENT_NAME = "notify-queue-details";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Namespace of the packet extension.
|
||||||
|
*/
|
||||||
|
public static final String NAMESPACE = "xmpp:workgroup";
|
||||||
|
|
||||||
|
private static final SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of users in the queue.
|
||||||
|
*/
|
||||||
|
private Set users;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new QueueDetails packet
|
||||||
|
*/
|
||||||
|
private QueueDetails() {
|
||||||
|
users = new HashSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of users currently in the queue that are waiting to
|
||||||
|
* be routed to an agent.
|
||||||
|
*
|
||||||
|
* @return the number of users in the queue.
|
||||||
|
*/
|
||||||
|
public int getUserCount() {
|
||||||
|
return users.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the set of users in the queue that are waiting to
|
||||||
|
* be routed to an agent (as QueueUser objects).
|
||||||
|
*
|
||||||
|
* @return a Set for the users waiting in a queue.
|
||||||
|
*/
|
||||||
|
public Set getUsers() {
|
||||||
|
synchronized (users) {
|
||||||
|
return users;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a user to the packet.
|
||||||
|
*
|
||||||
|
* @param user the user.
|
||||||
|
*/
|
||||||
|
private void addUser(QueueUser user) {
|
||||||
|
synchronized (users) {
|
||||||
|
users.add(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getElementName() {
|
||||||
|
return ELEMENT_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNamespace() {
|
||||||
|
return NAMESPACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toXML() {
|
||||||
|
StringBuffer buf = new StringBuffer();
|
||||||
|
buf.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(NAMESPACE).append("\">");
|
||||||
|
|
||||||
|
synchronized (users) {
|
||||||
|
for (Iterator i=users.iterator(); i.hasNext(); ) {
|
||||||
|
QueueUser user = (QueueUser)i.next();
|
||||||
|
int position = user.getQueuePosition();
|
||||||
|
int timeRemaining = user.getEstimatedRemainingTime();
|
||||||
|
Date timestamp = user.getQueueJoinTimestamp();
|
||||||
|
|
||||||
|
buf.append("<user jid=\"").append(user.getUserID()).append(">");
|
||||||
|
|
||||||
|
if (position != -1) {
|
||||||
|
buf.append("<position>").append(position).append("</position>");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeRemaining != -1) {
|
||||||
|
buf.append("<time>").append(timeRemaining).append("</time>");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timestamp != null) {
|
||||||
|
buf.append("<join-time>");
|
||||||
|
buf.append(DATE_FORMATTER.format(timestamp));
|
||||||
|
buf.append("</join-time>");
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.append("</user>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.append("</").append(ELEMENT_NAME).append(">");
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provider class for QueueDetails packet extensions.
|
||||||
|
*/
|
||||||
|
public static class Provider implements PacketExtensionProvider {
|
||||||
|
|
||||||
|
public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
|
||||||
|
QueueDetails queueDetails = new QueueDetails();
|
||||||
|
|
||||||
|
int eventType = parser.getEventType();
|
||||||
|
while (eventType != XmlPullParser.END_TAG &&
|
||||||
|
"notify-queue-details".equals(parser.getName()))
|
||||||
|
{
|
||||||
|
eventType = parser.next();
|
||||||
|
while ((eventType == XmlPullParser.START_TAG) && "user".equals(parser.getName())) {
|
||||||
|
String uid = null;
|
||||||
|
int position = -1;
|
||||||
|
int time = -1;
|
||||||
|
Date joinTime = null;
|
||||||
|
|
||||||
|
uid = parser.getAttributeValue("", "jid");
|
||||||
|
|
||||||
|
if (uid == null) {
|
||||||
|
// throw exception
|
||||||
|
}
|
||||||
|
|
||||||
|
eventType = parser.next();
|
||||||
|
while ((eventType != XmlPullParser.END_TAG)
|
||||||
|
|| (! "user".equals(parser.getName())))
|
||||||
|
{
|
||||||
|
if ("position".equals(parser.getName())) {
|
||||||
|
position = Integer.parseInt(parser.nextText());
|
||||||
|
}
|
||||||
|
else if ("time".equals(parser.getName())) {
|
||||||
|
time = Integer.parseInt(parser.nextText());
|
||||||
|
}
|
||||||
|
else if ("join-time".equals(parser.getName())) {
|
||||||
|
joinTime = DATE_FORMATTER.parse(parser.nextText());
|
||||||
|
}
|
||||||
|
else if( parser.getName().equals( "waitTime" ) ) {
|
||||||
|
Date wait = DATE_FORMATTER.parse( parser.nextText() );
|
||||||
|
System.out.println( wait );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
eventType = parser.next();
|
||||||
|
|
||||||
|
if (eventType != XmlPullParser.END_TAG) {
|
||||||
|
// throw exception
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
queueDetails.addUser(new QueueUser(uid, position, time, joinTime));
|
||||||
|
|
||||||
|
eventType = parser.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return queueDetails;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,140 @@
|
||||||
|
package org.jivesoftware.smackx.workgroup.packet;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.PacketExtension;
|
||||||
|
import org.jivesoftware.smack.provider.PacketExtensionProvider;
|
||||||
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.workgroup.agent.WorkgroupQueue;
|
||||||
|
|
||||||
|
public class QueueOverview implements PacketExtension {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Element name of the packet extension.
|
||||||
|
*/
|
||||||
|
public static String ELEMENT_NAME = "notify-queue";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Namespace of the packet extension.
|
||||||
|
*/
|
||||||
|
public static String NAMESPACE = "xmpp:workgroup";
|
||||||
|
|
||||||
|
private static final SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss");
|
||||||
|
|
||||||
|
private int averageWaitTime;
|
||||||
|
private Date oldestEntry;
|
||||||
|
private int userCount;
|
||||||
|
private WorkgroupQueue.Status status;
|
||||||
|
|
||||||
|
QueueOverview() {
|
||||||
|
this.averageWaitTime = -1;
|
||||||
|
this.oldestEntry = null;
|
||||||
|
this.userCount = -1;
|
||||||
|
this.status = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAverageWaitTime(int averageWaitTime) {
|
||||||
|
this.averageWaitTime = averageWaitTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAverageWaitTime () {
|
||||||
|
return averageWaitTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setOldestEntry(Date oldestEntry) {
|
||||||
|
this.oldestEntry = oldestEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getOldestEntry() {
|
||||||
|
return oldestEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setUserCount(int userCount) {
|
||||||
|
this.userCount = userCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getUserCount() {
|
||||||
|
return userCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WorkgroupQueue.Status getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setStatus(WorkgroupQueue.Status status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getElementName () {
|
||||||
|
return ELEMENT_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNamespace () {
|
||||||
|
return NAMESPACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toXML () {
|
||||||
|
StringBuffer buf = new StringBuffer();
|
||||||
|
buf.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(NAMESPACE).append("\">");
|
||||||
|
|
||||||
|
if (userCount != -1) {
|
||||||
|
buf.append("<count>").append(userCount).append("</count>");
|
||||||
|
}
|
||||||
|
if (oldestEntry != null) {
|
||||||
|
buf.append("<oldest>").append(DATE_FORMATTER.format(oldestEntry)).append("</oldest>");
|
||||||
|
}
|
||||||
|
if (averageWaitTime != -1) {
|
||||||
|
buf.append("<time>").append(averageWaitTime).append("</time>");
|
||||||
|
}
|
||||||
|
if (status != null) {
|
||||||
|
buf.append("<status>").append(status).append("</status>");
|
||||||
|
}
|
||||||
|
buf.append("</").append(ELEMENT_NAME).append(">");
|
||||||
|
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Provider implements PacketExtensionProvider {
|
||||||
|
|
||||||
|
public PacketExtension parseExtension (XmlPullParser parser) throws Exception {
|
||||||
|
int eventType = parser.getEventType();
|
||||||
|
QueueOverview queueOverview = new QueueOverview();
|
||||||
|
|
||||||
|
if (eventType != XmlPullParser.START_TAG) {
|
||||||
|
// throw exception
|
||||||
|
}
|
||||||
|
|
||||||
|
eventType = parser.next();
|
||||||
|
while ((eventType != XmlPullParser.END_TAG)
|
||||||
|
|| (!ELEMENT_NAME.equals(parser.getName())))
|
||||||
|
{
|
||||||
|
if ("count".equals(parser.getName())) {
|
||||||
|
queueOverview.setUserCount(Integer.parseInt(parser.nextText()));
|
||||||
|
}
|
||||||
|
else if ("time".equals(parser.getName())) {
|
||||||
|
queueOverview.setAverageWaitTime(Integer.parseInt(parser.nextText()));
|
||||||
|
}
|
||||||
|
else if ("oldest".equals(parser.getName())) {
|
||||||
|
queueOverview.setOldestEntry((DATE_FORMATTER.parse(parser.nextText())));
|
||||||
|
}
|
||||||
|
else if ("status".equals(parser.getName())) {
|
||||||
|
queueOverview.setStatus(WorkgroupQueue.Status.fromString(parser.nextText()));
|
||||||
|
}
|
||||||
|
|
||||||
|
eventType = parser.next();
|
||||||
|
|
||||||
|
if (eventType != XmlPullParser.END_TAG) {
|
||||||
|
// throw exception
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eventType != XmlPullParser.END_TAG) {
|
||||||
|
// throw exception
|
||||||
|
}
|
||||||
|
|
||||||
|
return queueOverview;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
package org.jivesoftware.smackx.workgroup.packet;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
|
import org.jivesoftware.smack.provider.IQProvider;
|
||||||
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An IQ packet that encapsulates both types of workgroup queue
|
||||||
|
* status notifications -- position updates, and estimated time
|
||||||
|
* left in the queue updates.
|
||||||
|
*/
|
||||||
|
public class QueueUpdate extends IQ {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Element name of the packet extension.
|
||||||
|
*/
|
||||||
|
public static final String ELEMENT_NAME = "queue-status";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Namespace of the packet extension.
|
||||||
|
*/
|
||||||
|
public static final String NAMESPACE = "xmpp:workgroup";
|
||||||
|
|
||||||
|
private int position;
|
||||||
|
private int remainingTime;
|
||||||
|
|
||||||
|
public QueueUpdate(int position, int remainingTime) {
|
||||||
|
this.position = position;
|
||||||
|
this.remainingTime = remainingTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the user's position in the workgroup queue, or -1 if the
|
||||||
|
* value isn't set on this packet.
|
||||||
|
*
|
||||||
|
* @return the position in the workgroup queue.
|
||||||
|
*/
|
||||||
|
public int getPosition() {
|
||||||
|
return this.position;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the user's estimated time left in the workgroup queue, or
|
||||||
|
* -1 if the value isn't set on this packet.
|
||||||
|
*
|
||||||
|
* @return the estimated time left in the workgroup queue.
|
||||||
|
*/
|
||||||
|
public int getRemaingTime() {
|
||||||
|
return remainingTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getChildElementXML () {
|
||||||
|
StringBuffer buf = new StringBuffer();
|
||||||
|
buf.append("<queue-status xmlns=\"xmpp:workgroup\">");
|
||||||
|
if (position != -1) {
|
||||||
|
buf.append("<queue-position>").append(position).append("</queue-position>");
|
||||||
|
}
|
||||||
|
else if (remainingTime != -1) {
|
||||||
|
buf.append("<queue-time>").append(remainingTime).append("</queue-time>");
|
||||||
|
}
|
||||||
|
buf.append("</queue-status>");
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Provider implements IQProvider {
|
||||||
|
|
||||||
|
public IQ parseIQ(XmlPullParser parser) throws Exception {
|
||||||
|
boolean done = false;
|
||||||
|
int position = -1;
|
||||||
|
int timeRemaining = -1;
|
||||||
|
while (!done) {
|
||||||
|
parser.next();
|
||||||
|
String elementName = parser.getName();
|
||||||
|
if (parser.getEventType() == XmlPullParser.START_TAG && "position".equals(elementName)) {
|
||||||
|
try {
|
||||||
|
position = Integer.parseInt(parser.nextText());
|
||||||
|
}
|
||||||
|
catch (NumberFormatException nfe) { }
|
||||||
|
}
|
||||||
|
else if (parser.getEventType() == XmlPullParser.START_TAG && "time".equals(elementName)) {
|
||||||
|
try {
|
||||||
|
timeRemaining = Integer.parseInt(parser.nextText());
|
||||||
|
}
|
||||||
|
catch (NumberFormatException nfe) { }
|
||||||
|
}
|
||||||
|
else if (parser.getEventType() == XmlPullParser.END_TAG && "queue-status".equals(elementName)) {
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new QueueUpdate(position, timeRemaining);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
package org.jivesoftware.smackx.workgroup.packet;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.PacketExtension;
|
||||||
|
import org.jivesoftware.smack.provider.PacketExtensionProvider;
|
||||||
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
|
||||||
|
public class SessionID implements PacketExtension {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Element name of the packet extension.
|
||||||
|
*/
|
||||||
|
public static final String ELEMENT_NAME = "jive";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Namespace of the packet extension.
|
||||||
|
*/
|
||||||
|
public static final String NAMESPACE = "http://jivesoftware.com/protocol/workgroup";
|
||||||
|
|
||||||
|
private String sessionID;
|
||||||
|
|
||||||
|
protected SessionID(String sessionID) {
|
||||||
|
this.sessionID = sessionID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSessionID () {
|
||||||
|
return this.sessionID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getElementName() {
|
||||||
|
return ELEMENT_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNamespace() {
|
||||||
|
return NAMESPACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toXML() {
|
||||||
|
StringBuffer buf = new StringBuffer();
|
||||||
|
|
||||||
|
buf.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(NAMESPACE).append("\" ");
|
||||||
|
buf.append("session=\"").append(this.getSessionID());
|
||||||
|
buf.append("\"/>");
|
||||||
|
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Provider implements PacketExtensionProvider {
|
||||||
|
|
||||||
|
public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
|
||||||
|
String sessionID = parser.getAttributeValue("", "session");
|
||||||
|
|
||||||
|
// Advance to end of extension.
|
||||||
|
parser.next();
|
||||||
|
|
||||||
|
return new SessionID(sessionID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
package org.jivesoftware.smackx.workgroup.packet;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.PacketExtension;
|
||||||
|
import org.jivesoftware.smack.provider.PacketExtensionProvider;
|
||||||
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A packet extension that contains information about the user and agent in a
|
||||||
|
* workgroup chat. The packet extension is attached to group chat invitations.
|
||||||
|
*/
|
||||||
|
public class WorkgroupInformation implements PacketExtension {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Element name of the packet extension.
|
||||||
|
*/
|
||||||
|
public static final String ELEMENT_NAME = "workgroup";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Namespace of the packet extension.
|
||||||
|
*/
|
||||||
|
public static final String NAMESPACE = "xmpp:workgroup";
|
||||||
|
|
||||||
|
private String userID;
|
||||||
|
private String agentID;
|
||||||
|
|
||||||
|
protected WorkgroupInformation(String userID, String agentID) {
|
||||||
|
this.userID = userID;
|
||||||
|
this.agentID = agentID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserID() {
|
||||||
|
return userID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAgentID() {
|
||||||
|
return agentID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getElementName() {
|
||||||
|
return ELEMENT_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNamespace() {
|
||||||
|
return NAMESPACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toXML() {
|
||||||
|
StringBuffer buf = new StringBuffer();
|
||||||
|
|
||||||
|
buf.append('<').append(ELEMENT_NAME);
|
||||||
|
buf.append(" user=\"").append(userID).append("\"");
|
||||||
|
buf.append(" agent=\"").append(agentID);
|
||||||
|
buf.append("\" xmlns=\"").append(NAMESPACE).append("\" />");
|
||||||
|
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Provider implements PacketExtensionProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PacketExtensionProvider implementation
|
||||||
|
*/
|
||||||
|
public PacketExtension parseExtension (XmlPullParser parser)
|
||||||
|
throws Exception {
|
||||||
|
String user = parser.getAttributeValue("", "user");
|
||||||
|
String agent = parser.getAttributeValue("", "agent");
|
||||||
|
|
||||||
|
// since this is a start and end tag, and we arrive on the start, this should guarantee
|
||||||
|
// we leave on the end
|
||||||
|
parser.next();
|
||||||
|
|
||||||
|
return new WorkgroupInformation(user, agent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
/**
|
||||||
|
* $RCSfile$
|
||||||
|
* $Revision$
|
||||||
|
* $Date$
|
||||||
|
*
|
||||||
|
* Copyright (C) 1999-2003 Jive Software. All rights reserved.
|
||||||
|
*
|
||||||
|
* This software is the proprietary information of Jive Software.
|
||||||
|
* Use is subject to license terms.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jivesoftware.smackx.workgroup.user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener interface for those that wish to be notified of workgroup queue events.
|
||||||
|
*
|
||||||
|
* @see Workgroup#addQueueListener(QueueListener)
|
||||||
|
* @author loki der quaeler
|
||||||
|
*/
|
||||||
|
public interface QueueListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The user joined the workgroup queue.
|
||||||
|
*/
|
||||||
|
public void joinedQueue();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The user departed the workgroup queue.
|
||||||
|
*/
|
||||||
|
public void departedQueue();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The user's queue position has been updated to a new value.
|
||||||
|
*
|
||||||
|
* @param currentPosition the user's current position in the queue.
|
||||||
|
*/
|
||||||
|
public void queuePositionUpdated(int currentPosition);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The user's estimated remaining wait time in the queue has been updated.
|
||||||
|
*
|
||||||
|
* @param secondsRemaining the estimated number of seconds remaining until the
|
||||||
|
* the user is routed to the agent.
|
||||||
|
*/
|
||||||
|
public void queueWaitTimeUpdated(int secondsRemaining);
|
||||||
|
|
||||||
|
}
|
456
source/org/jivesoftware/smackx/workgroup/user/Workgroup.java
Normal file
456
source/org/jivesoftware/smackx/workgroup/user/Workgroup.java
Normal file
|
@ -0,0 +1,456 @@
|
||||||
|
/**
|
||||||
|
* $RCSfile$
|
||||||
|
* $Revision$
|
||||||
|
* $Date$
|
||||||
|
*
|
||||||
|
* Copyright (C) 1999-2003 Jive Software. All rights reserved.
|
||||||
|
*
|
||||||
|
* This software is the proprietary information of Jive Software.
|
||||||
|
* Use is subject to license terms.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jivesoftware.smackx.workgroup.user;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.*;
|
||||||
|
import org.jivesoftware.smack.filter.*;
|
||||||
|
import org.jivesoftware.smack.packet.*;
|
||||||
|
import org.jivesoftware.smackx.GroupChatInvitation;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.workgroup.*;
|
||||||
|
import org.jivesoftware.smackx.workgroup.packet.*;
|
||||||
|
import org.jivesoftware.smackx.workgroup.util.MetaDataUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides workgroup services for users. Users can join the workgropu 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.<p>
|
||||||
|
*
|
||||||
|
* This class only provides a user's perspective into a workgroup and is not intended
|
||||||
|
* for use by agents.
|
||||||
|
*
|
||||||
|
* @author Matt Tucker
|
||||||
|
* @author loki der quaeler
|
||||||
|
*/
|
||||||
|
public class Workgroup {
|
||||||
|
|
||||||
|
private String workgroupName;
|
||||||
|
private XMPPConnection connection;
|
||||||
|
private boolean inQueue;
|
||||||
|
private List invitationListeners;
|
||||||
|
private List queueListeners;
|
||||||
|
|
||||||
|
private int queuePosition = -1;
|
||||||
|
private int queueRemainingTime = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new workgroup instance using the specified workgroup name
|
||||||
|
* (eg support@example.com) and XMPP connection. The connection must have
|
||||||
|
* undergone a successful login before being used to construct an instance of
|
||||||
|
* this class.
|
||||||
|
*
|
||||||
|
* @param workgroupName the fully qualified name of the workgroup.
|
||||||
|
* @param connection an XMPP connection which must have already undergone a
|
||||||
|
* successful login.
|
||||||
|
*/
|
||||||
|
public Workgroup(String workgroupName, XMPPConnection connection) {
|
||||||
|
// Login must have been done before passing in connection.
|
||||||
|
if (!connection.isAuthenticated()) {
|
||||||
|
throw new IllegalStateException("Must login to server before creating workgroup.");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.workgroupName = workgroupName;
|
||||||
|
this.connection = connection;
|
||||||
|
inQueue = false;
|
||||||
|
invitationListeners = new ArrayList();
|
||||||
|
queueListeners = new ArrayList();
|
||||||
|
|
||||||
|
// Register as a queue listener for internal usage by this instance.
|
||||||
|
addQueueListener(new QueueListener() {
|
||||||
|
public void joinedQueue() {
|
||||||
|
inQueue = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void departedQueue() {
|
||||||
|
inQueue = false;
|
||||||
|
queuePosition = -1;
|
||||||
|
queueRemainingTime = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void queuePositionUpdated(int currentPosition) {
|
||||||
|
queuePosition = currentPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void queueWaitTimeUpdated(int secondsRemaining) {
|
||||||
|
queueRemainingTime = secondsRemaining;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Register an invitation listener for internal usage by this instance.
|
||||||
|
addInvitationListener(new InvitationListener() {
|
||||||
|
public void invitationReceived(Invitation invitation) {
|
||||||
|
inQueue = false;
|
||||||
|
queuePosition = -1;
|
||||||
|
queueRemainingTime = -1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Register a packet listener for all queue events.
|
||||||
|
PacketFilter orFilter = new OrFilter(new PacketTypeFilter(Message.class),
|
||||||
|
new PacketTypeFilter(QueueUpdate.class));
|
||||||
|
|
||||||
|
PacketFilter filter = new AndFilter(new FromContainsFilter(this.workgroupName), orFilter);
|
||||||
|
|
||||||
|
connection.addPacketListener(new PacketListener() {
|
||||||
|
public void processPacket(Packet packet) {
|
||||||
|
handlePacket(packet);
|
||||||
|
}
|
||||||
|
}, filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of this workgroup (eg support@example.com).
|
||||||
|
*
|
||||||
|
* @return the name of the workgroup.
|
||||||
|
*/
|
||||||
|
public String getWorkgroupName() {
|
||||||
|
return workgroupName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the user is currently waiting in the workgroup queue.
|
||||||
|
*
|
||||||
|
* @return true if currently waiting in the queue.
|
||||||
|
*/
|
||||||
|
public boolean isInQueue() {
|
||||||
|
return inQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the user's current position in the workgroup queue. A value of 0 means
|
||||||
|
* the user is next in line to be routed; therefore, if the queue position
|
||||||
|
* is being displayed to the end user it is usually a good idea to add 1 to
|
||||||
|
* the value this method returns before display. If the user is not currently
|
||||||
|
* waiting in the workgorup, or no queue position information is available, -1
|
||||||
|
* will be returned.
|
||||||
|
*
|
||||||
|
* @return the user's current position in the workgorup queue, or -1 if the
|
||||||
|
* position isn't available or if the user isn't in the queue.
|
||||||
|
*/
|
||||||
|
public int getQueuePosition() {
|
||||||
|
return queuePosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the estimated time (in seconds) that the user has to left wait in
|
||||||
|
* the workgroup queue before being routed. If the user is not currently waiting
|
||||||
|
* int he workgroup, or no queue time information is available, -1 will be
|
||||||
|
* returned.
|
||||||
|
*
|
||||||
|
* @return the estimated time remaining (in seconds) that the user has to
|
||||||
|
* wait in the workgropu queue, or -1 if time information isn't available
|
||||||
|
* or if the user isn't int the queue.
|
||||||
|
*/
|
||||||
|
public int getQueueRemainingTime() {
|
||||||
|
return queueRemainingTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 will leave the queue: <ul>
|
||||||
|
* <li>The user is routed to an agent, which triggers a groupchat invitation.
|
||||||
|
* <li>The user asks to leave the queue by calling the {@link #departQueue} method.
|
||||||
|
* <li>A server error occurs, or an administrator explicitly removes the user
|
||||||
|
* from the queue.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* A user cannot request to join the queue again if already in the queue. Therefore, this
|
||||||
|
* method will do nothing if the user is already in the queue.<p>
|
||||||
|
*
|
||||||
|
* Some servers may be configured to require certain meta-data in
|
||||||
|
* order to join the queue. In that case, the {@link #joinQueue(Map)} method
|
||||||
|
* should be used instead of this method so that meta-data may be passed in.
|
||||||
|
*
|
||||||
|
* @throws XMPPException if an error occured joining the queue. An error may indicate
|
||||||
|
* that a connection failure occured or that the server explicitly rejected the
|
||||||
|
* request to join the queue.
|
||||||
|
*/
|
||||||
|
public void joinQueue() throws XMPPException {
|
||||||
|
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 will leave the queue: <ul>
|
||||||
|
* <li>The user is routed to an agent, which triggers a groupchat invitation.
|
||||||
|
* <li>The user asks to leave the queue by calling the {@link #departQueue} method.
|
||||||
|
* <li>A server error occurs, or an administrator explicitly removes the user
|
||||||
|
* from the queue.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* A user cannot request to join the queue again if already in the queue. Therefore, this
|
||||||
|
* method will do nothing if the user is already in the queue.<p>
|
||||||
|
*
|
||||||
|
* Arbitrary meta-data can be passed in with the queue join request in order to assist
|
||||||
|
* the server in routing the user to an agent and to provide information about the
|
||||||
|
* user to the agent. Some servers may be configured to require certain meta-data in
|
||||||
|
* order to join the queue.<p>
|
||||||
|
*
|
||||||
|
* The server may reject the join queue request, which will cause an XMPPException to
|
||||||
|
* be thrown. The error codes for specific cases are as follows:<ul>
|
||||||
|
*
|
||||||
|
* <li>503 -- the workgroup is closed or otherwise unavailable to take
|
||||||
|
* new chat requests.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param metaData the metaData for the join request.
|
||||||
|
* @throws XMPPException if an error occured joining the queue. An error may indicate
|
||||||
|
* that a connection failure occured or that the server explicitly rejected the
|
||||||
|
* request to join the queue (error code 503). The error code should be checked
|
||||||
|
* to determine the specific error.
|
||||||
|
*/
|
||||||
|
public void joinQueue(Map metaData) throws XMPPException {
|
||||||
|
// If already in the queue ignore the join request.
|
||||||
|
if (inQueue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JoinQueuePacket joinPacket = new JoinQueuePacket(workgroupName, metaData);
|
||||||
|
PacketCollector collector = connection.createPacketCollector(
|
||||||
|
new PacketIDFilter(joinPacket.getPacketID()));
|
||||||
|
|
||||||
|
this.connection.sendPacket(joinPacket);
|
||||||
|
|
||||||
|
IQ response = (IQ)collector.nextResult(10000);
|
||||||
|
|
||||||
|
// Cancel the collector.
|
||||||
|
collector.cancel();
|
||||||
|
if (response == null) {
|
||||||
|
throw new XMPPException("No response from the server.");
|
||||||
|
}
|
||||||
|
if (response.getError() != null) {
|
||||||
|
throw new XMPPException(response.getError());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify listeners that we've joined the queue.
|
||||||
|
fireQueueJoinedEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Departs the workgroup queue. If the user is not currently in the queue, this
|
||||||
|
* method will do nothing.<p>
|
||||||
|
*
|
||||||
|
* 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 XMPPException if an error occured trying to send the depart queue
|
||||||
|
* request to the server.
|
||||||
|
*/
|
||||||
|
public void departQueue() throws XMPPException {
|
||||||
|
// If not in the queue ignore the depart request.
|
||||||
|
if (!inQueue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DepartQueuePacket departPacket = new DepartQueuePacket(this.workgroupName);
|
||||||
|
PacketCollector collector = this.connection.createPacketCollector(
|
||||||
|
new PacketIDFilter(departPacket.getPacketID()));
|
||||||
|
|
||||||
|
connection.sendPacket(departPacket);
|
||||||
|
|
||||||
|
IQ response = (IQ)collector.nextResult(5000);
|
||||||
|
collector.cancel();
|
||||||
|
if (response == null) {
|
||||||
|
throw new XMPPException("No response from the server.");
|
||||||
|
}
|
||||||
|
if (response.getError() != null) {
|
||||||
|
throw new XMPPException(response.getError());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
synchronized(queueListeners) {
|
||||||
|
if (!queueListeners.contains(queueListener)) {
|
||||||
|
queueListeners.add(queueListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a queue listener.
|
||||||
|
*
|
||||||
|
* @param queueListener the queue listener.
|
||||||
|
*/
|
||||||
|
public void removeQueueListener(QueueListener queueListener) {
|
||||||
|
synchronized(queueListeners) {
|
||||||
|
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(InvitationListener invitationListener) {
|
||||||
|
synchronized(invitationListeners) {
|
||||||
|
if (!invitationListeners.contains(invitationListener)) {
|
||||||
|
invitationListeners.add(invitationListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an invitation listener.
|
||||||
|
*
|
||||||
|
* @param invitationListener the invitation listener.
|
||||||
|
*/
|
||||||
|
public void removeQueueListener(InvitationListener invitationListener) {
|
||||||
|
synchronized(invitationListeners) {
|
||||||
|
invitationListeners.remove(invitationListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fireInvitationEvent(Invitation invitation) {
|
||||||
|
synchronized (invitationListeners) {
|
||||||
|
for (Iterator i=invitationListeners.iterator(); i.hasNext(); ) {
|
||||||
|
InvitationListener listener = (InvitationListener)i.next();
|
||||||
|
listener.invitationReceived(invitation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fireQueueJoinedEvent() {
|
||||||
|
synchronized (queueListeners) {
|
||||||
|
for (Iterator i=queueListeners.iterator(); i.hasNext(); ) {
|
||||||
|
QueueListener listener = (QueueListener)i.next();
|
||||||
|
listener.joinedQueue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fireQueueDepartedEvent() {
|
||||||
|
synchronized (queueListeners) {
|
||||||
|
for (Iterator i=queueListeners.iterator(); i.hasNext(); ) {
|
||||||
|
QueueListener listener = (QueueListener)i.next();
|
||||||
|
listener.departedQueue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fireQueuePositionEvent(int currentPosition) {
|
||||||
|
synchronized (queueListeners) {
|
||||||
|
for (Iterator i=queueListeners.iterator(); i.hasNext(); ) {
|
||||||
|
QueueListener listener = (QueueListener)i.next();
|
||||||
|
listener.queuePositionUpdated(currentPosition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fireQueueTimeEvent(int secondsRemaining) {
|
||||||
|
synchronized (queueListeners) {
|
||||||
|
for (Iterator i=queueListeners.iterator(); i.hasNext(); ) {
|
||||||
|
QueueListener listener = (QueueListener)i.next();
|
||||||
|
listener.queueWaitTimeUpdated(secondsRemaining);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PacketListener Implementation.
|
||||||
|
|
||||||
|
private void handlePacket(Packet packet) {
|
||||||
|
if (packet instanceof Message) {
|
||||||
|
Message msg = (Message)packet;
|
||||||
|
// Check to see if the user left the queue.
|
||||||
|
PacketExtension pe = msg.getExtension("depart-queue", "xmpp:workgroup");
|
||||||
|
|
||||||
|
if (pe != null) {
|
||||||
|
fireQueueDepartedEvent();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Check to see if the user has been invited to a chat.
|
||||||
|
GroupChatInvitation invitation = (GroupChatInvitation)msg.getExtension(
|
||||||
|
"x", "jabber:x:conference");
|
||||||
|
|
||||||
|
if (invitation != null) {
|
||||||
|
String roomAddress = invitation.getRoomAddress();
|
||||||
|
String sessionID = null;
|
||||||
|
Map metaData = null;
|
||||||
|
|
||||||
|
pe = msg.getExtension(SessionID.ELEMENT_NAME,
|
||||||
|
SessionID.NAMESPACE);
|
||||||
|
if (pe != null) {
|
||||||
|
sessionID = ((SessionID)pe).getSessionID();
|
||||||
|
}
|
||||||
|
|
||||||
|
pe = msg.getExtension(MetaData.ELEMENT_NAME,
|
||||||
|
MetaData.NAMESPACE);
|
||||||
|
if (pe != null) {
|
||||||
|
metaData = ((MetaData)pe).getMetaData();
|
||||||
|
}
|
||||||
|
|
||||||
|
Invitation inv = new Invitation(connection.getUser(), roomAddress,
|
||||||
|
workgroupName, sessionID, msg.getBody(),
|
||||||
|
msg.getFrom(), metaData);
|
||||||
|
|
||||||
|
fireInvitationEvent(inv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check to see if it's a queue update notification.
|
||||||
|
else if (packet instanceof QueueUpdate) {
|
||||||
|
QueueUpdate queueUpdate = (QueueUpdate)packet;
|
||||||
|
if (queueUpdate.getPosition() != -1) {
|
||||||
|
fireQueuePositionEvent(queueUpdate.getPosition());
|
||||||
|
}
|
||||||
|
if (queueUpdate.getRemaingTime() != -1) {
|
||||||
|
fireQueueTimeEvent(queueUpdate.getRemaingTime());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IQ packet to request joining the workgroup queue.
|
||||||
|
*/
|
||||||
|
private class JoinQueuePacket extends IQ {
|
||||||
|
|
||||||
|
private Map metaData;
|
||||||
|
|
||||||
|
public JoinQueuePacket(String workgroup, Map metaData) {
|
||||||
|
this.metaData = metaData;
|
||||||
|
|
||||||
|
setTo(workgroup);
|
||||||
|
setType(IQ.Type.SET);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getChildElementXML() {
|
||||||
|
StringBuffer buf = new StringBuffer();
|
||||||
|
|
||||||
|
buf.append("<join-queue xmlns=\"xmpp:workgroup\">");
|
||||||
|
buf.append("<queue-notifications/>");
|
||||||
|
|
||||||
|
// Add any meta-data.
|
||||||
|
buf.append(MetaDataUtils.serializeMetaData(metaData));
|
||||||
|
|
||||||
|
buf.append("</join-queue>");
|
||||||
|
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,131 @@
|
||||||
|
/**
|
||||||
|
* $RCSfile$
|
||||||
|
* $Revision$
|
||||||
|
* $Date$
|
||||||
|
*
|
||||||
|
* Copyright (C) 1999-2003 Jive Software. All rights reserved.
|
||||||
|
*
|
||||||
|
* This software is the proprietary information of Jive Software.
|
||||||
|
* Use is subject to license terms.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.workgroup.util;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is a very flexible event dispatcher which implements Runnable so that it can
|
||||||
|
* dispatch easily from a newly created thread. The usage of this in code is more or less:
|
||||||
|
* create a new instance of this class, use addListenerTriplet to add as many listeners
|
||||||
|
* as desired to be messaged, create a new Thread using the instance of this class created
|
||||||
|
* as the argument to the constructor, start the new Thread instance.<br>
|
||||||
|
*
|
||||||
|
* Also, this is intended to be used to message methods that either return void, or have
|
||||||
|
* a return which the developer using this class is uninterested in receiving.<br>
|
||||||
|
*
|
||||||
|
* @author loki der quaeler
|
||||||
|
*/
|
||||||
|
public class ListenerEventDispatcher
|
||||||
|
implements Runnable {
|
||||||
|
|
||||||
|
protected transient ArrayList triplets;
|
||||||
|
|
||||||
|
protected transient boolean hasFinishedDispatching;
|
||||||
|
protected transient boolean isRunning;
|
||||||
|
|
||||||
|
public ListenerEventDispatcher () {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.triplets = new ArrayList();
|
||||||
|
|
||||||
|
this.hasFinishedDispatching = false;
|
||||||
|
this.isRunning = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a listener triplet - the instance of the listener to be messaged, the Method on which
|
||||||
|
* the listener should be messaged, and the Object array of arguments to be supplied to the
|
||||||
|
* Method. No attempts are made to determine whether this triplet was already added.<br>
|
||||||
|
*
|
||||||
|
* Messages are dispatched in the order in which they're added via this method; so if triplet
|
||||||
|
* X is added after triplet Z, then triplet Z will undergo messaging prior to triplet X.<br>
|
||||||
|
*
|
||||||
|
* This method should not be called once the owning Thread instance has been started; if it
|
||||||
|
* is called, the triplet will not be added to the messaging queue.<br>
|
||||||
|
*
|
||||||
|
* @param listenerInstance the instance of the listener to receive the associated notification
|
||||||
|
* @param listenerMethod the Method instance representing the method through which notification
|
||||||
|
* will occur
|
||||||
|
* @param methodArguments the arguments supplied to the notification method
|
||||||
|
*/
|
||||||
|
public void addListenerTriplet (Object listenerInstance, Method listenerMethod,
|
||||||
|
Object[] methodArguments) {
|
||||||
|
if (! this.isRunning) {
|
||||||
|
this.triplets.add(new TripletContainer(listenerInstance, listenerMethod,
|
||||||
|
methodArguments));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return whether this instance has finished dispatching its messages
|
||||||
|
*/
|
||||||
|
public boolean hasFinished () {
|
||||||
|
return this.hasFinishedDispatching;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Runnable implementation
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void run () {
|
||||||
|
ListIterator li = null;
|
||||||
|
|
||||||
|
this.isRunning = true;
|
||||||
|
|
||||||
|
li = this.triplets.listIterator();
|
||||||
|
while (li.hasNext()) {
|
||||||
|
TripletContainer tc = (TripletContainer)li.next();
|
||||||
|
|
||||||
|
try {
|
||||||
|
tc.getListenerMethod().invoke(tc.getListenerInstance(), tc.getMethodArguments());
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("Exception dispatching an event: " + e);
|
||||||
|
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.hasFinishedDispatching = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected class TripletContainer {
|
||||||
|
|
||||||
|
protected Object listenerInstance;
|
||||||
|
protected Method listenerMethod;
|
||||||
|
protected Object[] methodArguments;
|
||||||
|
|
||||||
|
protected TripletContainer (Object inst, Method meth, Object[] args) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.listenerInstance = inst;
|
||||||
|
this.listenerMethod = meth;
|
||||||
|
this.methodArguments = args;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Object getListenerInstance () {
|
||||||
|
return this.listenerInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Method getListenerMethod () {
|
||||||
|
return this.listenerMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Object[] getMethodArguments () {
|
||||||
|
return this.methodArguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
/**
|
||||||
|
* $RCSfile$
|
||||||
|
* $Revision$
|
||||||
|
* $Date$
|
||||||
|
*
|
||||||
|
* Copyright (C) 1999-2003 Jive Software. All rights reserved.
|
||||||
|
*
|
||||||
|
* This software is the proprietary information of Jive Software.
|
||||||
|
* Use is subject to license terms.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jivesoftware.smackx.workgroup.util;
|
||||||
|
|
||||||
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.workgroup.MetaData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for meta-data parsing and writing.
|
||||||
|
*
|
||||||
|
* @author MattTucker
|
||||||
|
*/
|
||||||
|
public class MetaDataUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses any available meta-data and returns it as a Map of String name/value pairs. The
|
||||||
|
* parser must be positioned at an opening meta-data tag, or the an empty map will be returned.
|
||||||
|
*
|
||||||
|
* @param parser the XML parser positioned at an opening meta-data tag.
|
||||||
|
* @return the meta-data.
|
||||||
|
* @throws XmlPullParserException if an error occurs while parsing the XML.
|
||||||
|
* @throws IOException if an error occurs while parsing the XML.
|
||||||
|
*/
|
||||||
|
public static Map parseMetaData(XmlPullParser parser) throws XmlPullParserException, IOException {
|
||||||
|
int eventType = parser.getEventType();
|
||||||
|
|
||||||
|
// If correctly positioned on an opening meta-data tag, parse meta-data.
|
||||||
|
if ((eventType == XmlPullParser.START_TAG)
|
||||||
|
&& parser.getName().equals(MetaData.ELEMENT_NAME)
|
||||||
|
&& parser.getNamespace().equals(MetaData.NAMESPACE)) {
|
||||||
|
Map metaData = new Hashtable();
|
||||||
|
|
||||||
|
eventType = parser.nextTag();
|
||||||
|
|
||||||
|
// Keep parsing until we've gotten to end of meta-data.
|
||||||
|
while ((eventType != XmlPullParser.END_TAG)
|
||||||
|
|| (! parser.getName().equals(MetaData.ELEMENT_NAME))) {
|
||||||
|
String name = parser.getAttributeValue(0);
|
||||||
|
String value = parser.nextText();
|
||||||
|
|
||||||
|
metaData.put(name, value);
|
||||||
|
|
||||||
|
eventType = parser.nextTag();
|
||||||
|
}
|
||||||
|
|
||||||
|
return metaData;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Collections.EMPTY_MAP;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes a Map of String name/value pairs into the meta-data XML format.
|
||||||
|
*
|
||||||
|
* @param metaData the Map of meta-data.
|
||||||
|
* @return the meta-data values in XML form.
|
||||||
|
*/
|
||||||
|
public static String serializeMetaData(Map metaData) {
|
||||||
|
StringBuffer buf = new StringBuffer();
|
||||||
|
if (metaData != null && metaData.size() > 0) {
|
||||||
|
buf.append("<metadata xmlns=\"http://www.jivesoftware.com/workgroup/metadata\">");
|
||||||
|
for (Iterator i=metaData.keySet().iterator(); i.hasNext(); ) {
|
||||||
|
Object key = i.next();
|
||||||
|
String value = metaData.get(key).toString();
|
||||||
|
buf.append("<value name=\"").append(key).append("\">");
|
||||||
|
buf.append(value);
|
||||||
|
buf.append("</value>");
|
||||||
|
}
|
||||||
|
buf.append("</metadata>");
|
||||||
|
}
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue