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