MessageEventManager.java

  1. /**
  2.  *
  3.  * Copyright 2003-2007 Jive Software.
  4.  *
  5.  * Licensed under the Apache License, Version 2.0 (the "License");
  6.  * you may not use this file except in compliance with the License.
  7.  * You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. package org.jivesoftware.smackx.xevent;

  18. import java.lang.reflect.Method;
  19. import java.util.List;
  20. import java.util.Map;
  21. import java.util.WeakHashMap;
  22. import java.util.concurrent.CopyOnWriteArrayList;
  23. import java.util.logging.Level;
  24. import java.util.logging.Logger;

  25. import org.jivesoftware.smack.Manager;
  26. import org.jivesoftware.smack.SmackException.NotConnectedException;
  27. import org.jivesoftware.smack.StanzaListener;
  28. import org.jivesoftware.smack.XMPPConnection;
  29. import org.jivesoftware.smack.filter.AndFilter;
  30. import org.jivesoftware.smack.filter.MessageTypeFilter;
  31. import org.jivesoftware.smack.filter.NotFilter;
  32. import org.jivesoftware.smack.filter.StanzaExtensionFilter;
  33. import org.jivesoftware.smack.filter.StanzaFilter;
  34. import org.jivesoftware.smack.packet.Message;
  35. import org.jivesoftware.smack.packet.Stanza;

  36. import org.jivesoftware.smackx.xevent.packet.MessageEvent;

  37. import org.jxmpp.jid.Jid;

  38. /**
  39.  *
  40.  * Manages message events requests and notifications. A MessageEventManager provides a high
  41.  * level access to request for notifications and send event notifications. It also provides
  42.  * an easy way to hook up custom logic when requests or notifications are received.
  43.  *
  44.  * @author Gaston Dombiak
  45.  * @see <a href="http://xmpp.org/extensions/xep-0022.html">XEP-22: Message Events</a>
  46.  */
  47. public final class MessageEventManager extends Manager {
  48.     private static final Logger LOGGER = Logger.getLogger(MessageEventManager.class.getName());

  49.     private static final Map<XMPPConnection, MessageEventManager> INSTANCES = new WeakHashMap<>();

  50.     private static final StanzaFilter PACKET_FILTER = new AndFilter(new StanzaExtensionFilter(
  51.                     new MessageEvent()), new NotFilter(MessageTypeFilter.ERROR));

  52.     private final List<MessageEventNotificationListener> messageEventNotificationListeners = new CopyOnWriteArrayList<>();
  53.     private final List<MessageEventRequestListener> messageEventRequestListeners = new CopyOnWriteArrayList<>();

  54.     public static synchronized MessageEventManager getInstanceFor(XMPPConnection connection) {
  55.         MessageEventManager messageEventManager = INSTANCES.get(connection);
  56.         if (messageEventManager == null) {
  57.             messageEventManager = new MessageEventManager(connection);
  58.             INSTANCES.put(connection, messageEventManager);
  59.         }
  60.         return messageEventManager;
  61.     }

  62.     /**
  63.      * Creates a new message event manager.
  64.      *
  65.      * @param connection an XMPPConnection to a XMPP server.
  66.      */
  67.     private MessageEventManager(XMPPConnection connection) {
  68.         super(connection);
  69.         // Listens for all message event packets and fire the proper message event listeners.
  70.         connection.addAsyncStanzaListener(new StanzaListener() {
  71.             @Override
  72.             public void processStanza(Stanza packet) {
  73.                 Message message = (Message) packet;
  74.                 MessageEvent messageEvent = (MessageEvent) message.getExtensionElement("x", "jabber:x:event");
  75.                 if (messageEvent.isMessageEventRequest()) {
  76.                     // Fire event for requests of message events
  77.                     for (String eventType : messageEvent.getEventTypes())
  78.                         fireMessageEventRequestListeners(
  79.                             message.getFrom(),
  80.                             message.getStanzaId(),
  81.                             eventType.concat("NotificationRequested"));
  82.                 } else
  83.                     // Fire event for notifications of message events
  84.                     for (String eventType : messageEvent.getEventTypes())
  85.                         fireMessageEventNotificationListeners(
  86.                             message.getFrom(),
  87.                             messageEvent.getStanzaId(),
  88.                             eventType.concat("Notification"));
  89.             }
  90.         }, PACKET_FILTER);
  91.     }

  92.     /**
  93.      * Adds event notification requests to a message. For each event type that
  94.      * the user wishes event notifications from the message recipient for, <code>true</code>
  95.      * should be passed in to this method.
  96.      *
  97.      * @param message the message to add the requested notifications.
  98.      * @param offline specifies if the offline event is requested.
  99.      * @param delivered specifies if the delivered event is requested.
  100.      * @param displayed specifies if the displayed event is requested.
  101.      * @param composing specifies if the composing event is requested.
  102.      */
  103.     public static void addNotificationsRequests(Message message, boolean offline,
  104.             boolean delivered, boolean displayed, boolean composing) {
  105.         // Create a MessageEvent Package and add it to the message
  106.         MessageEvent messageEvent = new MessageEvent();
  107.         messageEvent.setOffline(offline);
  108.         messageEvent.setDelivered(delivered);
  109.         messageEvent.setDisplayed(displayed);
  110.         messageEvent.setComposing(composing);
  111.         message.addExtension(messageEvent);
  112.     }

  113.     /**
  114.      * Adds a message event request listener. The listener will be fired anytime a request for
  115.      * event notification is received.
  116.      *
  117.      * @param messageEventRequestListener a message event request listener.
  118.      */
  119.     public void addMessageEventRequestListener(MessageEventRequestListener messageEventRequestListener) {
  120.         messageEventRequestListeners.add(messageEventRequestListener);

  121.     }

  122.     /**
  123.      * Removes a message event request listener. The listener will be fired anytime a request for
  124.      * event notification is received.
  125.      *
  126.      * @param messageEventRequestListener a message event request listener.
  127.      */
  128.     public void removeMessageEventRequestListener(MessageEventRequestListener messageEventRequestListener) {
  129.         messageEventRequestListeners.remove(messageEventRequestListener);
  130.     }

  131.     /**
  132.      * Adds a message event notification listener. The listener will be fired anytime a notification
  133.      * event is received.
  134.      *
  135.      * @param messageEventNotificationListener a message event notification listener.
  136.      */
  137.     public void addMessageEventNotificationListener(MessageEventNotificationListener messageEventNotificationListener) {
  138.         messageEventNotificationListeners.add(messageEventNotificationListener);
  139.     }

  140.     /**
  141.      * Removes a message event notification listener. The listener will be fired anytime a notification
  142.      * event is received.
  143.      *
  144.      * @param messageEventNotificationListener a message event notification listener.
  145.      */
  146.     public void removeMessageEventNotificationListener(MessageEventNotificationListener messageEventNotificationListener) {
  147.         messageEventNotificationListeners.remove(messageEventNotificationListener);
  148.     }

  149.     /**
  150.      * Fires message event request listeners.
  151.      */
  152.     private void fireMessageEventRequestListeners(
  153.         Jid from,
  154.         String packetID,
  155.         String methodName) {
  156.         try {
  157.             Method method =
  158.                 MessageEventRequestListener.class.getDeclaredMethod(
  159.                     methodName,
  160.                     new Class<?>[] { Jid.class, String.class, MessageEventManager.class });
  161.             for (MessageEventRequestListener listener : messageEventRequestListeners) {
  162.                 method.invoke(listener, new Object[] { from, packetID, this });
  163.             }
  164.         } catch (Exception e) {
  165.             LOGGER.log(Level.SEVERE, "Error while invoking MessageEventRequestListener's  " + methodName, e);
  166.         }
  167.     }

  168.     /**
  169.      * Fires message event notification listeners.
  170.      */
  171.     private void fireMessageEventNotificationListeners(
  172.         Jid from,
  173.         String packetID,
  174.         String methodName) {
  175.         try {
  176.             Method method =
  177.                 MessageEventNotificationListener.class.getDeclaredMethod(
  178.                     methodName,
  179.                     new Class<?>[] { Jid.class, String.class });
  180.             for (MessageEventNotificationListener listener : messageEventNotificationListeners) {
  181.                 method.invoke(listener, new Object[] { from, packetID });
  182.             }
  183.         } catch (Exception e) {
  184.             LOGGER.log(Level.SEVERE, "Error while invoking MessageEventNotificationListener's " + methodName, e);
  185.         }
  186.     }

  187.     /**
  188.      * Sends the notification that the message was delivered to the sender of the original message.
  189.      *
  190.      * @param to the recipient of the notification.
  191.      * @param packetID the id of the message to send.
  192.      * @throws NotConnectedException if the XMPP connection is not connected.
  193.      * @throws InterruptedException if the calling thread was interrupted.
  194.      */
  195.     public void sendDeliveredNotification(Jid to, String packetID) throws NotConnectedException, InterruptedException {
  196.         // Create a MessageEvent Package and add it to the message
  197.         MessageEvent messageEvent = new MessageEvent();
  198.         messageEvent.setDelivered(true);
  199.         messageEvent.setStanzaId(packetID);

  200.         XMPPConnection connection = connection();
  201.         Message msg = connection.getStanzaFactory().buildMessageStanza()
  202.                 .to(to)
  203.                 .addExtension(messageEvent)
  204.                 .build();
  205.         // Send the packet
  206.         connection.sendStanza(msg);
  207.     }

  208.     /**
  209.      * Sends the notification that the message was displayed to the sender of the original message.
  210.      *
  211.      * @param to the recipient of the notification.
  212.      * @param packetID the id of the message to send.
  213.      * @throws NotConnectedException if the XMPP connection is not connected.
  214.      * @throws InterruptedException if the calling thread was interrupted.
  215.      */
  216.     public void sendDisplayedNotification(Jid to, String packetID) throws NotConnectedException, InterruptedException {
  217.         // Create a MessageEvent Package and add it to the message
  218.         MessageEvent messageEvent = new MessageEvent();
  219.         messageEvent.setDisplayed(true);
  220.         messageEvent.setStanzaId(packetID);

  221.         XMPPConnection connection = connection();
  222.         Message msg = connection.getStanzaFactory().buildMessageStanza()
  223.                 .to(to)
  224.                 .addExtension(messageEvent)
  225.                 .build();
  226.         // Send the packet
  227.         connection.sendStanza(msg);
  228.     }

  229.     /**
  230.      * Sends the notification that the receiver of the message is composing a reply.
  231.      *
  232.      * @param to the recipient of the notification.
  233.      * @param packetID the id of the message to send.
  234.      * @throws NotConnectedException if the XMPP connection is not connected.
  235.      * @throws InterruptedException if the calling thread was interrupted.
  236.      */
  237.     public void sendComposingNotification(Jid to, String packetID) throws NotConnectedException, InterruptedException {
  238.         // Create a MessageEvent Package and add it to the message
  239.         MessageEvent messageEvent = new MessageEvent();
  240.         messageEvent.setComposing(true);
  241.         messageEvent.setStanzaId(packetID);

  242.         XMPPConnection connection = connection();
  243.         Message msg = connection.getStanzaFactory().buildMessageStanza()
  244.                 .to(to)
  245.                 .addExtension(messageEvent)
  246.                 .build();
  247.         // Send the packet
  248.         connection.sendStanza(msg);
  249.     }

  250.     /**
  251.      * Sends the notification that the receiver of the message has cancelled composing a reply.
  252.      *
  253.      * @param to the recipient of the notification.
  254.      * @param packetID the id of the message to send.
  255.      * @throws NotConnectedException if the XMPP connection is not connected.
  256.      * @throws InterruptedException if the calling thread was interrupted.
  257.      */
  258.     public void sendCancelledNotification(Jid to, String packetID) throws NotConnectedException, InterruptedException {
  259.         // Create a MessageEvent Package and add it to the message
  260.         MessageEvent messageEvent = new MessageEvent();
  261.         messageEvent.setCancelled(true);
  262.         messageEvent.setStanzaId(packetID);

  263.         XMPPConnection connection = connection();

  264.         Message msg = connection.getStanzaFactory().buildMessageStanza()
  265.                 .to(to)
  266.                 .addExtension(messageEvent)
  267.                 .build();
  268.         // Send the packet
  269.         connection().sendStanza(msg);
  270.     }
  271. }