001/** 002 * 003 * Copyright 2003-2007 Jive Software. 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.xevent; 019 020import java.lang.reflect.Method; 021import java.util.ArrayList; 022import java.util.List; 023import java.util.logging.Level; 024import java.util.logging.Logger; 025 026import org.jivesoftware.smack.PacketListener; 027import org.jivesoftware.smack.SmackException.NotConnectedException; 028import org.jivesoftware.smack.XMPPConnection; 029import org.jivesoftware.smack.filter.PacketExtensionFilter; 030import org.jivesoftware.smack.filter.PacketFilter; 031import org.jivesoftware.smack.packet.Message; 032import org.jivesoftware.smack.packet.Packet; 033import org.jivesoftware.smackx.xevent.packet.MessageEvent; 034 035/** 036 * Manages message events requests and notifications. A MessageEventManager provides a high 037 * level access to request for notifications and send event notifications. It also provides 038 * an easy way to hook up custom logic when requests or notifications are received. 039 * 040 * @author Gaston Dombiak 041 */ 042public class MessageEventManager { 043 private static final Logger LOGGER = Logger.getLogger(MessageEventManager.class.getName()); 044 045 private List<MessageEventNotificationListener> messageEventNotificationListeners = new ArrayList<MessageEventNotificationListener>(); 046 private List<MessageEventRequestListener> messageEventRequestListeners = new ArrayList<MessageEventRequestListener>(); 047 048 private XMPPConnection con; 049 050 private PacketFilter packetFilter = new PacketExtensionFilter("x", "jabber:x:event"); 051 private PacketListener packetListener; 052 053 /** 054 * Creates a new message event manager. 055 * 056 * @param con a XMPPConnection to a XMPP server. 057 */ 058 public MessageEventManager(XMPPConnection con) { 059 this.con = con; 060 init(); 061 } 062 063 /** 064 * Adds event notification requests to a message. For each event type that 065 * the user wishes event notifications from the message recepient for, <tt>true</tt> 066 * should be passed in to this method. 067 * 068 * @param message the message to add the requested notifications. 069 * @param offline specifies if the offline event is requested. 070 * @param delivered specifies if the delivered event is requested. 071 * @param displayed specifies if the displayed event is requested. 072 * @param composing specifies if the composing event is requested. 073 */ 074 public static void addNotificationsRequests(Message message, boolean offline, 075 boolean delivered, boolean displayed, boolean composing) 076 { 077 // Create a MessageEvent Package and add it to the message 078 MessageEvent messageEvent = new MessageEvent(); 079 messageEvent.setOffline(offline); 080 messageEvent.setDelivered(delivered); 081 messageEvent.setDisplayed(displayed); 082 messageEvent.setComposing(composing); 083 message.addExtension(messageEvent); 084 } 085 086 /** 087 * Adds a message event request listener. The listener will be fired anytime a request for 088 * event notification is received. 089 * 090 * @param messageEventRequestListener a message event request listener. 091 */ 092 public void addMessageEventRequestListener(MessageEventRequestListener messageEventRequestListener) { 093 synchronized (messageEventRequestListeners) { 094 if (!messageEventRequestListeners.contains(messageEventRequestListener)) { 095 messageEventRequestListeners.add(messageEventRequestListener); 096 } 097 } 098 } 099 100 /** 101 * Removes a message event request listener. The listener will be fired anytime a request for 102 * event notification is received. 103 * 104 * @param messageEventRequestListener a message event request listener. 105 */ 106 public void removeMessageEventRequestListener(MessageEventRequestListener messageEventRequestListener) { 107 synchronized (messageEventRequestListeners) { 108 messageEventRequestListeners.remove(messageEventRequestListener); 109 } 110 } 111 112 /** 113 * Adds a message event notification listener. The listener will be fired anytime a notification 114 * event is received. 115 * 116 * @param messageEventNotificationListener a message event notification listener. 117 */ 118 public void addMessageEventNotificationListener(MessageEventNotificationListener messageEventNotificationListener) { 119 synchronized (messageEventNotificationListeners) { 120 if (!messageEventNotificationListeners.contains(messageEventNotificationListener)) { 121 messageEventNotificationListeners.add(messageEventNotificationListener); 122 } 123 } 124 } 125 126 /** 127 * Removes a message event notification listener. The listener will be fired anytime a notification 128 * event is received. 129 * 130 * @param messageEventNotificationListener a message event notification listener. 131 */ 132 public void removeMessageEventNotificationListener(MessageEventNotificationListener messageEventNotificationListener) { 133 synchronized (messageEventNotificationListeners) { 134 messageEventNotificationListeners.remove(messageEventNotificationListener); 135 } 136 } 137 138 /** 139 * Fires message event request listeners. 140 */ 141 private void fireMessageEventRequestListeners( 142 String from, 143 String packetID, 144 String methodName) { 145 MessageEventRequestListener[] listeners = null; 146 Method method; 147 synchronized (messageEventRequestListeners) { 148 listeners = new MessageEventRequestListener[messageEventRequestListeners.size()]; 149 messageEventRequestListeners.toArray(listeners); 150 } 151 try { 152 method = 153 MessageEventRequestListener.class.getDeclaredMethod( 154 methodName, 155 new Class[] { String.class, String.class, MessageEventManager.class }); 156 for (int i = 0; i < listeners.length; i++) { 157 method.invoke(listeners[i], new Object[] { from, packetID, this }); 158 } 159 } catch (Exception e) { 160 LOGGER.log(Level.SEVERE, "Error while invoking MessageEventRequestListener", e); 161 } 162 } 163 164 /** 165 * Fires message event notification listeners. 166 */ 167 private void fireMessageEventNotificationListeners( 168 String from, 169 String packetID, 170 String methodName) { 171 MessageEventNotificationListener[] listeners = null; 172 Method method; 173 synchronized (messageEventNotificationListeners) { 174 listeners = 175 new MessageEventNotificationListener[messageEventNotificationListeners.size()]; 176 messageEventNotificationListeners.toArray(listeners); 177 } 178 try { 179 method = 180 MessageEventNotificationListener.class.getDeclaredMethod( 181 methodName, 182 new Class[] { String.class, String.class }); 183 for (int i = 0; i < listeners.length; i++) { 184 method.invoke(listeners[i], new Object[] { from, packetID }); 185 } 186 } catch (Exception e) { 187 LOGGER.log(Level.SEVERE, "Error while invoking MessageEventNotificationListener", e); 188 } 189 } 190 191 private void init() { 192 // Listens for all message event packets and fire the proper message event listeners. 193 packetListener = new PacketListener() { 194 public void processPacket(Packet packet) { 195 Message message = (Message) packet; 196 MessageEvent messageEvent = 197 (MessageEvent) message.getExtension("x", "jabber:x:event"); 198 if (messageEvent.isMessageEventRequest()) { 199 // Fire event for requests of message events 200 for (String eventType : messageEvent.getEventTypes()) 201 fireMessageEventRequestListeners( 202 message.getFrom(), 203 message.getPacketID(), 204 eventType.concat("NotificationRequested")); 205 } else 206 // Fire event for notifications of message events 207 for (String eventType : messageEvent.getEventTypes()) 208 fireMessageEventNotificationListeners( 209 message.getFrom(), 210 messageEvent.getPacketID(), 211 eventType.concat("Notification")); 212 213 }; 214 215 }; 216 con.addPacketListener(packetListener, packetFilter); 217 } 218 219 /** 220 * Sends the notification that the message was delivered to the sender of the original message 221 * 222 * @param to the recipient of the notification. 223 * @param packetID the id of the message to send. 224 * @throws NotConnectedException 225 */ 226 public void sendDeliveredNotification(String to, String packetID) throws NotConnectedException { 227 // Create the message to send 228 Message msg = new Message(to); 229 // Create a MessageEvent Package and add it to the message 230 MessageEvent messageEvent = new MessageEvent(); 231 messageEvent.setDelivered(true); 232 messageEvent.setPacketID(packetID); 233 msg.addExtension(messageEvent); 234 // Send the packet 235 con.sendPacket(msg); 236 } 237 238 /** 239 * Sends the notification that the message was displayed to the sender of the original message 240 * 241 * @param to the recipient of the notification. 242 * @param packetID the id of the message to send. 243 * @throws NotConnectedException 244 */ 245 public void sendDisplayedNotification(String to, String packetID) throws NotConnectedException { 246 // Create the message to send 247 Message msg = new Message(to); 248 // Create a MessageEvent Package and add it to the message 249 MessageEvent messageEvent = new MessageEvent(); 250 messageEvent.setDisplayed(true); 251 messageEvent.setPacketID(packetID); 252 msg.addExtension(messageEvent); 253 // Send the packet 254 con.sendPacket(msg); 255 } 256 257 /** 258 * Sends the notification that the receiver of the message is composing a reply 259 * 260 * @param to the recipient of the notification. 261 * @param packetID the id of the message to send. 262 * @throws NotConnectedException 263 */ 264 public void sendComposingNotification(String to, String packetID) throws NotConnectedException { 265 // Create the message to send 266 Message msg = new Message(to); 267 // Create a MessageEvent Package and add it to the message 268 MessageEvent messageEvent = new MessageEvent(); 269 messageEvent.setComposing(true); 270 messageEvent.setPacketID(packetID); 271 msg.addExtension(messageEvent); 272 // Send the packet 273 con.sendPacket(msg); 274 } 275 276 /** 277 * Sends the notification that the receiver of the message has cancelled composing a reply. 278 * 279 * @param to the recipient of the notification. 280 * @param packetID the id of the message to send. 281 * @throws NotConnectedException 282 */ 283 public void sendCancelledNotification(String to, String packetID) throws NotConnectedException { 284 // Create the message to send 285 Message msg = new Message(to); 286 // Create a MessageEvent Package and add it to the message 287 MessageEvent messageEvent = new MessageEvent(); 288 messageEvent.setCancelled(true); 289 messageEvent.setPacketID(packetID); 290 msg.addExtension(messageEvent); 291 // Send the packet 292 con.sendPacket(msg); 293 } 294 295 public void destroy() { 296 if (con != null) { 297 con.removePacketListener(packetListener); 298 } 299 } 300 301 protected void finalize() throws Throwable { 302 destroy(); 303 super.finalize(); 304 } 305}