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.StanzaListener; 028import org.jivesoftware.smack.SmackException.NotConnectedException; 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.FromJidTypeFilter; 034import org.jivesoftware.smack.filter.jidtype.AbstractJidTypeFilter.JidType; 035import org.jivesoftware.smack.packet.Message; 036import org.jivesoftware.smack.packet.Stanza; 037import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; 038import org.jivesoftware.smackx.pubsub.EventElement; 039import org.jivesoftware.smackx.pubsub.Item; 040import org.jivesoftware.smackx.pubsub.LeafNode; 041import org.jivesoftware.smackx.pubsub.PubSubFeature; 042import org.jivesoftware.smackx.pubsub.PubSubManager; 043import org.jivesoftware.smackx.pubsub.filter.EventExtensionFilter; 044import org.jxmpp.jid.BareJid; 045import org.jxmpp.jid.EntityBareJid; 046 047/** 048 * 049 * Manages Personal Event Publishing (XEP-163). A PEPManager provides a high level access to 050 * pubsub personal events. It also provides an easy way 051 * to hook up custom logic when events are received from another XMPP client through PEPListeners. 052 * 053 * Use example: 054 * 055 * <pre> 056 * PEPManager pepManager = new PEPManager(smackConnection); 057 * pepManager.addPEPListener(new PEPListener() { 058 * public void eventReceived(EntityBareJid from, EventElement event, Message message) { 059 * LOGGER.debug("Event received: " + event); 060 * } 061 * }); 062 * </pre> 063 * 064 * @author Jeff Williams 065 * @author Florian Schmaus 066 */ 067public final class PEPManager extends Manager { 068 069 private static final Map<XMPPConnection, PEPManager> INSTANCES = new WeakHashMap<>(); 070 071 public static synchronized PEPManager getInstanceFor(XMPPConnection connection) { 072 PEPManager pepManager = INSTANCES.get(connection); 073 if (pepManager == null) { 074 pepManager = new PEPManager(connection); 075 INSTANCES.put(connection, pepManager); 076 } 077 return pepManager; 078 } 079 080 private static final StanzaFilter FROM_BARE_JID_WITH_EVENT_EXTENSION_FILTER = new AndFilter( 081 new FromJidTypeFilter(JidType.BareJid), 082 EventExtensionFilter.INSTANCE); 083 084 private final Set<PEPListener> pepListeners = new CopyOnWriteArraySet<>(); 085 086 /** 087 * Creates a new PEP exchange manager. 088 * 089 * @param connection an XMPPConnection which is used to send and receive messages. 090 */ 091 private PEPManager(XMPPConnection connection) { 092 super(connection); 093 StanzaListener packetListener = new StanzaListener() { 094 @Override 095 public void processStanza(Stanza stanza) { 096 Message message = (Message) stanza; 097 EventElement event = EventElement.from(stanza); 098 assert(event != null); 099 EntityBareJid from = message.getFrom().asEntityBareJidIfPossible(); 100 assert(from != null); 101 for (PEPListener listener : pepListeners) { 102 listener.eventReceived(from, event, message); 103 } 104 } 105 }; 106 // TODO Add filter to check if from supports PubSub as per xep163 2 2.4 107 connection.addSyncStanzaListener(packetListener, FROM_BARE_JID_WITH_EVENT_EXTENSION_FILTER); 108 } 109 110 /** 111 * Adds a listener to PEPs. The listener will be fired anytime PEP events 112 * are received from remote XMPP clients. 113 * 114 * @param pepListener a roster exchange listener. 115 */ 116 public boolean addPEPListener(PEPListener pepListener) { 117 return pepListeners.add(pepListener); 118 } 119 120 /** 121 * Removes a listener from PEP events. 122 * 123 * @param pepListener a roster exchange listener. 124 */ 125 public boolean removePEPListener(PEPListener pepListener) { 126 return pepListeners.remove(pepListener); 127 } 128 129 /** 130 * Publish an event. 131 * 132 * @param item the item to publish. 133 * @param node the node to publish on. 134 * @throws NotConnectedException 135 * @throws InterruptedException 136 * @throws XMPPErrorException 137 * @throws NoResponseException 138 */ 139 public void publish(Item item, String node) throws NotConnectedException, InterruptedException, 140 NoResponseException, XMPPErrorException { 141 XMPPConnection connection = connection(); 142 PubSubManager pubSubManager = PubSubManager.getInstance(connection, connection.getUser().asEntityBareJid()); 143 LeafNode pubSubNode = pubSubManager.getNode(node); 144 pubSubNode.publish(item); 145 } 146 147 /** 148 * XEP-163 5. 149 */ 150 private static final PubSubFeature[] REQUIRED_FEATURES = new PubSubFeature[] { 151 // @formatter:off 152 PubSubFeature.auto_create, 153 PubSubFeature.auto_subscribe, 154 PubSubFeature.filtered_notifications, 155 // @formatter:on 156 }; 157 158 public boolean isSupported() throws NoResponseException, XMPPErrorException, 159 NotConnectedException, InterruptedException { 160 XMPPConnection connection = connection(); 161 ServiceDiscoveryManager serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(connection); 162 BareJid localBareJid = connection.getUser().asBareJid(); 163 return serviceDiscoveryManager.supportsFeatures(localBareJid, REQUIRED_FEATURES); 164 } 165}