001/** 002 * 003 * Copyright 2003-2007 Jive Software. 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jivesoftware.smackx.workgroup.user; 018 019import java.util.Iterator; 020import java.util.List; 021import java.util.Map; 022import java.util.concurrent.CopyOnWriteArraySet; 023 024import org.jivesoftware.smack.SmackException; 025import org.jivesoftware.smack.SmackException.NoResponseException; 026import org.jivesoftware.smack.SmackException.NotConnectedException; 027import org.jivesoftware.smack.StanzaCollector; 028import org.jivesoftware.smack.StanzaListener; 029import org.jivesoftware.smack.XMPPConnection; 030import org.jivesoftware.smack.XMPPException; 031import org.jivesoftware.smack.XMPPException.XMPPErrorException; 032import org.jivesoftware.smack.filter.AndFilter; 033import org.jivesoftware.smack.filter.FromMatchesFilter; 034import org.jivesoftware.smack.filter.StanzaFilter; 035import org.jivesoftware.smack.filter.StanzaTypeFilter; 036import org.jivesoftware.smack.packet.ExtensionElement; 037import org.jivesoftware.smack.packet.IQ; 038import org.jivesoftware.smack.packet.Message; 039import org.jivesoftware.smack.packet.Presence; 040import org.jivesoftware.smack.packet.Stanza; 041 042import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; 043import org.jivesoftware.smackx.disco.packet.DiscoverInfo; 044import org.jivesoftware.smackx.muc.MultiUserChatManager; 045import org.jivesoftware.smackx.muc.packet.MUCUser; 046import org.jivesoftware.smackx.workgroup.MetaData; 047import org.jivesoftware.smackx.workgroup.WorkgroupInvitation; 048import org.jivesoftware.smackx.workgroup.WorkgroupInvitationListener; 049import org.jivesoftware.smackx.workgroup.ext.forms.WorkgroupForm; 050import org.jivesoftware.smackx.workgroup.packet.DepartQueuePacket; 051import org.jivesoftware.smackx.workgroup.packet.QueueUpdate; 052import org.jivesoftware.smackx.workgroup.packet.SessionID; 053import org.jivesoftware.smackx.workgroup.packet.UserID; 054import org.jivesoftware.smackx.workgroup.settings.ChatSetting; 055import org.jivesoftware.smackx.workgroup.settings.ChatSettings; 056import org.jivesoftware.smackx.workgroup.settings.OfflineSettings; 057import org.jivesoftware.smackx.workgroup.settings.SoundSettings; 058import org.jivesoftware.smackx.workgroup.settings.WorkgroupProperties; 059import org.jivesoftware.smackx.xdata.Form; 060import org.jivesoftware.smackx.xdata.FormField; 061import org.jivesoftware.smackx.xdata.packet.DataForm; 062 063import org.jxmpp.jid.DomainBareJid; 064import org.jxmpp.jid.EntityBareJid; 065import org.jxmpp.jid.EntityJid; 066import org.jxmpp.jid.Jid; 067 068/** 069 * Provides workgroup services for users. Users can join the workgroup queue, depart the 070 * queue, find status information about their placement in the queue, and register to 071 * be notified when they are routed to an agent.<p> 072 * 073 * This class only provides a users perspective into a workgroup and is not intended 074 * for use by agents. 075 * 076 * @author Matt Tucker 077 * @author Derek DeMoro 078 */ 079public class Workgroup { 080 081 private final EntityBareJid workgroupJID; 082 private final XMPPConnection connection; 083 private boolean inQueue; 084 private final CopyOnWriteArraySet<WorkgroupInvitationListener> invitationListeners; 085 private final CopyOnWriteArraySet<QueueListener> queueListeners; 086 087 private int queuePosition = -1; 088 private int queueRemainingTime = -1; 089 090 /** 091 * Creates a new workgroup instance using the specified workgroup JID 092 * (eg support@workgroup.example.com) and XMPP connection. The connection must have 093 * undergone a successful login before being used to construct an instance of 094 * this class. 095 * 096 * @param workgroupJID the JID of the workgroup. 097 * @param connection an XMPP connection which must have already undergone a 098 * successful login. 099 */ 100 public Workgroup(EntityBareJid workgroupJID, XMPPConnection connection) { 101 // Login must have been done before passing in connection. 102 if (!connection.isAuthenticated()) { 103 throw new IllegalStateException("Must login to server before creating workgroup."); 104 } 105 106 this.workgroupJID = workgroupJID; 107 this.connection = connection; 108 inQueue = false; 109 invitationListeners = new CopyOnWriteArraySet<>(); 110 queueListeners = new CopyOnWriteArraySet<>(); 111 112 // Register as a queue listener for internal usage by this instance. 113 addQueueListener(new QueueListener() { 114 @Override 115 public void joinedQueue() { 116 inQueue = true; 117 } 118 119 @Override 120 public void departedQueue() { 121 inQueue = false; 122 queuePosition = -1; 123 queueRemainingTime = -1; 124 } 125 126 @Override 127 public void queuePositionUpdated(int currentPosition) { 128 queuePosition = currentPosition; 129 } 130 131 @Override 132 public void queueWaitTimeUpdated(int secondsRemaining) { 133 queueRemainingTime = secondsRemaining; 134 } 135 }); 136 137 /** 138 * Internal handling of an invitation.Recieving an invitation removes the user from the queue. 139 */ 140 MultiUserChatManager.getInstanceFor(connection).addInvitationListener( 141 new org.jivesoftware.smackx.muc.InvitationListener() { 142 @Override 143 public void invitationReceived(XMPPConnection conn, org.jivesoftware.smackx.muc.MultiUserChat room, EntityJid inviter, 144 String reason, String password, Message message, MUCUser.Invite invitation) { 145 inQueue = false; 146 queuePosition = -1; 147 queueRemainingTime = -1; 148 } 149 }); 150 151 // Register a packet listener for all the messages sent to this client. 152 StanzaFilter typeFilter = new StanzaTypeFilter(Message.class); 153 154 connection.addAsyncStanzaListener(new StanzaListener() { 155 @Override 156 public void processStanza(Stanza packet) { 157 handlePacket(packet); 158 } 159 }, typeFilter); 160 } 161 162 /** 163 * Returns the name of this workgroup (eg support@example.com). 164 * 165 * @return the name of the workgroup. 166 */ 167 public EntityBareJid getWorkgroupJID() { 168 return workgroupJID; 169 } 170 171 /** 172 * Returns true if the user is currently waiting in the workgroup queue. 173 * 174 * @return true if currently waiting in the queue. 175 */ 176 public boolean isInQueue() { 177 return inQueue; 178 } 179 180 /** 181 * Returns true if the workgroup is available for receiving new requests. The workgroup will be 182 * available only when agents are available for this workgroup. 183 * 184 * @return true if the workgroup is available for receiving new requests. 185 * @throws XMPPErrorException 186 * @throws NoResponseException 187 * @throws NotConnectedException 188 * @throws InterruptedException 189 */ 190 public boolean isAvailable() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 191 Presence directedPresence = new Presence(Presence.Type.available); 192 directedPresence.setTo(workgroupJID); 193 StanzaFilter typeFilter = new StanzaTypeFilter(Presence.class); 194 StanzaFilter fromFilter = FromMatchesFilter.create(workgroupJID); 195 StanzaCollector collector = connection.createStanzaCollectorAndSend(new AndFilter(fromFilter, 196 typeFilter), directedPresence); 197 198 Presence response = collector.nextResultOrThrow(); 199 return Presence.Type.available == response.getType(); 200 } 201 202 /** 203 * Returns the users current position in the workgroup queue. A value of 0 means 204 * the user is next in line to be routed; therefore, if the queue position 205 * is being displayed to the end user it is usually a good idea to add 1 to 206 * the value this method returns before display. If the user is not currently 207 * waiting in the workgroup, or no queue position information is available, -1 208 * will be returned. 209 * 210 * @return the user's current position in the workgroup queue, or -1 if the 211 * position isn't available or if the user isn't in the queue. 212 */ 213 public int getQueuePosition() { 214 return queuePosition; 215 } 216 217 /** 218 * Returns the estimated time (in seconds) that the user has to left wait in 219 * the workgroup queue before being routed. If the user is not currently waiting 220 * int he workgroup, or no queue time information is available, -1 will be 221 * returned. 222 * 223 * @return the estimated time remaining (in seconds) that the user has to 224 * wait in the workgroup queue, or -1 if time information isn't available 225 * or if the user isn't int the queue. 226 */ 227 public int getQueueRemainingTime() { 228 return queueRemainingTime; 229 } 230 231 /** 232 * Joins the workgroup queue to wait to be routed to an agent. After joining 233 * the queue, queue status events will be sent to indicate the user's position and 234 * estimated time left in the queue. Once joining the queue, there are three ways 235 * the user can leave the queue: <ul> 236 * 237 * <li>The user is routed to an agent, which triggers a GroupChat invitation. 238 * <li>The user asks to leave the queue by calling the {@link #departQueue} method. 239 * <li>A server error occurs, or an administrator explicitly removes the user 240 * from the queue. 241 * </ul> 242 * 243 * A user cannot request to join the queue again if already in the queue. Therefore, 244 * this method will throw an IllegalStateException if the user is already in the queue.<p> 245 * 246 * Some servers may be configured to require certain meta-data in order to 247 * join the queue. In that case, the {@link #joinQueue(Form)} method should be 248 * used instead of this method so that meta-data may be passed in.<p> 249 * 250 * The server tracks the conversations that a user has with agents over time. By 251 * default, that tracking is done using the user's JID. However, this is not always 252 * possible. For example, when the user is logged in anonymously using a web client. 253 * In that case the user ID might be a randomly generated value put into a persistent 254 * cookie or a username obtained via the session. A userID can be explicitly 255 * passed in by using the {@link #joinQueue(Form, Jid)} method. When specified, 256 * that userID will be used instead of the user's JID to track conversations. The 257 * server will ignore a manually specified userID if the user's connection to the server 258 * is not anonymous. 259 * 260 * @throws XMPPException if an error occurred joining the queue. An error may indicate 261 * that a connection failure occurred or that the server explicitly rejected the 262 * request to join the queue. 263 * @throws SmackException 264 * @throws InterruptedException 265 */ 266 public void joinQueue() throws XMPPException, SmackException, InterruptedException { 267 joinQueue(null); 268 } 269 270 /** 271 * Joins the workgroup queue to wait to be routed to an agent. After joining 272 * the queue, queue status events will be sent to indicate the user's position and 273 * estimated time left in the queue. Once joining the queue, there are three ways 274 * the user can leave the queue: <ul> 275 * 276 * <li>The user is routed to an agent, which triggers a GroupChat invitation. 277 * <li>The user asks to leave the queue by calling the {@link #departQueue} method. 278 * <li>A server error occurs, or an administrator explicitly removes the user 279 * from the queue. 280 * </ul> 281 * 282 * A user cannot request to join the queue again if already in the queue. Therefore, 283 * this method will throw an IllegalStateException if the user is already in the queue.<p> 284 * 285 * Some servers may be configured to require certain meta-data in order to 286 * join the queue.<p> 287 * 288 * The server tracks the conversations that a user has with agents over time. By 289 * default, that tracking is done using the user's JID. However, this is not always 290 * possible. For example, when the user is logged in anonymously using a web client. 291 * In that case the user ID might be a randomly generated value put into a persistent 292 * cookie or a username obtained via the session. A userID can be explicitly 293 * passed in by using the {@link #joinQueue(Form, Jid)} method. When specified, 294 * that userID will be used instead of the user's JID to track conversations. The 295 * server will ignore a manually specified userID if the user's connection to the server 296 * is not anonymous. 297 * 298 * @param answerForm the completed form the send for the join request. 299 * @throws XMPPException if an error occurred joining the queue. An error may indicate 300 * that a connection failure occurred or that the server explicitly rejected the 301 * request to join the queue. 302 * @throws SmackException 303 * @throws InterruptedException 304 */ 305 public void joinQueue(Form answerForm) throws XMPPException, SmackException, InterruptedException { 306 joinQueue(answerForm, null); 307 } 308 309 /** 310 * <p>Joins the workgroup queue to wait to be routed to an agent. After joining 311 * the queue, queue status events will be sent to indicate the user's position and 312 * estimated time left in the queue. Once joining the queue, there are three ways 313 * the user can leave the queue: <ul> 314 * 315 * <li>The user is routed to an agent, which triggers a GroupChat invitation. 316 * <li>The user asks to leave the queue by calling the {@link #departQueue} method. 317 * <li>A server error occurs, or an administrator explicitly removes the user 318 * from the queue. 319 * </ul> 320 * 321 * A user cannot request to join the queue again if already in the queue. Therefore, 322 * this method will throw an IllegalStateException if the user is already in the queue.<p> 323 * 324 * Some servers may be configured to require certain meta-data in order to 325 * join the queue.<p> 326 * 327 * The server tracks the conversations that a user has with agents over time. By 328 * default, that tracking is done using the user's JID. However, this is not always 329 * possible. For example, when the user is logged in anonymously using a web client. 330 * In that case the user ID might be a randomly generated value put into a persistent 331 * cookie or a username obtained via the session. When specified, that userID will 332 * be used instead of the user's JID to track conversations. The server will ignore a 333 * manually specified userID if the user's connection to the server is not anonymous. 334 * 335 * @param answerForm the completed form associated with the join request. 336 * @param userID String that represents the ID of the user when using anonymous sessions 337 * or <tt>null</tt> if a userID should not be used. 338 * @throws XMPPErrorException if an error occurred joining the queue. An error may indicate 339 * that a connection failure occurred or that the server explicitly rejected the 340 * request to join the queue. 341 * @throws NoResponseException 342 * @throws NotConnectedException 343 * @throws InterruptedException 344 */ 345 public void joinQueue(Form answerForm, Jid userID) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 346 // If already in the queue ignore the join request. 347 if (inQueue) { 348 throw new IllegalStateException("Already in queue " + workgroupJID); 349 } 350 351 JoinQueuePacket joinPacket = new JoinQueuePacket(workgroupJID, answerForm, userID); 352 353 connection.createStanzaCollectorAndSend(joinPacket).nextResultOrThrow(); 354 // Notify listeners that we've joined the queue. 355 fireQueueJoinedEvent(); 356 } 357 358 /** 359 * <p>Joins the workgroup queue to wait to be routed to an agent. After joining 360 * the queue, queue status events will be sent to indicate the user's position and 361 * estimated time left in the queue. Once joining the queue, there are three ways 362 * the user can leave the queue: <ul> 363 * 364 * <li>The user is routed to an agent, which triggers a GroupChat invitation. 365 * <li>The user asks to leave the queue by calling the {@link #departQueue} method. 366 * <li>A server error occurs, or an administrator explicitly removes the user 367 * from the queue. 368 * </ul> 369 * 370 * A user cannot request to join the queue again if already in the queue. Therefore, 371 * this method will throw an IllegalStateException if the user is already in the queue.<p> 372 * 373 * Some servers may be configured to require certain meta-data in order to 374 * join the queue.<p> 375 * 376 * The server tracks the conversations that a user has with agents over time. By 377 * default, that tracking is done using the user's JID. However, this is not always 378 * possible. For example, when the user is logged in anonymously using a web client. 379 * In that case the user ID might be a randomly generated value put into a persistent 380 * cookie or a username obtained via the session. When specified, that userID will 381 * be used instead of the user's JID to track conversations. The server will ignore a 382 * manually specified userID if the user's connection to the server is not anonymous. 383 * 384 * @param metadata metadata to create a dataform from. 385 * @param userID String that represents the ID of the user when using anonymous sessions 386 * or <tt>null</tt> if a userID should not be used. 387 * @throws XMPPException if an error occurred joining the queue. An error may indicate 388 * that a connection failure occurred or that the server explicitly rejected the 389 * request to join the queue. 390 * @throws SmackException 391 * @throws InterruptedException 392 */ 393 public void joinQueue(Map<String,Object> metadata, Jid userID) throws XMPPException, SmackException, InterruptedException { 394 // If already in the queue ignore the join request. 395 if (inQueue) { 396 throw new IllegalStateException("Already in queue " + workgroupJID); 397 } 398 399 // Build dataform from metadata 400 Form form = new Form(DataForm.Type.submit); 401 Iterator<String> iter = metadata.keySet().iterator(); 402 while (iter.hasNext()) { 403 String name = iter.next(); 404 String value = metadata.get(name).toString(); 405 406 FormField field = new FormField(name); 407 field.setType(FormField.Type.text_single); 408 form.addField(field); 409 form.setAnswer(name, value); 410 } 411 joinQueue(form, userID); 412 } 413 414 /** 415 * Departs the workgroup queue. If the user is not currently in the queue, this 416 * method will do nothing.<p> 417 * 418 * Normally, the user would not manually leave the queue. However, they may wish to 419 * under certain circumstances -- for example, if they no longer wish to be routed 420 * to an agent because they've been waiting too long. 421 * 422 * @throws XMPPErrorException if an error occurred trying to send the depart queue 423 * request to the server. 424 * @throws NoResponseException 425 * @throws NotConnectedException 426 * @throws InterruptedException 427 */ 428 public void departQueue() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 429 // If not in the queue ignore the depart request. 430 if (!inQueue) { 431 return; 432 } 433 434 DepartQueuePacket departPacket = new DepartQueuePacket(this.workgroupJID); 435 connection.createStanzaCollectorAndSend(departPacket).nextResultOrThrow(); 436 437 // Notify listeners that we're no longer in the queue. 438 fireQueueDepartedEvent(); 439 } 440 441 /** 442 * Adds a queue listener that will be notified of queue events for the user 443 * that created this Workgroup instance. 444 * 445 * @param queueListener the queue listener. 446 */ 447 public void addQueueListener(QueueListener queueListener) { 448 queueListeners.add(queueListener); 449 } 450 451 /** 452 * Removes a queue listener. 453 * 454 * @param queueListener the queue listener. 455 */ 456 public void removeQueueListener(QueueListener queueListener) { 457 queueListeners.remove(queueListener); 458 } 459 460 /** 461 * Adds an invitation listener that will be notified of groupchat invitations 462 * from the workgroup for the the user that created this Workgroup instance. 463 * 464 * @param invitationListener the invitation listener. 465 */ 466 public void addInvitationListener(WorkgroupInvitationListener invitationListener) { 467 invitationListeners.add(invitationListener); 468 } 469 470 /** 471 * Removes an invitation listener. 472 * 473 * @param invitationListener the invitation listener. 474 */ 475 public void removeQueueListener(WorkgroupInvitationListener invitationListener) { 476 invitationListeners.remove(invitationListener); 477 } 478 479 private void fireInvitationEvent(WorkgroupInvitation invitation) { 480 for (WorkgroupInvitationListener listener : invitationListeners) { 481 // CHECKSTYLE:OFF 482 listener.invitationReceived(invitation); 483 // CHECKSTYLE:ON 484 } 485 } 486 487 private void fireQueueJoinedEvent() { 488 for (QueueListener listener : queueListeners) { 489 // CHECKSTYLE:OFF 490 listener.joinedQueue(); 491 // CHECKSTYLE:ON 492 } 493 } 494 495 private void fireQueueDepartedEvent() { 496 for (QueueListener listener : queueListeners) { 497 listener.departedQueue(); 498 } 499 } 500 501 private void fireQueuePositionEvent(int currentPosition) { 502 for (QueueListener listener : queueListeners) { 503 listener.queuePositionUpdated(currentPosition); 504 } 505 } 506 507 private void fireQueueTimeEvent(int secondsRemaining) { 508 for (QueueListener listener : queueListeners) { 509 listener.queueWaitTimeUpdated(secondsRemaining); 510 } 511 } 512 513 // PacketListener Implementation. 514 515 private void handlePacket(Stanza packet) { 516 if (packet instanceof Message) { 517 Message msg = (Message) packet; 518 // Check to see if the user left the queue. 519 ExtensionElement pe = msg.getExtension("depart-queue", "http://jabber.org/protocol/workgroup"); 520 ExtensionElement queueStatus = msg.getExtension("queue-status", "http://jabber.org/protocol/workgroup"); 521 522 if (pe != null) { 523 fireQueueDepartedEvent(); 524 } 525 else if (queueStatus != null) { 526 QueueUpdate queueUpdate = (QueueUpdate) queueStatus; 527 if (queueUpdate.getPosition() != -1) { 528 fireQueuePositionEvent(queueUpdate.getPosition()); 529 } 530 if (queueUpdate.getRemaingTime() != -1) { 531 fireQueueTimeEvent(queueUpdate.getRemaingTime()); 532 } 533 } 534 535 else { 536 // Check if a room invitation was sent and if the sender is the workgroup 537 MUCUser mucUser = msg.getExtension("x", "http://jabber.org/protocol/muc#user"); 538 MUCUser.Invite invite = mucUser != null ? mucUser.getInvite() : null; 539 if (invite != null && workgroupJID.equals(invite.getFrom())) { 540 String sessionID = null; 541 Map<String, List<String>> metaData = null; 542 543 pe = msg.getExtension(SessionID.ELEMENT_NAME, 544 SessionID.NAMESPACE); 545 if (pe != null) { 546 sessionID = ((SessionID) pe).getSessionID(); 547 } 548 549 pe = msg.getExtension(MetaData.ELEMENT_NAME, 550 MetaData.NAMESPACE); 551 if (pe != null) { 552 metaData = ((MetaData) pe).getMetaData(); 553 } 554 555 WorkgroupInvitation inv = new WorkgroupInvitation(connection.getUser(), msg.getFrom(), 556 workgroupJID, sessionID, msg.getBody(), 557 msg.getFrom(), metaData); 558 559 fireInvitationEvent(inv); 560 } 561 } 562 } 563 } 564 565 /** 566 * IQ stanza to request joining the workgroup queue. 567 */ 568 private final class JoinQueuePacket extends IQ { 569 570 private final Jid userID; 571 private final DataForm form; 572 573 private JoinQueuePacket(EntityBareJid workgroup, Form answerForm, Jid userID) { 574 super("join-queue", "http://jabber.org/protocol/workgroup"); 575 this.userID = userID; 576 577 setTo(workgroup); 578 setType(IQ.Type.set); 579 580 form = answerForm.getDataFormToSend(); 581 addExtension(form); 582 } 583 584 @Override 585 protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder buf) { 586 buf.rightAngleBracket(); 587 buf.append("<queue-notifications/>"); 588 // Add the user unique identification if the session is anonymous 589 if (connection.isAnonymous()) { 590 buf.append(new UserID(userID).toXML(null)); 591 } 592 593 // Append data form text 594 buf.append(form.toXML(null)); 595 596 return buf; 597 } 598 } 599 600 /** 601 * Returns a single chat setting based on it's identified key. 602 * 603 * @param key the key to find. 604 * @return the ChatSetting if found, otherwise false. 605 * @throws XMPPException if an error occurs while getting information from the server. 606 * @throws SmackException 607 * @throws InterruptedException 608 */ 609 public ChatSetting getChatSetting(String key) throws XMPPException, SmackException, InterruptedException { 610 ChatSettings chatSettings = getChatSettings(key, -1); 611 return chatSettings.getFirstEntry(); 612 } 613 614 /** 615 * Returns ChatSettings based on type. 616 * 617 * @param type the type of ChatSettings to return. 618 * @return the ChatSettings of given type, otherwise null. 619 * @throws XMPPException if an error occurs while getting information from the server. 620 * @throws SmackException 621 * @throws InterruptedException 622 */ 623 public ChatSettings getChatSettings(int type) throws XMPPException, SmackException, InterruptedException { 624 return getChatSettings(null, type); 625 } 626 627 /** 628 * Returns all ChatSettings. 629 * 630 * @return all ChatSettings of a given workgroup. 631 * @throws XMPPException if an error occurs while getting information from the server. 632 * @throws SmackException 633 * @throws InterruptedException 634 */ 635 public ChatSettings getChatSettings() throws XMPPException, SmackException, InterruptedException { 636 return getChatSettings(null, -1); 637 } 638 639 640 /** 641 * Asks the workgroup for it's Chat Settings. 642 * 643 * @return key specify a key to retrieve only that settings. Otherwise for all settings, key should be null. 644 * @throws NoResponseException 645 * @throws XMPPErrorException if an error occurs while getting information from the server. 646 * @throws NotConnectedException 647 * @throws InterruptedException 648 */ 649 private ChatSettings getChatSettings(String key, int type) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 650 ChatSettings request = new ChatSettings(); 651 if (key != null) { 652 request.setKey(key); 653 } 654 if (type != -1) { 655 request.setType(type); 656 } 657 request.setType(IQ.Type.get); 658 request.setTo(workgroupJID); 659 660 ChatSettings response = connection.createStanzaCollectorAndSend(request).nextResultOrThrow(); 661 662 return response; 663 } 664 665 /** 666 * The workgroup service may be configured to send email. This queries the Workgroup Service 667 * to see if the email service has been configured and is available. 668 * 669 * @return true if the email service is available, otherwise return false. 670 * @throws SmackException 671 * @throws InterruptedException 672 */ 673 public boolean isEmailAvailable() throws SmackException, InterruptedException { 674 ServiceDiscoveryManager discoManager = ServiceDiscoveryManager.getInstanceFor(connection); 675 676 try { 677 DomainBareJid workgroupService = workgroupJID.asDomainBareJid(); 678 DiscoverInfo infoResult = discoManager.discoverInfo(workgroupService); 679 return infoResult.containsFeature("jive:email:provider"); 680 } 681 catch (XMPPException e) { 682 return false; 683 } 684 } 685 686 /** 687 * Asks the workgroup for it's Offline Settings. 688 * 689 * @return offlineSettings the offline settings for this workgroup. 690 * @throws XMPPErrorException 691 * @throws NoResponseException 692 * @throws NotConnectedException 693 * @throws InterruptedException 694 */ 695 public OfflineSettings getOfflineSettings() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 696 OfflineSettings request = new OfflineSettings(); 697 request.setType(IQ.Type.get); 698 request.setTo(workgroupJID); 699 700 return connection.createStanzaCollectorAndSend(request).nextResultOrThrow(); 701 } 702 703 /** 704 * Asks the workgroup for it's Sound Settings. 705 * 706 * @return soundSettings the sound settings for the specified workgroup. 707 * @throws XMPPErrorException 708 * @throws NoResponseException 709 * @throws NotConnectedException 710 * @throws InterruptedException 711 */ 712 public SoundSettings getSoundSettings() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 713 SoundSettings request = new SoundSettings(); 714 request.setType(IQ.Type.get); 715 request.setTo(workgroupJID); 716 717 return connection.createStanzaCollectorAndSend(request).nextResultOrThrow(); 718 } 719 720 /** 721 * Asks the workgroup for it's Properties. 722 * 723 * @return the WorkgroupProperties for the specified workgroup. 724 * @throws XMPPErrorException 725 * @throws NoResponseException 726 * @throws NotConnectedException 727 * @throws InterruptedException 728 */ 729 public WorkgroupProperties getWorkgroupProperties() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 730 WorkgroupProperties request = new WorkgroupProperties(); 731 request.setType(IQ.Type.get); 732 request.setTo(workgroupJID); 733 734 return connection.createStanzaCollectorAndSend(request).nextResultOrThrow(); 735 } 736 737 /** 738 * Asks the workgroup for it's Properties. 739 * 740 * @param jid the jid of the user who's information you would like the workgroup to retreive. 741 * @return the WorkgroupProperties for the specified workgroup. 742 * @throws XMPPErrorException 743 * @throws NoResponseException 744 * @throws NotConnectedException 745 * @throws InterruptedException 746 */ 747 public WorkgroupProperties getWorkgroupProperties(String jid) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 748 WorkgroupProperties request = new WorkgroupProperties(); 749 request.setJid(jid); 750 request.setType(IQ.Type.get); 751 request.setTo(workgroupJID); 752 753 return connection.createStanzaCollectorAndSend( 754 request).nextResultOrThrow(); 755 } 756 757 758 /** 759 * Returns the Form to use for all clients of a workgroup. It is unlikely that the server 760 * will change the form (without a restart) so it is safe to keep the returned form 761 * for future submissions. 762 * 763 * @return the Form to use for searching transcripts. 764 * @throws XMPPErrorException 765 * @throws NoResponseException 766 * @throws NotConnectedException 767 * @throws InterruptedException 768 */ 769 public Form getWorkgroupForm() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 770 WorkgroupForm workgroupForm = new WorkgroupForm(); 771 workgroupForm.setType(IQ.Type.get); 772 workgroupForm.setTo(workgroupJID); 773 774 WorkgroupForm response = connection.createStanzaCollectorAndSend( 775 workgroupForm).nextResultOrThrow(); 776 return Form.getFormFrom(response); 777 } 778}