![JAR search and dependency download from the Maven repository](/logo.png)
org.jivesoftware.openfire.roster.RosterItem Maven / Gradle / Ivy
/*
* Copyright (C) 2004-2008 Jive Software. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.openfire.roster;
import org.jivesoftware.openfire.SharedGroupException;
import org.jivesoftware.openfire.group.Group;
import org.jivesoftware.openfire.group.GroupManager;
import org.jivesoftware.openfire.group.GroupNotFoundException;
import org.jivesoftware.openfire.user.UserNameManager;
import org.jivesoftware.openfire.user.UserNotFoundException;
import org.jivesoftware.util.cache.CacheSizes;
import org.jivesoftware.util.cache.Cacheable;
import org.jivesoftware.util.cache.CannotCalculateSizeException;
import org.jivesoftware.util.cache.ExternalizableUtil;
import org.xmpp.packet.JID;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.*;
/**
* Represents a single roster item for a User's Roster.
* The server doesn't need to know anything about roster groups so they are
* not stored with easy retrieval or manipulation in mind. The important data
* elements of a roster item (beyond the jid adddress of the roster entry) includes:
*
* - nick - A nickname for the user when used in this roster
* - sub - A subscription type: to, from, none, both
* - ask - An optional subscription ask status: subscribe, unsubscribe
* - groups - A list of groups to organize roster entries under (e.g. friends, co-workers, etc)
*
*
* @author Gaston Dombiak
*/
public class RosterItem implements Cacheable, Externalizable {
public enum SubType {
/**
* Indicates the roster item should be removed.
*/
REMOVE(-1),
/**
* No subscription is established.
*/
NONE(0),
/**
* The roster owner has a subscription to the roster item's presence.
*/
TO(1),
/**
* The roster item has a subscription to the roster owner's presence.
*/
FROM(2),
/**
* The roster item and owner have a mutual subscription.
*/
BOTH(3);
private final int value;
SubType(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public String getName() {
return name().toLowerCase();
}
public static SubType getTypeFromInt(int value) {
for (SubType subType : values()) {
if (subType.value == value) {
return subType;
}
}
return null;
}
}
public enum AskType {
/**
* The roster item has no pending subscription requests.
*/
NONE(-1),
/**
* The roster item has been asked for permission to subscribe to their presence
* but no response has been received.
*/
SUBSCRIBE(0),
/**
* The roster owner has asked to the roster item to unsubscribe from it's
* presence but has not received confirmation.
*/
UNSUBSCRIBE(1);
private final int value;
AskType(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public static AskType getTypeFromInt(int value) {
for (AskType askType : values()) {
if (askType.value == value) {
return askType;
}
}
return null;
}
}
public enum RecvType {
/**
* There are no subscriptions that have been received but not presented to the user.
*/
NONE(-1),
/**
* The server has received a subscribe request, but has not forwarded it to the user.
*/
SUBSCRIBE(1),
/**
* The server has received an unsubscribe request, but has not forwarded it to the user.
*/
UNSUBSCRIBE(2);
private final int value;
RecvType(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public static RecvType getTypeFromInt(int value) {
for (RecvType recvType : values()) {
if (recvType.value == value) {
return recvType;
}
}
return null;
}
}
/**
* Indicates the roster item should be removed.
*/
public static final SubType SUB_REMOVE = SubType.REMOVE;
/**
* No subscription is established.
*/
public static final SubType SUB_NONE = SubType.NONE;
/**
* The roster owner has a subscription to the roster item's presence.
*/
public static final SubType SUB_TO = SubType.TO;
/**
* The roster item has a subscription to the roster owner's presence.
*/
public static final SubType SUB_FROM = SubType.FROM;
/**
* The roster item and owner have a mutual subscription.
*/
public static final SubType SUB_BOTH = SubType.BOTH;
/**
* The roster item has no pending subscription requests.
*/
public static final AskType ASK_NONE = AskType.NONE;
/**
* The roster item has been asked for permission to subscribe to their presence
* but no response has been received.
*/
public static final AskType ASK_SUBSCRIBE = AskType.SUBSCRIBE;
/**
* The roster owner has asked to the roster item to unsubscribe from it's
* presence but has not received confirmation.
*/
public static final AskType ASK_UNSUBSCRIBE = AskType.UNSUBSCRIBE;
/**
* There are no subscriptions that have been received but not presented to the user.
*/
public static final RecvType RECV_NONE = RecvType.NONE;
/**
* The server has received a subscribe request, but has not forwarded it to the user.
*/
public static final RecvType RECV_SUBSCRIBE = RecvType.SUBSCRIBE;
/**
* The server has received an unsubscribe request, but has not forwarded it to the user.
*/
public static final RecvType RECV_UNSUBSCRIBE = RecvType.UNSUBSCRIBE;
protected RecvType recvStatus;
protected JID jid;
protected String nickname;
protected List groups;
protected Set sharedGroups = new HashSet<>();
protected Set invisibleSharedGroups = new HashSet<>();
protected SubType subStatus;
protected AskType askStatus;
/**
* Holds the ID that uniquely identifies the roster in the backend store. A value of
* zero means that the roster item is not persistent.
*/
private long rosterID;
/**
* Constructor added for Externalizable. Do not use this constructor.
*/
public RosterItem() {
}
public RosterItem(long id,
JID jid,
SubType subStatus,
AskType askStatus,
RecvType recvStatus,
String nickname,
List groups) {
this(jid, subStatus, askStatus, recvStatus, nickname, groups);
this.rosterID = id;
}
public RosterItem(JID jid,
SubType subStatus,
AskType askStatus,
RecvType recvStatus,
String nickname,
List groups) {
this.jid = jid;
this.subStatus = subStatus;
this.askStatus = askStatus;
this.recvStatus = recvStatus;
this.nickname = nickname;
this.groups = new LinkedList<>();
if (groups != null) {
for (String group : groups) {
this.groups.add(group);
}
}
}
/**
* Create a roster item from the data in another one.
*
* @param item Item that contains the info of the roster item.
*/
public RosterItem(org.xmpp.packet.Roster.Item item) {
this(item.getJID(),
getSubType(item),
getAskStatus(item),
RosterItem.RECV_NONE,
item.getName(),
new LinkedList<>(item.getGroups()));
}
public static RosterItem.AskType getAskStatus(org.xmpp.packet.Roster.Item item) {
if (item.getAsk() == org.xmpp.packet.Roster.Ask.subscribe) {
return RosterItem.ASK_SUBSCRIBE;
}
else if (item.getAsk() == org.xmpp.packet.Roster.Ask.unsubscribe) {
return RosterItem.ASK_UNSUBSCRIBE;
}
else {
return RosterItem.ASK_NONE;
}
}
public static RosterItem.SubType getSubType(org.xmpp.packet.Roster.Item item) {
if (item.getSubscription() == org.xmpp.packet.Roster.Subscription.to) {
return RosterItem.SUB_TO;
}
else if (item.getSubscription() == org.xmpp.packet.Roster.Subscription.from) {
return RosterItem.SUB_FROM;
}
else if (item.getSubscription() == org.xmpp.packet.Roster.Subscription.both) {
return RosterItem.SUB_BOTH;
}
else if (item.getSubscription() == org.xmpp.packet.Roster.Subscription.remove) {
return RosterItem.SUB_REMOVE;
}
else {
return RosterItem.SUB_NONE;
}
}
/**
* Obtain the current subscription status of the item.
*
* @return The subscription status of the item
*/
public SubType getSubStatus() {
return subStatus;
}
/**
* Set the current subscription status of the item.
*
* @param subStatus The subscription status of the item
*/
public void setSubStatus(SubType subStatus) {
// Optimization: Load user only if we need to set the nickname of the roster item
if ("".equals(nickname) && (subStatus == SUB_BOTH || subStatus == SUB_TO)) {
try {
nickname = UserNameManager.getUserName(jid);
}
catch (UserNotFoundException e) {
// Do nothing
}
}
this.subStatus = subStatus;
}
/**
* Obtain the current ask status of the item.
*
* @return The ask status of the item
*/
public AskType getAskStatus() {
if (isShared()) {
// Redefine the ask status since the item belongs to a shared group
return ASK_NONE;
}
else {
return askStatus;
}
}
/**
* Set the current ask status of the item.
*
* @param askStatus The ask status of the item
*/
public void setAskStatus(AskType askStatus) {
this.askStatus = askStatus;
}
/**
* Obtain the current recv status of the item.
*
* @return The recv status of the item
*/
public RecvType getRecvStatus() {
return recvStatus;
}
/**
* Set the current recv status of the item.
*
* @param recvStatus The recv status of the item
*/
public void setRecvStatus(RecvType recvStatus) {
this.recvStatus = recvStatus;
}
/**
* Obtain the address of the item.
*
* @return The address of the item
*/
public JID getJid() {
return jid;
}
/**
* Obtain the current nickname for the item.
*
* @return The subscription status of the item
*/
public String getNickname() {
return nickname;
}
/**
* Set the current nickname for the item.
*
* @param nickname The subscription status of the item
*/
public void setNickname(String nickname) {
this.nickname = nickname;
}
/**
* Returns the groups for the item. Shared groups won't be included in the answer.
*
* @return The groups for the item.
*/
public List getGroups() {
return groups;
}
/**
* Set the current groups for the item.
*
* @param groups The new lists of groups the item belongs to.
* @throws org.jivesoftware.openfire.SharedGroupException if trying to remove shared group.
*/
public void setGroups(List groups) throws SharedGroupException {
if (groups == null) {
this.groups = new LinkedList<>();
}
else {
// Raise an error if the user is trying to remove the item from a shared group
for (Group group: getSharedGroups()) {
// Get the display name of the group
String groupName = group.getProperties().get("sharedRoster.displayName");
// Check if the group has been removed from the new groups list
if (!groups.contains(groupName)) {
throw new SharedGroupException("Cannot remove item from shared group");
}
}
// Remove shared groups from the param
for (Iterator it=groups.iterator(); it.hasNext();) {
String groupName = it.next();
try {
Group group = GroupManager.getInstance().getGroup(groupName);
if (RosterManager.isSharedGroup(group)) {
it.remove();
}
} catch (GroupNotFoundException e) {
// Check now if there is a group whose display name matches the requested group
Collection groupsWithProp = GroupManager
.getInstance()
.search("sharedRoster.displayName", groupName);
Iterator itr = groupsWithProp.iterator();
while(itr.hasNext()) {
Group group = itr.next();
if (RosterManager.isSharedGroup(group)) {
it.remove();
}
}
}
}
this.groups = groups;
}
}
/**
* Returns the shared groups for the item.
*
* @return The shared groups this item belongs to.
*/
public Collection getSharedGroups() {
Collection groups = new ArrayList<>(sharedGroups.size());
for (String groupName : sharedGroups) {
try {
groups.add(GroupManager.getInstance().getGroup(groupName));
}
catch (GroupNotFoundException e) {
// Do nothing
}
}
return groups;
}
/**
* Returns the invisible shared groups for the item. These groups are for internal use
* and help track the reason why a roster item has a presence subscription of type FROM
* when using shared groups.
*
* @return The shared groups this item belongs to.
*/
public Collection getInvisibleSharedGroups() {
Collection groups = new ArrayList<>(invisibleSharedGroups.size());
for (String groupName : invisibleSharedGroups) {
try {
groups.add(GroupManager.getInstance().getGroup(groupName));
}
catch (GroupNotFoundException e) {
// Do nothing
}
}
return groups;
}
Set getInvisibleSharedGroupsNames() {
return invisibleSharedGroups;
}
void setInvisibleSharedGroupsNames(Set groupsNames) {
invisibleSharedGroups = groupsNames;
}
/**
* Adds a new group to the shared groups list.
*
* @param sharedGroup The shared group to add to the list of shared groups.
*/
public void addSharedGroup(Group sharedGroup) {
sharedGroups.add(sharedGroup.getName());
invisibleSharedGroups.remove(sharedGroup.getName());
}
/**
* Adds a new group to the list shared groups that won't be sent to the user. These groups
* are for internal use and help track the reason why a roster item has a presence
* subscription of type FROM when using shared groups.
*
* @param sharedGroup The shared group to add to the list of shared groups.
*/
public void addInvisibleSharedGroup(Group sharedGroup) {
invisibleSharedGroups.add(sharedGroup.getName());
}
/**
* Removes a group from the shared groups list.
*
* @param sharedGroup The shared group to remove from the list of shared groups.
*/
public void removeSharedGroup(Group sharedGroup) {
sharedGroups.remove(sharedGroup.getName());
invisibleSharedGroups.remove(sharedGroup.getName());
}
/**
* Returns true if this item belongs to a shared group. Return true even if the item belongs
* to a personal group and a shared group.
*
* @return true if this item belongs to a shared group.
*/
public boolean isShared() {
return !sharedGroups.isEmpty() || !invisibleSharedGroups.isEmpty();
}
/**
* Returns true if this item belongs ONLY to shared groups. This means that the the item is
* considered to be "only shared" if it doesn't belong to a personal group but only to shared
* groups.
*
* @return true if this item belongs ONLY to shared groups.
*/
public boolean isOnlyShared() {
return isShared() && groups.isEmpty();
}
/**
* Returns the roster ID associated with this particular roster item. A value of zero
* means that the roster item is not being persisted in the backend store.
*
* Databases can use the roster ID as the key in locating roster items.
*
* @return The roster ID
*/
public long getID() {
return rosterID;
}
/**
* Sets the roster ID associated with this particular roster item. A value of zero
* means that the roster item is not being persisted in the backend store.
*
* Databases can use the roster ID as the key in locating roster items.
*
* @param rosterID The roster ID.
*/
public void setID(long rosterID) {
this.rosterID = rosterID;
}
/**
*
Update the cached item as a copy of the given item.
* A convenience for getting the item and setting each attribute.
*
* @param item The item who's settings will be copied into the cached copy
* @throws org.jivesoftware.openfire.SharedGroupException if trying to remove shared group.
*/
public void setAsCopyOf(org.xmpp.packet.Roster.Item item) throws SharedGroupException {
setGroups(new LinkedList<>(item.getGroups()));
setNickname(item.getName());
}
/*
* (non-Javadoc)
*
* @see org.jivesoftware.util.cache.Cacheable#getCachedSize()
*/
@Override
public int getCachedSize() throws CannotCalculateSizeException {
int size = jid.toBareJID().length();
size += CacheSizes.sizeOfString(nickname);
size += CacheSizes.sizeOfCollection(groups);
size += CacheSizes.sizeOfCollection(invisibleSharedGroups);
size += CacheSizes.sizeOfCollection(sharedGroups);
size += CacheSizes.sizeOfInt(); // subStatus
size += CacheSizes.sizeOfInt(); // askStatus
size += CacheSizes.sizeOfInt(); // recvStatus
size += CacheSizes.sizeOfLong(); // id
return size;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
ExternalizableUtil.getInstance().writeSerializable(out, jid);
ExternalizableUtil.getInstance().writeBoolean(out, nickname != null);
if (nickname != null) {
ExternalizableUtil.getInstance().writeSafeUTF(out, nickname);
}
ExternalizableUtil.getInstance().writeStrings(out, groups);
ExternalizableUtil.getInstance().writeStrings(out, sharedGroups);
ExternalizableUtil.getInstance().writeStrings(out, invisibleSharedGroups);
ExternalizableUtil.getInstance().writeInt(out, recvStatus.getValue());
ExternalizableUtil.getInstance().writeInt(out, subStatus.getValue());
ExternalizableUtil.getInstance().writeInt(out, askStatus.getValue());
ExternalizableUtil.getInstance().writeLong(out, rosterID);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
jid = (JID) ExternalizableUtil.getInstance().readSerializable(in);
if (ExternalizableUtil.getInstance().readBoolean(in)) {
nickname = ExternalizableUtil.getInstance().readSafeUTF(in);
}
this.groups = new LinkedList<>();
ExternalizableUtil.getInstance().readStrings(in, groups);
ExternalizableUtil.getInstance().readStrings(in, sharedGroups);
ExternalizableUtil.getInstance().readStrings(in, invisibleSharedGroups);
recvStatus = RecvType.getTypeFromInt(ExternalizableUtil.getInstance().readInt(in));
subStatus = SubType.getTypeFromInt(ExternalizableUtil.getInstance().readInt(in));
askStatus = AskType.getTypeFromInt(ExternalizableUtil.getInstance().readInt(in));
rosterID = ExternalizableUtil.getInstance().readLong(in);
}
}