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.FormField; 060import org.jivesoftware.smackx.xdata.TextSingleFormField; 061import org.jivesoftware.smackx.xdata.form.FillableForm; 062import org.jivesoftware.smackx.xdata.form.Form; 063import org.jivesoftware.smackx.xdata.packet.DataForm; 064 065import org.jxmpp.jid.DomainBareJid; 066import org.jxmpp.jid.EntityBareJid; 067import org.jxmpp.jid.EntityJid; 068import org.jxmpp.jid.Jid; 069 070/** 071 * Provides workgroup services for users. Users can join the workgroup queue, depart the 072 * queue, find status information about their placement in the queue, and register to 073 * be notified when they are routed to an agent.<p> 074 * 075 * This class only provides a users perspective into a workgroup and is not intended 076 * for use by agents. 077 * 078 * @author Matt Tucker 079 * @author Derek DeMoro 080 */ 081public class Workgroup { 082 083 private final EntityBareJid workgroupJID; 084 private final XMPPConnection connection; 085 private boolean inQueue; 086 private final CopyOnWriteArraySet<WorkgroupInvitationListener> invitationListeners; 087 private final CopyOnWriteArraySet<QueueListener> queueListeners; 088 089 private int queuePosition = -1; 090 private int queueRemainingTime = -1; 091 092 /** 093 * Creates a new workgroup instance using the specified workgroup JID 094 * (eg support@workgroup.example.com) and XMPP connection. The connection must have 095 * undergone a successful login before being used to construct an instance of 096 * this class. 097 * 098 * @param workgroupJID the JID of the workgroup. 099 * @param connection an XMPP connection which must have already undergone a 100 * successful login. 101 */ 102 public Workgroup(EntityBareJid workgroupJID, XMPPConnection connection) { 103 // Login must have been done before passing in connection. 104 if (!connection.isAuthenticated()) { 105 throw new IllegalStateException("Must login to server before creating workgroup."); 106 } 107 108 this.workgroupJID = workgroupJID; 109 this.connection = connection; 110 inQueue = false; 111 invitationListeners = new CopyOnWriteArraySet<>(); 112 queueListeners = new CopyOnWriteArraySet<>(); 113 114 // Register as a queue listener for internal usage by this instance. 115 addQueueListener(new QueueListener() { 116 @Override 117 public void joinedQueue() { 118 inQueue = true; 119 } 120 121 @Override 122 public void departedQueue() { 123 inQueue = false; 124 queuePosition = -1; 125 queueRemainingTime = -1; 126 } 127 128 @Override 129 public void queuePositionUpdated(int currentPosition) { 130 queuePosition = currentPosition; 131 } 132 133 @Override 134 public void queueWaitTimeUpdated(int secondsRemaining) { 135 queueRemainingTime = secondsRemaining; 136 } 137 }); 138 139 /** 140 * Internal handling of an invitation.Recieving an invitation removes the user from the queue. 141 */ 142 MultiUserChatManager.getInstanceFor(connection).addInvitationListener( 143 new org.jivesoftware.smackx.muc.InvitationListener() { 144 @Override 145 public void invitationReceived(XMPPConnection conn, org.jivesoftware.smackx.muc.MultiUserChat room, EntityJid inviter, 146 String reason, String password, Message message, MUCUser.Invite invitation) { 147 inQueue = false; 148 queuePosition = -1; 149 queueRemainingTime = -1; 150 } 151 }); 152 153 // Register a packet listener for all the messages sent to this client. 154 StanzaFilter typeFilter = new StanzaTypeFilter(Message.class); 155 156 connection.addAsyncStanzaListener(new StanzaListener() { 157 @Override 158 public void processStanza(Stanza packet) { 159 handlePacket(packet); 160 } 161 }, typeFilter); 162 } 163 164 /** 165 * Returns the name of this workgroup (eg support@example.com). 166 * 167 * @return the name of the workgroup. 168 */ 169 public EntityBareJid getWorkgroupJID() { 170 return workgroupJID; 171 } 172 173 /** 174 * Returns true if the user is currently waiting in the workgroup queue. 175 * 176 * @return true if currently waiting in the queue. 177 */ 178 public boolean isInQueue() { 179 return inQueue; 180 } 181 182 /** 183 * Returns true if the workgroup is available for receiving new requests. The workgroup will be 184 * available only when agents are available for this workgroup. 185 * 186 * @return true if the workgroup is available for receiving new requests. 187 * @throws XMPPErrorException if there was an XMPP error returned. 188 * @throws NoResponseException if there was no response from the remote entity. 189 * @throws NotConnectedException if the XMPP connection is not connected. 190 * @throws InterruptedException if the calling thread was interrupted. 191 */ 192 public boolean isAvailable() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 193 Presence directedPresence = connection.getStanzaFactory().buildPresenceStanza() 194 .ofType(Presence.Type.available) 195 .to(workgroupJID) 196 .build(); 197 198 StanzaFilter typeFilter = new StanzaTypeFilter(Presence.class); 199 StanzaFilter fromFilter = FromMatchesFilter.create(workgroupJID); 200 StanzaCollector collector = connection.createStanzaCollectorAndSend(new AndFilter(fromFilter, 201 typeFilter), directedPresence); 202 203 Presence response = collector.nextResultOrThrow(); 204 return Presence.Type.available == response.getType(); 205 } 206 207 /** 208 * Returns the users current position in the workgroup queue. A value of 0 means 209 * the user is next in line to be routed; therefore, if the queue position 210 * is being displayed to the end user it is usually a good idea to add 1 to 211 * the value this method returns before display. If the user is not currently 212 * waiting in the workgroup, or no queue position information is available, -1 213 * will be returned. 214 * 215 * @return the user's current position in the workgroup queue, or -1 if the 216 * position isn't available or if the user isn't in the queue. 217 */ 218 public int getQueuePosition() { 219 return queuePosition; 220 } 221 222 /** 223 * Returns the estimated time (in seconds) that the user has to left wait in 224 * the workgroup queue before being routed. If the user is not currently waiting 225 * int he workgroup, or no queue time information is available, -1 will be 226 * returned. 227 * 228 * @return the estimated time remaining (in seconds) that the user has to 229 * wait in the workgroup queue, or -1 if time information isn't available 230 * or if the user isn't int the queue. 231 */ 232 public int getQueueRemainingTime() { 233 return queueRemainingTime; 234 } 235 236 /** 237 * Joins the workgroup queue to wait to be routed to an agent. After joining 238 * the queue, queue status events will be sent to indicate the user's position and 239 * estimated time left in the queue. Once joining the queue, there are three ways 240 * the user can leave the queue: <ul> 241 * 242 * <li>The user is routed to an agent, which triggers a GroupChat invitation. 243 * <li>The user asks to leave the queue by calling the {@link #departQueue} method. 244 * <li>A server error occurs, or an administrator explicitly removes the user 245 * from the queue. 246 * </ul> 247 * 248 * A user cannot request to join the queue again if already in the queue. Therefore, 249 * this method will throw an IllegalStateException if the user is already in the queue.<p> 250 * 251 * Some servers may be configured to require certain meta-data in order to 252 * join the queue. In that case, the {@link #joinQueue(FillableForm)} method should be 253 * used instead of this method so that meta-data may be passed in.<p> 254 * 255 * The server tracks the conversations that a user has with agents over time. By 256 * default, that tracking is done using the user's JID. However, this is not always 257 * possible. For example, when the user is logged in anonymously using a web client. 258 * In that case the user ID might be a randomly generated value put into a persistent 259 * cookie or a username obtained via the session. A userID can be explicitly 260 * passed in by using the {@link #joinQueue(DataForm, Jid)} method. When specified, 261 * that userID will be used instead of the user's JID to track conversations. The 262 * server will ignore a manually specified userID if the user's connection to the server 263 * is not anonymous. 264 * 265 * @throws XMPPException if an error occurred joining the queue. An error may indicate 266 * that a connection failure occurred or that the server explicitly rejected the 267 * request to join the queue. 268 * @throws SmackException if Smack detected an exceptional situation. 269 * @throws InterruptedException if the calling thread was interrupted. 270 */ 271 public void joinQueue() throws XMPPException, SmackException, InterruptedException { 272 joinQueue(null); 273 } 274 275 /** 276 * Joins the workgroup queue to wait to be routed to an agent. After joining 277 * the queue, queue status events will be sent to indicate the user's position and 278 * estimated time left in the queue. Once joining the queue, there are three ways 279 * the user can leave the queue: <ul> 280 * 281 * <li>The user is routed to an agent, which triggers a GroupChat invitation. 282 * <li>The user asks to leave the queue by calling the {@link #departQueue} method. 283 * <li>A server error occurs, or an administrator explicitly removes the user 284 * from the queue. 285 * </ul> 286 * 287 * A user cannot request to join the queue again if already in the queue. Therefore, 288 * this method will throw an IllegalStateException if the user is already in the queue.<p> 289 * 290 * Some servers may be configured to require certain meta-data in order to 291 * join the queue.<p> 292 * 293 * The server tracks the conversations that a user has with agents over time. By 294 * default, that tracking is done using the user's JID. However, this is not always 295 * possible. For example, when the user is logged in anonymously using a web client. 296 * In that case the user ID might be a randomly generated value put into a persistent 297 * cookie or a username obtained via the session. A userID can be explicitly 298 * passed in by using the {@link #joinQueue(DataForm, Jid)} method. When specified, 299 * that userID will be used instead of the user's JID to track conversations. The 300 * server will ignore a manually specified userID if the user's connection to the server 301 * is not anonymous. 302 * 303 * @param answerForm the completed form the send for the join request. 304 * @throws XMPPException if an error occurred joining the queue. An error may indicate 305 * that a connection failure occurred or that the server explicitly rejected the 306 * request to join the queue. 307 * @throws SmackException if Smack detected an exceptional situation. 308 * @throws InterruptedException if the calling thread was interrupted. 309 */ 310 public void joinQueue(FillableForm answerForm) throws XMPPException, SmackException, InterruptedException { 311 joinQueue(answerForm.getDataFormToSubmit(), null); 312 } 313 314 /** 315 * <p>Joins the workgroup queue to wait to be routed to an agent. After joining 316 * the queue, queue status events will be sent to indicate the user's position and 317 * estimated time left in the queue. Once joining the queue, there are three ways 318 * the user can leave the queue: <ul> 319 * 320 * <li>The user is routed to an agent, which triggers a GroupChat invitation. 321 * <li>The user asks to leave the queue by calling the {@link #departQueue} method. 322 * <li>A server error occurs, or an administrator explicitly removes the user 323 * from the queue. 324 * </ul> 325 * 326 * A user cannot request to join the queue again if already in the queue. Therefore, 327 * this method will throw an IllegalStateException if the user is already in the queue.<p> 328 * 329 * Some servers may be configured to require certain meta-data in order to 330 * join the queue.<p> 331 * 332 * The server tracks the conversations that a user has with agents over time. By 333 * default, that tracking is done using the user's JID. However, this is not always 334 * possible. For example, when the user is logged in anonymously using a web client. 335 * In that case the user ID might be a randomly generated value put into a persistent 336 * cookie or a username obtained via the session. When specified, that userID will 337 * be used instead of the user's JID to track conversations. The server will ignore a 338 * manually specified userID if the user's connection to the server is not anonymous. 339 * 340 * @param answerForm the completed form associated with the join request. 341 * @param userID String that represents the ID of the user when using anonymous sessions 342 * or <code>null</code> if a userID should not be used. 343 * @throws XMPPErrorException if an error occurred joining the queue. An error may indicate 344 * that a connection failure occurred or that the server explicitly rejected the 345 * request to join the queue. 346 * @throws NoResponseException if there was no response from the remote entity. 347 * @throws NotConnectedException if the XMPP connection is not connected. 348 * @throws InterruptedException if the calling thread was interrupted. 349 */ 350 public void joinQueue(DataForm answerForm, Jid userID) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 351 // If already in the queue ignore the join request. 352 if (inQueue) { 353 throw new IllegalStateException("Already in queue " + workgroupJID); 354 } 355 356 JoinQueuePacket joinPacket = new JoinQueuePacket(workgroupJID, answerForm, userID); 357 358 connection.createStanzaCollectorAndSend(joinPacket).nextResultOrThrow(); 359 // Notify listeners that we've joined the queue. 360 fireQueueJoinedEvent(); 361 } 362 363 /** 364 * <p>Joins the workgroup queue to wait to be routed to an agent. After joining 365 * the queue, queue status events will be sent to indicate the user's position and 366 * estimated time left in the queue. Once joining the queue, there are three ways 367 * the user can leave the queue: <ul> 368 * 369 * <li>The user is routed to an agent, which triggers a GroupChat invitation. 370 * <li>The user asks to leave the queue by calling the {@link #departQueue} method. 371 * <li>A server error occurs, or an administrator explicitly removes the user 372 * from the queue. 373 * </ul> 374 * 375 * A user cannot request to join the queue again if already in the queue. Therefore, 376 * this method will throw an IllegalStateException if the user is already in the queue.<p> 377 * 378 * Some servers may be configured to require certain meta-data in order to 379 * join the queue.<p> 380 * 381 * The server tracks the conversations that a user has with agents over time. By 382 * default, that tracking is done using the user's JID. However, this is not always 383 * possible. For example, when the user is logged in anonymously using a web client. 384 * In that case the user ID might be a randomly generated value put into a persistent 385 * cookie or a username obtained via the session. When specified, that userID will 386 * be used instead of the user's JID to track conversations. The server will ignore a 387 * manually specified userID if the user's connection to the server is not anonymous. 388 * 389 * @param metadata metadata to create a dataform from. 390 * @param userID String that represents the ID of the user when using anonymous sessions 391 * or <code>null</code> if a userID should not be used. 392 * @throws XMPPException if an error occurred joining the queue. An error may indicate 393 * that a connection failure occurred or that the server explicitly rejected the 394 * request to join the queue. 395 * @throws SmackException if Smack detected an exceptional situation. 396 * @throws InterruptedException if the calling thread was interrupted. 397 */ 398 public void joinQueue(Map<String, Object> metadata, Jid userID) throws XMPPException, SmackException, InterruptedException { 399 // If already in the queue ignore the join request. 400 if (inQueue) { 401 throw new IllegalStateException("Already in queue " + workgroupJID); 402 } 403 404 // Build dataform from metadata 405 DataForm.Builder form = DataForm.builder(); 406 Iterator<String> iter = metadata.keySet().iterator(); 407 while (iter.hasNext()) { 408 String name = iter.next(); 409 String value = metadata.get(name).toString(); 410 411 TextSingleFormField.Builder field = FormField.builder(name); 412 field.setValue(value); 413 form.addField(field.build()); 414 } 415 joinQueue(form.build(), userID); 416 } 417 418 /** 419 * Departs the workgroup queue. If the user is not currently in the queue, this 420 * method will do nothing.<p> 421 * 422 * Normally, the user would not manually leave the queue. However, they may wish to 423 * under certain circumstances -- for example, if they no longer wish to be routed 424 * to an agent because they've been waiting too long. 425 * 426 * @throws XMPPErrorException if an error occurred trying to send the depart queue 427 * request to the server. 428 * @throws NoResponseException if there was no response from the remote entity. 429 * @throws NotConnectedException if the XMPP connection is not connected. 430 * @throws InterruptedException if the calling thread was interrupted. 431 */ 432 public void departQueue() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 433 // If not in the queue ignore the depart request. 434 if (!inQueue) { 435 return; 436 } 437 438 DepartQueuePacket departPacket = new DepartQueuePacket(this.workgroupJID); 439 connection.createStanzaCollectorAndSend(departPacket).nextResultOrThrow(); 440 441 // Notify listeners that we're no longer in the queue. 442 fireQueueDepartedEvent(); 443 } 444 445 /** 446 * Adds a queue listener that will be notified of queue events for the user 447 * that created this Workgroup instance. 448 * 449 * @param queueListener the queue listener. 450 */ 451 public void addQueueListener(QueueListener queueListener) { 452 queueListeners.add(queueListener); 453 } 454 455 /** 456 * Removes a queue listener. 457 * 458 * @param queueListener the queue listener. 459 */ 460 public void removeQueueListener(QueueListener queueListener) { 461 queueListeners.remove(queueListener); 462 } 463 464 /** 465 * Adds an invitation listener that will be notified of groupchat invitations 466 * from the workgroup for the the user that created this Workgroup instance. 467 * 468 * @param invitationListener the invitation listener. 469 */ 470 public void addInvitationListener(WorkgroupInvitationListener invitationListener) { 471 invitationListeners.add(invitationListener); 472 } 473 474 /** 475 * Removes an invitation listener. 476 * 477 * @param invitationListener the invitation listener. 478 */ 479 public void removeQueueListener(WorkgroupInvitationListener invitationListener) { 480 invitationListeners.remove(invitationListener); 481 } 482 483 private void fireInvitationEvent(WorkgroupInvitation invitation) { 484 for (WorkgroupInvitationListener listener : invitationListeners) { 485 listener.invitationReceived(invitation); 486 } 487 } 488 489 private void fireQueueJoinedEvent() { 490 for (QueueListener listener : queueListeners) { 491 listener.joinedQueue(); 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.getExtensionElement("depart-queue", "http://jabber.org/protocol/workgroup"); 520 ExtensionElement queueStatus = msg.getExtensionElement("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 = MUCUser.from(msg); 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.getExtensionElement(SessionID.ELEMENT_NAME, 544 SessionID.NAMESPACE); 545 if (pe != null) { 546 sessionID = ((SessionID) pe).getSessionID(); 547 } 548 549 pe = msg.getExtensionElement(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, DataForm 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; 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()); 591 } 592 593 // Append data form text 594 buf.append(form.toXML()); 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 if Smack detected an exceptional situation. 607 * @throws InterruptedException if the calling thread was interrupted. 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 if Smack detected an exceptional situation. 621 * @throws InterruptedException if the calling thread was interrupted. 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 if Smack detected an exceptional situation. 633 * @throws InterruptedException if the calling thread was interrupted. 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 if there was no response from the remote entity. 645 * @throws XMPPErrorException if an error occurs while getting information from the server. 646 * @throws NotConnectedException if the XMPP connection is not connected. 647 * @throws InterruptedException if the calling thread was interrupted. 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 if Smack detected an exceptional situation. 671 * @throws InterruptedException if the calling thread was interrupted. 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 if there was an XMPP error returned. 691 * @throws NoResponseException if there was no response from the remote entity. 692 * @throws NotConnectedException if the XMPP connection is not connected. 693 * @throws InterruptedException if the calling thread was interrupted. 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 if there was an XMPP error returned. 708 * @throws NoResponseException if there was no response from the remote entity. 709 * @throws NotConnectedException if the XMPP connection is not connected. 710 * @throws InterruptedException if the calling thread was interrupted. 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 if there was an XMPP error returned. 725 * @throws NoResponseException if there was no response from the remote entity. 726 * @throws NotConnectedException if the XMPP connection is not connected. 727 * @throws InterruptedException if the calling thread was interrupted. 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 if there was an XMPP error returned. 743 * @throws NoResponseException if there was no response from the remote entity. 744 * @throws NotConnectedException if the XMPP connection is not connected. 745 * @throws InterruptedException if the calling thread was interrupted. 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 if there was an XMPP error returned. 765 * @throws NoResponseException if there was no response from the remote entity. 766 * @throws NotConnectedException if the XMPP connection is not connected. 767 * @throws InterruptedException if the calling thread was interrupted. 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.from(response); 777 } 778}