/** * $RCSfile$ * $Revision$ * $Date$ * * Copyright (C) 2002-2003 Jive Software. All rights reserved. * ==================================================================== * The Jive Software License (based on Apache Software License, Version 1.1) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgment: * "This product includes software developed by * Jive Software (http://www.jivesoftware.com)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "Smack" and "Jive Software" must not be used to * endorse or promote products derived from this software without * prior written permission. For written permission, please * contact webmaster@jivesoftware.com. * * 5. Products derived from this software may not be called "Smack", * nor may "Smack" appear in their name, without prior written * permission of Jive Software. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL JIVE SOFTWARE OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== */ package org.jivesoftware.smack; import org.jivesoftware.smack.packet.*; import org.jivesoftware.smack.filter.*; import org.jivesoftware.smack.util.StringUtils; import java.util.*; /** * Represents a user's roster, which is the collection of users a person receives * presence updates for. Roster items are categorized into groups for easier management.
* * Others users may attempt to subscribe to this user using a subscription request. Three * modes are supported for handling these requests:
* * If using the manual mode, a PacketListener should be registered that * listens for Presence packets that have a type of {@link Presence.Type#SUBSCRIBE}. * * @return the subscription mode. */ public int getSubscriptionMode() { return subscriptionMode; } /** * Sets the subscription processing mode, which dictates what action * Smack will take when subscription requests from other users are made. * The default subscription mode is {@link #SUBSCRIPTION_ACCEPT_ALL}.
* * If using the manual mode, a PacketListener should be registered that * listens for Presence packets that have a type of {@link Presence.Type#SUBSCRIBE}. * * @param subscriptionMode the subscription mode. */ public void setSubscriptionMode(int subscriptionMode) { if (subscriptionMode != SUBSCRIPTION_ACCEPT_ALL && subscriptionMode != SUBSCRIPTION_REJECT_ALL && subscriptionMode != SUBSCRIPTION_MANUAL) { throw new IllegalArgumentException("Invalid mode."); } this.subscriptionMode = subscriptionMode; } /** * Reloads the entire roster from the server. This is an asynchronous operation, * which means the method will return immediately, and the roster will be * reloaded at a later point when the server responds to the reload request. * */ public void reload() { connection.sendPacket(new RosterPacket()); } /** * Adds a listener to this roster. The listener will be fired anytime one or more * changes to the roster are pushed from the server. * * @param rosterListener a roster listener. */ public void addRosterListener(RosterListener rosterListener) { synchronized (rosterListeners) { if (!rosterListeners.contains(rosterListener)) { rosterListeners.add(rosterListener); } } } /** * Removes a listener from this roster. The listener will be fired anytime one or more * changes to the roster are pushed from the server. * * @param rosterListener a roster listener. */ public void removeRosterListener(RosterListener rosterListener) { synchronized (rosterListeners) { rosterListeners.remove(rosterListener); } } /** * Creates a new group.
*
* Note: you must add at least one entry to the group for the group to be kept
* after a logout/login. This is due to the way that XMPP stores group information.
*
* @param name the name of the group.
* @return a new group.
*/
public RosterGroup createGroup(String name) {
if (!rosterInitialized) {
waitUntilInitialized();
}
synchronized (groups) {
if (groups.containsKey(name)) {
throw new IllegalArgumentException("Group with name " + name + " alread exists.");
}
RosterGroup group = new RosterGroup(name, connection);
groups.put(name, group);
return group;
}
}
/**
* Cretaes a new roster entry and prsence subscription. The server will asynchronously
* update the roster with the subscription status.
*
* @param user the user.
* @param name the nickname of the user.
* @param groups the list of group names the entry will belong to, or null if the
* the roster entry won't belong to a group.
*/
public void createEntry(String user, String name, String [] groups) throws XMPPException {
if (!rosterInitialized) {
waitUntilInitialized();
}
// Create and send roster entry creation packet.
RosterPacket rosterPacket = new RosterPacket();
rosterPacket.setType(IQ.Type.SET);
RosterPacket.Item item = new RosterPacket.Item(user, name);
if (groups != null) {
for (int i=0; i
*
* Since the user can ask for the entries after calling login, it's possible that the
* entries were not received yet. Therefore, the method call won't return until the
* roster has been initialized or two seconds has elapsed.
*
* @return filed entries in the roster.
*/
private List getFiledEntriesList() {
waitUntilInitialized();
return entries;
}
/**
* Returns the roster groups.
*
* Since the user can ask for the groups after calling login, it's possible that the
* groups were not received yet. Therefore, the method call won't return until the
* roster has been initialized or two seconds has elapsed.
*
* @return filed entries in the roster.
*/
private Map getGroupsMap() {
waitUntilInitialized();
return groups;
}
/**
* Waits until the roster has been initialized or 2 seconds has elapsed. It is required to
* wait before the user can make use of the roster when for example this is the first time
* the user has asked for the entries after calling login and we want to wait up to 2 seconds
* for the server to send back the user's roster.
*
* This behavior shields API users from having to worry about the fact that roster operations
* are asynchronous, although they'll still have to listen for changes to the roster.
*
*/
private void waitUntilInitialized() {
if (!rosterInitialized) {
int elapsed = 0;
while (!rosterInitialized && elapsed <= 2000) {
try {
Thread.sleep(500);
}
catch (Exception e) {
}
elapsed += 500;
}
}
}
/**
* Returns the presence info for a particular user, or null if the user
* is unavailable (offline) or if no presence information is available, such as
* when you are not subscribed to the user's presence updates.
*
* If the user has several presences (one for each resource) then answer the presence
* with the highest priority.
*
* @param user a fully qualified xmpp ID, e.g. jdoe@example.com
* @return the user's current presence, or null if the user is unavailable
* or if no presence information is available..
*/
public Presence getPresence(String user) {
String key = StringUtils.parseName(user) + "@" + StringUtils.parseServer(user);
Map userPresences = (Map) presenceMap.get(key);
if (userPresences == null) {
return null;
}
else {
// Find the resource with the highest priority
// Might be changed to use the resource with the highest availability instead.
Iterator it = userPresences.keySet().iterator();
Presence p;
Presence presence = null;
while (it.hasNext()) {
p = (Presence) userPresences.get(it.next());
if (presence == null) {
presence = p;
}
else {
if (p.getPriority() > presence.getPriority()) {
presence = p;
}
}
}
return presence;
}
}
/**
* Returns the presence info for a particular user's resource, or null if the user
* is unavailable (offline) or if no presence information is available, such as
* when you are not subscribed to the user's presence updates.
*
* @param user a fully qualified xmpp ID including a resource, e.g. jdoe@example.com/Home
* @return the user's current presence, or null if the user is unavailable
* or if no presence information is available.
*/
public Presence getPresenceResource(String userResource) {
String key =
StringUtils.parseName(userResource) + "@" + StringUtils.parseServer(userResource);
String resource = StringUtils.parseResource(userResource);
Map userPresences = (Map) presenceMap.get(key);
if (userPresences == null) {
return null;
}
else {
return (Presence) userPresences.get(resource);
}
}
/**
* Returns an iterator for all the user's current presences or null if the user
* is unavailable (offline) or if no presence information is available, such as
* when you are not subscribed to the user's presence updates.
*
* @param user a fully qualified xmpp ID, e.g. jdoe@example.com
* @return an iterator for all the user's current presences, or null if the user is
* unavailable or if no presence information is available.
*/
public Iterator getPresences(String user) {
String key = StringUtils.parseName(user) + "@" + StringUtils.parseServer(user);
Map userPresences = (Map) presenceMap.get(key);
if (userPresences == null) {
return null;
}
else {
return userPresences.entrySet().iterator();
}
}
/**
* Fires roster changed event to roster listeners.
*/
private void fireRosterChangedEvent() {
RosterListener [] listeners = null;
synchronized (rosterListeners) {
listeners = new RosterListener[rosterListeners.size()];
rosterListeners.toArray(listeners);
}
for (int i=0; i