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.xroster;
018
019import java.lang.ref.WeakReference;
020import java.util.Collections;
021import java.util.HashSet;
022import java.util.Iterator;
023import java.util.Map;
024import java.util.Set;
025import java.util.WeakHashMap;
026
027import org.jivesoftware.smack.SmackException.NotConnectedException;
028import org.jivesoftware.smack.StanzaListener;
029import org.jivesoftware.smack.XMPPConnection;
030import org.jivesoftware.smack.filter.StanzaExtensionFilter;
031import org.jivesoftware.smack.filter.StanzaFilter;
032import org.jivesoftware.smack.packet.Message;
033import org.jivesoftware.smack.packet.Stanza;
034import org.jivesoftware.smack.roster.Roster;
035import org.jivesoftware.smack.roster.RosterEntry;
036import org.jivesoftware.smack.roster.RosterGroup;
037
038import org.jivesoftware.smackx.xroster.packet.RosterExchange;
039
040import org.jxmpp.jid.Jid;
041
042/**
043 *
044 * Manages Roster exchanges. A RosterExchangeManager provides a high level access to send 
045 * rosters, roster groups and roster entries to XMPP clients. It also provides an easy way
046 * to hook up custom logic when entries are received from another XMPP client through 
047 * RosterExchangeListeners.
048 *
049 * @author Gaston Dombiak
050 */
051public class RosterExchangeManager {
052
053    public final static String NAMESPACE = "jabber:x:roster";
054    public final static String ELEMENT = "x";
055
056    private final static Map<XMPPConnection, RosterExchangeManager> INSTANCES = new WeakHashMap<>();
057
058    private final static StanzaFilter PACKET_FILTER = new StanzaExtensionFilter(ELEMENT, NAMESPACE);
059
060    private final Set<RosterExchangeListener> rosterExchangeListeners = Collections.synchronizedSet(new HashSet<RosterExchangeListener>());
061
062    private final WeakReference<XMPPConnection> weakRefConnection;
063    private final StanzaListener packetListener;
064
065    public synchronized static RosterExchangeManager getInstanceFor(XMPPConnection connection) {
066        RosterExchangeManager rosterExchangeManager = INSTANCES.get(connection);
067        if (rosterExchangeManager == null) {
068            rosterExchangeManager = new RosterExchangeManager(connection);
069            INSTANCES.put(connection, rosterExchangeManager);
070        }
071        return rosterExchangeManager;
072    }
073
074    /**
075     * Creates a new roster exchange manager.
076     *
077     * @param connection an XMPPConnection which is used to send and receive messages.
078     */
079    public RosterExchangeManager(XMPPConnection connection) {
080        weakRefConnection = new WeakReference<XMPPConnection>(connection);
081        // Listens for all roster exchange packets and fire the roster exchange listeners.
082        packetListener = new StanzaListener() {
083            @Override
084            public void processStanza(Stanza packet) {
085                Message message = (Message) packet;
086                RosterExchange rosterExchange =
087                    (RosterExchange) message.getExtension(ELEMENT, NAMESPACE);
088                // Fire event for roster exchange listeners
089                fireRosterExchangeListeners(message.getFrom(), rosterExchange.getRosterEntries());
090            }
091        };
092        connection.addAsyncStanzaListener(packetListener, PACKET_FILTER);
093    }
094
095    /**
096     * Adds a listener to roster exchanges. The listener will be fired anytime roster entries
097     * are received from remote XMPP clients.
098     *
099     * @param rosterExchangeListener a roster exchange listener.
100     */
101    public void addRosterListener(RosterExchangeListener rosterExchangeListener) {
102        rosterExchangeListeners.add(rosterExchangeListener);
103    }
104
105    /**
106     * Removes a listener from roster exchanges. The listener will be fired anytime roster 
107     * entries are received from remote XMPP clients.
108     *
109     * @param rosterExchangeListener a roster exchange listener..
110     */
111    public void removeRosterListener(RosterExchangeListener rosterExchangeListener) {
112        rosterExchangeListeners.remove(rosterExchangeListener);
113    }
114
115    /**
116     * Sends a roster to userID. All the entries of the roster will be sent to the
117     * target user.
118     * 
119     * @param roster the roster to send
120     * @param targetUserID the user that will receive the roster entries
121     * @throws NotConnectedException 
122     * @throws InterruptedException 
123     */
124    public void send(Roster roster, Jid targetUserID) throws NotConnectedException, InterruptedException {
125        // Create a new message to send the roster
126        Message msg = new Message(targetUserID);
127        // Create a RosterExchange Package and add it to the message
128        RosterExchange rosterExchange = new RosterExchange(roster);
129        msg.addExtension(rosterExchange);
130
131        XMPPConnection connection = weakRefConnection.get();
132        // Send the message that contains the roster
133        connection.sendStanza(msg);
134    }
135
136    /**
137     * Sends a roster entry to userID.
138     * 
139     * @param rosterEntry the roster entry to send
140     * @param targetUserID the user that will receive the roster entries
141     * @throws NotConnectedException 
142     * @throws InterruptedException 
143     */
144    public void send(RosterEntry rosterEntry, Jid targetUserID) throws NotConnectedException, InterruptedException {
145        // Create a new message to send the roster
146        Message msg = new Message(targetUserID);
147        // Create a RosterExchange Package and add it to the message
148        RosterExchange rosterExchange = new RosterExchange();
149        rosterExchange.addRosterEntry(rosterEntry);
150        msg.addExtension(rosterExchange);
151
152        XMPPConnection connection = weakRefConnection.get();
153        // Send the message that contains the roster
154        connection.sendStanza(msg);
155    }
156
157    /**
158     * Sends a roster group to userID. All the entries of the group will be sent to the 
159     * target user.
160     * 
161     * @param rosterGroup the roster group to send
162     * @param targetUserID the user that will receive the roster entries
163     * @throws NotConnectedException 
164     * @throws InterruptedException 
165     */
166    public void send(RosterGroup rosterGroup, Jid targetUserID) throws NotConnectedException, InterruptedException {
167        // Create a new message to send the roster
168        Message msg = new Message(targetUserID);
169        // Create a RosterExchange Package and add it to the message
170        RosterExchange rosterExchange = new RosterExchange();
171        for (RosterEntry entry : rosterGroup.getEntries()) {
172            rosterExchange.addRosterEntry(entry);
173        }
174        msg.addExtension(rosterExchange);
175
176        XMPPConnection connection = weakRefConnection.get();
177        // Send the message that contains the roster
178        connection.sendStanza(msg);
179    }
180
181    /**
182     * Fires roster exchange listeners.
183     */
184    private void fireRosterExchangeListeners(Jid from, Iterator<RemoteRosterEntry> remoteRosterEntries) {
185        RosterExchangeListener[] listeners = null;
186        synchronized (rosterExchangeListeners) {
187            listeners = new RosterExchangeListener[rosterExchangeListeners.size()];
188            rosterExchangeListeners.toArray(listeners);
189        }
190        for (int i = 0; i < listeners.length; i++) {
191            listeners[i].entriesReceived(from, remoteRosterEntries);
192        }
193    }
194}