From 3db54ce3c7811065dfc9fef98e99ea369b7f65a2 Mon Sep 17 00:00:00 2001 From: Yana Stamcheva Date: Wed, 28 Nov 2007 11:09:06 +0000 Subject: [PATCH] whiteboard implementation --- .../OperationSetWhiteboardingJabberImpl.java | 494 +++++++ .../WhiteboardInvitationJabberImpl.java | 111 ++ .../WhiteboardParticipantJabberImpl.java | 420 ++++++ .../jabber/WhiteboardSessionJabberImpl.java | 1220 +++++++++++++++++ 4 files changed, 2245 insertions(+) create mode 100644 src/net/java/sip/communicator/impl/protocol/jabber/OperationSetWhiteboardingJabberImpl.java create mode 100644 src/net/java/sip/communicator/impl/protocol/jabber/WhiteboardInvitationJabberImpl.java create mode 100644 src/net/java/sip/communicator/impl/protocol/jabber/WhiteboardParticipantJabberImpl.java create mode 100644 src/net/java/sip/communicator/impl/protocol/jabber/WhiteboardSessionJabberImpl.java diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetWhiteboardingJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetWhiteboardingJabberImpl.java new file mode 100644 index 000000000..802c8ac9e --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetWhiteboardingJabberImpl.java @@ -0,0 +1,494 @@ +/* + * 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 java.util.*; + +import net.java.sip.communicator.impl.protocol.jabber.extensions.whiteboard.*; +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.event.*; +import net.java.sip.communicator.service.protocol.whiteboardobjects.*; +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 org.jivesoftware.smack.util.*; + +/** + * Provides basic functionality for white-board. + * + * @author Julien Waechter + * @author Yana Stamcheva + */ +public class OperationSetWhiteboardingJabberImpl + implements OperationSetWhiteboarding +{ + private static final Logger logger = + Logger.getLogger(OperationSetWhiteboardingJabberImpl.class); + + /** + * The provider that created us. + */ + private ProtocolProviderServiceJabberImpl jabberProvider = null; + + /** + * A list of listeners subscribed for invitations multi user chat events. + */ + private Vector invitationListeners = new Vector(); + + /** + * A list of listeners subscribed for events indicating rejection of a + * multi user chat invitation sent by us. + */ + private Vector invitationRejectionListeners = new Vector(); + + /** + * Listeners that will be notified of changes in our status in the + * room such as us being kicked, banned, or granted admin permissions. + */ + private Vector presenceListeners = new Vector(); + + private Vector whiteboardSessions = new Vector(); + + private OperationSetPersistentPresenceJabberImpl presenceOpSet; + + /** + * Creates an instance of this operation set. + * @param provider a ref to the ProtocolProviderServiceImpl + * that created us and that we'll use for retrieving the underlying aim + * connection. + */ + public OperationSetWhiteboardingJabberImpl( + ProtocolProviderServiceJabberImpl provider) + { + this.jabberProvider = provider; + + provider.addRegistrationStateChangeListener( + new RegistrationStateListener()); + + // Add the custom WhiteboardObjectJabberProvider to the Smack library + ProviderManager pManager = ProviderManager.getInstance(); + + pManager.addExtensionProvider( + WhiteboardObjectPacketExtensionImpl.ELEMENT_NAME, + WhiteboardObjectPacketExtensionImpl.NAMESPACE, + new WhiteboardObjectJabberProvider()); + } + + /** + * Adds a listener to invitation notifications. + * + * @param listener an invitation listener. + */ + public void addInvitationListener(WhiteboardInvitationListener listener) + { + synchronized(invitationListeners) + { + if (!invitationListeners.contains(listener)) + invitationListeners.add(listener); + } + } + + /** + * Removes listener from the list of invitation listeners + * registered to receive invitation events. + * + * @param listener the invitation listener to remove. + */ + public void removeInvitationListener(WhiteboardInvitationListener listener) + { + synchronized(invitationListeners) + { + invitationListeners.remove(listener); + } + } + + /** + * Subscribes listener so that it would receive events indicating + * rejection of a multi-user chat invitation that we've sent earlier. + * + * @param listener the listener that we'll subscribe for invitation + * rejection events. + */ + public void addInvitationRejectionListener( + WhiteboardInvitationRejectionListener listener) + { + synchronized(invitationRejectionListeners) + { + if (!invitationRejectionListeners.contains(listener)) + invitationRejectionListeners.add(listener); + } + } + + /** + * Removes listener from the list of invitation listeners + * registered to receive invitation rejection events. + * + * @param listener the invitation listener to remove. + */ + public void removeInvitationRejectionListener( + WhiteboardInvitationRejectionListener listener) + { + synchronized(invitationRejectionListeners) + { + invitationRejectionListeners.remove(listener); + } + } + + /** + * Adds a listener that will be notified of changes in our status in a chat + * room such as us being kicked, banned or dropped. + * + * @param listener the LocalUserChatRoomPresenceListener. + */ + public void addPresenceListener(WhiteboardSessionPresenceListener listener) + { + synchronized(presenceListeners) + { + if (!presenceListeners.contains(listener)) + presenceListeners.add(listener); + } + } + + /** + * Removes a listener that was being notified of changes in our status in + * a room such as us being kicked, banned or dropped. + * + * @param listener the LocalUserChatRoomPresenceListener. + */ + public void removePresenceListener( + WhiteboardSessionPresenceListener listener) + { + synchronized(presenceListeners) + { + presenceListeners.remove(listener); + } + } + + /** + * Creates a WhiteboardSession. For now the session is created + * locally and neither the sessionName, nor the sessionProperties are + * used. + * @param sessionName the name of the session + * @param sessionProperties the settings of the session + * @throws OperationFailedException if the room couldn't be created for some + * reason (e.g. room already exists; user already joined to an existent + * room or user has no permissions to create a chat room). + * @throws OperationNotSupportedException if chat room creation is not + * supported by this server + * @return the created white-board session + */ + public WhiteboardSession createWhiteboardSession( + String sessionName, + Hashtable sessionProperties) + throws OperationFailedException, + OperationNotSupportedException + { + WhiteboardSessionJabberImpl session + = new WhiteboardSessionJabberImpl(jabberProvider, this); + + whiteboardSessions.add(session); + + return session; + } + + /** + * Returns a reference to a WhiteboardSession named + * sessionName or null if no such session exists. + *

+ * @param sessionName the name of the WhiteboardSession that we're + * looking for. + * @return the WhiteboardSession named sessionName or null + * if no such session exists on the server that this provider is currently + * connected to. + * + * @throws OperationFailedException if an error occurs while trying to + * discover the white-board session on the server. + * @throws OperationNotSupportedException if the server does not support + * white-boarding + */ + public WhiteboardSession findWhiteboardSession(String sessionName) + throws OperationFailedException, + OperationNotSupportedException + { + // TODO: Implement findWhiteboardSession + return null; + } + + /** + * Returns a list of the white-board sessions that we have joined and are + * currently active in. + * + * @return a List of the white-board sessions where the user has + * joined using a given connection. + */ + public List getCurrentlyJoinedWhiteboards() + { + synchronized(whiteboardSessions) + { + List joinedWhiteboards + = new LinkedList(whiteboardSessions); + + Iterator joinedWhiteboardsIter = whiteboardSessions.iterator(); + + while (joinedWhiteboardsIter.hasNext()) + { + if (!((WhiteboardSession) joinedWhiteboardsIter.next()) + .isJoined()) + joinedWhiteboardsIter.remove(); + } + + return joinedWhiteboards; + } + } + + /** + * Returns a list of the WhiteboardSessions that + * WhiteboardParticipant has joined and is currently active in. + * + * @param participant the participant whose current + * WhiteboardSessions we will be querying. + * @return a list of the WhiteboardSessions that + * WhiteboardParticipant has joined and is currently active in. + * + * @throws OperationFailedException if an error occurs while trying to + * discover the session. + * @throws OperationNotSupportedException if the server does not support + * white-boarding + */ + public List getCurrentlyJoinedWhiteboards(WhiteboardParticipant participant) + throws OperationFailedException, + OperationNotSupportedException + { + // TODO: Implement getCurrentlyJoinedWhiteboards( + // WhiteboardParticipant participant) + return null; + } + + /** + * Returns true if contact supports white-board sessions. + * + * @param contact reference to the contact whose support for white-boards + * we are currently querying. + * @return a boolean indicating whether contact supports + * white-boards. + */ + public boolean isWhiteboardingSupportedByContact(Contact contact) + { + if(contact.getProtocolProvider() + .getOperationSet(OperationSetWhiteboarding.class) != null) + return true; + + return false; + } + + /** + * Informs the sender of an invitation that we decline their invitation. + * + * @param invitation the invitation we are rejecting. + * @param rejectReason the reason to reject the invitation (optional) + */ + public void rejectInvitation(WhiteboardInvitation invitation, + String rejectReason) + { + // TODO: Implement rejectInvitation(WhiteboardInvitation invitation, + // String rejectReason) + } + + /** + * 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) + { + presenceOpSet + = (OperationSetPersistentPresenceJabberImpl) jabberProvider + .getOperationSet(OperationSetPresence.class); + + PacketExtensionFilter filterWhiteboard = + new PacketExtensionFilter( + WhiteboardObjectPacketExtensionImpl.ELEMENT_NAME, + WhiteboardObjectPacketExtensionImpl.NAMESPACE); + + jabberProvider.getConnection().addPacketListener( + new WhiteboardSmackMessageListener(), filterWhiteboard); + } + } + } + + /** + * Listens for white-board messages and checks if a white-board session + * already exists and if not simulates an invitation to the user. + */ + private class WhiteboardSmackMessageListener + implements PacketListener + { + public void processPacket(Packet packet) + { + if (!(packet instanceof org.jivesoftware.smack.packet.Message)) + return; + + PacketExtension ext = + packet.getExtension( + WhiteboardObjectPacketExtensionImpl.ELEMENT_NAME, + WhiteboardObjectPacketExtensionImpl.NAMESPACE); + + org.jivesoftware.smack.packet.Message msg = + (org.jivesoftware.smack.packet.Message) packet; + + if (ext == null) + return; + + String fromUserID = StringUtils.parseBareAddress(msg.getFrom()); + + // We check if a white-board session with the given contact already + // exists and if this is the case we don't continue. + for (int i = 0; i < whiteboardSessions.size(); i ++) + { + WhiteboardSessionJabberImpl session + = (WhiteboardSessionJabberImpl) whiteboardSessions.get(i); + + // Should be replaced by getParticipants when implementing + // the multi user white-boarding + if(session.isJoined() + && session.isParticipantContained(fromUserID)) + return; + } + + // If we're here this means that no white board session has been + // found and we will send an invitation to the user to join a + // white-board session created by us. + WhiteboardObjectPacketExtensionImpl newMessage + = (WhiteboardObjectPacketExtensionImpl) ext; + + WhiteboardSessionJabberImpl session + = new WhiteboardSessionJabberImpl( + jabberProvider, + OperationSetWhiteboardingJabberImpl.this); + + whiteboardSessions.add(session); + + ContactJabberImpl sourceContact + = (ContactJabberImpl) presenceOpSet.findContactByID(fromUserID); + + if (sourceContact == null) + { + logger.debug("Received a message from an unknown contact: " + + fromUserID); + + //create the volatile contact + sourceContact + = presenceOpSet.createVolatileContact(fromUserID); + } + + session.addWhiteboardParticipant( + new WhiteboardParticipantJabberImpl(sourceContact, session)); + + fireInvitationEvent(session, + newMessage.getWhiteboardObject(), + fromUserID, + null, + null); + } + } + + /** + * Delivers a WhiteboardInvitationEvent to all + * registered WhiteboardInvitationListeners. + * + * @param targetWhiteboard the white-board that invitation refers to + * @param whiteboardObject the white-board object that inviter send + * with this invitation and which will be shown on the white-board if the + * user accepts the invitation + * @param inviter the inviter that sent the invitation + * @param reason the reason why the inviter sent the invitation + * @param password the password to use when joining the room + */ + public void fireInvitationEvent(WhiteboardSession targetWhiteboard, + WhiteboardObject whiteboardObject, + String inviter, + String reason, + byte[] password) + { + WhiteboardInvitationJabberImpl invitation + = new WhiteboardInvitationJabberImpl( targetWhiteboard, + whiteboardObject, + inviter, + reason, + password); + + WhiteboardInvitationReceivedEvent evt + = new WhiteboardInvitationReceivedEvent(this, invitation, + new Date(System.currentTimeMillis())); + + logger.debug("Dispatching a WhiteboardInvitation event to " + + invitationListeners.size() + " listeners. event is: " + + evt.toString()); + + Iterator listeners = null; + synchronized (invitationListeners) + { + listeners = new ArrayList(invitationListeners).iterator(); + } + + while (listeners.hasNext()) + { + WhiteboardInvitationListener listener + = (WhiteboardInvitationListener) listeners.next(); + + listener.invitationReceived(evt); + } + } + + /** + * Delivers a WhiteboardSessionPresenceChangeEvent to all + * registered WhiteboardSessionPresenceChangeEvents. + * + * @param session the WhiteboardSession which has been joined, + * left, etc. + * @param eventType the type of this event; one of LOCAL_USER_JOINED, + * LOCAL_USER_LEFT, etc. + * @param reason the reason + */ + public void fireWhiteboardSessionPresenceEvent( WhiteboardSession session, + String eventType, + String reason) + { + WhiteboardSessionPresenceChangeEvent evt + = new WhiteboardSessionPresenceChangeEvent( this, + session, + eventType, + reason); + + Iterator listeners = null; + synchronized (presenceListeners) + { + listeners = new ArrayList(presenceListeners).iterator(); + } + + while (listeners.hasNext()) + { + WhiteboardSessionPresenceListener listener + = (WhiteboardSessionPresenceListener) listeners.next(); + + listener.whiteboardSessionPresenceChanged(evt); + } + } +} \ No newline at end of file diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/WhiteboardInvitationJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/WhiteboardInvitationJabberImpl.java new file mode 100644 index 000000000..1675ac8ce --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/WhiteboardInvitationJabberImpl.java @@ -0,0 +1,111 @@ +/* + * 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.whiteboardobjects.*; + +/** + * The Jabber implementation of the WhiteboardInvitation interface. + * + * @author Yana Stamcheva + */ +public class WhiteboardInvitationJabberImpl + implements WhiteboardInvitation +{ + private WhiteboardSession whiteboardSession; + + private WhiteboardObject firstWhiteboardObject; + + private String inviter; + + private String reason; + + private byte[] password; + + /** + * Creates an invitation for the given targetWhiteboard, from the + * given inviter. + * + * @param targetWhiteboard the WhiteboardSession for which the + * invitation is + * @param firstWhiteboardObject the white-board object that inviter send + * with this invitation and which will be shown on the white-board if the + * user accepts the invitation + * @param inviter the WhiteboardParticipant, which sent the + * invitation + * @param reason the reason of the invitation + * @param password the password to use to join the given white-board + */ + public WhiteboardInvitationJabberImpl( + WhiteboardSession targetWhiteboard, + WhiteboardObject firstWhiteboardObject, + String inviter, + String reason, + byte[] password) + { + this.whiteboardSession = targetWhiteboard; + this.firstWhiteboardObject = firstWhiteboardObject; + this.inviter = inviter; + this.reason = reason; + this.password = password; + } + + /** + * Returns the WhiteboardSession, that this invitation is about. + * + * @return the WhiteboardSession, that this invitation is about + */ + public WhiteboardSession getTargetWhiteboard() + { + return whiteboardSession; + } + + /** + * Returns the inviter, who sent this invitation. + * + * @return the inviter, who sent this invitation + */ + public String getInviter() + { + return inviter; + } + + /** + * Returns the reason of the invitation. + * + * @return the reason of the invitation + */ + public String getReason() + { + return reason; + } + + /** + * Returns the password to use in order to join the white-board, that this + * invitation is about. + * + * @return the password to use in order to join the white-board, that this + * invitation is about. + */ + public byte[] getWhiteboardPassword() + { + return password; + } + + /** + * Returns the first white-board object that the inviter would like to + * exchange with the user. If the user accepts this invitation he/she + * should see this object on his white-board. + * + * @return the first white-board object + */ + public WhiteboardObject getWhiteboardInitialObject() + { + return firstWhiteboardObject; + } +} \ No newline at end of file diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/WhiteboardParticipantJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/WhiteboardParticipantJabberImpl.java new file mode 100644 index 000000000..dff702a73 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/WhiteboardParticipantJabberImpl.java @@ -0,0 +1,420 @@ +/* + * 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 java.util.*; + +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.event.*; +import net.java.sip.communicator.util.*; + +/** + * The WhiteboardParticipantJabberImpl is a class that represents participants + * in a whiteboard. + * + * @author Julien Waechter + */ +public class WhiteboardParticipantJabberImpl + implements WhiteboardParticipant +{ + + private static final Logger logger = + Logger.getLogger(WhiteboardParticipantJabberImpl.class); + + /** + * The participant + */ + private ContactJabberImpl participant = null; + + /** + * The state of the whiteboard participant. + */ + protected WhiteboardParticipantState whiteboardParticipantState = + WhiteboardParticipantState.UNKNOWN; + + /** + * Indicates the date when is whiteboard participant passed into its current + * state. + */ + protected Date currentStateStartDate = new Date(); + + /** + * A byte array containing the image/photo representing the whiteboard + * participant. + */ + private byte[] image; + + /** + * A string uniquely identifying the participant. + */ + private String participantID; + + /** + * The whiteboard this participant belongs to. + */ + private WhiteboardSessionJabberImpl whiteboard; + + /** + * Creates a new whiteboard participant with address + * participantAddress. + * + * @param participant the JAIN SIP Address of the new whiteboard + * participant. + * + * @param owningWhiteboard the whiteboard that contains this whiteboard + * participant. + */ + public WhiteboardParticipantJabberImpl(ContactJabberImpl participant, + WhiteboardSessionJabberImpl owningWhiteboard) + { + this.participant = participant; + this.whiteboard = owningWhiteboard; + whiteboard.addWhiteboardParticipant(this); + + // create the uid + this.participantID = + String.valueOf(System.currentTimeMillis()) + + String.valueOf(hashCode()); + } + + /** + * Returns the contact identifier representing this contact. + * + * @return a String contact address + */ + public String getContactAddress() + { + return this.participant.getAddress(); + } + + /** + * Returns an object representing the current state of that participant. + * WhiteboardParticipantState may vary among CONNECTING, BUSY, CONNECTED... + * + * @return a WhiteboardParticipantState instance representin the + * participant's state. + */ + public WhiteboardParticipantState getState() + { + return whiteboardParticipantState; + } + + /** + * Causes this WhiteboardParticipant to enter the specified state. The + * method also sets the currentStateStartDate field and fires a + * WhiteboardParticipantChangeEvent. + * + * @param newState the state this whiteboard participant should enter. + * @param reason a string that could be set to contain a human readable + * explanation for the transition (particularly handy when moving + * into a FAILED state). + */ + protected void setState(WhiteboardParticipantState newState, String reason) + { + WhiteboardParticipantState oldState = getState(); + + if (oldState == newState) + return; + + this.whiteboardParticipantState = newState; + this.currentStateStartDate = new Date(); + fireWhiteboardParticipantChangeEvent( + WhiteboardParticipantChangeEvent.WHITEBOARD_PARTICIPANT_STATE_CHANGE, + oldState, newState); + } + + /** + * Causes this WhiteboardParticipant to enter the specified state. The + * method also sets the currentStateStartDate field and fires a + * WhiteboardParticipantChangeEvent. + * + * @param newState the state this whiteboard participant should enter. + */ + protected void setState(WhiteboardParticipantState newState) + { + setState(newState, null); + } + + /** + * Returns the date (time) when this whiteboard participant acquired its + * current status. + * + * @return a java.util.Date object containing the date when this whiteboard + * participant entered its current state. + */ + public Date getCurrentStateStartDate() + { + return currentStateStartDate; + } + + /** + * Returns a human readable name representing this participant. + * + * @return a String containing a name for that participant. + */ + public String getDisplayName() + { + String displayName = participant.getDisplayName(); + return (displayName == null) ? "" : displayName; + } + + /** + * Sets a human readable name representing this participant. + * + * @param displayName the participant's display name + */ + protected void setDisplayName(String displayName) + { + String oldName = getDisplayName(); + /* + * try { this.participant.setDisplayName(displayName); } catch + * (ParseException ex) { //couldn't happen logger.error(ex.getMessage(), + * ex); throw new IllegalArgumentException(ex.getMessage()); } + */ + // Fire the Event + fireWhiteboardParticipantChangeEvent( + WhiteboardParticipantChangeEvent.WHITEBOARD_PARTICIPANT_DISPLAY_NAME_CHANGE, + oldName, displayName); + } + + /** + * The method returns an image representation of the whiteboard participant + * (e.g. + * + * @return byte[] a byte array containing the image or null if no image is + * available. + */ + public byte[] getImage() + { + return image; + } + + /** + * Sets the byte array containing an image representation (photo or picture) + * of the whiteboard participant. + * + * @param image a byte array containing the image + */ + protected void setImage(byte[] image) + { + byte[] oldImage = getImage(); + this.image = image; + + // Fire the Event + fireWhiteboardParticipantChangeEvent( + WhiteboardParticipantChangeEvent.WHITEBOARD_PARTICIPANT_IMAGE_CHANGE, + oldImage, image); + } + + /** + * Returns a unique identifier representing this participant. + * + * @return an identifier representing this whiteboard participant. + */ + public String getParticipantID() + { + return participantID; + } + + /** + * Sets the String that serves as a unique identifier of this + * WhiteboardParticipant. + * + * @param participantID the ID of this whiteboard participant. + */ + protected void setParticipantID(String participantID) + { + this.participantID = participantID; + } + + /** + * Returns the chat room that this member is participating in. + * + * @return the WhiteboardSession instance that this member + * belongs to. + */ + public WhiteboardSession getWhiteboardSession() + { + return whiteboard; + } + + /** + * Sets the whiteboard containing this participant. + * + * @param whiteboard the whiteboard that this whiteboard participant is + * partdicipating in. + */ + protected void setWhiteboard(WhiteboardSessionJabberImpl whiteboard) + { + this.whiteboard = whiteboard; + } + + /** + * Returns the protocol provider instance that this member has originated + * in. + * + * @return the ProtocolProviderService instance that created this + * member and its containing cht room + */ + public ProtocolProviderService getProtocolProvider() + { + return this.getWhiteboardSession().getProtocolProvider(); + } + + /** + * Returns the contact corresponding to this participant or null if no + * particular contact has been associated. + *

+ * + * @return the Contact corresponding to this participant or null + * if no particular contact has been associated. + */ + public Contact getContact() + { + return this.participant; + } + + /** + * All the WhiteboardParticipant listeners registered with this + * WhiteboardParticipant. + */ + protected ArrayList whiteboardParticipantListeners = new ArrayList(); + + /** + * Allows the user interface to register a listener interested in changes + * + * @param listener a listener instance to register with this participant. + */ + public void addWhiteboardParticipantListener( + WhiteboardParticipantListener listener) + { + synchronized (whiteboardParticipantListeners) + { + if (!whiteboardParticipantListeners.contains(listener)) + this.whiteboardParticipantListeners.add(listener); + } + } + + /** + * Unregisters the specified listener. + * + * @param listener the listener to unregister. + */ + public void removeWhiteboardParticipantListener( + WhiteboardParticipantListener listener) + { + + synchronized (whiteboardParticipantListeners) + { + if (listener == null) + return; + whiteboardParticipantListeners.remove(listener); + } + } + + /** + * Constructs a WhiteboardParticipantChangeEvent using this + * whiteboard participant as source, setting it to be of type + * eventType and the corresponding oldValue and + * newValue, + * + * @param eventType the type of the event to create and dispatch. + * @param oldValue the value of the source property before it changed. + * @param newValue the current value of the source property. + */ + protected void fireWhiteboardParticipantChangeEvent(String eventType, + Object oldValue, Object newValue) + { + this.fireWhiteboardParticipantChangeEvent(eventType, oldValue, + newValue, null); + } + + /** + * Constructs a WhiteboardParticipantChangeEvent using this + * whiteboard participant as source, setting it to be of type + * eventType and the corresponding oldValue and + * newValue, + * + * @param eventType the type of the event to create and dispatch. + * @param oldValue the value of the source property before it changed. + * @param newValue the current value of the source property. + * @param reason a string that could be set to contain a human readable + * explanation for the transition (particularly handy when moving + * into a FAILED state). + */ + protected void fireWhiteboardParticipantChangeEvent(String eventType, + Object oldValue, Object newValue, String reason) + { + WhiteboardParticipantChangeEvent evt = + new WhiteboardParticipantChangeEvent(this, eventType, oldValue, + newValue, reason); + + logger.debug("Dispatching a WhiteboardParticipantChangeEvent event to " + + whiteboardParticipantListeners.size() + " listeners. event is: " + + evt.toString()); + + Iterator listeners = null; + synchronized (whiteboardParticipantListeners) + { + listeners = + new ArrayList(whiteboardParticipantListeners).iterator(); + } + + while (listeners.hasNext()) + { + WhiteboardParticipantListener listener = + (WhiteboardParticipantListener) listeners.next(); + + if (eventType + .equals(WhiteboardParticipantChangeEvent.WHITEBOARD_PARTICIPANT_DISPLAY_NAME_CHANGE)) + { + listener.participantDisplayNameChanged(evt); + } + else if (eventType + .equals(WhiteboardParticipantChangeEvent.WHITEBOARD_PARTICIPANT_IMAGE_CHANGE)) + { + listener.participantImageChanged(evt); + } + else if (eventType + .equals(WhiteboardParticipantChangeEvent.WHITEBOARD_PARTICIPANT_STATE_CHANGE)) + { + listener.participantStateChanged(evt); + } + } + } + + /** + * Returns a string representation of the participant in the form of
+ * Display Name

;status=WhiteboardParticipantStatus + * + * @return a string representation of the participant and its state. + */ + public String toString() + { + return getDisplayName(); + } + + /** + * Returns the name of this member + * + * @return the name of this member in the room (nickname). + */ + public String getName() + { + return this.participant.getDisplayName(); + } + + /** + * Sets the chat room that this member is participating in. + * + * @param session + */ + public void setWhiteboardSession(WhiteboardSessionJabberImpl session) + { + this.whiteboard = session; + } + +} \ No newline at end of file diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/WhiteboardSessionJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/WhiteboardSessionJabberImpl.java new file mode 100644 index 000000000..973a10fd3 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/jabber/WhiteboardSessionJabberImpl.java @@ -0,0 +1,1220 @@ +/* + * 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 java.util.*; + +import net.java.sip.communicator.impl.protocol.jabber.extensions.version.*; +import net.java.sip.communicator.impl.protocol.jabber.extensions.whiteboard.*; +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.whiteboardobjects.*; + +import org.jivesoftware.smack.*; +import org.jivesoftware.smack.filter.*; +import org.jivesoftware.smack.packet.*; +import org.jivesoftware.smack.util.*; +import org.jivesoftware.smackx.*; + +import net.java.sip.communicator.service.protocol.event.*; +import net.java.sip.communicator.util.*; + +/** + * A representation of a WhiteboardSession. + * + * @author Julien Waechter + * @author Yana Stamcheva + */ +public class WhiteboardSessionJabberImpl + implements WhiteboardParticipantListener, + WhiteboardSession +{ + private static final Logger logger = + Logger.getLogger(WhiteboardSessionJabberImpl.class); + + /** + * A list of listeners registered for message events. + */ + private Vector messageListeners = new Vector(); + + /** + * A list containing all WhiteboardParticipants of this session. + */ + private Hashtable wbParticipants = new Hashtable(); + + /** + * The state that this white-board is currently in. + */ + private WhiteboardSessionState whiteboardState = + WhiteboardSessionState.WHITEBOARD_INITIALIZATION; + + /** + * The provider that created us. + */ + private ProtocolProviderServiceJabberImpl jabberProvider = null; + + /** + * An identifier uniquely representing the white-board. + */ + private String whiteboardID = null; + + /** + * A list of all listeners currently registered for + * WhiteboardChangeEvents + */ + private Vector whiteboardListeners = new Vector(); + + private Vector whiteboardObjects = new Vector(); + + private OperationSetWhiteboardingJabberImpl whiteboardOpSet; + + /** + * WhiteboardSessionJabberImpl constructor. + * + * @param sourceProvider Jabber protocol provider + * @param opSet the whiteboard operation set + */ + public WhiteboardSessionJabberImpl( + ProtocolProviderServiceJabberImpl sourceProvider, + OperationSetWhiteboardingJabberImpl opSet) + { + this.jabberProvider = sourceProvider; + this.whiteboardOpSet = opSet; + + //create the UID + this.whiteboardID = + String.valueOf(System.currentTimeMillis()) + + String.valueOf(super.hashCode()); + } + + /** + * Returns an iterator over all white-board participants. + * + * @return an Iterator over all participants currently involved in the + * white-board. + */ + public Iterator getWhiteboardParticipants() + { + return new LinkedList(wbParticipants.values()).iterator(); + } + + /** + * Returns the number of participants currently associated + * with this white-board session. + * + * @return an int indicating the number of participants currently + * associated with this white-board. + */ + public int getWhiteboardParticipantsCount() + { + return wbParticipants.size(); + } + + /** + * Joins this white-board with the nickname of the local user so that the + * user would start receiving events and WhiteboardObject for it. + * + * @throws OperationFailedException with the corresponding code if an error + * occurs while joining the room. + */ + public void join() throws OperationFailedException + { + PacketExtensionFilter filterWhiteboard = + new PacketExtensionFilter( + WhiteboardObjectPacketExtensionImpl.ELEMENT_NAME, + WhiteboardObjectPacketExtensionImpl.NAMESPACE); + + jabberProvider.getConnection().addPacketListener( + new WhiteboardSmackMessageListener(), filterWhiteboard); + + whiteboardOpSet.fireWhiteboardSessionPresenceEvent( + this, + WhiteboardSessionPresenceChangeEvent.LOCAL_USER_JOINED, + null); + } + + /** + * Joins this white-board so that the user would start receiving events and + * WhiteboardObject for it. The method uses the nickname of the local user + * and the specified password in order to enter the white-board session. + * + * @param password the password to use when authenticating on the + * white-board session. + * @throws OperationFailedException with the corresponding code if an error + * occurs while joining the room. + */ + public void join(byte[] password) throws OperationFailedException + { + } + + /** + * Returns true if the local user is currently in the white-board session + * (after white-boarding one of the {@link #join(String)} methods). + * + * @return true if currently we're currently in this white-board and false + * otherwise. + */ + public boolean isJoined() + { + return true; + } + + /** + * Leave this whiteboard. Once this method is whiteboarded, the user won't + * be listed as a member of the whiteboard any more and no further + * whiteboard events will be delivered. Depending on the underlying protocol + * and implementation leave() might cause the room to be destroyed if it has + * been created by the local user. + */ + public void leave() + { + } + + /** + * Invites another user to this room. + *

+ * If the room is password-protected, the invitee will receive a password to + * use to join the room. If the room is members-only, the the invitee may + * be added to the member list. + * + * @param contactAddress the address of the user to invite to the room. + * (one may also invite users not on their contact list). + */ + public void invite(String contactAddress) + { + OperationSetPersistentPresenceJabberImpl presenceOpSet + = (OperationSetPersistentPresenceJabberImpl) jabberProvider + .getOperationSet(OperationSetPresence.class); + + // If there's no presence operation set we return, because there's + // not contact to associate the event with. + if (presenceOpSet == null) + return; + + ContactJabberImpl sourceContact + = (ContactJabberImpl) presenceOpSet.findContactByID(contactAddress); + + if (sourceContact == null) + { + sourceContact = presenceOpSet.createVolatileContact(contactAddress); + } + + this.addWhiteboardParticipant( + new WhiteboardParticipantJabberImpl(sourceContact, this)); + + try + { + System.out.println("SEND WHITE BOARD OBJECT"); + sendWhiteboardObject( + createWhiteboardObject(WhiteboardObjectLine.NAME)); + } + catch (OperationFailedException e) + { + logger.error("Could not send an invite whiteboard object.", e); + } + } + + /** + * returns the current WhiteboardSession + * @return current WhiteboardSession + */ + public WhiteboardSession getWhiteboardSession() + { + return this; + } + + /** + * Verifies whether the whiteboard participant has entered a state. + * + * @param evt The WhiteboardParticipantChangeEvent instance + * containing the source event as well as its previous and its new status. + */ + public void participantStateChanged(WhiteboardParticipantChangeEvent evt) + { + if (((WhiteboardParticipantState) evt.getNewValue()) + == WhiteboardParticipantState.DISCONNECTED + || ((WhiteboardParticipantState) evt.getNewValue()) + == WhiteboardParticipantState.FAILED) + { + removeWhiteboardParticipant((WhiteboardParticipantJabberImpl) evt + .getSourceWhiteboardParticipant()); + } + } + + /** + * Indicates that a change has occurred in the display name of the source + * WhiteboardParticipant. + * + * @param evt The WhiteboardParticipantChangeEvent instance + * containing the source event as well as its previous and its new display + * names. + */ + public void participantDisplayNameChanged( + WhiteboardParticipantChangeEvent evt) + { + } + + /** + * Indicates that a change has occurred in the address of the source + * WhiteboardParticipant. + * + * @param evt The WhiteboardParticipantChangeEvent instance + * containing the source event as well as its previous and its new address. + */ + public void participantAddressChanged(WhiteboardParticipantChangeEvent evt) + { + } + + /** + * Indicates that a change has occurred in the transport address that we + * use to communicate with the participant. + * + * @param evt The WhiteboardParticipantChangeEvent instance + * containing the source event as well as its previous and its new transport + * address. + */ + public void participantTransportAddressChanged( + WhiteboardParticipantChangeEvent evt) + { + } + + /** + * Indicates that a change has occurred in the image of the source + * WhiteboardParticipant. + * + * @param evt The WhiteboardParticipantChangeEvent instance + * containing the source event as well as its previous and its new image. + */ + public void participantImageChanged(WhiteboardParticipantChangeEvent evt) + { + } + + /** + * Adds wbParticipant to the list of participants in this + * white-board. + * If the white-board participant is already included in the white-board, + * the method has no effect. + * + * @param wbParticipant the new WhiteboardParticipant + */ + public void addWhiteboardParticipant(WhiteboardParticipant wbParticipant) + { + if (wbParticipants.contains(wbParticipant)) + return; + + wbParticipant.addWhiteboardParticipantListener(this); + + this.wbParticipants.put( + wbParticipant.getContactAddress(), wbParticipant); + + fireWhiteboardParticipantEvent(wbParticipant, + WhiteboardParticipantEvent.WHITEBOARD_PARTICIPANT_ADDED); + } + + /** + * Removes whiteboardParticipant from the list of participants in + * this whiteboard. The method has no effect if there was no + * such participant in the whiteboard. + * + * @param wbParticipant the WhiteboardParticipant leaving the + * whiteboard; + */ + public void removeWhiteboardParticipant(WhiteboardParticipant wbParticipant) + { + if (!wbParticipants.contains(wbParticipant)) + return; + + this.wbParticipants.remove(wbParticipant); + + if (wbParticipant instanceof WhiteboardParticipantJabberImpl) + ((WhiteboardParticipantJabberImpl) wbParticipant) + .setWhiteboardSession(null); + + wbParticipant.removeWhiteboardParticipantListener(this); + + fireWhiteboardParticipantEvent(wbParticipant, + WhiteboardParticipantEvent.WHITEBOARD_PARTICIPANT_REMVOVED); + + if (wbParticipants.size() == 0) + setWhiteboardSessionState(WhiteboardSessionState.WHITEBOARD_ENDED); + } + + /** + * Sets the state of this whiteboard and fires a whiteboard change event + * notifying registered listeners for the change. + * + * @param newState a reference to the WhiteboardState instance that + * the whiteboard is to enter. + */ + public void setWhiteboardSessionState(WhiteboardSessionState newState) + { + WhiteboardSessionState oldState = getWhiteboardSessionState(); + + if (oldState == newState) + return; + + this.whiteboardState = newState; + + fireWhiteboardChangeEvent( + WhiteboardChangeEvent.WHITEBOARD_STATE_CHANGE, oldState, newState); + } + + /** + * Returns the state that this whiteboard is currently in. + * @return a reference to the WhiteboardState + * instance that the whiteboard is currently in. + */ + public WhiteboardSessionState getWhiteboardSessionState() + { + return whiteboardState; + } + + /** + * Registers listener so that it would receive events every time a + * new WhiteboardObject is received on this whiteboard. + * + * + * @param listener a WhiteboardObjectListener that would be + * notified every time a new WhiteboardObject + * is received on this whiteboard. + */ + public void addWhiteboardObjectListener(WhiteboardObjectListener listener) + { + synchronized (messageListeners) + { + if (!messageListeners.contains(listener)) + { + this.messageListeners.add(listener); + } + } + } + + /** + * Removes listener so that it won't receive + * any further WhiteboardObject events from this room. + * + * + * @param listener the WhiteboardObjectListener + * to remove from this room + */ + public void removeWhiteboardObjectListener( + WhiteboardObjectListener listener) + { + synchronized (messageListeners) + { + this.messageListeners.remove(listener); + } + } + + /** + * Create a WhiteboardObject instance with the specified type. This method + * only creates the object locally and it would not be visible to other + * session participants until it is resolved with the + * sendWhiteboardObject(WhiteboardObject) method. + * + * @param name the name of the object to create (should be one of the + * WhiteboardObjectXXX.NAME fields). + * + * @return the newly created WhiteboardObject with an id + */ + public WhiteboardObject createWhiteboardObject(String name) + { + WhiteboardObjectJabberImpl wbObj = null; + logger.debug("[log] WhiteboardObjectXXX.NAME: " + name); + if (name.equals(WhiteboardObjectPath.NAME)) + { + wbObj = new WhiteboardObjectPathJabberImpl(); + } + else if (name.equals(WhiteboardObjectPolyLine.NAME)) + { + wbObj = new WhiteboardObjectPolyLineJabberImpl(); + } + else if (name.equals(WhiteboardObjectPolygon.NAME)) + { + wbObj = new WhiteboardObjectPolygonJabberImpl(); + } + else if (name.equals(WhiteboardObjectLine.NAME)) + { + wbObj = new WhiteboardObjectLineJabberImpl(); + } + else if (name.equals(WhiteboardObjectRect.NAME)) + { + wbObj = new WhiteboardObjectRectJabberImpl(); + } + else if (name.equals(WhiteboardObjectCircle.NAME)) + { + wbObj = new WhiteboardObjectCircleJabberImpl(); + } + else if (name.equals(WhiteboardObjectText.NAME)) + { + wbObj = new WhiteboardObjectTextJabberImpl(); + } + else if (name.equals(WhiteboardObjectImage.NAME)) + { + wbObj = new WhiteboardObjectImageJabberImpl(); + } + whiteboardObjects.add(wbObj); + return wbObj; + } + + /** + * Returns the id of the specified Whiteboard. + * + * @return a String uniquely identifying the whiteboard. + */ + public String getWhiteboardID() + { + return whiteboardID; + } + + /** + * Determines wheter the protocol provider (or the protocol itself) support + * sending and receiving offline messages. Most often this method would + * return true for protocols that support offline messages and false for + * those that don't. It is however possible for a protocol to support these + * messages and yet have a particular account that does not (i.e. feature + * not enabled on the protocol server). In cases like this it is possible + * for this method to return true even when offline messaging is not + * supported, and then have the sendMessage method throw an + * OperationFailedException with code - OFFLINE_MESSAGES_NOT_SUPPORTED. + * + * @return true if the protocol supports offline messages and + * false otherwise. + */ + public boolean isOfflineMessagingSupported() + { + return true; + } + + /** + * Sends a WhiteboardObject to modify + * and modifies the local WhiteboardObject + * + * @param obj the WhiteboardObject to send and modify + * @throws OperationFailedException if sending + * the WhiteboardObject fails for some reason. + */ + public void moveWhiteboardObject(WhiteboardObject obj) + throws OperationFailedException + { + WhiteboardObject wbObj = updateWhiteboardObjects(obj); + if (wbObj != null) + sendWhiteboardObject(wbObj); + } + + /** + * Sends a WhiteboardObject to delete + * and delete the local WhiteboardObject + * + * @param obj the WhiteboardObject to send and delete + * @throws OperationFailedException if sending + * the WhiteboardObject fails for some reason. + */ + public void deleteWhiteboardObject(WhiteboardObject obj) + throws OperationFailedException + { + Iterator participants = getWhiteboardParticipants(); + if (!participants.hasNext()) + return; + + WhiteboardParticipantJabberImpl participant + = (WhiteboardParticipantJabberImpl) participants.next(); + + Contact contact = participant.getContact(); + + try + { + assertConnected(); + + org.jivesoftware.smack.MessageListener msgListener = + new org.jivesoftware.smack.MessageListener() + { + public void processMessage(Chat arg0, + org.jivesoftware.smack.packet.Message arg1) + { + } + }; + + Chat chat + = jabberProvider.getConnection().getChatManager() + .createChat(contact.getAddress(), msgListener); + + org.jivesoftware.smack.packet.Message msg = + new org.jivesoftware.smack.packet.Message(); + + WhiteboardObjectPacketExtensionImpl messageJI = + new WhiteboardObjectPacketExtensionImpl(obj.getID(), + WhiteboardObjectPacketExtensionImpl.ACTION_DELETE); + + msg.addExtension(messageJI); + msg.addExtension(new Version()); + + MessageEventManager.addNotificationsRequests(msg, true, false, + false, true); + + chat.sendMessage(msg); + + WhiteboardObjectDeliveredEvent msgDeliveredEvt = + new WhiteboardObjectDeliveredEvent( + this, obj, contact, new Date()); + + fireMessageEvent(msgDeliveredEvt); + + int i = 0; + while (i < whiteboardObjects.size()) + { + WhiteboardObjectJabberImpl wbObj = + (WhiteboardObjectJabberImpl) whiteboardObjects.get(i); + if (wbObj.getID().equals(obj.getID())) + whiteboardObjects.remove(i); + else + i++; + } + } + catch (XMPPException ex) + { + ex.printStackTrace(); + logger.error("message not send", ex); + } + + } + + /** + * Sends the message to the destination indicated by the + * to contact. + * @param to the Contact to send message to + * @param message the Message to send. + * @throws java.lang.IllegalStateException if the underlying stack is + * not registered and initialized. + * @throws java.lang.IllegalArgumentException if to is not an + * instance of ContactImpl. + */ + public void sendWhiteboardObject(WhiteboardObject message) + throws OperationFailedException + { + Iterator participants = getWhiteboardParticipants(); + + if (!participants.hasNext()) + return; + + WhiteboardParticipantJabberImpl participant + = (WhiteboardParticipantJabberImpl) participants.next(); + + Contact contact = participant.getContact(); + + try + { + assertConnected(); + + org.jivesoftware.smack.MessageListener msgListener = + new org.jivesoftware.smack.MessageListener() + { + public void processMessage(Chat arg0, + org.jivesoftware.smack.packet.Message arg1) + { + } + }; + + Chat chat = + jabberProvider.getConnection().getChatManager().createChat( + contact.getAddress(), msgListener); + + org.jivesoftware.smack.packet.Message msg = + new org.jivesoftware.smack.packet.Message(); + + WhiteboardObjectPacketExtensionImpl messageJI = + new WhiteboardObjectPacketExtensionImpl( + (WhiteboardObjectJabberImpl) message, + WhiteboardObjectPacketExtensionImpl.ACTION_DRAW); + + msg.addExtension(messageJI); + msg.addExtension(new Version()); + + MessageEventManager.addNotificationsRequests(msg, true, false, + false, true); + + chat.sendMessage(msg); + + WhiteboardObjectDeliveredEvent msgDeliveredEvt = + new WhiteboardObjectDeliveredEvent( + this, message, contact, new Date()); + + fireMessageEvent(msgDeliveredEvt); + } + catch (XMPPException ex) + { + ex.printStackTrace(); + logger.error("message not send", ex); + } + } + + /** + * Utility method throwing an exception if the stack is not properly + * initialized. + * @throws java.lang.IllegalStateException if the underlying stack is + * not registered and initialized. + */ + private void assertConnected() throws IllegalStateException + { + if (jabberProvider == null) + throw new IllegalStateException( + "The provider must be non-null and signed on the " + + "service before being able to communicate."); + if (!jabberProvider.isRegistered()) + throw new IllegalStateException( + "The provider must be signed on the service before " + + "being able to communicate."); + } + + /** + * Compares the specified object with this whiteboard and returns true if it + * the specified object is an instance of a Whiteboard object and if the + * extending telephony protocol considers the whiteboards represented by + * both objects to be the same. + * + * @param obj the whiteboard to compare this one with. + * @return true in case both objects are pertaining to the same whiteboard + * and false otherwise. + */ + public boolean equals(Object obj) + { + if (obj == null || !(obj instanceof WhiteboardSession)) + return false; + if (obj == this + || ((WhiteboardSession) obj).getWhiteboardID().equals( + getWhiteboardID())) + return true; + + return false; + } + + /** + * Returns a hash code value for this whiteboard. + * + * @return a hash code value for this whiteboard. + */ + public int hashCode() + { + return getWhiteboardID().hashCode(); + } + + /** + * Returns a string textually representing this Whiteboard. + * + * @return a string representation of the object. + */ + public String toString() + { + return "Whiteboard: id=" + getWhiteboardID() + " participants=" + + getWhiteboardParticipantsCount(); + } + + /** + * Adds a whiteboard change listener to this whiteboard so that it could + * receive events on new whiteboard participants, theme changes and others. + * + * @param listener the listener to register + */ + public void addWhiteboardChangeListener(WhiteboardChangeListener listener) + { + synchronized (whiteboardListeners) + { + if (!whiteboardListeners.contains(listener)) + this.whiteboardListeners.add(listener); + } + } + + /** + * Removes listener to this whiteboard so that it won't receive + * further WhiteboardChangeEvents. + * + * @param listener the listener to register + */ + public void removeWhiteboardChangeListener(WhiteboardChangeListener listener) + { + synchronized (whiteboardListeners) + { + this.whiteboardListeners.remove(listener); + } + } + + /** + * Returns a reference to the ProtocolProviderService instance + * that created this whiteboard. + * + * @return a reference to the ProtocolProviderService instance that + * created this whiteboard. + */ + public ProtocolProviderService getProtocolProvider() + { + return this.jabberProvider; + } + + /** + * Creates a WhiteboardParticipantEvent with + * sourceWhiteboardParticipant and eventID and dispatches + * it on all currently registered listeners. + * + * @param sourceWhiteboardParticipant the source + * WhiteboardParticipant for the newly created event. + * @param eventID the ID of the event to create (see CPE member ints) + */ + public void fireWhiteboardParticipantEvent( + WhiteboardParticipant sourceWhiteboardParticipant, int eventID) + { + + WhiteboardParticipantEvent cpEvent = + new WhiteboardParticipantEvent(this, sourceWhiteboardParticipant, + eventID); + + logger.debug("Dispatching a WhiteboardParticipant event to " + + whiteboardListeners.size() + " listeners. event is: " + + cpEvent.toString()); + + Iterator listeners = null; + synchronized (whiteboardListeners) + { + listeners = new ArrayList(whiteboardListeners).iterator(); + } + + while (listeners.hasNext()) + { + WhiteboardChangeListener listener = + (WhiteboardChangeListener) listeners.next(); + + if (eventID + == WhiteboardParticipantEvent + .WHITEBOARD_PARTICIPANT_ADDED) + listener.whiteboardParticipantAdded(cpEvent); + else if (eventID + == WhiteboardParticipantEvent + .WHITEBOARD_PARTICIPANT_REMVOVED) + listener.whiteboardParticipantRemoved(cpEvent); + + } + } + + /** + * Creates a WhiteboardChangeEvent with this class as + * sourceWhiteboard, and the specified eventID and old + * and new values and dispatches it on all currently registered listeners. + * + * @param type the type of the event to create (see WhiteboardChangeEvent + * member ints) + * @param oldValue the value of the Whiteboard property that changed, before + * the event had occurred. + * @param newValue the value of the Whiteboard property that changed, after + * the event has occurred. + */ + public void fireWhiteboardChangeEvent(String type, Object oldValue, + Object newValue) + { + WhiteboardChangeEvent ccEvent = + new WhiteboardChangeEvent(this, type, oldValue, newValue); + + logger.debug("Dispatching a WhiteboardChange event to " + + whiteboardListeners.size() + " listeners. event is: " + + ccEvent.toString()); + + Iterator listeners = null; + synchronized (whiteboardListeners) + { + listeners = new ArrayList(whiteboardListeners).iterator(); + } + + while (listeners.hasNext()) + { + WhiteboardChangeListener listener = + (WhiteboardChangeListener) listeners.next(); + + if (type.equals(WhiteboardChangeEvent.WHITEBOARD_STATE_CHANGE)) + listener.whiteboardStateChanged(ccEvent); + } + } + + /** + * Returns the WhiteboardObjects in this whiteboard session. + * @return an Vector of WhiteboardObjects associated + * with this whiteboard. + */ + public Vector getWhiteboardObjects() + { + return whiteboardObjects; + } + + /** + * Sets the state of this whiteboard + * + * @param newState a reference to the WhiteboardState instance that + * the whiteboard is to enter. + */ + public void setState(WhiteboardSessionState newState) + { + this.whiteboardState = newState; + } + + /** + * Returns the state that this whiteboard is currently in. + * + * @return a reference to the WhiteboardState instance + * that the whiteboard is currently in. + */ + public WhiteboardSessionState getState() + { + return this.whiteboardState; + } + + /** + * Delivers the specified event to all registered message listeners. + * @param evt the EventObject that we'd like delivered to all + * registered message listeners. + */ + public void fireMessageEvent(EventObject evt) + { + logger.debug("Dispatching a WhiteboardMessageEvent event to " + + messageListeners.size() + " listeners. event is: " + + evt.toString()); + + Iterator listeners = null; + synchronized (messageListeners) + { + listeners = new ArrayList(messageListeners).iterator(); + } + + while (listeners.hasNext()) + { + WhiteboardObjectListener listener = + (WhiteboardObjectListener) listeners.next(); + + if (evt instanceof WhiteboardObjectDeliveredEvent) + { + listener.whiteboardObjectDelivered( + (WhiteboardObjectDeliveredEvent) evt); + } + else if (evt instanceof WhiteboardObjectReceivedEvent) + { + WhiteboardObjectJabberImpl wbObj = + (WhiteboardObjectJabberImpl) ( + (WhiteboardObjectReceivedEvent) evt) + .getSourceWhiteboardObject(); + /* + for(int i = 0; i < whiteboardObjects.size (); i++) + { + WhiteboardObjectJabberImpl obj = + (WhiteboardObjectJabberImpl) whiteboardObjects.get (i); + if(wbObj.getID ().equals (obj.getID ())) + { + whiteboardObjects.set (i, wbObj); + listener.whiteboardObjecModified ( + (WhiteboardObjectModifiedEvent) evt); + return; + } + } + */ + listener.whiteboardObjectReceived( + (WhiteboardObjectReceivedEvent) evt); + + whiteboardObjects.add(wbObj); + } + else if (evt instanceof WhiteboardObjectDeletedEvent) + { + String wbObjID = ((WhiteboardObjectDeletedEvent) evt).getId(); + + listener + .whiteboardObjectDeleted((WhiteboardObjectDeletedEvent) evt); + int i = 0; + while (i < whiteboardObjects.size()) + { + WhiteboardObjectJabberImpl wbObj + = (WhiteboardObjectJabberImpl) whiteboardObjects.get(i); + if (wbObj.getID().equals(wbObjID)) + whiteboardObjects.remove(i); + else + i++; + } + + } + else if (evt instanceof WhiteboardObjectModifiedEvent) + { + WhiteboardObjectJabberImpl wbObj + = (WhiteboardObjectJabberImpl) + ((WhiteboardObjectReceivedEvent) evt) + .getSourceWhiteboardObject(); + + listener.whiteboardObjecModified( + (WhiteboardObjectModifiedEvent) evt); + + whiteboardObjects.remove(wbObj);//remove the old id object + whiteboardObjects.add(wbObj); //add the new object for this id + } + else if (evt instanceof WhiteboardObjectDeliveryFailedEvent) + { + listener.whiteboardObjectDeliveryFailed( + (WhiteboardObjectDeliveryFailedEvent) evt); + } + } + } + + private WhiteboardObject updateWhiteboardObjects(WhiteboardObject ws) + { + WhiteboardObjectJabberImpl wbObj = null; + int i = 0; + while (i < whiteboardObjects.size()) + { + WhiteboardObjectJabberImpl wbObjTmp = + (WhiteboardObjectJabberImpl) whiteboardObjects.get(i); + if (wbObjTmp.getID().equals(ws.getID())) + { + wbObj = wbObjTmp; + break; + } + else + i++; + } + if (wbObj == null) + return null; + + if (ws instanceof WhiteboardObjectPath) + { + WhiteboardObjectPathJabberImpl obj = + (WhiteboardObjectPathJabberImpl) wbObj; + obj.setPoints(((WhiteboardObjectPath) ws).getPoints()); + obj.setColor(ws.getColor()); + obj.setThickness(ws.getThickness()); + } + else if (ws instanceof WhiteboardObjectPolyLine) + { + WhiteboardObjectPolyLineJabberImpl obj = + (WhiteboardObjectPolyLineJabberImpl) wbObj; + obj.setPoints(((WhiteboardObjectPolyLine) ws).getPoints()); + obj.setColor(ws.getColor()); + obj.setThickness(ws.getThickness()); + } + else if (ws instanceof WhiteboardObjectPolygon) + { + WhiteboardObjectPolygonJabberImpl obj = + (WhiteboardObjectPolygonJabberImpl) wbObj; + obj.setPoints(((WhiteboardObjectPolygon) ws).getPoints()); + obj.setBackgroundColor(((WhiteboardObjectPolygon) ws) + .getBackgroundColor()); + obj.setFill(((WhiteboardObjectPolygon) ws).isFill()); + obj.setColor(ws.getColor()); + obj.setThickness(ws.getThickness()); + } + else if (ws instanceof WhiteboardObjectLine) + { + WhiteboardObjectLineJabberImpl obj = + (WhiteboardObjectLineJabberImpl) wbObj; + obj.setWhiteboardPointStart(((WhiteboardObjectLine) ws) + .getWhiteboardPointStart()); + obj.setWhiteboardPointEnd(((WhiteboardObjectLine) ws) + .getWhiteboardPointEnd()); + obj.setColor(ws.getColor()); + obj.setThickness(ws.getThickness()); + } + else if (ws instanceof WhiteboardObjectRect) + { + WhiteboardObjectRectJabberImpl obj = + (WhiteboardObjectRectJabberImpl) wbObj; + obj.setFill(((WhiteboardObjectRect) ws).isFill()); + obj.setHeight(((WhiteboardObjectRect) ws).getHeight()); + obj.setWhiteboardPoint(((WhiteboardObjectRect) ws) + .getWhiteboardPoint()); + obj.setWidth((((WhiteboardObjectRect) ws)).getWidth()); + obj.setColor(ws.getColor()); + obj.setThickness(ws.getThickness()); + } + else if (ws instanceof WhiteboardObjectCircle) + { + WhiteboardObjectCircleJabberImpl obj = + (WhiteboardObjectCircleJabberImpl) wbObj; + obj.setFill(((WhiteboardObjectCircle) ws).isFill()); + obj.setRadius(((WhiteboardObjectCircle) ws).getRadius()); + obj.setWhiteboardPoint(((WhiteboardObjectCircle) ws) + .getWhiteboardPoint()); + obj.setBackgroundColor((((WhiteboardObjectCircle) ws)) + .getBackgroundColor()); + obj.setColor(ws.getColor()); + obj.setThickness(ws.getThickness()); + } + else if (ws instanceof WhiteboardObjectText) + { + WhiteboardObjectTextJabberImpl obj = + (WhiteboardObjectTextJabberImpl) wbObj; + obj.setFontName(((WhiteboardObjectText) ws).getFontName()); + obj.setFontSize(((WhiteboardObjectText) ws).getFontSize()); + obj.setText(((WhiteboardObjectText) ws).getText()); + obj.setWhiteboardPoint(((WhiteboardObjectText) ws) + .getWhiteboardPoint()); + obj.setColor(ws.getColor()); + obj.setThickness(ws.getThickness()); + } + else if (ws instanceof WhiteboardObjectImage) + { + WhiteboardObjectImageJabberImpl obj = + (WhiteboardObjectImageJabberImpl) wbObj; + obj.setBackgroundImage(((WhiteboardObjectImage) ws) + .getBackgroundImage()); + obj.setHeight(((WhiteboardObjectImage) ws).getHeight()); + obj.setWhiteboardPoint(((WhiteboardObjectImage) ws) + .getWhiteboardPoint()); + obj.setWidth(((WhiteboardObjectImage) ws).getWidth()); + obj.setColor(ws.getColor()); + obj.setThickness(ws.getThickness()); + } + whiteboardObjects.set(i, wbObj); + return wbObj; + } + + /** + * Returns all the type of WhiteboardObject that this whiteboard support. + * + * @return all the WhiteboardObject supported by this WhiteboardSession. + */ + public String[] getSupportedWhiteboardObjects() + { + String[] type = new String[8]; + type[0] = WhiteboardObjectPath.NAME; + type[1] = WhiteboardObjectPolyLine.NAME; + type[2] = WhiteboardObjectPolygon.NAME; + type[3] = WhiteboardObjectLine.NAME; + type[4] = WhiteboardObjectRect.NAME; + type[5] = WhiteboardObjectCircle.NAME; + type[6] = WhiteboardObjectText.NAME; + type[7] = WhiteboardObjectImage.NAME; + + return type; + } + + /** + * Listens for white-board messages and fires the appropriate events to + * notify all interested listeners. + */ + private class WhiteboardSmackMessageListener + implements PacketListener + { + public void processPacket(Packet packet) + { + if (!(packet instanceof org.jivesoftware.smack.packet.Message)) + return; + + PacketExtension ext = + packet.getExtension( + WhiteboardObjectPacketExtensionImpl.ELEMENT_NAME, + WhiteboardObjectPacketExtensionImpl.NAMESPACE); + + org.jivesoftware.smack.packet.Message msg = + (org.jivesoftware.smack.packet.Message) packet; + + if (ext == null) + return; + + String fromUserID = StringUtils.parseBareAddress(msg.getFrom()); + + if (logger.isDebugEnabled()) + { + logger.debug("Received from " + fromUserID + " the message " + + msg.toXML()); + } + + OperationSetPersistentPresenceJabberImpl presenceOpSet + = (OperationSetPersistentPresenceJabberImpl) jabberProvider + .getOperationSet(OperationSetPresence.class); + + // If there's no presence operation set we return, because there's + // not contact to associate the event with. + if (presenceOpSet == null) + return; + + Contact sourceContact + = presenceOpSet.findContactByID(fromUserID); + + // If the sender is not our contact we don't care of this message + if (!wbParticipants.containsKey(sourceContact.getAddress())) + return; + + WhiteboardObjectPacketExtensionImpl newMessage + = (WhiteboardObjectPacketExtensionImpl) ext; + + if (msg.getType() + == org.jivesoftware.smack.packet.Message.Type.error) + { + logger.info("WBObject error received from " + fromUserID); + + int errorCode = packet.getError().getCode(); + int errorResultCode = + WhiteboardObjectDeliveryFailedEvent.UNKNOWN_ERROR; + + if (errorCode == 503) + { + org.jivesoftware.smackx.packet.MessageEvent msgEvent = + (org.jivesoftware.smackx.packet.MessageEvent) packet + .getExtension("x", "jabber:x:event"); + if (msgEvent != null && msgEvent.isOffline()) + { + errorResultCode + = WhiteboardObjectDeliveryFailedEvent + .OFFLINE_MESSAGES_NOT_SUPPORTED; + } + } + + WhiteboardObjectDeliveryFailedEvent evt = + new WhiteboardObjectDeliveryFailedEvent( + WhiteboardSessionJabberImpl.this, + newMessage.getWhiteboardObject(), + sourceContact, + errorResultCode, + new Date()); + + fireMessageEvent(evt); + + return; + } + + if (newMessage.getAction().equals( + WhiteboardObjectPacketExtensionImpl.ACTION_DELETE)) + { + WhiteboardObjectDeletedEvent msgDeletedEvt + = new WhiteboardObjectDeletedEvent( + WhiteboardSessionJabberImpl.this, + newMessage.getWhiteboardObjectID(), + sourceContact, + new Date()); + + fireMessageEvent(msgDeletedEvt); + } + else if (newMessage.getAction().equals( + WhiteboardObjectPacketExtensionImpl.ACTION_DRAW)) + { + WhiteboardObjectReceivedEvent msgReceivedEvt + = new WhiteboardObjectReceivedEvent( + WhiteboardSessionJabberImpl.this, + newMessage.getWhiteboardObject(), + sourceContact, + new Date()); + + fireMessageEvent(msgReceivedEvt); + } + } + } + + /** + * Checks if the participant given by participantName is contained + * in this white-board session. + * + * @param participantName the name of the participant to search for + * @return true if a participant with the given name is + * contained in this session, false - otherwise + */ + public boolean isParticipantContained(String participantName) + { + if (wbParticipants.containsKey(participantName)) + return true; + + return false; + } +} \ No newline at end of file