/servers/jain-slee/resources/xmpp/library/src/main/java/org/jivesoftware/smackx/MultipleRecipientManager.java
Java | 355 lines | 198 code | 22 blank | 135 comment | 66 complexity | 7748a4128ead9e2bbad48d038daecc17 MD5 | raw file
Possible License(s): LGPL-3.0, GPL-3.0, LGPL-2.1, GPL-2.0, CC-BY-SA-3.0, CC0-1.0, Apache-2.0, BSD-3-Clause
- /*
- * JBoss, Home of Professional Open Source
- * Copyright 2011, Red Hat, Inc. and individual contributors
- * by the @authors tag. See the copyright.txt in the distribution for a
- * full listing of individual contributors.
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
- package org.jivesoftware.smackx;
- import org.jivesoftware.smack.XMPPConnection;
- import org.jivesoftware.smack.XMPPException;
- import org.jivesoftware.smack.packet.Message;
- import org.jivesoftware.smack.packet.Packet;
- import org.jivesoftware.smack.util.Cache;
- import org.jivesoftware.smack.util.StringUtils;
- import org.jivesoftware.smackx.packet.DiscoverInfo;
- import org.jivesoftware.smackx.packet.DiscoverItems;
- import org.jivesoftware.smackx.packet.MultipleAddresses;
- import java.util.ArrayList;
- import java.util.Iterator;
- import java.util.List;
- /**
- * A MultipleRecipientManager allows to send packets to multiple recipients by making use of
- * <a href="http://www.jabber.org/jeps/jep-0033.html">JEP-33: Extended Stanza Addressing</a>.
- * It also allows to send replies to packets that were sent to multiple recipients.
- *
- * @author Gaston Dombiak
- */
- public class MultipleRecipientManager {
- /**
- * Create a cache to hold the 100 most recently accessed elements for a period of
- * 24 hours.
- */
- private static Cache services = new Cache(100, 24 * 60 * 60 * 1000);
- /**
- * Sends the specified packet to the list of specified recipients using the
- * specified connection. If the server has support for JEP-33 then only one
- * packet is going to be sent to the server with the multiple recipient instructions.
- * However, if JEP-33 is not supported by the server then the client is going to send
- * the packet to each recipient.
- *
- * @param connection the connection to use to send the packet.
- * @param packet the packet to send to the list of recipients.
- * @param to the list of JIDs to include in the TO list or <tt>null</tt> if no TO
- * list exists.
- * @param cc the list of JIDs to include in the CC list or <tt>null</tt> if no CC
- * list exists.
- * @param bcc the list of JIDs to include in the BCC list or <tt>null</tt> if no BCC
- * list exists.
- * @throws XMPPException if server does not support JEP-33: Extended Stanza Addressing and
- * some JEP-33 specific features were requested.
- */
- public static void send(XMPPConnection connection, Packet packet, List to, List cc, List bcc)
- throws XMPPException {
- send(connection, packet, to, cc, bcc, null, null, false);
- }
- /**
- * Sends the specified packet to the list of specified recipients using the
- * specified connection. If the server has support for JEP-33 then only one
- * packet is going to be sent to the server with the multiple recipient instructions.
- * However, if JEP-33 is not supported by the server then the client is going to send
- * the packet to each recipient.
- *
- * @param connection the connection to use to send the packet.
- * @param packet the packet to send to the list of recipients.
- * @param to the list of JIDs to include in the TO list or <tt>null</tt> if no TO
- * list exists.
- * @param cc the list of JIDs to include in the CC list or <tt>null</tt> if no CC
- * list exists.
- * @param bcc the list of JIDs to include in the BCC list or <tt>null</tt> if no BCC
- * list exists.
- * @param replyTo address to which all replies are requested to be sent or <tt>null</tt>
- * indicating that they can reply to any address.
- * @param replyRoom JID of a MUC room to which responses should be sent or <tt>null</tt>
- * indicating that they can reply to any address.
- * @param noReply true means that receivers should not reply to the message.
- * @throws XMPPException if server does not support JEP-33: Extended Stanza Addressing and
- * some JEP-33 specific features were requested.
- */
- public static void send(XMPPConnection connection, Packet packet, List to, List cc, List bcc,
- String replyTo, String replyRoom, boolean noReply) throws XMPPException {
- String serviceAddress = getMultipleRecipienServiceAddress(connection);
- if (serviceAddress != null) {
- // Send packet to target users using multiple recipient service provided by the server
- sendThroughService(connection, packet, to, cc, bcc, replyTo, replyRoom, noReply,
- serviceAddress);
- }
- else {
- // Server does not support JEP-33 so try to send the packet to each recipient
- if (noReply || (replyTo != null && replyTo.trim().length() > 0) ||
- (replyRoom != null && replyRoom.trim().length() > 0)) {
- // Some specified JEP-33 features were requested so throw an exception alerting
- // the user that this features are not available
- throw new XMPPException("Extended Stanza Addressing not supported by server");
- }
- // Send the packet to each individual recipient
- sendToIndividualRecipients(connection, packet, to, cc, bcc);
- }
- }
- /**
- * Sends a reply to a previously received packet that was sent to multiple recipients. Before
- * attempting to send the reply message some checkings are performed. If any of those checkings
- * fail then an XMPPException is going to be thrown with the specific error detail.
- *
- * @param connection the connection to use to send the reply.
- * @param original the previously received packet that was sent to multiple recipients.
- * @param reply the new message to send as a reply.
- * @throws XMPPException if the original message was not sent to multiple recipients, or the
- * original message cannot be replied or reply should be sent to a room.
- */
- public static void reply(XMPPConnection connection, Message original, Message reply)
- throws XMPPException {
- MultipleRecipientInfo info = getMultipleRecipientInfo(original);
- if (info == null) {
- throw new XMPPException("Original message does not contain multiple recipient info");
- }
- if (info.shouldNotReply()) {
- throw new XMPPException("Original message should not be replied");
- }
- if (info.getReplyRoom() != null) {
- throw new XMPPException("Reply should be sent through a room");
- }
- // Any <thread/> element from the initial message MUST be copied into the reply.
- if (original.getThread() != null) {
- reply.setThread(original.getThread());
- }
- MultipleAddresses.Address replyAddress = info.getReplyAddress();
- if (replyAddress != null && replyAddress.getJid() != null) {
- // Send reply to the reply_to address
- reply.setTo(replyAddress.getJid());
- connection.sendPacket(reply);
- }
- else {
- // Send reply to multiple recipients
- List to = new ArrayList();
- List cc = new ArrayList();
- for (Iterator it = info.getTOAddresses().iterator(); it.hasNext();) {
- String jid = ((MultipleAddresses.Address) it.next()).getJid();
- to.add(jid);
- }
- for (Iterator it = info.getCCAddresses().iterator(); it.hasNext();) {
- String jid = ((MultipleAddresses.Address) it.next()).getJid();
- cc.add(jid);
- }
- // Add original sender as a 'to' address (if not already present)
- if (!to.contains(original.getFrom()) && !cc.contains(original.getFrom())) {
- to.add(original.getFrom());
- }
- // Remove the sender from the TO/CC list (try with bare JID too)
- String from = connection.getUser();
- if (!to.remove(from) && !cc.remove(from)) {
- String bareJID = StringUtils.parseBareAddress(from);
- to.remove(bareJID);
- cc.remove(bareJID);
- }
- String serviceAddress = getMultipleRecipienServiceAddress(connection);
- if (serviceAddress != null) {
- // Send packet to target users using multiple recipient service provided by the server
- sendThroughService(connection, reply, to, cc, null, null, null, false,
- serviceAddress);
- }
- else {
- // Server does not support JEP-33 so try to send the packet to each recipient
- sendToIndividualRecipients(connection, reply, to, cc, null);
- }
- }
- }
- /**
- * Returns the {@link MultipleRecipientInfo} contained in the specified packet or
- * <tt>null</tt> if none was found. Only packets sent to multiple recipients will
- * contain such information.
- *
- * @param packet the packet to check.
- * @return the MultipleRecipientInfo contained in the specified packet or <tt>null</tt>
- * if none was found.
- */
- public static MultipleRecipientInfo getMultipleRecipientInfo(Packet packet) {
- MultipleAddresses extension = (MultipleAddresses) packet
- .getExtension("addresses", "http://jabber.org/protocol/address");
- return extension == null ? null : new MultipleRecipientInfo(extension);
- }
- private static void sendToIndividualRecipients(XMPPConnection connection, Packet packet,
- List to, List cc, List bcc) {
- if (to != null) {
- for (Iterator it = to.iterator(); it.hasNext();) {
- String jid = (String) it.next();
- packet.setTo(jid);
- connection.sendPacket(new PacketCopy(packet.toXML()));
- }
- }
- if (cc != null) {
- for (Iterator it = cc.iterator(); it.hasNext();) {
- String jid = (String) it.next();
- packet.setTo(jid);
- connection.sendPacket(new PacketCopy(packet.toXML()));
- }
- }
- if (bcc != null) {
- for (Iterator it = bcc.iterator(); it.hasNext();) {
- String jid = (String) it.next();
- packet.setTo(jid);
- connection.sendPacket(new PacketCopy(packet.toXML()));
- }
- }
- }
- private static void sendThroughService(XMPPConnection connection, Packet packet, List to,
- List cc, List bcc, String replyTo, String replyRoom, boolean noReply,
- String serviceAddress) {
- // Create multiple recipient extension
- MultipleAddresses multipleAddresses = new MultipleAddresses();
- if (to != null) {
- for (Iterator it = to.iterator(); it.hasNext();) {
- String jid = (String) it.next();
- multipleAddresses.addAddress(MultipleAddresses.TO, jid, null, null, false, null);
- }
- }
- if (cc != null) {
- for (Iterator it = cc.iterator(); it.hasNext();) {
- String jid = (String) it.next();
- multipleAddresses.addAddress(MultipleAddresses.CC, jid, null, null, false, null);
- }
- }
- if (bcc != null) {
- for (Iterator it = bcc.iterator(); it.hasNext();) {
- String jid = (String) it.next();
- multipleAddresses.addAddress(MultipleAddresses.BCC, jid, null, null, false, null);
- }
- }
- if (noReply) {
- multipleAddresses.setNoReply();
- }
- else {
- if (replyTo != null && replyTo.trim().length() > 0) {
- multipleAddresses
- .addAddress(MultipleAddresses.REPLY_TO, replyTo, null, null, false, null);
- }
- if (replyRoom != null && replyRoom.trim().length() > 0) {
- multipleAddresses.addAddress(MultipleAddresses.REPLY_ROOM, replyRoom, null, null,
- false, null);
- }
- }
- // Set the multiple recipient service address as the target address
- packet.setTo(serviceAddress);
- // Add extension to packet
- packet.addExtension(multipleAddresses);
- // Send the packet
- connection.sendPacket(packet);
- }
- /**
- * Returns the address of the multiple recipients service. To obtain such address service
- * discovery is going to be used on the connected server and if none was found then another
- * attempt will be tried on the server items. The discovered information is going to be
- * cached for 24 hours.
- *
- * @param connection the connection to use for disco. The connected server is going to be
- * queried.
- * @return the address of the multiple recipients service or <tt>null</tt> if none was found.
- */
- private static String getMultipleRecipienServiceAddress(XMPPConnection connection) {
- String serviceName = connection.getServiceName();
- String serviceAddress = (String) services.get(serviceName);
- if (serviceAddress == null) {
- synchronized (services) {
- serviceAddress = (String) services.get(serviceName);
- if (serviceAddress == null) {
- // Send the disco packet to the server itself
- try {
- DiscoverInfo info = ServiceDiscoveryManager.getInstanceFor(connection)
- .discoverInfo(serviceName);
- // Check if the server supports JEP-33
- if (info.containsFeature("http://jabber.org/protocol/address")) {
- serviceAddress = serviceName;
- }
- else {
- // Get the disco items and send the disco packet to each server item
- DiscoverItems items = ServiceDiscoveryManager.getInstanceFor(connection)
- .discoverItems(serviceName);
- for (Iterator it = items.getItems(); it.hasNext();) {
- DiscoverItems.Item item = (DiscoverItems.Item) it.next();
- info = ServiceDiscoveryManager.getInstanceFor(connection)
- .discoverInfo(item.getEntityID(), item.getNode());
- if (info.containsFeature("http://jabber.org/protocol/address")) {
- serviceAddress = serviceName;
- break;
- }
- }
- }
- // Cache the discovered information
- services.put(serviceName, serviceAddress == null ? "" : serviceAddress);
- }
- catch (XMPPException e) {
- e.printStackTrace();
- }
- }
- }
- }
- return "".equals(serviceAddress) ? null : serviceAddress;
- }
- /**
- * Packet that holds the XML stanza to send. This class is useful when the same packet
- * is needed to be sent to different recipients. Since using the same packet is not possible
- * (i.e. cannot change the TO address of a queues packet to be sent) then this class was
- * created to keep the XML stanza to send.
- */
- private static class PacketCopy extends Packet {
- private String text;
- /**
- * Create a copy of a packet with the text to send. The passed text must be a valid text to
- * send to the server, no validation will be done on the passed text.
- *
- * @param text the whole text of the packet to send
- */
- public PacketCopy(String text) {
- this.text = text;
- }
- public String toXML() {
- return text;
- }
- }
- }