1
0
Fork 0
mirror of https://github.com/vanitasvitae/Smack.git synced 2024-11-25 21:42:07 +01:00

Refactor PEP to use PubSub API.

Fixes SMACK-416.
This commit is contained in:
Florian Schmaus 2015-08-17 08:16:49 +02:00
parent 0d6f00873f
commit 33e5c37af8
17 changed files with 479 additions and 453 deletions

View file

@ -0,0 +1,89 @@
/**
*
* Copyright 2015 Florian Schmaus.
*
* 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.smack.filter.jidtype;
import org.jivesoftware.smack.filter.StanzaFilter;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.util.Objects;
import org.jxmpp.jid.Jid;
/**
* Base class for XMPP address type filters.
*
* @author Florian Schmaus
*
*/
public abstract class AbstractJidTypeFilter implements StanzaFilter {
private final JidType jidType;
protected AbstractJidTypeFilter(JidType jidType) {
this.jidType = Objects.requireNonNull(jidType, "jidType must not be null");
}
@Override
public boolean accept(Stanza stanza) {
Jid toMatch = getJidToMatchFrom(stanza);
if (toMatch == null) {
return false;
}
return jidType.isTypeOf(toMatch);
}
protected abstract Jid getJidToMatchFrom(Stanza stanza);
@Override
public final String toString() {
return getClass().getSimpleName() + ": " + jidType;
}
public enum JidType {
BareJid,
DomainBareJid,
DomainFullJid,
DomainJid,
EntityBareJid,
EntityFullJid,
EntityJid,
FullJid,
;
public boolean isTypeOf(Jid jid) {
if (jid == null) {
return false;
}
switch (this) {
case BareJid:
return jid.hasNoResource();
case DomainBareJid:
return jid.isDomainBareJid();
case DomainFullJid:
return jid.isDomainFullJid();
case EntityBareJid:
return jid.isEntityBareJid();
case EntityFullJid:
return jid.isEntityFullJid();
case EntityJid:
return jid.isEntityJid();
case FullJid:
return jid.hasResource();
default:
throw new IllegalStateException();
}
}
}
}

View file

@ -0,0 +1,39 @@
/**
*
* Copyright 2015 Florian Schmaus.
*
* 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.smack.filter.jidtype;
import org.jivesoftware.smack.packet.Stanza;
import org.jxmpp.jid.Jid;
/**
* Filter based on the 'from' XMPP address type.
*
* @author Florian Schmaus
*
*/
public class FromJidTypeFilter extends AbstractJidTypeFilter {
public FromJidTypeFilter(JidType jidType) {
super(jidType);
}
@Override
protected Jid getJidToMatchFrom(Stanza stanza) {
return stanza.getFrom();
}
}

View file

@ -16,6 +16,6 @@
*/ */
/** /**
* TODO describe me. * Filter based on the type of an XMPP address.
*/ */
package org.jivesoftware.smackx.pep.packet; package org.jivesoftware.smack.filter.jidtype;

View file

@ -0,0 +1,41 @@
/**
*
* Copyright 2015 Florian Schmaus
*
* 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.smackx.disco;
/**
* Utility class for Features.
*
* @author Florian Schmaus
*
*/
public class Feature {
public enum Support {
optional,
recommended,
required,
;
public boolean isRequired() {
return this == required;
}
public boolean isNotRequired() {
return !isRequired();
}
}
}

View file

@ -41,6 +41,8 @@ import org.jxmpp.util.cache.Cache;
import org.jxmpp.util.cache.ExpirationCache; import org.jxmpp.util.cache.ExpirationCache;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
@ -659,9 +661,20 @@ public final class ServiceDiscoveryManager extends Manager {
* @throws InterruptedException * @throws InterruptedException
* @since 4.1 * @since 4.1
*/ */
public boolean serverSupportsFeature(String feature) throws NoResponseException, XMPPErrorException, public boolean serverSupportsFeature(CharSequence feature) throws NoResponseException, XMPPErrorException,
NotConnectedException, InterruptedException { NotConnectedException, InterruptedException {
return supportsFeature(connection().getXMPPServiceDomain(), feature); return serverSupportsFeatures(feature);
}
public boolean serverSupportsFeatures(CharSequence... features) throws NoResponseException,
XMPPErrorException, NotConnectedException, InterruptedException {
return serverSupportsFeatures(Arrays.asList(features));
}
public boolean serverSupportsFeatures(Collection<? extends CharSequence> features)
throws NoResponseException, XMPPErrorException, NotConnectedException,
InterruptedException {
return supportsFeatures(connection().getXMPPServiceDomain(), features);
} }
/** /**
@ -675,9 +688,22 @@ public final class ServiceDiscoveryManager extends Manager {
* @throws NotConnectedException * @throws NotConnectedException
* @throws InterruptedException * @throws InterruptedException
*/ */
public boolean supportsFeature(Jid jid, String feature) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { public boolean supportsFeature(Jid jid, CharSequence feature) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
return supportsFeatures(jid, feature);
}
public boolean supportsFeatures(Jid jid, CharSequence... features) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
return supportsFeatures(jid, Arrays.asList(features));
}
public boolean supportsFeatures(Jid jid, Collection<? extends CharSequence> features) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
DiscoverInfo result = discoverInfo(jid); DiscoverInfo result = discoverInfo(jid);
return result.containsFeature(feature); for (CharSequence feature : features) {
if (!result.containsFeature(feature)) {
return false;
}
}
return true;
} }
/** /**

View file

@ -209,7 +209,7 @@ public class DiscoverInfo extends IQ implements TypedCloneable<DiscoverInfo> {
* @param feature the feature to check * @param feature the feature to check
* @return true if the requestes feature has been discovered * @return true if the requestes feature has been discovered
*/ */
public boolean containsFeature(String feature) { public boolean containsFeature(CharSequence feature) {
return features.contains(new Feature(feature)); return features.contains(new Feature(feature));
} }
@ -482,6 +482,10 @@ public class DiscoverInfo extends IQ implements TypedCloneable<DiscoverInfo> {
this.variable = feature.variable; this.variable = feature.variable;
} }
public Feature(CharSequence variable) {
this(variable.toString());
}
/** /**
* Creates a new feature offered by an XMPP entity or item. * Creates a new feature offered by an XMPP entity or item.
* *

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2003-2007 Jive Software. * Copyright 2003-2007 Jive Software, 2015 Florian Schmaus.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,8 +17,9 @@
package org.jivesoftware.smackx.pep; package org.jivesoftware.smackx.pep;
import org.jivesoftware.smackx.pep.packet.PEPEvent; import org.jivesoftware.smack.packet.Message;
import org.jxmpp.jid.Jid; import org.jivesoftware.smackx.pubsub.EventElement;
import org.jxmpp.jid.EntityBareJid;
/** /**
@ -26,6 +27,7 @@ import org.jxmpp.jid.Jid;
* A listener that is fired anytime a PEP event message is received. * A listener that is fired anytime a PEP event message is received.
* *
* @author Jeff Williams * @author Jeff Williams
* @author Florian Schmaus
*/ */
public interface PEPListener { public interface PEPListener {
@ -34,7 +36,8 @@ public interface PEPListener {
* *
* @param from the user that sent the entries. * @param from the user that sent the entries.
* @param event the event contained in the message. * @param event the event contained in the message.
* @param message the message stanza containing the PEP event.
*/ */
public void eventReceived(Jid from, PEPEvent event); public void eventReceived(EntityBareJid from, EventElement event, Message message);
} }

View file

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2003-2007 Jive Software. * Copyright 2003-2007 Jive Software, 2015 Florian Schmaus
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,21 +17,32 @@
package org.jivesoftware.smackx.pep; package org.jivesoftware.smackx.pep;
import java.util.ArrayList; import java.util.Map;
import java.util.List; import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import org.jivesoftware.smack.Manager;
import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.StanzaListener; import org.jivesoftware.smack.StanzaListener;
import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.filter.StanzaExtensionFilter; import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smack.filter.AndFilter;
import org.jivesoftware.smack.filter.StanzaFilter; import org.jivesoftware.smack.filter.StanzaFilter;
import org.jivesoftware.smack.filter.jidtype.FromJidTypeFilter;
import org.jivesoftware.smack.filter.jidtype.AbstractJidTypeFilter.JidType;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.packet.IQ.Type; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
import org.jivesoftware.smackx.pep.packet.PEPEvent; import org.jivesoftware.smackx.pubsub.EventElement;
import org.jivesoftware.smackx.pep.packet.PEPItem; import org.jivesoftware.smackx.pubsub.Item;
import org.jivesoftware.smackx.pep.packet.PEPPubSub; import org.jivesoftware.smackx.pubsub.LeafNode;
import org.jxmpp.jid.Jid; import org.jivesoftware.smackx.pubsub.PubSubFeature;
import org.jivesoftware.smackx.pubsub.PubSubManager;
import org.jivesoftware.smackx.pubsub.filter.EventExtensionFilter;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.EntityBareJid;
/** /**
* *
@ -44,38 +55,55 @@ import org.jxmpp.jid.Jid;
* <pre> * <pre>
* PEPManager pepManager = new PEPManager(smackConnection); * PEPManager pepManager = new PEPManager(smackConnection);
* pepManager.addPEPListener(new PEPListener() { * pepManager.addPEPListener(new PEPListener() {
* public void eventReceived(String inFrom, PEPEvent inEvent) { * public void eventReceived(EntityBareJid from, EventElement event, Message message) {
* LOGGER.debug("Event received: " + inEvent); * LOGGER.debug("Event received: " + event);
* } * }
* }); * });
*
* PEPProvider pepProvider = new PEPProvider();
* pepProvider.registerPEPParserExtension("http://jabber.org/protocol/tune", new TuneProvider());
* ProviderManager.getInstance().addExtensionProvider("event", "http://jabber.org/protocol/pubsub#event", pepProvider);
*
* Tune tune = new Tune("jeff", "1", "CD", "My Title", "My Track");
* pepManager.publish(tune);
* </pre> * </pre>
* *
* @author Jeff Williams * @author Jeff Williams
* @author Florian Schmaus
*/ */
public class PEPManager { public final class PEPManager extends Manager {
private List<PEPListener> pepListeners = new ArrayList<PEPListener>(); private static final Map<XMPPConnection, PEPManager> INSTANCES = new WeakHashMap<>();
private XMPPConnection connection; public static synchronized PEPManager getInstanceFor(XMPPConnection connection) {
PEPManager pepManager = INSTANCES.get(connection);
if (pepManager == null) {
pepManager = new PEPManager(connection);
INSTANCES.put(connection, pepManager);
}
return pepManager;
}
private StanzaFilter packetFilter = new StanzaExtensionFilter("event", "http://jabber.org/protocol/pubsub#event"); private static final StanzaFilter FROM_BARE_JID_WITH_EVENT_EXTENSION_FILTER = new AndFilter(
private StanzaListener packetListener; new FromJidTypeFilter(JidType.BareJid),
EventExtensionFilter.INSTANCE);
private final Set<PEPListener> pepListeners = new CopyOnWriteArraySet<>();
/** /**
* Creates a new PEP exchange manager. * Creates a new PEP exchange manager.
* *
* @param connection an XMPPConnection which is used to send and receive messages. * @param connection an XMPPConnection which is used to send and receive messages.
*/ */
public PEPManager(XMPPConnection connection) { private PEPManager(XMPPConnection connection) {
this.connection = connection; super(connection);
init(); StanzaListener packetListener = new StanzaListener() {
public void processPacket(Stanza stanza) {
Message message = (Message) stanza;
EventElement event = EventElement.from(stanza);
assert(event != null);
EntityBareJid from = message.getFrom().asEntityBareJidIfPossible();
assert(from != null);
for (PEPListener listener : pepListeners) {
listener.eventReceived(from, event, message);
}
}
};
// TODO Add filter to check if from supports PubSub as per xep163 2 2.4
connection.addSyncStanzaListener(packetListener, FROM_BARE_JID_WITH_EVENT_EXTENSION_FILTER);
} }
/** /**
@ -84,12 +112,8 @@ public class PEPManager {
* *
* @param pepListener a roster exchange listener. * @param pepListener a roster exchange listener.
*/ */
public void addPEPListener(PEPListener pepListener) { public boolean addPEPListener(PEPListener pepListener) {
synchronized (pepListeners) { return pepListeners.add(pepListener);
if (!pepListeners.contains(pepListener)) {
pepListeners.add(pepListener);
}
}
} }
/** /**
@ -97,63 +121,44 @@ public class PEPManager {
* *
* @param pepListener a roster exchange listener. * @param pepListener a roster exchange listener.
*/ */
public void removePEPListener(PEPListener pepListener) { public boolean removePEPListener(PEPListener pepListener) {
synchronized (pepListeners) { return pepListeners.remove(pepListener);
pepListeners.remove(pepListener);
}
} }
/** /**
* Publish an event. * Publish an event.
* *
* @param item the item to publish. * @param item the item to publish.
* @throws NotConnectedException * @param node the node to publish on.
* @throws InterruptedException * @throws NotConnectedException
* @throws InterruptedException
* @throws XMPPErrorException
* @throws NoResponseException
*/ */
public void publish(PEPItem item) throws NotConnectedException, InterruptedException { public void publish(Item item, String node) throws NotConnectedException, InterruptedException,
// Create a new message to publish the event. NoResponseException, XMPPErrorException {
PEPPubSub pubSub = new PEPPubSub(item); XMPPConnection connection = connection();
pubSub.setType(Type.set); PubSubManager pubSubManager = PubSubManager.getInstance(connection, connection.getUser().asEntityBareJid());
//pubSub.setFrom(connection.getUser()); LeafNode pubSubNode = pubSubManager.getNode(node);
pubSubNode.publish(item);
// Send the message that contains the roster
connection.sendStanza(pubSub);
} }
/** /**
* Fires roster exchange listeners. * XEP-163 5.
*/ */
private void firePEPListeners(Jid from, PEPEvent event) { private static final PubSubFeature[] REQUIRED_FEATURES = new PubSubFeature[] {
PEPListener[] listeners = null; // @formatter:off
synchronized (pepListeners) { PubSubFeature.auto_create,
listeners = new PEPListener[pepListeners.size()]; PubSubFeature.auto_subscribe,
pepListeners.toArray(listeners); PubSubFeature.filtered_notifications,
} // @formatter:on
for (int i = 0; i < listeners.length; i++) { };
listeners[i].eventReceived(from, event);
}
}
private void init() { public boolean isSupported() throws NoResponseException, XMPPErrorException,
// Listens for all roster exchange packets and fire the roster exchange listeners. NotConnectedException, InterruptedException {
packetListener = new StanzaListener() { XMPPConnection connection = connection();
public void processPacket(Stanza packet) { ServiceDiscoveryManager serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(connection);
Message message = (Message) packet; BareJid localBareJid = connection.getUser().asBareJid();
PEPEvent event = (PEPEvent) message.getExtension("event", "http://jabber.org/protocol/pubsub#event"); return serviceDiscoveryManager.supportsFeatures(localBareJid, REQUIRED_FEATURES);
// Fire event for roster exchange listeners
firePEPListeners(message.getFrom(), event);
}
};
connection.addSyncStanzaListener(packetListener, packetFilter);
}
public void destroy() {
if (connection != null)
connection.removeSyncStanzaListener(packetListener);
}
protected void finalize() throws Throwable {
destroy();
super.finalize();
} }
} }

View file

@ -1,102 +0,0 @@
/**
*
* Copyright 2003-2007 Jive Software.
*
* 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.smackx.pep.packet;
import org.jivesoftware.smack.packet.ExtensionElement;
/**
* Represents XMPP Personal Event Protocol packets.<p>
*
* The 'http://jabber.org/protocol/pubsub#event' namespace is used to publish personal events items from one client
* to subscribed clients (See XEP-163).
*
* @author Jeff Williams
*/
public class PEPEvent implements ExtensionElement {
PEPItem item;
/**
* Creates a new empty roster exchange package.
*
*/
public PEPEvent() {
super();
}
/**
* Creates a new empty roster exchange package.
*
*/
public PEPEvent(PEPItem item) {
super();
this.item = item;
}
public void addPEPItem(PEPItem item) {
this.item = item;
}
/**
* Returns the XML element name of the extension sub-packet root element.
* Always returns "x"
*
* @return the XML element name of the stanza(/packet) extension.
*/
public String getElementName() {
return "event";
}
/**
* Returns the XML namespace of the extension sub-packet root element.
* According the specification the namespace is always "jabber:x:roster"
* (which is not to be confused with the 'jabber:iq:roster' namespace
*
* @return the XML namespace of the stanza(/packet) extension.
*/
public String getNamespace() {
return "http://jabber.org/protocol/pubsub";
}
/**
* Returns the XML representation of a Personal Event Publish according the specification.
*
* Usually the XML representation will be inside of a Message XML representation like
* in the following example:
* <pre>
* &lt;message id="MlIpV-4" to="gato1@gato.home" from="gato3@gato.home/Smack"&gt;
* &lt;subject&gt;Any subject you want&lt;/subject&gt;
* &lt;body&gt;This message contains roster items.&lt;/body&gt;
* &lt;x xmlns="jabber:x:roster"&gt;
* &lt;item jid="gato1@gato.home"/&gt;
* &lt;item jid="gato2@gato.home"/&gt;
* &lt;/x&gt;
* &lt;/message&gt;
* </pre>
*
*/
public String toXML() {
StringBuilder buf = new StringBuilder();
buf.append('<').append(getElementName()).append(" xmlns=\"").append(getNamespace()).append("\">");
buf.append(item.toXML());
buf.append("</").append(getElementName()).append('>');
return buf.toString();
}
}

View file

@ -1,89 +0,0 @@
/**
*
* Copyright 2003-2007 Jive Software.
*
* 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.smackx.pep.packet;
import org.jivesoftware.smack.packet.ExtensionElement;
/**
* Represents XMPP Personal Event Protocol packets.<p>
*
* The 'http://jabber.org/protocol/pubsub#event' namespace is used to publish personal events items from one client
* to subscribed clients (See XEP-163).
*
* @author Jeff Williams
*/
public abstract class PEPItem implements ExtensionElement {
String id;
public abstract String getNode();
public abstract String getItemDetailsXML();
/**
* Creates a new PEPItem.
*
*/
public PEPItem(String id) {
super();
this.id = id;
}
/**
* Returns the XML element name of the extension sub-packet root element.
* Always returns "x"
*
* @return the XML element name of the stanza(/packet) extension.
*/
public String getElementName() {
return "item";
}
/**
* Returns the XML namespace of the extension sub-packet root element.
*
* @return the XML namespace of the stanza(/packet) extension.
*/
public String getNamespace() {
return "http://jabber.org/protocol/pubsub";
}
/**
* Returns the XML representation of a Personal Event Publish according the specification.
*
* Usually the XML representation will be inside of a Message XML representation like
* in the following example:
* <pre>
* &lt;message id="MlIpV-4" to="gato1@gato.home" from="gato3@gato.home/Smack"&gt;
* &lt;subject&gt;Any subject you want&lt;/subject&gt;
* &lt;body&gt;This message contains roster items.&lt;/body&gt;
* &lt;x xmlns="jabber:x:roster"&gt;
* &lt;item jid="gato1@gato.home"/&gt;
* &lt;item jid="gato2@gato.home"/&gt;
* &lt;/x&gt;
* &lt;/message&gt;
* </pre>
*
*/
public String toXML() {
StringBuilder buf = new StringBuilder();
buf.append('<').append(getElementName()).append(" id=\"").append(id).append("\">");
buf.append(getItemDetailsXML());
buf.append("</").append(getElementName()).append('>');
return buf.toString();
}
}

View file

@ -1,74 +0,0 @@
/**
*
* Copyright 2003-2007 Jive Software.
*
* 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.smackx.pep.packet;
import org.jivesoftware.smack.packet.IQ;
/**
* Represents XMPP PEP/XEP-163 pubsub packets.<p>
*
* The 'http://jabber.org/protocol/pubsub' namespace is used to publish personal events items from one client
* to subscribed clients (See XEP-163).
*
* @author Jeff Williams
*/
public class PEPPubSub extends IQ {
public static final String ELEMENT = "pubsub";
public static final String NAMESPACE = "http://jabber.org/protocol/pubsub";
private final PEPItem item;
/**
* Creates a new PubSub.
*
*/
public PEPPubSub(PEPItem item) {
super(ELEMENT, NAMESPACE);
this.item = item;
}
/**
* Returns the XML representation of a Personal Event Publish according the specification.
*
* Usually the XML representation will be inside of a Message XML representation like
* in the following example:
* <pre>
* &lt;message id="MlIpV-4" to="gato1@gato.home" from="gato3@gato.home/Smack"&gt;
* &lt;subject&gt;Any subject you want&lt;/subject&gt;
* &lt;body&gt;This message contains roster items.&lt;/body&gt;
* &lt;x xmlns="jabber:x:roster"&gt;
* &lt;item jid="gato1@gato.home"/&gt;
* &lt;item jid="gato2@gato.home"/&gt;
* &lt;/x&gt;
* &lt;/message&gt;
* </pre>
*
*/
@Override
protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder buf) {
buf.rightAngleBracket();
buf.openElement("publish").attribute("node", item.getNode()).rightAngleBracket();
buf.append(item.toXML());
buf.closeElement("publish");
return buf;
}
}

View file

@ -1,84 +0,0 @@
/**
*
* Copyright 2003-2007 Jive Software.
*
* 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.smackx.pep.provider;
import java.util.HashMap;
import java.util.Map;
import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.provider.ExtensionElementProvider;
import org.xmlpull.v1.XmlPullParser;
/**
*
* The PEPProvider parses incoming PEPEvent packets.
* (XEP-163 has a weird asymmetric deal: outbound PEP are <iq> + <pubsub> and inbound are <message> + <event>.
* The provider only deals with inbound, and so it only deals with <message>.
*
* Anyhoo...
*
* The way this works is that PEPxxx classes are generic <pubsub> and <message> providers, and anyone who
* wants to publish/receive PEPs, such as <tune>, <geoloc>, etc., simply need to extend PEPItem and register (here)
* a PacketExtensionProvider that knows how to parse that PEPItem extension.
*
* @author Jeff Williams
*/
public class PEPProvider extends ExtensionElementProvider<ExtensionElement> {
private static final Map<String, ExtensionElementProvider<?>> nodeParsers = new HashMap<String, ExtensionElementProvider<?>>();
public static void registerPEPParserExtension(String node, ExtensionElementProvider<?> pepItemParser) {
nodeParsers.put(node, pepItemParser);
}
/**
* Parses a PEPEvent stanza(/packet) and extracts a PEPItem from it.
* (There is only one per <event>.)
*
* @param parser the XML parser, positioned at the starting element of the extension.
* @return a PacketExtension.
* @throws Exception
*/
@Override
public ExtensionElement parse(XmlPullParser parser, int initialDepth)
throws Exception {
ExtensionElement pepItem = null;
boolean done = false;
while (!done) {
int eventType = parser.next();
if (eventType == XmlPullParser.START_TAG) {
if (parser.getName().equals("event")) {
} else if (parser.getName().equals("items")) {
// Figure out the node for this event.
String node = parser.getAttributeValue("", "node");
// Get the parser for this kind of node, and if found then parse the node.
ExtensionElementProvider<?> nodeParser = nodeParsers.get(node);
if (nodeParser != null) {
pepItem = nodeParser.parse(parser);
}
}
} else if (eventType == XmlPullParser.END_TAG) {
if (parser.getName().equals("event")) {
done = true;
}
}
}
return pepItem;
}
}

View file

@ -20,6 +20,8 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
import org.jivesoftware.smack.packet.ExtensionElement; import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace; import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace;
/** /**
@ -32,6 +34,16 @@ import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace;
*/ */
public class EventElement implements EmbeddedPacketExtension public class EventElement implements EmbeddedPacketExtension
{ {
/**
* The constant String "event".
*/
public static final String ELEMENT = "event";
/**
* The constant String "http://jabber.org/protocol/pubsub#event".
*/
public static final String NAMESPACE = PubSubNamespace.EVENT.getXmlns();
private EventElementType type; private EventElementType type;
private NodeExtension ext; private NodeExtension ext;
@ -66,12 +78,16 @@ public class EventElement implements EmbeddedPacketExtension
return PubSubNamespace.EVENT.getXmlns(); return PubSubNamespace.EVENT.getXmlns();
} }
public String toXML() @Override
{ public XmlStringBuilder toXML() {
StringBuilder builder = new StringBuilder("<event xmlns='" + PubSubNamespace.EVENT.getXmlns() + "'>"); XmlStringBuilder xml = new XmlStringBuilder(this);
xml.rightAngleBracket();
xml.append(ext.toXML());
xml.closeElement(this);
return xml;
}
builder.append(ext.toXML()); public static EventElement from(Stanza stanza) {
builder.append("</event>"); return stanza.getExtension(ELEMENT, NAMESPACE);
return builder.toString(); }
}
} }

View file

@ -0,0 +1,115 @@
/**
*
* Copyright 2015 Florian Schmaus
*
* 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.smackx.pubsub;
import org.jivesoftware.smackx.disco.Feature;
import org.jivesoftware.smackx.disco.Feature.Support;
import org.jivesoftware.smackx.pubsub.packet.PubSub;
/**
* The features a PubSub service may provides. Some are optional or recommended, while others are required.
*
* @author Florian Schmaus
* @see <a href="http://www.xmpp.org/extensions/xep-0060.html#features">XEP-60 § 10</a>
*
*/
public enum PubSubFeature implements CharSequence {
access_authorize(Support.optional),
access_open(Support.optional),
access_presence(Support.optional),
access_roster(Support.optional),
acccess_whitelist(Support.optional),
auto_create(Support.optional),
auto_subscribe(Support.recommended),
collections(Support.optional),
config_node(Support.recommended),
create_and_configure(Support.recommended),
create_nodes(Support.recommended),
delete_items(Support.recommended),
delete_nodes(Support.recommended),
get_pending(Support.optional),
item_ids(Support.recommended),
last_published(Support.recommended),
leased_subscription(Support.optional),
manage_subscriptions(Support.optional),
member_affiliation(Support.recommended),
meta_data(Support.recommended),
modify_affiliations(Support.optional),
multi_collection(Support.optional),
multi_subscribe(Support.optional),
outcast_affiliation(Support.recommended),
persistent_items(Support.recommended),
presence_notifications(Support.optional),
presence_subscribe(Support.recommended),
publish(Support.required),
publish_options(Support.optional),
publish_only_affiliation(Support.optional),
publisher_affiliation(Support.recommended),
purge_nodes(Support.optional),
retract_items(Support.optional),
retrieve_affiliations(Support.recommended),
retrieve_default(Support.recommended),
retrieve_default_sub(Support.optional),
retrieve_items(Support.recommended),
retrieve_subscriptions(Support.recommended),
subscribe(Support.required),
subscription_options(Support.optional),
subscriptions_notifications(Support.optional),
instant_nodes(Support.recommended),
filtered_notifications(Support.recommended),
;
private final String feature;
private final String qualifiedFeature;
private final Feature.Support support;
private PubSubFeature(Feature.Support support) {
this.feature = name().replace('_', '-');
this.qualifiedFeature = PubSub.NAMESPACE + '#' + this.feature;
this.support = support;
}
public String getFeatureName() {
return feature;
}
@Override
public String toString() {
return qualifiedFeature;
}
public Feature.Support support() {
return support;
}
@Override
public int length() {
return qualifiedFeature.length();
}
@Override
public char charAt(int index) {
return qualifiedFeature.charAt(index);
}
@Override
public CharSequence subSequence(int start, int end) {
return qualifiedFeature.subSequence(start, end);
}
}

View file

@ -44,6 +44,7 @@ import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace;
import org.jivesoftware.smackx.pubsub.util.NodeUtils; import org.jivesoftware.smackx.pubsub.util.NodeUtils;
import org.jivesoftware.smackx.xdata.Form; import org.jivesoftware.smackx.xdata.Form;
import org.jivesoftware.smackx.xdata.FormField; import org.jivesoftware.smackx.xdata.FormField;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.DomainBareJid; import org.jxmpp.jid.DomainBareJid;
import org.jxmpp.jid.Jid; import org.jxmpp.jid.Jid;
import org.jxmpp.jid.impl.JidCreate; import org.jxmpp.jid.impl.JidCreate;
@ -61,12 +62,12 @@ import org.jxmpp.stringprep.XmppStringprepException;
public final class PubSubManager extends Manager { public final class PubSubManager extends Manager {
private static final Logger LOGGER = Logger.getLogger(PubSubManager.class.getName()); private static final Logger LOGGER = Logger.getLogger(PubSubManager.class.getName());
private static final Map<XMPPConnection, Map<DomainBareJid, PubSubManager>> INSTANCES = new WeakHashMap<>(); private static final Map<XMPPConnection, Map<BareJid, PubSubManager>> INSTANCES = new WeakHashMap<>();
/** /**
* The JID of the PubSub service this manager manages. * The JID of the PubSub service this manager manages.
*/ */
private final DomainBareJid pubSubService; private final BareJid pubSubService;
/** /**
* A map of node IDs to Nodes, used to cache those Nodes. This does only cache the type of Node, * A map of node IDs to Nodes, used to cache those Nodes. This does only cache the type of Node,
@ -112,8 +113,8 @@ public final class PubSubManager extends Manager {
* @param pubSubService the PubSub service. * @param pubSubService the PubSub service.
* @return a PubSub manager for the connection and service. * @return a PubSub manager for the connection and service.
*/ */
public static synchronized PubSubManager getInstance(XMPPConnection connection, DomainBareJid pubSubService) { public static synchronized PubSubManager getInstance(XMPPConnection connection, BareJid pubSubService) {
Map<DomainBareJid, PubSubManager> managers = INSTANCES.get(connection); Map<BareJid, PubSubManager> managers = INSTANCES.get(connection);
if (managers == null) { if (managers == null) {
managers = new HashMap<>(); managers = new HashMap<>();
INSTANCES.put(connection, managers); INSTANCES.put(connection, managers);
@ -133,7 +134,7 @@ public final class PubSubManager extends Manager {
* @param connection The XMPP connection * @param connection The XMPP connection
* @param toAddress The pubsub specific to address (required for some servers) * @param toAddress The pubsub specific to address (required for some servers)
*/ */
PubSubManager(XMPPConnection connection, DomainBareJid toAddress) PubSubManager(XMPPConnection connection, BareJid toAddress)
{ {
super(connection); super(connection);
pubSubService = toAddress; pubSubService = toAddress;
@ -356,7 +357,7 @@ public final class PubSubManager extends Manager {
* *
* @return the JID of the PubSub service. * @return the JID of the PubSub service.
*/ */
public DomainBareJid getServiceJid() { public BareJid getServiceJid() {
return pubSubService; return pubSubService;
} }

View file

@ -0,0 +1,36 @@
/**
*
* Copyright 2015 Florian Schmaus.
*
* 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.smackx.pubsub.filter;
import org.jivesoftware.smack.filter.StanzaExtensionFilter;
import org.jivesoftware.smackx.pubsub.EventElement;
/**
* Filter for stanzas with the PubSub 'event' extension.
*
* @author Florian Schmaus
*
*/
public final class EventExtensionFilter extends StanzaExtensionFilter {
public static final EventExtensionFilter INSTANCE = new EventExtensionFilter();
private EventExtensionFilter() {
super(EventElement.ELEMENT, EventElement.NAMESPACE);
}
}

View file

@ -16,6 +16,6 @@
*/ */
/** /**
* TODO describe me. * Filters for Publish-Subscribe (XEP-60).
*/ */
package org.jivesoftware.smackx.pep.provider; package org.jivesoftware.smackx.pubsub.filter;