Commit e11d4699 authored by Alex Wenckus's avatar Alex Wenckus Committed by alex

Initial check-in

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/branches/rsm_branch@8733 b35dd754-fafc-0310-a699-88a17e54d16e
parent cb3a8ffd
...@@ -112,23 +112,20 @@ public class AdHocCommandHandler extends IQHandler ...@@ -112,23 +112,20 @@ public class AdHocCommandHandler extends IQHandler
} }
} }
public Iterator<Element> getItems(String name, String node, JID senderJID) { public Iterator<DiscoItem> getItems(String name, String node, JID senderJID) {
List<Element> answer = new ArrayList<Element>(); List<DiscoItem> answer = new ArrayList<DiscoItem>();
if (!NAMESPACE.equals(node)) { if (!NAMESPACE.equals(node)) {
answer = Collections.emptyList(); answer = Collections.emptyList();
} }
else { else {
Element item;
for (AdHocCommand command : manager.getCommands()) { for (AdHocCommand command : manager.getCommands()) {
// Only include commands that the sender can invoke (i.e. has enough permissions) // Only include commands that the sender can invoke (i.e. has enough permissions)
if (command.hasPermission(senderJID)) { if (command.hasPermission(senderJID)) {
item = DocumentHelper.createElement("item"); final DiscoItem item = new DiscoItem(new JID(serverName),
item.addAttribute("jid", serverName); command.getLabel(), command.getCode(), null);
item.addAttribute("node", command.getCode()); answer.add(item);
item.addAttribute("name", command.getLabel()); }
answer.add(item);
}
} }
} }
return answer.iterator(); return answer.iterator();
......
...@@ -445,8 +445,7 @@ public class InternalComponentManager extends BasicModule implements ComponentMa ...@@ -445,8 +445,7 @@ public class InternalComponentManager extends BasicModule implements ComponentMa
return; return;
} }
try { try {
XMPPServer.getInstance().getIQDiscoItemsHandler().addComponentItem(packet.getFrom() XMPPServer.getInstance().getIQDiscoItemsHandler().addComponentItem(packet.getFrom(),
.toBareJID(),
identity.attributeValue("name")); identity.attributeValue("name"));
if (component instanceof ComponentSession.ExternalComponent) { if (component instanceof ComponentSession.ExternalComponent) {
ComponentSession.ExternalComponent externalComponent = ComponentSession.ExternalComponent externalComponent =
......
...@@ -11,51 +11,169 @@ ...@@ -11,51 +11,169 @@
package org.jivesoftware.openfire.disco; package org.jivesoftware.openfire.disco;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.jivesoftware.util.rsm.Result;
import org.xmpp.packet.JID;
/** /**
* An item is associated with an XMPP Entity, usually thought of a children of the parent * <p>
* entity and normally are addressable as a JID.<p> * An item is associated with an XMPP Entity, usually thought of a children of
* <p/> * the parent entity and normally are addressable as a JID.
* An item associated with an entity may not be addressable as a JID. In order to handle * </p>
* such items, Service Discovery uses an optional 'node' attribute that supplements the * <p>
* 'jid' attribute. * An item associated with an entity may not be addressable as a JID. In order
* * to handle such items, Service Discovery uses an optional 'node' attribute
* that supplements the 'jid' attribute.
* </p>
*
* @author Gaston Dombiak * @author Gaston Dombiak
*/ */
public interface DiscoItem { public class DiscoItem implements Result {
/** private final JID jid;
* Returns the entity's ID.
* private final String name;
* @return the entity's ID.
*/ private final String node;
public abstract String getJID();
private final String action;
/**
* Returns the node attribute that supplements the 'jid' attribute. A node is merely /**
* something that is associated with a JID and for which the JID can provide information.<p> * <p>
* <p/> * Creates a new DiscoItem instance.
* Node attributes SHOULD be used only when trying to provide or query information which * </p>
* is not directly addressable. *
* * @param jid
* @return the node attribute that supplements the 'jid' attribute * specifies the Jabber ID of the item "owner" or location
*/ * (required).
public abstract String getNode(); * @param name
* specifies a natural-language name for the item (can be null).
/** * @param node
* Returns the entity's name. The entity's name specifies in natural-language the name for the * specifies the particular node associated with the JID of the
* item. * item "owner" or location (can be null).
* * @param action
* @return the entity's name. * specifies the action to be taken for the item.
*/ * @throws IllegalArgumentException
public abstract String getName(); * If a required parameter was null, or if the supplied 'action'
* parameter has another value than 'null', "update" or
/** * "remove".
* Returns the action (i.e. update or remove) that indicates what must be done with this item or */
* null if none. An "update" action requests the server to create or update the item. Whilst a public DiscoItem(JID jid, String name, String node, String action) {
* "remove" action requests to remove the item. if (jid == null) {
* throw new IllegalArgumentException("Argument 'jid' cannot be null.");
* @return the action (i.e. update or remove) that indicates what must be done with this item or }
* null if none.
*/ if (action != null && !action.equals("update")
public abstract String getAction(); && !action.equals("remove")) {
throw new IllegalArgumentException(
"Argument 'jid' cannot have any other value than null, \"update\" or \"remove\".");
}
this.jid = jid;
this.name = name;
this.node = node;
this.action = action;
}
/**
* <p>
* Returns the entity's ID.
* </p>
*
* @return the entity's ID.
*/
public JID getJID() {
return jid;
}
/**
* <p>
* Returns the node attribute that supplements the 'jid' attribute. A node
* is merely something that is associated with a JID and for which the JID
* can provide information.
* </p>
* <p>
* Node attributes SHOULD be used only when trying to provide or query
* information which is not directly addressable.
* </p>
*
* @return the node attribute that supplements the 'jid' attribute
*/
public String getNode() {
return node;
}
/**
* <p>
* Returns the entity's name. The entity's name specifies in
* natural-language the name for the item.
* </p>
*
* @return the entity's name.
*/
public String getName() {
return name;
}
/**
* <p>
* Returns the action (i.e. update or remove) that indicates what must be
* done with this item or null if none. An "update" action requests the
* server to create or update the item. Whilst a "remove" action requests to
* remove the item.
* </p>
*
* @return the action (i.e. update or remove) that indicates what must be
* done with this item or null if none.
*/
public String getAction() {
return action;
}
/**
* Returns a dom4j element that represents this DiscoItem object.
*
* @return element representing this object.
*/
public Element getElement() {
final Element element = DocumentHelper.createElement("item");
if (action != null) {
element.addAttribute("action", action);
}
if (jid != null) {
element.addAttribute("jid", jid.toString());
}
if (name != null) {
element.addAttribute("name", name);
}
if (node != null) {
element.addAttribute("node", node);
}
return element;
}
/*
* (non-Javadoc)
*
* @see org.jivesoftware.util.resultsetmanager.Result#getUID()
*/
public String getUID() {
final StringBuilder sb = new StringBuilder(jid.toString());
if (name != null) {
sb.append(name);
}
if (node != null) {
sb.append(node);
}
if (action != null) {
sb.append(action);
}
return sb.toString();
}
} }
...@@ -11,11 +11,10 @@ ...@@ -11,11 +11,10 @@
package org.jivesoftware.openfire.disco; package org.jivesoftware.openfire.disco;
import org.dom4j.Element;
import org.xmpp.packet.JID;
import java.util.Iterator; import java.util.Iterator;
import org.xmpp.packet.JID;
/** /**
* A DiscoItemsProvider is responsible for providing the items associated with a JID's name and * A DiscoItemsProvider is responsible for providing the items associated with a JID's name and
* node. For example, the room service could implement this interface in order to provide * node. For example, the room service could implement this interface in order to provide
...@@ -30,7 +29,7 @@ import java.util.Iterator; ...@@ -30,7 +29,7 @@ import java.util.Iterator;
public interface DiscoItemsProvider { public interface DiscoItemsProvider {
/** /**
* Returns an Iterator (of Element) with the target entity's items or null if none. Each Element * Returns an Iterator (of DiscoItem) with the target entity's items or null if none. Each DiscoItem
* must include a JID attribute and may include the name and node attributes of the entity. In * must include a JID attribute and may include the name and node attributes of the entity. In
* case that the sender of the disco request is not authorized to discover items an * case that the sender of the disco request is not authorized to discover items an
* UnauthorizedException will be thrown. * UnauthorizedException will be thrown.
...@@ -38,8 +37,8 @@ public interface DiscoItemsProvider { ...@@ -38,8 +37,8 @@ public interface DiscoItemsProvider {
* @param name the recipient JID's name. * @param name the recipient JID's name.
* @param node the requested disco node. * @param node the requested disco node.
* @param senderJID the XMPPAddress of user that sent the disco items request. * @param senderJID the XMPPAddress of user that sent the disco items request.
* @return an Iterator (of Element) with the target entity's items or null if none. * @return an Iterator (of DiscoItem) with the target entity's items or null if none.
*/ */
public abstract Iterator<Element> getItems(String name, String node, JID senderJID); public abstract Iterator<DiscoItem> getItems(String name, String node, JID senderJID);
} }
...@@ -11,6 +11,8 @@ ...@@ -11,6 +11,8 @@
package org.jivesoftware.openfire.disco; package org.jivesoftware.openfire.disco;
import org.xmpp.packet.JID;
/** /**
* Represent a DiscoItem provided by the server. Therefore, the DiscoServerItems are responsible * Represent a DiscoItem provided by the server. Therefore, the DiscoServerItems are responsible
* for providing the DiscoInfoProvider and DiscoItemsProvider that will provide the information and * for providing the DiscoInfoProvider and DiscoItemsProvider that will provide the information and
...@@ -26,8 +28,29 @@ package org.jivesoftware.openfire.disco; ...@@ -26,8 +28,29 @@ package org.jivesoftware.openfire.disco;
* *
* @author Gaston Dombiak * @author Gaston Dombiak
*/ */
public interface DiscoServerItem extends DiscoItem { public class DiscoServerItem extends DiscoItem {
private final DiscoInfoProvider infoProvider;
private final DiscoItemsProvider itemsProvider;
public DiscoServerItem(JID jid, String name, String node, String action,
DiscoInfoProvider infoProvider, DiscoItemsProvider itemsProvider)
{
super(jid, name, node, action);
if (infoProvider == null)
{
throw new IllegalArgumentException("Argument 'infoProvider' cannot be null.");
}
if (itemsProvider == null)
{
throw new IllegalArgumentException("Argument 'itemsProvider' cannot be null.");
}
this.infoProvider = infoProvider;
this.itemsProvider = itemsProvider;
}
/** /**
* Returns the DiscoInfoProvider responsible for providing the information related to this item. * Returns the DiscoInfoProvider responsible for providing the information related to this item.
* The DiscoInfoProvider will be automatically included in IQDiscoInfoHandler as the provider * The DiscoInfoProvider will be automatically included in IQDiscoInfoHandler as the provider
...@@ -35,7 +58,9 @@ public interface DiscoServerItem extends DiscoItem { ...@@ -35,7 +58,9 @@ public interface DiscoServerItem extends DiscoItem {
* *
* @return the DiscoInfoProvider responsible for providing the information related to this item. * @return the DiscoInfoProvider responsible for providing the information related to this item.
*/ */
public abstract DiscoInfoProvider getDiscoInfoProvider(); public DiscoInfoProvider getDiscoInfoProvider() {
return infoProvider;
}
/** /**
* Returns the DiscoItemsProvider responsible for providing the items related to this item. * Returns the DiscoItemsProvider responsible for providing the items related to this item.
...@@ -44,5 +69,7 @@ public interface DiscoServerItem extends DiscoItem { ...@@ -44,5 +69,7 @@ public interface DiscoServerItem extends DiscoItem {
* *
* @return the DiscoItemsProvider responsible for providing the items related to this item. * @return the DiscoItemsProvider responsible for providing the items related to this item.
*/ */
public abstract DiscoItemsProvider getDiscoItemsProvider(); public DiscoItemsProvider getDiscoItemsProvider() {
return itemsProvider;
}
} }
...@@ -25,6 +25,8 @@ import org.jivesoftware.openfire.handler.IQHandler; ...@@ -25,6 +25,8 @@ import org.jivesoftware.openfire.handler.IQHandler;
import org.jivesoftware.openfire.user.UserManager; import org.jivesoftware.openfire.user.UserManager;
import org.jivesoftware.openfire.user.UserNotFoundException; import org.jivesoftware.openfire.user.UserNotFoundException;
import org.jivesoftware.util.JiveGlobals; import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.rsm.ResultSet;
import org.jivesoftware.util.cache.Cache; import org.jivesoftware.util.cache.Cache;
import org.jivesoftware.util.cache.CacheFactory; import org.jivesoftware.util.cache.CacheFactory;
import org.jivesoftware.util.lock.LockManager; import org.jivesoftware.util.lock.LockManager;
...@@ -59,6 +61,7 @@ import java.util.concurrent.locks.Lock; ...@@ -59,6 +61,7 @@ import java.util.concurrent.locks.Lock;
*/ */
public class IQDiscoInfoHandler extends IQHandler implements ClusterEventListener { public class IQDiscoInfoHandler extends IQHandler implements ClusterEventListener {
private static final String NAMESPACE_DISCO_INFO = "http://jabber.org/protocol/disco#info";
private Map<String, DiscoInfoProvider> entities = new HashMap<String, DiscoInfoProvider>(); private Map<String, DiscoInfoProvider> entities = new HashMap<String, DiscoInfoProvider>();
private Set<String> localServerFeatures = new CopyOnWriteArraySet<String>(); private Set<String> localServerFeatures = new CopyOnWriteArraySet<String>();
private Cache<String, Set<NodeID>> serverFeatures; private Cache<String, Set<NodeID>> serverFeatures;
...@@ -71,7 +74,8 @@ public class IQDiscoInfoHandler extends IQHandler implements ClusterEventListene ...@@ -71,7 +74,8 @@ public class IQDiscoInfoHandler extends IQHandler implements ClusterEventListene
public IQDiscoInfoHandler() { public IQDiscoInfoHandler() {
super("XMPP Disco Info Handler"); super("XMPP Disco Info Handler");
info = new IQHandlerInfo("query", "http://jabber.org/protocol/disco#info"); info = new IQHandlerInfo("query", NAMESPACE_DISCO_INFO);
addServerFeature(NAMESPACE_DISCO_INFO);
// Initialize the user identity and features collections (optimization to avoid creating // Initialize the user identity and features collections (optimization to avoid creating
// the same objects for each response) // the same objects for each response)
Element userIdentity = DocumentHelper.createElement("identity"); Element userIdentity = DocumentHelper.createElement("identity");
...@@ -82,7 +86,7 @@ public class IQDiscoInfoHandler extends IQHandler implements ClusterEventListene ...@@ -82,7 +86,7 @@ public class IQDiscoInfoHandler extends IQHandler implements ClusterEventListene
userIdentity.addAttribute("category", "account"); userIdentity.addAttribute("category", "account");
userIdentity.addAttribute("type", "registered"); userIdentity.addAttribute("type", "registered");
registeredUserIdentities.add(userIdentity); registeredUserIdentities.add(userIdentity);
userFeatures.add("http://jabber.org/protocol/disco#info"); userFeatures.add(NAMESPACE_DISCO_INFO);
} }
public IQHandlerInfo getInfo() { public IQHandlerInfo getInfo() {
...@@ -91,16 +95,18 @@ public class IQDiscoInfoHandler extends IQHandler implements ClusterEventListene ...@@ -91,16 +95,18 @@ public class IQDiscoInfoHandler extends IQHandler implements ClusterEventListene
public IQ handleIQ(IQ packet) { public IQ handleIQ(IQ packet) {
// Create a copy of the sent pack that will be used as the reply // Create a copy of the sent pack that will be used as the reply
// we only need to add the requested info to the reply if any otherwise add // we only need to add the requested info to the reply if any otherwise add
// a not found error // a not found error
IQ reply = IQ.createResultIQ(packet); IQ reply = IQ.createResultIQ(packet);
// Look for a DiscoInfoProvider associated with the requested entity. // Look for a DiscoInfoProvider associated with the requested entity.
// We consider the host of the recipient JID of the packet as the entity. It's the // We consider the host of the recipient JID of the packet as the entity. It's the
// DiscoInfoProvider responsibility to provide information about the JID's name together // DiscoInfoProvider responsibility to provide information about the JID's name together
// with any possible requested node. // with any possible requested node.
DiscoInfoProvider infoProvider = getProvider(packet.getTo() == null ? final String toAddress = packet.getTo() == null ? XMPPServer
XMPPServer.getInstance().getServerInfo().getName() : packet.getTo().getDomain()); .getInstance().getServerInfo().getName() : packet.getTo()
.getDomain();
DiscoInfoProvider infoProvider = getProvider(toAddress);
if (infoProvider != null) { if (infoProvider != null) {
// Get the JID's name // Get the JID's name
String name = packet.getTo() == null ? null : packet.getTo().getNode(); String name = packet.getTo() == null ? null : packet.getTo().getNode();
...@@ -119,18 +125,50 @@ public class IQDiscoInfoHandler extends IQHandler implements ClusterEventListene ...@@ -119,18 +125,50 @@ public class IQDiscoInfoHandler extends IQHandler implements ClusterEventListene
// Add to the reply all the identities provided by the DiscoInfoProvider // Add to the reply all the identities provided by the DiscoInfoProvider
Element identity; Element identity;
Iterator identities = infoProvider.getIdentities(name, node, packet.getFrom()); Iterator<Element> identities = infoProvider.getIdentities(name, node,
packet.getFrom());
while (identities.hasNext()) { while (identities.hasNext()) {
identity = (Element)identities.next(); identity = identities.next();
identity.setQName(new QName(identity.getName(), queryElement.getNamespace())); identity.setQName(new QName(identity.getName(), queryElement.getNamespace()));
queryElement.add((Element)identity.clone()); queryElement.add((Element)identity.clone());
} }
// Add to the reply all the features provided by the DiscoInfoProvider // Add to the reply all the features provided by the DiscoInfoProvider
Iterator features = infoProvider.getFeatures(name, node, packet.getFrom()); Iterator<String> features = infoProvider.getFeatures(name, node, packet.getFrom());
boolean hasDiscoInfoFeature = false;
boolean hasDiscoItemsFeature = false;
boolean hasResultSetManagementFeature = false;
while (features.hasNext()) { while (features.hasNext()) {
queryElement.addElement("feature").addAttribute("var", (String)features.next()); final String feature = features.next();
} queryElement.addElement("feature").addAttribute("var", feature);
if (feature.equals(NAMESPACE_DISCO_INFO)) {
hasDiscoInfoFeature = true;
} else if (feature.equals("http://jabber.org/protocol/disco#items")) {
hasDiscoItemsFeature = true;
} else if (feature.equals(ResultSet.NAMESPACE_RESULT_SET_MANAGEMENT)) {
hasResultSetManagementFeature = true;
}
}
if (hasDiscoItemsFeature && !hasResultSetManagementFeature) {
// IQDiscoItemsHandler provides result set management
// support.
queryElement.addElement("feature").addAttribute("var",
ResultSet.NAMESPACE_RESULT_SET_MANAGEMENT);
}
if (!hasDiscoInfoFeature) {
// XEP-0030 requires that every entity that supports service
// discovery broadcasts the disco#info feature.
Log.info("InfoProvider for '" + toAddress
+ "' does not return the disco#info feature. "
+ "Openfire will add the feature on the entities behalf, "
+ "but the entities response to Service Discovery request should be modified.");
queryElement.addElement("feature").addAttribute("var",
NAMESPACE_DISCO_INFO);
}
// Add to the reply the extended info (XDataForm) provided by the DiscoInfoProvider // Add to the reply the extended info (XDataForm) provided by the DiscoInfoProvider
XDataFormImpl dataForm = infoProvider.getExtendedInfo(name, node, packet.getFrom()); XDataFormImpl dataForm = infoProvider.getExtendedInfo(name, node, packet.getFrom());
...@@ -139,7 +177,7 @@ public class IQDiscoInfoHandler extends IQHandler implements ClusterEventListene ...@@ -139,7 +177,7 @@ public class IQDiscoInfoHandler extends IQHandler implements ClusterEventListene
} }
} }
else { else {
// If the DiscoInfoProvider has no information for the requested name and node // If the DiscoInfoProvider has no information for the requested name and node
// then answer a not found error // then answer a not found error
reply.setChildElement(packet.getChildElement().createCopy()); reply.setChildElement(packet.getChildElement().createCopy());
reply.setError(PacketError.Condition.item_not_found); reply.setError(PacketError.Condition.item_not_found);
...@@ -440,4 +478,4 @@ public class IQDiscoInfoHandler extends IQHandler implements ClusterEventListene ...@@ -440,4 +478,4 @@ public class IQDiscoInfoHandler extends IQHandler implements ClusterEventListene
} }
}; };
} }
} }
\ No newline at end of file
...@@ -11,12 +11,26 @@ ...@@ -11,12 +11,26 @@
package org.jivesoftware.openfire.filetransfer.proxy; package org.jivesoftware.openfire.filetransfer.proxy;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.dom4j.DocumentHelper; import org.dom4j.DocumentHelper;
import org.dom4j.Element; import org.dom4j.Element;
import org.jivesoftware.openfire.*; import org.jivesoftware.openfire.IQHandlerInfo;
import org.jivesoftware.openfire.PacketException;
import org.jivesoftware.openfire.PacketRouter;
import org.jivesoftware.openfire.RoutableChannelHandler;
import org.jivesoftware.openfire.RoutingTable;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.auth.UnauthorizedException; import org.jivesoftware.openfire.auth.UnauthorizedException;
import org.jivesoftware.openfire.container.BasicModule; import org.jivesoftware.openfire.container.BasicModule;
import org.jivesoftware.openfire.disco.DiscoInfoProvider; import org.jivesoftware.openfire.disco.DiscoInfoProvider;
import org.jivesoftware.openfire.disco.DiscoItem;
import org.jivesoftware.openfire.disco.DiscoItemsProvider; import org.jivesoftware.openfire.disco.DiscoItemsProvider;
import org.jivesoftware.openfire.disco.DiscoServerItem; import org.jivesoftware.openfire.disco.DiscoServerItem;
import org.jivesoftware.openfire.disco.ServerItemsProvider; import org.jivesoftware.openfire.disco.ServerItemsProvider;
...@@ -31,10 +45,6 @@ import org.xmpp.packet.JID; ...@@ -31,10 +45,6 @@ import org.xmpp.packet.JID;
import org.xmpp.packet.Packet; import org.xmpp.packet.Packet;
import org.xmpp.packet.PacketError; import org.xmpp.packet.PacketError;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;
/** /**
* Manages the transfering of files between two remote entities on the jabber network. * Manages the transfering of files between two remote entities on the jabber network.
* This class acts independtly as a Jabber component from the rest of the server, according to * This class acts independtly as a Jabber component from the rest of the server, according to
...@@ -284,31 +294,8 @@ public class FileTransferProxy extends BasicModule ...@@ -284,31 +294,8 @@ public class FileTransferProxy extends BasicModule
return items.iterator(); return items.iterator();
} }
items.add(new DiscoServerItem() { items.add(new DiscoServerItem(new JID(getServiceDomain()),
public String getJID() { "Socks 5 Bytestreams Proxy", null, null, this, this));
return getServiceDomain();
}
public String getName() {
return "Socks 5 Bytestreams Proxy";
}
public String getAction() {
return null;
}
public String getNode() {
return null;
}
public DiscoInfoProvider getDiscoInfoProvider() {
return FileTransferProxy.this;
}
public DiscoItemsProvider getDiscoItemsProvider() {
return FileTransferProxy.this;
}
});
return items.iterator(); return items.iterator();
} }
...@@ -338,9 +325,9 @@ public class FileTransferProxy extends BasicModule ...@@ -338,9 +325,9 @@ public class FileTransferProxy extends BasicModule
return true; return true;
} }
public Iterator<Element> getItems(String name, String node, JID senderJID) { public Iterator<DiscoItem> getItems(String name, String node, JID senderJID) {
// A proxy server has no items // A proxy server has no items
return new ArrayList<Element>().iterator(); return new ArrayList<DiscoItem>().iterator();
} }
public void process(Packet packet) throws UnauthorizedException, PacketException { public void process(Packet packet) throws UnauthorizedException, PacketException {
......
...@@ -159,19 +159,15 @@ public class IQOfflineMessagesHandler extends IQHandler implements ServerFeature ...@@ -159,19 +159,15 @@ public class IQOfflineMessagesHandler extends IQHandler implements ServerFeature
return NAMESPACE.equals(node) && userManager.isRegisteredUser(senderJID.getNode()); return NAMESPACE.equals(node) && userManager.isRegisteredUser(senderJID.getNode());
} }
public Iterator<Element> getItems(String name, String node, JID senderJID) { public Iterator<DiscoItem> getItems(String name, String node, JID senderJID) {
// Mark that offline messages shouldn't be sent when the user becomes available // Mark that offline messages shouldn't be sent when the user becomes available
stopOfflineFlooding(senderJID); stopOfflineFlooding(senderJID);
List<Element> answer = new ArrayList<Element>(); List<DiscoItem> answer = new ArrayList<DiscoItem>();
Element item;
for (OfflineMessage offlineMessage : messageStore.getMessages(senderJID.getNode(), false)) { for (OfflineMessage offlineMessage : messageStore.getMessages(senderJID.getNode(), false)) {
item = DocumentHelper.createElement("item"); final DiscoItem item;
item.addAttribute("jid", senderJID.toBareJID());
item.addAttribute("name", offlineMessage.getFrom().toString());
synchronized (dateFormat) { synchronized (dateFormat) {
item.addAttribute("node", dateFormat.format(offlineMessage.getCreationDate())); item = new DiscoItem(senderJID, offlineMessage.getFrom().toString(), dateFormat.format(offlineMessage.getCreationDate()), null);
} }
answer.add(item); answer.add(item);
} }
......
...@@ -17,6 +17,7 @@ import org.jivesoftware.openfire.*; ...@@ -17,6 +17,7 @@ import org.jivesoftware.openfire.*;
import org.jivesoftware.openfire.auth.UnauthorizedException; import org.jivesoftware.openfire.auth.UnauthorizedException;
import org.jivesoftware.openfire.container.BasicModule; import org.jivesoftware.openfire.container.BasicModule;
import org.jivesoftware.openfire.disco.DiscoInfoProvider; import org.jivesoftware.openfire.disco.DiscoInfoProvider;
import org.jivesoftware.openfire.disco.DiscoItem;
import org.jivesoftware.openfire.disco.DiscoItemsProvider; import org.jivesoftware.openfire.disco.DiscoItemsProvider;
import org.jivesoftware.openfire.disco.DiscoServerItem; import org.jivesoftware.openfire.disco.DiscoServerItem;
import org.jivesoftware.openfire.disco.ServerItemsProvider; import org.jivesoftware.openfire.disco.ServerItemsProvider;
...@@ -98,14 +99,14 @@ public class MediaProxyService extends BasicModule ...@@ -98,14 +99,14 @@ public class MediaProxyService extends BasicModule
XMPPServer.getInstance().getIQDiscoItemsHandler().addServerItemsProvider(this); XMPPServer.getInstance().getIQDiscoItemsHandler().addServerItemsProvider(this);
} else { } else {
if (echo != null) echo.cancel(); if (echo != null) echo.cancel();
XMPPServer.getInstance().getIQDiscoItemsHandler().removeComponentItem(getAddress().toString()); XMPPServer.getInstance().getIQDiscoItemsHandler().removeComponentItem(getAddress());
} }
} }
public void stop() { public void stop() {
super.stop(); super.stop();
mediaProxy.stopProxy(); mediaProxy.stopProxy();
XMPPServer.getInstance().getIQDiscoItemsHandler().removeComponentItem(getAddress().toString()); XMPPServer.getInstance().getIQDiscoItemsHandler().removeComponentItem(getAddress());
routingTable.removeComponentRoute(getAddress()); routingTable.removeComponentRoute(getAddress());
if (echo != null) echo.cancel(); if (echo != null) echo.cancel();
} }
...@@ -117,9 +118,9 @@ public class MediaProxyService extends BasicModule ...@@ -117,9 +118,9 @@ public class MediaProxyService extends BasicModule
return serviceName; return serviceName;
} }
public Iterator<Element> getItems(String name, String node, JID senderJID) { public Iterator<DiscoItem> getItems(String name, String node, JID senderJID) {
// A proxy server has no items // A proxy server has no items
return new ArrayList<Element>().iterator(); return new ArrayList<DiscoItem>().iterator();
} }
public void process(Packet packet) throws UnauthorizedException, PacketException { public void process(Packet packet) throws UnauthorizedException, PacketException {
...@@ -293,31 +294,8 @@ public class MediaProxyService extends BasicModule ...@@ -293,31 +294,8 @@ public class MediaProxyService extends BasicModule
return items.iterator(); return items.iterator();
} }
items.add(new DiscoServerItem() { final DiscoServerItem item = new DiscoServerItem(new JID(getServiceDomain()), "Media Proxy Service", null, null, this, this);
public String getJID() { items.add(item);
return getServiceDomain();
}
public String getName() {
return "Media Proxy Service";
}
public String getAction() {
return null;
}
public String getNode() {
return null;
}
public DiscoInfoProvider getDiscoInfoProvider() {
return MediaProxyService.this;
}
public DiscoItemsProvider getDiscoItemsProvider() {
return MediaProxyService.this;
}
});
return items.iterator(); return items.iterator();
} }
......
...@@ -19,6 +19,7 @@ import org.jivesoftware.openfire.cluster.ClusterEventListener; ...@@ -19,6 +19,7 @@ import org.jivesoftware.openfire.cluster.ClusterEventListener;
import org.jivesoftware.openfire.cluster.ClusterManager; import org.jivesoftware.openfire.cluster.ClusterManager;
import org.jivesoftware.openfire.container.BasicModule; import org.jivesoftware.openfire.container.BasicModule;
import org.jivesoftware.openfire.disco.DiscoInfoProvider; import org.jivesoftware.openfire.disco.DiscoInfoProvider;
import org.jivesoftware.openfire.disco.DiscoItem;
import org.jivesoftware.openfire.disco.DiscoItemsProvider; import org.jivesoftware.openfire.disco.DiscoItemsProvider;
import org.jivesoftware.openfire.disco.DiscoServerItem; import org.jivesoftware.openfire.disco.DiscoServerItem;
import org.jivesoftware.openfire.disco.ServerItemsProvider; import org.jivesoftware.openfire.disco.ServerItemsProvider;
...@@ -960,37 +961,19 @@ public class MultiUserChatServerImpl extends BasicModule implements MultiUserCha ...@@ -960,37 +961,19 @@ public class MultiUserChatServerImpl extends BasicModule implements MultiUserCha
return null; return null;
} }
ArrayList<DiscoServerItem> items = new ArrayList<DiscoServerItem>(); ArrayList<DiscoServerItem> items = new ArrayList<DiscoServerItem>();
items.add(new DiscoServerItem() { final String name;
public String getJID() { // Check if there is a system property that overrides the default value
return getServiceDomain(); String serviceName = JiveGlobals.getProperty("muc.service-name");
} if (serviceName != null && serviceName.trim().length() > 0) {
name = serviceName;
public String getName() { }
// Check if there is a system property that overrides the default value else {
String serviceName = JiveGlobals.getProperty("muc.service-name"); // Return the default service name based on the current locale
if (serviceName != null && serviceName.trim().length() > 0) { name = LocaleUtils.getLocalizedString("muc.service-name");
return serviceName; }
}
// Return the default service name based on the current locale
return LocaleUtils.getLocalizedString("muc.service-name");
}
public String getAction() {
return null;
}
public String getNode() {
return null;
}
public DiscoInfoProvider getDiscoInfoProvider() {
return MultiUserChatServerImpl.this;
}
public DiscoItemsProvider getDiscoItemsProvider() { final DiscoServerItem item = new DiscoServerItem(new JID(getServiceDomain()), name, null, null, this, this);
return MultiUserChatServerImpl.this; items.add(item);
}
});
return items.iterator(); return items.iterator();
} }
...@@ -1150,22 +1133,17 @@ public class MultiUserChatServerImpl extends BasicModule implements MultiUserCha ...@@ -1150,22 +1133,17 @@ public class MultiUserChatServerImpl extends BasicModule implements MultiUserCha
return false; return false;
} }
public Iterator<Element> getItems(String name, String node, JID senderJID) { public Iterator<DiscoItem> getItems(String name, String node, JID senderJID) {
// Check if the service is disabled. Info is not available when disabled. // Check if the service is disabled. Info is not available when disabled.
if (!isServiceEnabled()) { if (!isServiceEnabled()) {
return null; return null;
} }
List<Element> answer = new ArrayList<Element>(); List<DiscoItem> answer = new ArrayList<DiscoItem>();
if (name == null && node == null) { if (name == null && node == null) {
Element item;
// Answer all the public rooms as items // Answer all the public rooms as items
for (MUCRoom room : rooms.values()) { for (MUCRoom room : rooms.values()) {
if (canDiscoverRoom(room)) { if (canDiscoverRoom(room)) {
item = DocumentHelper.createElement("item"); answer.add(new DiscoItem(room.getRole().getRoleAddress(), room.getNaturalLanguageName(), null, null));
item.addAttribute("jid", room.getRole().getRoleAddress().toString());
item.addAttribute("name", room.getNaturalLanguageName());
answer.add(item);
} }
} }
} }
...@@ -1173,13 +1151,9 @@ public class MultiUserChatServerImpl extends BasicModule implements MultiUserCha ...@@ -1173,13 +1151,9 @@ public class MultiUserChatServerImpl extends BasicModule implements MultiUserCha
// Answer the room occupants as items if that info is publicly available // Answer the room occupants as items if that info is publicly available
MUCRoom room = getChatRoom(name); MUCRoom room = getChatRoom(name);
if (room != null && canDiscoverRoom(room)) { if (room != null && canDiscoverRoom(room)) {
Element item;
for (MUCRole role : room.getOccupants()) { for (MUCRole role : room.getOccupants()) {
// TODO Should we filter occupants that are invisible (presence is not broadcasted)? // TODO Should we filter occupants that are invisible (presence is not broadcasted)?
item = DocumentHelper.createElement("item"); answer.add(new DiscoItem(role.getRoleAddress(), null, null, null));
item.addAttribute("jid", role.getRoleAddress().toString());
answer.add(item);
} }
} }
} }
...@@ -1379,4 +1353,4 @@ public class MultiUserChatServerImpl extends BasicModule implements MultiUserCha ...@@ -1379,4 +1353,4 @@ public class MultiUserChatServerImpl extends BasicModule implements MultiUserCha
}; };
StatisticsManager.getInstance().addMultiStatistic(outgoingStatKey, trafficStatGroup, statistic); StatisticsManager.getInstance().addMultiStatistic(outgoingStatKey, trafficStatGroup, statistic);
} }
} }
\ No newline at end of file
...@@ -22,6 +22,7 @@ import org.jivesoftware.openfire.cluster.ClusterManager; ...@@ -22,6 +22,7 @@ import org.jivesoftware.openfire.cluster.ClusterManager;
import org.jivesoftware.openfire.component.InternalComponentManager; import org.jivesoftware.openfire.component.InternalComponentManager;
import org.jivesoftware.openfire.container.BasicModule; import org.jivesoftware.openfire.container.BasicModule;
import org.jivesoftware.openfire.disco.DiscoInfoProvider; import org.jivesoftware.openfire.disco.DiscoInfoProvider;
import org.jivesoftware.openfire.disco.DiscoItem;
import org.jivesoftware.openfire.disco.DiscoItemsProvider; import org.jivesoftware.openfire.disco.DiscoItemsProvider;
import org.jivesoftware.openfire.disco.DiscoServerItem; import org.jivesoftware.openfire.disco.DiscoServerItem;
import org.jivesoftware.openfire.disco.ServerItemsProvider; import org.jivesoftware.openfire.disco.ServerItemsProvider;
...@@ -468,32 +469,8 @@ public class PubSubModule extends BasicModule implements ServerItemsProvider, Di ...@@ -468,32 +469,8 @@ public class PubSubModule extends BasicModule implements ServerItemsProvider, Di
public Iterator<DiscoServerItem> getItems() { public Iterator<DiscoServerItem> getItems() {
ArrayList<DiscoServerItem> items = new ArrayList<DiscoServerItem>(); ArrayList<DiscoServerItem> items = new ArrayList<DiscoServerItem>();
final DiscoServerItem item = new DiscoServerItem(new JID(getServiceDomain()), "Publish-Subscribe service", null, null, this, this);
items.add(new DiscoServerItem() { items.add(item);
public String getJID() {
return getServiceDomain();
}
public String getName() {
return "Publish-Subscribe service";
}
public String getAction() {
return null;
}
public String getNode() {
return null;
}
public DiscoInfoProvider getDiscoInfoProvider() {
return PubSubModule.this;
}
public DiscoItemsProvider getDiscoItemsProvider() {
return PubSubModule.this;
}
});
return items.iterator(); return items.iterator();
} }
...@@ -636,18 +613,14 @@ public class PubSubModule extends BasicModule implements ServerItemsProvider, Di ...@@ -636,18 +613,14 @@ public class PubSubModule extends BasicModule implements ServerItemsProvider, Di
return false; return false;
} }
public Iterator<Element> getItems(String name, String node, JID senderJID) { public Iterator<DiscoItem> getItems(String name, String node, JID senderJID) {
List<Element> answer = new ArrayList<Element>(); List<DiscoItem> answer = new ArrayList<DiscoItem>();
String serviceDomain = getServiceDomain(); String serviceDomain = getServiceDomain();
if (name == null && node == null) { if (name == null && node == null) {
Element item;
// Answer all first level nodes // Answer all first level nodes
for (Node pubNode : rootCollectionNode.getNodes()) { for (Node pubNode : rootCollectionNode.getNodes()) {
if (canDiscoverNode(pubNode)) { if (canDiscoverNode(pubNode)) {
item = DocumentHelper.createElement("item"); final DiscoItem item = new DiscoItem(new JID(serviceDomain), pubNode.getName(), pubNode.getNodeID(), null);
item.addAttribute("jid", serviceDomain);
item.addAttribute("node", pubNode.getNodeID());
item.addAttribute("name", pubNode.getName());
answer.add(item); answer.add(item);
} }
} }
...@@ -656,25 +629,18 @@ public class PubSubModule extends BasicModule implements ServerItemsProvider, Di ...@@ -656,25 +629,18 @@ public class PubSubModule extends BasicModule implements ServerItemsProvider, Di
Node pubNode = getNode(node); Node pubNode = getNode(node);
if (pubNode != null && canDiscoverNode(pubNode)) { if (pubNode != null && canDiscoverNode(pubNode)) {
if (pubNode.isCollectionNode()) { if (pubNode.isCollectionNode()) {
Element item;
// Answer all nested nodes as items // Answer all nested nodes as items
for (Node nestedNode : pubNode.getNodes()) { for (Node nestedNode : pubNode.getNodes()) {
if (canDiscoverNode(nestedNode)) { if (canDiscoverNode(nestedNode)) {
item = DocumentHelper.createElement("item"); final DiscoItem item = new DiscoItem(new JID(serviceDomain), nestedNode.getName(), nestedNode.getNodeID(), null);
item.addAttribute("jid", serviceDomain);
item.addAttribute("node", nestedNode.getNodeID());
item.addAttribute("name", nestedNode.getName());
answer.add(item); answer.add(item);
} }
} }
} }
else { else {
// This is a leaf node so answer the published items which exist on the service // This is a leaf node so answer the published items which exist on the service
Element item;
for (PublishedItem publishedItem : pubNode.getPublishedItems()) { for (PublishedItem publishedItem : pubNode.getPublishedItems()) {
item = DocumentHelper.createElement("item"); final DiscoItem item = new DiscoItem(new JID(serviceDomain), publishedItem.getID(), null, null);
item.addAttribute("jid", serviceDomain);
item.addAttribute("name", publishedItem.getID());
answer.add(item); answer.add(item);
} }
} }
......
...@@ -20,6 +20,7 @@ import org.jivesoftware.util.Log; ...@@ -20,6 +20,7 @@ import org.jivesoftware.util.Log;
import org.jivesoftware.util.cache.CacheSizes; import org.jivesoftware.util.cache.CacheSizes;
import org.jivesoftware.util.cache.Cacheable; import org.jivesoftware.util.cache.Cacheable;
import org.jivesoftware.util.cache.ExternalizableUtil; import org.jivesoftware.util.cache.ExternalizableUtil;
import org.jivesoftware.util.rsm.Result;
import java.io.Externalizable; import java.io.Externalizable;
import java.io.IOException; import java.io.IOException;
...@@ -42,7 +43,7 @@ import java.util.concurrent.ConcurrentHashMap; ...@@ -42,7 +43,7 @@ import java.util.concurrent.ConcurrentHashMap;
* *
* @author Matt Tucker * @author Matt Tucker
*/ */
public class User implements Cacheable, Externalizable { public class User implements Cacheable, Externalizable, Result {
private static final String LOAD_PROPERTIES = private static final String LOAD_PROPERTIES =
"SELECT name, propValue FROM jiveUserProp WHERE username=?"; "SELECT name, propValue FROM jiveUserProp WHERE username=?";
...@@ -529,4 +530,8 @@ public class User implements Cacheable, Externalizable { ...@@ -529,4 +530,8 @@ public class User implements Cacheable, Externalizable {
creationDate = new Date(ExternalizableUtil.getInstance().readLong(in)); creationDate = new Date(ExternalizableUtil.getInstance().readLong(in));
modificationDate = new Date(ExternalizableUtil.getInstance().readLong(in)); modificationDate = new Date(ExternalizableUtil.getInstance().readLong(in));
} }
}
\ No newline at end of file public String getUID() {
return username;
}
}
package org.jivesoftware.util.rsm;
/**
* Elements from a result set as defined by XEP-0059 have certain
* characteristics. This interface defines these characteristics.
*
* Applying this interface to a class will allow you to use ResultSet operations
* on collections of your class. In other words: you are making collections of
* your class managable/navigable.
*
* @author Guus der Kinderen, guus@nimbuzz.com
* @see http://www.xmpp.org/extensions/xep-0059.html
*/
public interface Result {
/**
* Returns a unique identifier for this Result. Each element in a ResultSet
* must have a distinct UIDs.
*
* XEP-0059 says: <quote>(...) the UIDs are
* unique in the context of all possible members of the full result set.
* Each UID MAY be based on part of the content of its associated item (...)
* or on an internal table index. Another possible method is to serialize
* the XML of the item and then hash it to generate the UID. Note: The
* requesting entity MUST treat all UIDs as opaque.</quote>
*
* @return Unique ID of the Result
*/
public String getUID();
}
\ No newline at end of file
This diff is collapsed.
package org.jivesoftware.util.rsm;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
/**
* A result set representation as described in XEP-0059. Note that this result
* 'set' actually makes use of a List implementations, as the Java Set
* definition disallows duplicate elements, while the List definition supplies
* most of the required indexing operations.
*
* This ResultSet implementation loads all all results from the set into memory,
* which might be undesirable for very large sets, or for sets where the
* retrieval of a result is an expensive operation. sets.
*
* As most methods are backed by the {@link List#subList(int, int)} method,
* non-structural changes in the returned lists are reflected in the ResultSet,
* and vice-versa.
*
* @author Guus der Kinderen, guus@nimbuzz.com
*
* @param <E>
* Each result set should be a collection of instances of the exact
* same class. This class must implement the {@link Result}
* interface.
* @see java.util.List#subList(int, int)
*
*/
/*
* TODO: do we want changes to the returned Lists of methods in this class be
* applied to the content of the ResultSet itself? Currently, because of the
* usage of java.util.List#subList(int, int), it does. I'm thinking a
* immodifiable solution would cause less problems. -Guus
*/
public class ResultSetImpl<E extends Result> extends ResultSet<E> {
/**
* A list of all results in this ResultSet
*/
public final List<E> resultList;
/**
* A mapping of the UIDs of all results in resultList, to the index of those
* entries in that list.
*/
public final Map<String, Integer> uidToIndex;
/**
* Creates a new Result Set instance, based on a collection of Result
* implementing objects. The collection should contain elements of the exact
* same class only, and cannot contain 'null' elements.
*
* The order that's being used in the new ResultSet instance is the same
* order in which {@link Collection#iterator()} iterates over the
* collection.
*
* Note that this constructor throws an IllegalArgumentException if the
* Collection that is provided contains Results that have duplicate UIDs.
*
* @param results
* The collection of Results that make up this result set.
*/
public ResultSetImpl(Collection<E> results) {
this(results, null);
}
/**
* Creates a new Result Set instance, based on a collection of Result
* implementing objects. The collection should contain elements of the exact
* same class only, and cannot contain 'null' elements.
*
* The order that's being used in the new ResultSet instance is defined by
* the supplied Comparator class.
*
* Note that this constructor throws an IllegalArgumentException if the
* Collection that is provided contains Results that have duplicate UIDs.
*
* @param results
* The collection of Results that make up this result set.
* @param comparator
* The Comparator that defines the order of the Results in this
* result set.
*/
public ResultSetImpl(Collection<E> results, Comparator<E> comparator) {
if (results == null) {
throw new NullPointerException("Argument 'results' cannot be null.");
}
final int size = results.size();
resultList = new ArrayList<E>(size);
uidToIndex = new Hashtable<String, Integer>(size);
// sort the collection, if need be.
List<E> sortedResults = null;
if (comparator != null) {
sortedResults = new ArrayList<E>(results);
Collections.sort(sortedResults, comparator);
}
int index = 0;
// iterate over either the sorted or unsorted collection
for (final E result : (sortedResults != null ? sortedResults : results)) {
if (result == null) {
throw new NullPointerException(
"The result set must not contain 'null' elements.");
}
final String uid = result.getUID();
if (uidToIndex.containsKey(uid)) {
throw new IllegalArgumentException(
"The result set can not contain elements that have the same UID.");
}
resultList.add(result);
uidToIndex.put(uid, Integer.valueOf(index));
index++;
}
}
/*
* (non-Javadoc)
*
* @see com.buzzaa.xmpp.resultsetmanager.ResultSet#size()
*/
@Override
public int size() {
return resultList.size();
}
/*
* (non-Javadoc)
*
* @see com.buzzaa.xmpp.resultsetmanager.ResultSet#getAfter(E, int)
*/
@Override
public List<E> getAfter(String uid, int maxAmount) {
if (uid == null || uid.length() == 0) {
throw new NullPointerException("Argument 'uid' cannot be null or an empty String.");
}
if (maxAmount < 1) {
throw new IllegalArgumentException(
"Argument 'maxAmount' must be a integer higher than zero.");
}
// the result of this method is exclusive 'result'
final int index = uidToIndex.get(uid) + 1;
return get(index, maxAmount);
}
/*
* (non-Javadoc)
*
* @see com.buzzaa.xmpp.resultsetmanager.ResultSet#getBefore(E, int)
*/
@Override
public List<E> getBefore(String uid, int maxAmount) {
if (uid == null || uid.length() == 0) {
throw new NullPointerException("Argument 'uid' cannot be null or an empty String.");
}
if (maxAmount < 1) {
throw new IllegalArgumentException(
"Argument 'maxAmount' must be a integer higher than zero.");
}
// the result of this method is exclusive 'result'
final int indexOfLastElement = uidToIndex.get(uid);
final int indexOfFirstElement = indexOfLastElement - maxAmount;
if (indexOfFirstElement < 0) {
return get(0, indexOfLastElement);
}
return get(indexOfFirstElement, maxAmount);
}
/*
* (non-Javadoc)
*
* @see com.buzzaa.xmpp.resultsetmanager.ResultSet#get(int)
*/
@Override
public E get(int index) {
return resultList.get(index);
}
/*
* (non-Javadoc)
*
* @see com.buzzaa.xmpp.resultsetmanager.ResultSet#getFirst(int)
*/
@Override
public List<E> getFirst(int maxAmount) {
if (maxAmount < 1) {
throw new IllegalArgumentException(
"Argument 'maxAmount' must be a integer higher than zero.");
}
return get(0, maxAmount);
}
/*
* (non-Javadoc)
*
* @see com.buzzaa.xmpp.resultsetmanager.ResultSet#getLast(int)
*/
@Override
public List<E> getLast(int maxAmount) {
if (maxAmount < 1) {
throw new IllegalArgumentException(
"Argument 'maxAmount' must be a integer higher than zero.");
}
final int indexOfFirstElement = size() - maxAmount;
if (indexOfFirstElement < 0) {
return get(0, maxAmount);
}
return get(indexOfFirstElement, maxAmount);
}
/*
* (non-Javadoc)
*
* @see com.buzzaa.xmpp.resultsetmanager.ResultSet#get(int, int)
*/
@Override
public List<E> get(int fromIndex, int maxAmount) {
if (fromIndex < 0) {
throw new IllegalArgumentException(
"Argument 'fromIndex' must be zero or higher.");
}
if (maxAmount < 1) {
throw new IllegalArgumentException(
"Argument 'maxAmount' must be a integer higher than zero.");
}
if (fromIndex >= size()) {
return new ArrayList<E>(0);
}
// calculate the last index to return, or return up to the end of last
// index if 'amount' surpasses the list length.
final int absoluteTo = fromIndex + maxAmount;
final int toIndex = (absoluteTo > size() ? size() : absoluteTo);
return resultList.subList(fromIndex, toIndex);
}
/*
* (non-Javadoc)
* @see org.jivesoftware.util.resultsetmanager.ResultSet#indexOf(java.lang.String)
*/
@Override
public int indexOf(String uid) {
return uidToIndex.get(uid).intValue();
}
}
...@@ -44,6 +44,11 @@ ...@@ -44,6 +44,11 @@
Search Plugin Changelog Search Plugin Changelog
</h1> </h1>
<p><b>1.4.2</b> -- XXX</p>
<ul>
<li>Implemented XEP-0059 "Result Set Management".</li>
</ul>
<p><b>1.4.1</b> -- June 20, 2007</p> <p><b>1.4.1</b> -- June 20, 2007</p>
<ul> <ul>
<li>Unescape username before returning search results to the client.</li> <li>Unescape username before returning search results to the client.</li>
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
<version>1.4.1</version> <version>1.4.1</version>
<date>6/20/2007</date> <date>6/20/2007</date>
<minServerVersion>3.3.0</minServerVersion> <minServerVersion>3.3.0</minServerVersion>
<adminconsole> <adminconsole>
<tab id="tab-server"> <tab id="tab-server">
<sidebar id="sidebar-server-settings"> <sidebar id="sidebar-server-settings">
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment