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