From 4a917320aa163d60f2590121f6fff2eb14756765 Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Thu, 30 Oct 2008 21:29:17 +0000 Subject: [PATCH] Added a first version of Personal Event Publishing. git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/trunk@10853 b35dd754-fafc-0310-a699-88a17e54d16e --- .../org/jivesoftware/smackx/PEPListener.java | 42 +++++ .../org/jivesoftware/smackx/PEPManager.java | 158 ++++++++++++++++++ .../jivesoftware/smackx/packet/PEPEvent.java | 105 ++++++++++++ .../jivesoftware/smackx/packet/PEPItem.java | 92 ++++++++++ .../jivesoftware/smackx/packet/PEPPubSub.java | 95 +++++++++++ .../smackx/provider/PEPProvider.java | 93 +++++++++++ 6 files changed, 585 insertions(+) create mode 100644 source/org/jivesoftware/smackx/PEPListener.java create mode 100644 source/org/jivesoftware/smackx/PEPManager.java create mode 100644 source/org/jivesoftware/smackx/packet/PEPEvent.java create mode 100644 source/org/jivesoftware/smackx/packet/PEPItem.java create mode 100644 source/org/jivesoftware/smackx/packet/PEPPubSub.java create mode 100644 source/org/jivesoftware/smackx/provider/PEPProvider.java diff --git a/source/org/jivesoftware/smackx/PEPListener.java b/source/org/jivesoftware/smackx/PEPListener.java new file mode 100644 index 000000000..1d3948438 --- /dev/null +++ b/source/org/jivesoftware/smackx/PEPListener.java @@ -0,0 +1,42 @@ +/** + * $RCSfile: PEPListener.java,v $ + * $Revision: 1.1 $ + * $Date: 2007/11/03 00:14:32 $ + * + * Copyright 2003-2007 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.smackx; + +import org.jivesoftware.smackx.packet.PEPEvent; + + +/** + * + * A listener that is fired anytime a PEP event message is received. + * + * @author Jeff Williams + */ +public interface PEPListener { + + /** + * Called when PEP events are received as part of a presence subscribe or message filter. + * + * @param from the user that sent the entries. + * @param event the event contained in the message. + */ + public void eventReceived(String from, PEPEvent event); + +} diff --git a/source/org/jivesoftware/smackx/PEPManager.java b/source/org/jivesoftware/smackx/PEPManager.java new file mode 100644 index 000000000..400664d20 --- /dev/null +++ b/source/org/jivesoftware/smackx/PEPManager.java @@ -0,0 +1,158 @@ +/** + * $RCSfile: PEPManager.java,v $ + * $Revision: 1.4 $ + * $Date: 2007/11/06 21:43:40 $ + * + * Copyright 2003-2007 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.smackx; + +import java.util.ArrayList; +import java.util.List; + +import org.jivesoftware.smack.PacketListener; +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.filter.PacketExtensionFilter; +import org.jivesoftware.smack.filter.PacketFilter; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smack.packet.IQ.Type; +import org.jivesoftware.smackx.packet.PEPEvent; +import org.jivesoftware.smackx.packet.PEPItem; +import org.jivesoftware.smackx.packet.PEPPubSub; + +/** + * + * Manages Personal Event Publishing (XEP-163). A PEPManager provides a high level access to + * pubsub personal events. It also provides an easy way + * to hook up custom logic when events are received from another XMPP client through PEPListeners. + * + * Use example: + * + * PEPManager pepManager = new PEPManager(smackConnection); + * pepManager.addPEPListener(new PEPListener() { + * public void eventReceived(String inFrom, PEPEvent inEvent) { + * LOGGER.debug("Event received: " + inEvent); + * } + * }); + * + * 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); + * + * @author Jeff Williams + */ +public class PEPManager { + + private List pepListeners = new ArrayList(); + + private XMPPConnection connection; + + private PacketFilter packetFilter = new PacketExtensionFilter("event", "http://jabber.org/protocol/pubsub#event"); + private PacketListener packetListener; + + /** + * Creates a new PEP exchange manager. + * + * @param con an XMPPConnection. + */ + public PEPManager(XMPPConnection connection) { + this.connection = connection; + init(); + } + + /** + * Adds a listener to PEPs. The listener will be fired anytime PEP events + * are received from remote XMPP clients. + * + * @param pepListener a roster exchange listener. + */ + public void addPEPListener(PEPListener pepListener) { + synchronized (pepListeners) { + if (!pepListeners.contains(pepListener)) { + pepListeners.add(pepListener); + } + } + } + + /** + * Removes a listener from PEP events. + * + * @param pepListener a roster exchange listener.. + */ + public void removePEPListener(PEPListener pepListener) { + synchronized (pepListeners) { + pepListeners.remove(pepListener); + } + } + + /** + * Publish an event. + * + * @param roster the roster to send + * @param targetUserID the user that will receive the roster entries + */ + public void publish(PEPItem item) { + // Create a new message to publish the event. + PEPPubSub pubSub = new PEPPubSub(item); + pubSub.setType(Type.SET); + //pubSub.setFrom(connection.getUser()); + + // Send the message that contains the roster + connection.sendPacket(pubSub); + } + + /** + * Fires roster exchange listeners. + */ + private void firePEPListeners(String from, PEPEvent event) { + PEPListener[] listeners = null; + synchronized (pepListeners) { + listeners = new PEPListener[pepListeners.size()]; + pepListeners.toArray(listeners); + } + for (int i = 0; i < listeners.length; i++) { + listeners[i].eventReceived(from, event); + } + } + + private void init() { + // Listens for all roster exchange packets and fire the roster exchange listeners. + packetListener = new PacketListener() { + public void processPacket(Packet packet) { + Message message = (Message) packet; + PEPEvent event = (PEPEvent) message.getExtension("event", "http://jabber.org/protocol/pubsub#event"); + // Fire event for roster exchange listeners + firePEPListeners(message.getFrom(), event); + }; + + }; + connection.addPacketListener(packetListener, packetFilter); + } + + public void destroy() { + if (connection != null) + connection.removePacketListener(packetListener); + + } + + public void finalize() { + destroy(); + } +} \ No newline at end of file diff --git a/source/org/jivesoftware/smackx/packet/PEPEvent.java b/source/org/jivesoftware/smackx/packet/PEPEvent.java new file mode 100644 index 000000000..48f1de25a --- /dev/null +++ b/source/org/jivesoftware/smackx/packet/PEPEvent.java @@ -0,0 +1,105 @@ +/** + * $RCSfile: PEPEvent.java,v $ + * $Revision: 1.1 $ + * $Date: 2007/11/03 00:14:32 $ + * + * Copyright 2003-2007 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.smackx.packet; + +import org.jivesoftware.smack.packet.PacketExtension; + +/** + * Represents XMPP Personal Event Protocol packets.

+ * + * 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 PacketExtension { + + 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 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 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: + *

+     * <message id="MlIpV-4" to="gato1@gato.home" from="gato3@gato.home/Smack">
+     *     <subject>Any subject you want</subject>
+     *     <body>This message contains roster items.</body>
+     *     <x xmlns="jabber:x:roster">
+     *         <item jid="gato1@gato.home"/>
+     *         <item jid="gato2@gato.home"/>
+     *     </x>
+     * </message>
+     * 
+ * + */ + public String toXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append("\">"); + buf.append(item.toXML()); + buf.append(""); + return buf.toString(); + } + +} diff --git a/source/org/jivesoftware/smackx/packet/PEPItem.java b/source/org/jivesoftware/smackx/packet/PEPItem.java new file mode 100644 index 000000000..c3ff6f4f0 --- /dev/null +++ b/source/org/jivesoftware/smackx/packet/PEPItem.java @@ -0,0 +1,92 @@ +/** + * $RCSfile: PEPItem.java,v $ + * $Revision: 1.2 $ + * $Date: 2007/11/06 02:05:09 $ + * + * Copyright 2003-2007 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.smackx.packet; + +import org.jivesoftware.smack.packet.PacketExtension; + +/** + * Represents XMPP Personal Event Protocol packets.

+ * + * 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 PacketExtension { + + String id; + abstract String getNode(); + 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 packet extension. + */ + public String getElementName() { + return "item"; + } + + /** + * Returns the XML namespace of the extension sub-packet root element. + * + * @return the XML namespace of the 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: + *

+     * <message id="MlIpV-4" to="gato1@gato.home" from="gato3@gato.home/Smack">
+     *     <subject>Any subject you want</subject>
+     *     <body>This message contains roster items.</body>
+     *     <x xmlns="jabber:x:roster">
+     *         <item jid="gato1@gato.home"/>
+     *         <item jid="gato2@gato.home"/>
+     *     </x>
+     * </message>
+     * 
+ * + */ + public String toXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<").append(getElementName()).append(" id=\"").append(id).append("\">"); + buf.append(getItemDetailsXML()); + buf.append(""); + return buf.toString(); + } + +} diff --git a/source/org/jivesoftware/smackx/packet/PEPPubSub.java b/source/org/jivesoftware/smackx/packet/PEPPubSub.java new file mode 100644 index 000000000..420ce6127 --- /dev/null +++ b/source/org/jivesoftware/smackx/packet/PEPPubSub.java @@ -0,0 +1,95 @@ +/** + * $RCSfile: PEPPubSub.java,v $ + * $Revision: 1.2 $ + * $Date: 2007/11/03 04:46:52 $ + * + * Copyright 2003-2007 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.smackx.packet; + +import org.jivesoftware.smack.packet.IQ; + +/** + * Represents XMPP PEP/XEP-163 pubsub packets.

+ * + * 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 { + + PEPItem item; + + /** + * Creates a new PubSub. + * + */ + public PEPPubSub(PEPItem item) { + super(); + + 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 packet extension. + */ + public String getElementName() { + return "pubsub"; + } + + /** + * 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 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: + *

+     * <message id="MlIpV-4" to="gato1@gato.home" from="gato3@gato.home/Smack">
+     *     <subject>Any subject you want</subject>
+     *     <body>This message contains roster items.</body>
+     *     <x xmlns="jabber:x:roster">
+     *         <item jid="gato1@gato.home"/>
+     *         <item jid="gato2@gato.home"/>
+     *     </x>
+     * </message>
+     * 
+ * + */ + public String getChildElementXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<").append(getElementName()).append(" xmlns=\"").append(getNamespace()).append("\">"); + buf.append(""); + buf.append(item.toXML()); + buf.append(""); + buf.append(""); + return buf.toString(); + } + +} diff --git a/source/org/jivesoftware/smackx/provider/PEPProvider.java b/source/org/jivesoftware/smackx/provider/PEPProvider.java new file mode 100644 index 000000000..f33dcde91 --- /dev/null +++ b/source/org/jivesoftware/smackx/provider/PEPProvider.java @@ -0,0 +1,93 @@ +/** + * $RCSfile: PEPProvider.java,v $ + * $Revision: 1.2 $ + * $Date: 2007/11/06 02:05:09 $ + * + * Copyright 2003-2007 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.smackx.provider; + +import java.util.HashMap; +import java.util.Map; + +import org.jivesoftware.smack.packet.PacketExtension; +import org.jivesoftware.smack.provider.PacketExtensionProvider; +import org.xmlpull.v1.XmlPullParser; + +/** + * + * The PEPProvider parses incoming PEPEvent packets. + * (XEP-163 has a weird asymmetric deal: outbound PEP are + and inbound are + . + * The provider only deals with inbound, and so it only deals with . + * + * Anyhoo... + * + * The way this works is that PEPxxx classes are generic and providers, and anyone who + * wants to publish/receive PEPs, such as , , 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 implements PacketExtensionProvider { + + Map nodeParsers = new HashMap(); + PacketExtension pepItem; + + /** + * Creates a new PEPProvider. + * ProviderManager requires that every PacketExtensionProvider has a public, no-argument constructor + */ + public PEPProvider() { + } + + public void registerPEPParserExtension(String node, PacketExtensionProvider pepItemParser) { + nodeParsers.put(node, pepItemParser); + } + + /** + * Parses a PEPEvent packet and extracts a PEPItem from it. + * (There is only one per .) + * + * @param parser the XML parser, positioned at the starting element of the extension. + * @return a PacketExtension. + * @throws Exception if a parsing error occurs. + */ + public PacketExtension parseExtension(XmlPullParser parser) throws Exception { + + 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. + PacketExtensionProvider nodeParser = nodeParsers.get(node); + if (nodeParser != null) { + pepItem = nodeParser.parseExtension(parser); + } + } + } else if (eventType == XmlPullParser.END_TAG) { + if (parser.getName().equals("event")) { + done = true; + } + } + } + + return pepItem; + } +}