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