From f73d03ef5eaceaa4c4092f3fb4bba13fa55e0ab9 Mon Sep 17 00:00:00 2001 From: Damian Minkov Date: Thu, 25 Nov 2010 09:24:02 +0000 Subject: [PATCH] Introduce sending/receiving generic events operation set and its implementation for jabber protocol. --- ...tionSetGenericNotificationsJabberImpl.java | 314 ++++++++++++++++++ .../ProtocolProviderServiceJabberImpl.java | 4 + .../notification/NotificationEventIQ.java | 149 +++++++++ .../NotificationEventIQProvider.java | 67 ++++ .../OperationSetGenericNotifications.java | 68 ++++ .../service/protocol/event/GenericEvent.java | 121 +++++++ .../protocol/event/GenericEventListener.java | 22 ++ 7 files changed, 745 insertions(+) create mode 100644 src/net/java/sip/communicator/impl/protocol/jabber/OperationSetGenericNotificationsJabberImpl.java create mode 100644 src/net/java/sip/communicator/impl/protocol/jabber/extensions/notification/NotificationEventIQ.java create mode 100644 src/net/java/sip/communicator/impl/protocol/jabber/extensions/notification/NotificationEventIQProvider.java create mode 100644 src/net/java/sip/communicator/service/protocol/OperationSetGenericNotifications.java create mode 100644 src/net/java/sip/communicator/service/protocol/event/GenericEvent.java create mode 100644 src/net/java/sip/communicator/service/protocol/event/GenericEventListener.java diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetGenericNotificationsJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetGenericNotificationsJabberImpl.java new file mode 100644 index 000000000..5b080f2e4 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetGenericNotificationsJabberImpl.java @@ -0,0 +1,314 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.jabber; + +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.event.*; +import net.java.sip.communicator.impl.protocol.jabber.extensions.notification.*; +import net.java.sip.communicator.util.*; +import org.jivesoftware.smack.*; +import org.jivesoftware.smack.filter.*; +import org.jivesoftware.smack.packet.*; +import org.jivesoftware.smack.provider.*; + +import java.util.*; + +/** + * Provides notification for generic events with name and value, also + * option to generate such events. + * + * @author Damian Minkov + */ +public class OperationSetGenericNotificationsJabberImpl + implements OperationSetGenericNotifications, + PacketListener +{ + /** + * Our class logger + */ + private static final Logger logger = + Logger.getLogger(OperationSetGenericNotificationsJabberImpl.class); + + /** + * The provider that created us. + */ + private final ProtocolProviderServiceJabberImpl jabberProvider; + + /** + * A reference to the persistent presence operation set that we use + * to match incoming event to Contacts and vice versa. + */ + private OperationSetPersistentPresenceJabberImpl opSetPersPresence = null; + + /** + * Listeners that would receive event notifications for + * new event notifications + */ + private final Map> + genericEventListeners + = new HashMap>(); + + /** + * Creates an instance of this operation set. + * @param provider a reference to the ProtocolProviderServiceImpl + * that created us and that we'll use for retrieving the underlying aim + * connection. + */ + OperationSetGenericNotificationsJabberImpl( + ProtocolProviderServiceJabberImpl provider + ) + { + this.jabberProvider = provider; + + provider.addRegistrationStateChangeListener( + new RegistrationStateListener()); + + // register the notification event Extension in the smack library + ProviderManager.getInstance() + .addIQProvider(NotificationEventIQ.ELEMENT_NAME, + NotificationEventIQ.NAMESPACE, + new NotificationEventIQProvider()); + } + + /** + * Generates new event notification. + * + * @param contact the contact to receive the notification. + * @param eventName the event name of the notification. + * @param eventValue the event value of the notification. + */ + public void notifyForEvent( + Contact contact, + String eventName, + String eventValue) + { + // if we are not registered do nothing + if(!jabberProvider.isRegistered()) + { + if (logger.isTraceEnabled()) + logger.trace("provider not registered. " + +"won't send keep alive. acc.id=" + + jabberProvider.getAccountID() + .getAccountUniqueID()); + return; + } + + NotificationEventIQ newEvent = new NotificationEventIQ(); + newEvent.setEventName(eventName); + newEvent.setEventValue(eventValue); + newEvent.setTo(contact.getAddress()); + newEvent.setEventSource(jabberProvider.getOurJID()); + + jabberProvider.getConnection().sendPacket(newEvent); + } + + /** + * Generates new generic event notification and send it to the + * supplied contact. + * @param jid the contact jid which will receive the event notification. + * @param eventName the event name of the notification. + * @param eventValue the event value of the notification. + */ + public void notifyForEvent( + String jid, + String eventName, + String eventValue) + { + // if we are not registered do nothing + if(!jabberProvider.isRegistered()) + { + if (logger.isTraceEnabled()) + logger.trace("provider not registered. " + +"won't send keep alive. acc.id=" + + jabberProvider.getAccountID() + .getAccountUniqueID()); + return; + } + + NotificationEventIQ newEvent = new NotificationEventIQ(); + newEvent.setEventName(eventName); + newEvent.setEventValue(eventValue); + newEvent.setTo(jid); + newEvent.setEventSource(jabberProvider.getOurJID()); + + jabberProvider.getConnection().sendPacket(newEvent); + } + + /** + * Registers a GenericEventListener with this + * operation set so that it gets notifications for new + * event notifications. + * + * @param eventName register the listener for certain event name. + * @param listener the GenericEventListener + * to register. + */ + public void addGenericEventListener( + String eventName, + GenericEventListener listener) + { + synchronized (genericEventListeners) + { + List l = + this.genericEventListeners.get(eventName); + if(l == null) + { + l = new ArrayList(); + this.genericEventListeners.put(eventName, l); + } + + if(!l.contains(listener)) + l.add(listener); + } + } + + /** + * Unregisters listener so that it won't receive any further + * notifications upon new event notifications. + * + * @param eventName unregister the listener for certain event name. + * @param listener the GenericEventListener + * to unregister. + */ + public void removeGenericEventListener( + String eventName, + GenericEventListener listener) + { + synchronized (genericEventListeners) + { + List l = this.genericEventListeners.get(eventName); + if(l != null) + this.genericEventListeners.remove(listener); + } + } + + /** + * Process the next packet sent to this packet listener.

+ *

+ * + * @param packet the packet to process. + */ + public void processPacket(Packet packet) + { + if(packet != null && !(packet instanceof NotificationEventIQ)) + return; + + NotificationEventIQ notifyEvent = (NotificationEventIQ)packet; + + if(logger.isDebugEnabled()) + { + if (logger.isDebugEnabled()) + logger.debug("Received notificationEvent from " + + notifyEvent.getFrom() + + " msg : " + + notifyEvent.toXML()); + } + + String fromUserID + = org.jivesoftware.smack.util.StringUtils.parseBareAddress( + notifyEvent.getFrom()); + + Contact sender = opSetPersPresence.findContactByID(fromUserID); + + if(sender == null) + sender = opSetPersPresence.createVolatileContact(fromUserID); + + fireNewEventNotification( + sender, + notifyEvent.getEventName(), + notifyEvent.getEventValue(), + notifyEvent.getEventSource()); + } + + /** + * Fires new notification event. + * @param from common from Contact. + * @param eventName the event name. + * @param eventValue the event value. + */ + private void fireNewEventNotification( + Contact from, + String eventName, + String eventValue, + String source) + { + String sourceUserID + = org.jivesoftware.smack.util.StringUtils.parseBareAddress( + source); + Contact sourceContact = + opSetPersPresence.findContactByID(sourceUserID); + if(sourceContact == null) + sourceContact = opSetPersPresence + .createVolatileContact(sourceUserID); + + GenericEvent + event = new GenericEvent( + jabberProvider, from, eventName, eventValue, sourceContact); + + Iterable listeners; + synchronized (genericEventListeners) + { + List ls = + genericEventListeners.get(eventName); + + if(ls == null) + return; + + listeners = new ArrayList(ls); + } + for (GenericEventListener listener : listeners) + { + listener.notify(event); + } + } + + /** + * Our listener that will tell us when we're registered to + */ + private class RegistrationStateListener + implements RegistrationStateChangeListener + { + /** + * The method is called by a ProtocolProvider implementation whenever + * a change in the registration state of the corresponding provider had + * occurred. + * @param evt ProviderStatusChangeEvent the event describing the status + * change. + */ + public void registrationStateChanged(RegistrationStateChangeEvent evt) + { + if (evt.getNewState() == RegistrationState.REGISTERED) + { + OperationSetGenericNotificationsJabberImpl.this.opSetPersPresence + = (OperationSetPersistentPresenceJabberImpl) + jabberProvider.getOperationSet( + OperationSetPersistentPresence.class); + + if(jabberProvider.getConnection() != null) + { + jabberProvider.getConnection().addPacketListener( + OperationSetGenericNotificationsJabberImpl.this, + new PacketTypeFilter(NotificationEventIQ.class)); + } + } + else if(evt.getNewState() == RegistrationState.UNREGISTERED + || evt.getNewState() == RegistrationState.CONNECTION_FAILED + || evt.getNewState() == RegistrationState.AUTHENTICATION_FAILED) + { + if(jabberProvider.getConnection() != null) + { + jabberProvider.getConnection().removePacketListener( + OperationSetGenericNotificationsJabberImpl.this); + } + + OperationSetGenericNotificationsJabberImpl.this.opSetPersPresence + = null; + } + } + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java index 4ccb7abba..b4acd7df5 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java @@ -1166,6 +1166,10 @@ protected void initialize(String screenname, OperationSetContactCapabilities.class, opsetContactCapabilities); + addSupportedOperationSet( + OperationSetGenericNotifications.class, + new OperationSetGenericNotificationsJabberImpl(this)); + isInitialized = true; } } diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/notification/NotificationEventIQ.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/notification/NotificationEventIQ.java new file mode 100644 index 000000000..d71b03b26 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/notification/NotificationEventIQ.java @@ -0,0 +1,149 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.jabber.extensions.notification; + +import org.jivesoftware.smack.packet.*; +import org.jivesoftware.smack.util.*; + +/** + * Notification Event. Events are send to notify for some event + * holding a value. + * + * @author Damian Minkov + */ +public class NotificationEventIQ + extends IQ +{ + /** + * The namespace that input notification belongs to. + */ + public static final String NAMESPACE = "sip-communicator:iq:notification"; + + /** + * The name of the element that contains the notification event data. + */ + public static final String ELEMENT_NAME = "notification"; + + /** + * The name of the argument that contains the event name. + */ + static final String EVENT_NAME = "name"; + + /** + * The name of the argument that contains the event value. + */ + static final String EVENT_VALUE = "value"; + + /** + * The name of the argument that contains the event source. + */ + static final String EVENT_SOURCE = "source"; + + /** + * Current notification event name. + */ + private String eventName; + + /** + * Current notification event value. + */ + private String eventValue; + + /** + * Current notification event source. + */ + private String eventSource; + + /** + * Returns the sub-element XML section of the IQ packet, + * or null if there isn't one. Packet extensions must + * be included, if any are defined.

+ *

+ * Extensions of this class must override this method. + * + * @return the child element section of the IQ XML. + */ + @Override + public String getChildElementXML() + { + StringBuilder buf = new StringBuilder(); + buf.append("<").append(ELEMENT_NAME). + append(" xmlns=\"").append(NAMESPACE). + append("\">"); + + buf.append("<").append(EVENT_NAME).append(">"). + append(StringUtils.escapeForXML(this.getEventName())) + .append(""); + + buf.append("<"). + append(EVENT_VALUE).append(">"). + append(StringUtils.escapeForXML(this.getEventValue())) + .append(""); + + buf.append("<"). + append(EVENT_SOURCE).append(">"). + append(StringUtils.escapeForXML(this.getEventSource())) + .append(""); + + buf.append(""); + return buf.toString(); + } + + /** + * Current notification event name. + * @return current event name. + */ + public String getEventName() + { + return eventName; + } + + /** + * Sets event new name. + * @param eventName new event name. + */ + public void setEventName(String eventName) + { + this.eventName = eventName; + } + + /** + * Current notification event value. + * @return current event value. + */ + public String getEventValue() + { + return eventValue; + } + + /** + * Sets event new value. + * @param eventValue new event value. + */ + public void setEventValue(String eventValue) + { + this.eventValue = eventValue; + } + + /** + * Current notification event source. + * @return the current notification event source. + */ + public String getEventSource() + { + return eventSource; + } + + /** + * Sets event source new value. + * @param eventSource value. + */ + public void setEventSource(String eventSource) + { + this.eventSource = eventSource; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/notification/NotificationEventIQProvider.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/notification/NotificationEventIQProvider.java new file mode 100644 index 000000000..1601fb982 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/notification/NotificationEventIQProvider.java @@ -0,0 +1,67 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.protocol.jabber.extensions.notification; + +import org.jivesoftware.smack.packet.*; +import org.jivesoftware.smack.provider.*; +import org.xmlpull.v1.*; + +/** + * The NotificationEventIQProvider parses Notification Event packets. + * + * @author Damian Minkov + */ +public class NotificationEventIQProvider + implements IQProvider +{ + /** + * Parse the IQ sub-document and create an IQ instance. Each IQ must have a + * single child element. At the beginning of the method call, the xml parser + * will be positioned at the opening tag of the IQ child element. At the end + * of the method call, the parser must be positioned on the closing + * tag of the child element. + * + * @param parser an XML parser. + * @return a new IQ instance. + * @throws Exception if an error occurs parsing the XML. + */ + public IQ parseIQ(XmlPullParser parser) + throws Exception + { + NotificationEventIQ result = new NotificationEventIQ(); + + boolean done = false; + while (!done) + { + int eventType = parser.next(); + if(eventType == XmlPullParser.START_TAG) + { + if(parser.getName().equals(NotificationEventIQ.EVENT_NAME)) + { + result.setEventName(parser.nextText()); + } + else if(parser.getName().equals(NotificationEventIQ.EVENT_VALUE)) + { + result.setEventValue(parser.nextText()); + } + else if(parser.getName().equals(NotificationEventIQ.EVENT_SOURCE)) + { + result.setEventSource(parser.nextText()); + } + } + else if(eventType == XmlPullParser.END_TAG) + { + if(parser.getName().equals(NotificationEventIQ.ELEMENT_NAME)) + { + done = true; + } + } + } + + return result; + } +} diff --git a/src/net/java/sip/communicator/service/protocol/OperationSetGenericNotifications.java b/src/net/java/sip/communicator/service/protocol/OperationSetGenericNotifications.java new file mode 100644 index 000000000..923635795 --- /dev/null +++ b/src/net/java/sip/communicator/service/protocol/OperationSetGenericNotifications.java @@ -0,0 +1,68 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.service.protocol; + +import net.java.sip.communicator.service.protocol.event.*; + +/** + * Provides notification for custom/generic events and possibility to generate + * such events. + * + * @author Damian Minkov + */ +public interface OperationSetGenericNotifications + extends OperationSet +{ + /** + * Generates new generic event notification and send it to the + * supplied contact. + * @param contact the contact to receive the event notification. + * @param eventName the event name of the notification. + * @param eventValue the event value of the notification. + */ + public void notifyForEvent( + Contact contact, + String eventName, + String eventValue); + + /** + * Generates new generic event notification and send it to the + * supplied contact. + * @param jid the contact jid which will receive the event notification. + * @param eventName the event name of the notification. + * @param eventValue the event value of the notification. + */ + public void notifyForEvent( + String jid, + String eventName, + String eventValue); + + /** + * Registers a GenericEventListener with this + * operation set so that it gets notifications for new + * event notifications. + * + * @param eventName register the listener for certain event name. + * @param listener the GenericEventListener + * to register. + */ + public void addGenericEventListener( + String eventName, + GenericEventListener listener); + + /** + * Unregisters listener so that it won't receive any further + * notifications upon new event notifications. + * + * @param eventName unregister the listener for certain event name. + * @param listener the GenericEventListener + * to unregister. + */ + public void removeGenericEventListener( + String eventName, + GenericEventListener listener); +} diff --git a/src/net/java/sip/communicator/service/protocol/event/GenericEvent.java b/src/net/java/sip/communicator/service/protocol/event/GenericEvent.java new file mode 100644 index 000000000..b5b52c612 --- /dev/null +++ b/src/net/java/sip/communicator/service/protocol/event/GenericEvent.java @@ -0,0 +1,121 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.service.protocol.event; + +import net.java.sip.communicator.service.protocol.*; + +import java.util.*; + +/** + * GenericEvents indicate reception of an new generic event. + * + * @author Damian Minkov + */ +public class GenericEvent + extends EventObject +{ + /** + * The contact which is the source of this event. + */ + private Contact from; + + /** + * The event name. + */ + private String eventName; + + /** + * The event value. + */ + private String eventValue; + + /** + * The source contact for this event. + */ + private Contact sourceContact; + + /** + * Constructs a GenericEvent. + * + * @param sourceProtocolProvider The object on which the Event initially occurred. + * @param from the contact from which this event is coming from. + * @param eventName the event name. + * @param eventValue the event value. + * @param sourceContact contact for this event. + * @throws IllegalArgumentException if source is null. + */ + public GenericEvent(ProtocolProviderService sourceProtocolProvider, + Contact from, + String eventName, + String eventValue, + Contact sourceContact) + { + super(sourceProtocolProvider); + + this.from = from; + this.eventName = eventName; + this.eventValue = eventValue; + this.sourceContact = sourceContact; + } + + /** + * The event name. + * @return the event name. + */ + public String getEventName() + { + return eventName; + } + + /** + * The event value. + * @return the event value. + */ + public String getEventValue() + { + return eventValue; + } + + /** + * The contact which is the source of this event. + * @return the contact which is the source of this event. + */ + public Contact getFrom() + { + return from; + } + + /** + * Returns the ProtocolProviderService which originated this event. + * + * @return the source ProtocolProviderService + */ + public ProtocolProviderService getSourceProvider() + { + return (ProtocolProviderService) getSource(); + } + + /** + * Returns a String representation of this GenericEvent. + * + * @return A a String representation of this GenericEvent. + */ + public String toString() + { + return "GenericEvent from:" + from + " - eventName:"+ eventName + + " eventValue:" + eventValue; + } + + /** + * Returns The source contact for this event. + * @return the event source contact. + */ + public Contact getSourceContact() + { + return sourceContact; + } +} diff --git a/src/net/java/sip/communicator/service/protocol/event/GenericEventListener.java b/src/net/java/sip/communicator/service/protocol/event/GenericEventListener.java new file mode 100644 index 000000000..b4f0bb321 --- /dev/null +++ b/src/net/java/sip/communicator/service/protocol/event/GenericEventListener.java @@ -0,0 +1,22 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.service.protocol.event; + +/** + * A listener that would gather and notify for incoming generic + * events. + * + * @author Damian Minkov + */ +public interface GenericEventListener +{ + /** + * Notify for incoming GenericEvent. + * @param event the incoming event. + */ + public void notify(GenericEvent event); +}