001/** 002 * 003 * Copyright 2003-2007 Jive Software, 2015 Florian Schmaus 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.jivesoftware.smackx.pep; 019 020import java.util.Map; 021import java.util.Set; 022import java.util.WeakHashMap; 023import java.util.concurrent.CopyOnWriteArraySet; 024 025import org.jivesoftware.smack.Manager; 026import org.jivesoftware.smack.SmackException.NoResponseException; 027import org.jivesoftware.smack.SmackException.NotConnectedException; 028import org.jivesoftware.smack.StanzaListener; 029import org.jivesoftware.smack.XMPPConnection; 030import org.jivesoftware.smack.XMPPException.XMPPErrorException; 031import org.jivesoftware.smack.filter.AndFilter; 032import org.jivesoftware.smack.filter.StanzaFilter; 033import org.jivesoftware.smack.filter.jidtype.AbstractJidTypeFilter.JidType; 034import org.jivesoftware.smack.filter.jidtype.FromJidTypeFilter; 035import org.jivesoftware.smack.packet.Message; 036import org.jivesoftware.smack.packet.Stanza; 037 038import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; 039import org.jivesoftware.smackx.pubsub.EventElement; 040import org.jivesoftware.smackx.pubsub.Item; 041import org.jivesoftware.smackx.pubsub.LeafNode; 042import org.jivesoftware.smackx.pubsub.PubSubException.NotAPubSubNodeException; 043import org.jivesoftware.smackx.pubsub.PubSubFeature; 044import org.jivesoftware.smackx.pubsub.PubSubManager; 045import org.jivesoftware.smackx.pubsub.filter.EventExtensionFilter; 046 047import org.jxmpp.jid.BareJid; 048import org.jxmpp.jid.EntityBareJid; 049 050/** 051 * 052 * Manages Personal Event Publishing (XEP-163). A PEPManager provides a high level access to 053 * PubSub personal events. It also provides an easy way 054 * to hook up custom logic when events are received from another XMPP client through PEPListeners. 055 * 056 * Use example: 057 * 058 * <pre> 059 * PEPManager pepManager = new PEPManager(smackConnection); 060 * pepManager.addPEPListener(new PEPListener() { 061 * public void eventReceived(EntityBareJid from, EventElement event, Message message) { 062 * LOGGER.debug("Event received: " + event); 063 * } 064 * }); 065 * </pre> 066 * 067 * @author Jeff Williams 068 * @author Florian Schmaus 069 */ 070public final class PEPManager extends Manager { 071 072 private static final Map<XMPPConnection, PEPManager> INSTANCES = new WeakHashMap<>(); 073 074 public static synchronized PEPManager getInstanceFor(XMPPConnection connection) { 075 PEPManager pepManager = INSTANCES.get(connection); 076 if (pepManager == null) { 077 pepManager = new PEPManager(connection); 078 INSTANCES.put(connection, pepManager); 079 } 080 return pepManager; 081 } 082 083 private static final StanzaFilter FROM_BARE_JID_WITH_EVENT_EXTENSION_FILTER = new AndFilter( 084 new FromJidTypeFilter(JidType.BareJid), 085 EventExtensionFilter.INSTANCE); 086 087 private final Set<PEPListener> pepListeners = new CopyOnWriteArraySet<>(); 088 089 /** 090 * Creates a new PEP exchange manager. 091 * 092 * @param connection an XMPPConnection which is used to send and receive messages. 093 */ 094 private PEPManager(XMPPConnection connection) { 095 super(connection); 096 StanzaListener packetListener = new StanzaListener() { 097 @Override 098 public void processStanza(Stanza stanza) { 099 Message message = (Message) stanza; 100 EventElement event = EventElement.from(stanza); 101 assert (event != null); 102 EntityBareJid from = message.getFrom().asEntityBareJidIfPossible(); 103 assert (from != null); 104 for (PEPListener listener : pepListeners) { 105 listener.eventReceived(from, event, message); 106 } 107 } 108 }; 109 // TODO Add filter to check if from supports PubSub as per xep163 2 2.4 110 connection.addSyncStanzaListener(packetListener, FROM_BARE_JID_WITH_EVENT_EXTENSION_FILTER); 111 } 112 113 /** 114 * Adds a listener to PEPs. The listener will be fired anytime PEP events 115 * are received from remote XMPP clients. 116 * 117 * @param pepListener a roster exchange listener. 118 * @return true if pepListener was added. 119 */ 120 public boolean addPEPListener(PEPListener pepListener) { 121 return pepListeners.add(pepListener); 122 } 123 124 /** 125 * Removes a listener from PEP events. 126 * 127 * @param pepListener a roster exchange listener. 128 * @return true, if pepListener was removed. 129 */ 130 public boolean removePEPListener(PEPListener pepListener) { 131 return pepListeners.remove(pepListener); 132 } 133 134 /** 135 * Publish an event. 136 * 137 * @param item the item to publish. 138 * @param node the node to publish on. 139 * @throws NotConnectedException 140 * @throws InterruptedException 141 * @throws XMPPErrorException 142 * @throws NoResponseException 143 * @throws NotAPubSubNodeException 144 */ 145 public void publish(Item item, String node) throws NotConnectedException, InterruptedException, 146 NoResponseException, XMPPErrorException, NotAPubSubNodeException { 147 XMPPConnection connection = connection(); 148 PubSubManager pubSubManager = PubSubManager.getInstance(connection, connection.getUser().asEntityBareJid()); 149 LeafNode pubSubNode = pubSubManager.getNode(node); 150 pubSubNode.publish(item); 151 } 152 153 /** 154 * XEP-163 5. 155 */ 156 private static final PubSubFeature[] REQUIRED_FEATURES = new PubSubFeature[] { 157 // @formatter:off 158 PubSubFeature.auto_create, 159 PubSubFeature.auto_subscribe, 160 PubSubFeature.filtered_notifications, 161 // @formatter:on 162 }; 163 164 public boolean isSupported() throws NoResponseException, XMPPErrorException, 165 NotConnectedException, InterruptedException { 166 XMPPConnection connection = connection(); 167 ServiceDiscoveryManager serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(connection); 168 BareJid localBareJid = connection.getUser().asBareJid(); 169 return serviceDiscoveryManager.supportsFeatures(localBareJid, REQUIRED_FEATURES); 170 } 171}