/******************************************************************************* * Copyright (c) 2004 Composent, Inc. and others. All rights reserved. This * program and the accompanying materials are made available under the terms of * the Eclipse Public License v1.0 which accompanies this distribution, and is * available at http://www.eclipse.org/legal/epl-v10.html * * Contributors: Composent, Inc. - initial API and implementation ******************************************************************************/ package org.eclipse.ecf.internal.provider.xmpp; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.eclipse.ecf.core.ContainerConnectException; import org.eclipse.ecf.core.events.ContainerConnectedEvent; import org.eclipse.ecf.core.events.ContainerConnectingEvent; import org.eclipse.ecf.core.events.ContainerDisconnectedEvent; import org.eclipse.ecf.core.events.ContainerDisconnectingEvent; import org.eclipse.ecf.core.identity.ID; import org.eclipse.ecf.core.identity.IDCreateException; import org.eclipse.ecf.core.identity.IDFactory; import org.eclipse.ecf.core.identity.Namespace; import org.eclipse.ecf.core.security.Callback; import org.eclipse.ecf.core.security.CallbackHandler; import org.eclipse.ecf.core.security.IConnectContext; import org.eclipse.ecf.core.security.NameCallback; import org.eclipse.ecf.core.sharedobject.ISharedObjectContainerConfig; import org.eclipse.ecf.core.sharedobject.SharedObjectAddException; import org.eclipse.ecf.core.sharedobject.util.IQueueEnqueue; import org.eclipse.ecf.core.util.ECFException; import org.eclipse.ecf.internal.provider.xmpp.events.ChatMembershipEvent; import org.eclipse.ecf.internal.provider.xmpp.events.IQEvent; import org.eclipse.ecf.internal.provider.xmpp.events.MessageEvent; import org.eclipse.ecf.internal.provider.xmpp.events.PresenceEvent; import org.eclipse.ecf.internal.provider.xmpp.smack.ECFConnection; import org.eclipse.ecf.presence.IIMMessageListener; import org.eclipse.ecf.presence.chatroom.IChatRoomAdminListener; import org.eclipse.ecf.presence.chatroom.IChatRoomAdminSender; import org.eclipse.ecf.presence.chatroom.IChatRoomContainer; import org.eclipse.ecf.presence.chatroom.IChatRoomMessageSender; import org.eclipse.ecf.presence.chatroom.IChatRoomParticipantListener; import org.eclipse.ecf.presence.im.IChatMessageSender; import org.eclipse.ecf.presence.im.IChatMessage.Type; import org.eclipse.ecf.provider.comm.ConnectionCreateException; import org.eclipse.ecf.provider.comm.ISynchAsynchConnection; import org.eclipse.ecf.provider.generic.ClientSOContainer; import org.eclipse.ecf.provider.generic.ContainerMessage; import org.eclipse.ecf.provider.generic.SOConfig; import org.eclipse.ecf.provider.generic.SOContainerConfig; import org.eclipse.ecf.provider.generic.SOContext; import org.eclipse.ecf.provider.generic.SOWrapper; import org.eclipse.ecf.provider.xmpp.XMPPContainer; import org.eclipse.ecf.provider.xmpp.identity.XMPPRoomID; import org.eclipse.osgi.util.NLS; import org.jivesoftware.smack.PacketListener; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Packet; import org.jivesoftware.smack.packet.Presence; import org.jivesoftware.smackx.muc.InvitationRejectionListener; import org.jivesoftware.smackx.muc.MultiUserChat; import org.jivesoftware.smackx.muc.ParticipantStatusListener; import org.jivesoftware.smackx.muc.SubjectUpdatedListener; public class XMPPChatRoomContainer extends ClientSOContainer implements IChatRoomContainer { private static final String CONTAINER_HELPER_ID = XMPPContainer.class.getName() + ".xmppgroupchathandler"; //$NON-NLS-1$ private ID containerHelperID; private XMPPChatRoomContainerHelper containerHelper; private MultiUserChat multiuserchat; private List chatRoomAdminListeners; private IChatRoomAdminSender chatRoomAdminSender; public XMPPChatRoomContainer(ISharedObjectContainerConfig config, ECFConnection conn, Namespace usernamespace) throws IDCreateException { super(config); this.connection = conn; this.config = config; this.containerHelperID = IDFactory.getDefault().createStringID(CONTAINER_HELPER_ID); this.containerHelper = new XMPPChatRoomContainerHelper(usernamespace, getXMPPConnection()); this.chatRoomAdminListeners = new ArrayList(); } protected void sendInvitation(ID toUser, String subject, String body) throws ECFException { if (toUser == null) throw new ECFException(Messages.XMPPChatRoomContainer_EXCEPTION_TARGET_USER_NOT_NULL); synchronized (getConnectLock()) { if (multiuserchat == null) throw new ContainerConnectException(Messages.XMPPChatRoomContainer_EXCEPTION_NOT_CONNECTED); multiuserchat.invite(toUser.getName(), (body == null) ? "" : body); //$NON-NLS-1$ } } public XMPPChatRoomContainer(ECFConnection conn, Namespace usernamespace) throws IDCreateException { this(new SOContainerConfig(IDFactory.getDefault().createGUID()), conn, usernamespace); } public void dispose() { disconnect(); if (containerHelperID != null) { getSharedObjectManager().removeSharedObject(containerHelperID); containerHelperID = null; } if (containerHelper != null) containerHelper.dispose(getID()); containerHelper = null; if (chatRoomAdminListeners != null) chatRoomAdminListeners.clear(); chatRoomAdminListeners = null; super.dispose(); } protected void sendMessage(ContainerMessage data) throws IOException { synchronized (getConnectLock()) { final ID toID = data.getToContainerID(); if (toID == null) { data.setToContainerID(remoteServerID); } super.sendMessage(data); } } protected void handleChatMessage(Message mess) throws IOException { final SOWrapper wrap = getSharedObjectWrapper(containerHelperID); if (wrap != null) { wrap.deliverEvent(new MessageEvent(mess)); } } protected boolean verifyToIDForSharedObjectMessage(ID toID) { return true; } public void handleContainerMessage(ContainerMessage mess) throws IOException { if (mess == null) { debug("got null container message...ignoring"); //$NON-NLS-1$ return; } final Object data = mess.getData(); if (data instanceof ContainerMessage.CreateMessage) { handleCreateMessage(mess); } else if (data instanceof ContainerMessage.CreateResponseMessage) { handleCreateResponseMessage(mess); } else if (data instanceof ContainerMessage.SharedObjectMessage) { handleSharedObjectMessage(mess); } else if (data instanceof ContainerMessage.SharedObjectDisposeMessage) { handleSharedObjectDisposeMessage(mess); } else { debug("got unrecognized container message...ignoring: " + mess); //$NON-NLS-1$ } } protected void handleIQMessage(IQ mess) throws IOException { final SOWrapper wrap = getSharedObjectWrapper(containerHelperID); if (wrap != null) { wrap.deliverEvent(new IQEvent(mess)); } } protected void handlePresenceMessage(Presence mess) throws IOException { final SOWrapper wrap = getSharedObjectWrapper(containerHelperID); if (wrap != null) { wrap.deliverEvent(new PresenceEvent(mess)); } } protected void handleChatMembershipEvent(String from, boolean add) { final SOWrapper wrap = getSharedObjectWrapper(containerHelperID); if (wrap != null) { wrap.deliverEvent(new ChatMembershipEvent(from, add)); } } protected void handleXMPPMessage(Packet aPacket) { try { if (aPacket instanceof IQ) { handleIQMessage((IQ) aPacket); } else if (aPacket instanceof Message) { handleChatMessage((Message) aPacket); } else if (aPacket instanceof Presence) { handlePresenceMessage((Presence) aPacket); } else { // unexpected message debug("got unexpected packet " + aPacket); //$NON-NLS-1$ } } catch (final IOException e) { traceStack("Exception in handleXMPPMessage", e); //$NON-NLS-1$ } } protected XMPPConnection getXMPPConnection() { return ((ECFConnection) getConnection()).getXMPPConnection(); } protected void addSharedObjectToContainer(ID remote) throws SharedObjectAddException { getSharedObjectManager().addSharedObject(containerHelperID, containerHelper, new HashMap()); } protected void cleanUpConnectFail() { if (containerHelper != null) { getSharedObjectManager().removeSharedObject(containerHelperID); containerHelper = null; containerHelperID = null; } connectionState = DISCONNECTED; remoteServerID = null; } public Namespace getConnectNamespace() { return IDFactory.getDefault().getNamespaceByName(XmppPlugin.getDefault().getRoomNamespaceIdentifier()); } public void connect(ID remote, IConnectContext connectContext) throws ContainerConnectException { if (!(remote instanceof XMPPRoomID)) { throw new ContainerConnectException(NLS.bind(Messages.XMPPChatRoomContainer_Exception_Connect_Wrong_Type, remote)); } final XMPPRoomID roomID = (XMPPRoomID) remote; fireContainerEvent(new ContainerConnectingEvent(this.getID(), remote, connectContext)); synchronized (getConnectLock()) { try { connectionState = CONNECTING; remoteServerID = null; addSharedObjectToContainer(remote); multiuserchat = new MultiUserChat(getXMPPConnection(), roomID.getMucString()); // Get nickname from join context String nick = null; try { final Callback[] callbacks = new Callback[1]; callbacks[0] = new NameCallback(Messages.XMPPChatRoomContainer_NAME_CALLBACK_NICK, roomID.getNickname()); if (connectContext != null) { final CallbackHandler handler = connectContext.getCallbackHandler(); if (handler != null) { handler.handle(callbacks); } } if (callbacks[0] instanceof NameCallback) { final NameCallback cb = (NameCallback) callbacks[0]; nick = cb.getName(); } } catch (final Exception e) { throw new ContainerConnectException(Messages.XMPPChatRoomContainer_EXCEPTION_CALLBACKHANDLER, e); } String nickname = null; if (nick == null || nick.equals("")) //$NON-NLS-1$ nickname = roomID.getNickname(); else nickname = nick; multiuserchat.addSubjectUpdatedListener(new SubjectUpdatedListener() { public void subjectUpdated(String subject, String from) { fireSubjectUpdated(subject, from); } }); multiuserchat.addMessageListener(new PacketListener() { public void processPacket(Packet arg0) { handleXMPPMessage(arg0); } }); multiuserchat.addParticipantListener(new PacketListener() { public void processPacket(Packet arg0) { handleXMPPMessage(arg0); } }); multiuserchat.addParticipantStatusListener(new ParticipantStatusListener() { public void joined(String arg0) { handleChatMembershipEvent(arg0, true); } public void left(String arg0) { handleChatMembershipEvent(arg0, false); } public void voiceGranted(String arg0) { // TODO Auto-generated method stub System.out.println("voiceGranted(" + arg0 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ } public void voiceRevoked(String arg0) { // TODO Auto-generated method stub System.out.println("voiceRevoked(" + arg0 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ } public void membershipGranted(String arg0) { // TODO Auto-generated method stub System.out.println("membershipGranted(" + arg0 //$NON-NLS-1$ + ")"); //$NON-NLS-1$ } public void membershipRevoked(String arg0) { // TODO Auto-generated method stub System.out.println("membershipRevoked(" + arg0 //$NON-NLS-1$ + ")"); //$NON-NLS-1$ } public void moderatorGranted(String arg0) { // TODO Auto-generated method stub System.out.println("moderatorGranted(" + arg0 //$NON-NLS-1$ + ")"); //$NON-NLS-1$ } public void moderatorRevoked(String arg0) { // TODO Auto-generated method stub System.out.println("moderatorRevoked(" + arg0 //$NON-NLS-1$ + ")"); //$NON-NLS-1$ } public void ownershipGranted(String arg0) { // TODO Auto-generated method stub System.out.println("ownershipGranted(" + arg0 //$NON-NLS-1$ + ")"); //$NON-NLS-1$ } public void ownershipRevoked(String arg0) { // TODO Auto-generated method stub System.out.println("ownershipRevoked(" + arg0 //$NON-NLS-1$ + ")"); //$NON-NLS-1$ } public void adminGranted(String arg0) { // TODO Auto-generated method stub System.out.println("adminGranted(" + arg0 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ } public void adminRevoked(String arg0) { // TODO Auto-generated method stub System.out.println("adminRevoked(" + arg0 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ } public void kicked(String arg0, String arg1, String arg2) { // TODO Auto-generated method stub System.out.println("kicked(" + arg0 + "," //$NON-NLS-1$ //$NON-NLS-2$ + arg1 + "," + arg2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ } public void banned(String arg0, String arg1, String arg2) { // TODO Auto-generated method stub System.out.println("banned(" + arg0 + "," //$NON-NLS-1$ //$NON-NLS-2$ + arg1 + "," + arg2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ } public void nicknameChanged(String arg0, String arg1) { // TODO Auto-generated method stub System.out.println("nicknameChanged(" + arg0 //$NON-NLS-1$ + "," + arg1 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ } }); multiuserchat.addInvitationRejectionListener(new InvitationRejectionListener() { public void invitationDeclined(String arg0, String arg1) { // TODO Auto-generated method stub System.out.println("invitationDeclined(" + arg0 //$NON-NLS-1$ + "," + arg1 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ } }); multiuserchat.join(nickname); connectionState = CONNECTED; remoteServerID = roomID; containerHelper.setRoomID(remoteServerID); fireContainerEvent(new ContainerConnectedEvent(this.getID(), roomID)); } catch (final Exception e) { cleanUpConnectFail(); final ContainerConnectException ce = new ContainerConnectException(NLS.bind(Messages.XMPPChatRoomContainer_EXCEPTION_JOINING_ROOM, roomID)); ce.setStackTrace(e.getStackTrace()); throw ce; } } } /** * @param subject * @param from */ protected void fireSubjectUpdated(String subject, String from) { List notify = null; synchronized (chatRoomAdminListeners) { notify = new ArrayList(chatRoomAdminListeners); } for (final Iterator i = notify.iterator(); i.hasNext();) { final IChatRoomAdminListener l = (IChatRoomAdminListener) i.next(); l.handleSubjectChange(containerHelper.createUserIDFromName(from), subject); } } public void disconnect() { final ID groupID = getConnectedID(); fireContainerEvent(new ContainerDisconnectingEvent(this.getID(), groupID)); synchronized (getConnectLock()) { // If we are currently connected if (isConnected()) { try { multiuserchat.leave(); } catch (final Exception e) { traceStack("Exception in multi user chat.leave", e); //$NON-NLS-1$ } } connectionState = DISCONNECTED; remoteServerID = null; if (containerHelper != null) containerHelper.disconnect(); this.connection = null; } // notify listeners fireContainerEvent(new ContainerDisconnectedEvent(this.getID(), groupID)); } protected SOContext createSharedObjectContext(SOConfig soconfig, IQueueEnqueue queue) { return new XMPPContainerContext(soconfig.getSharedObjectID(), soconfig.getHomeContainerID(), this, soconfig.getProperties(), queue); } protected ID createChatRoomID(String groupName) throws IDCreateException { String username = getXMPPConnection().getUser(); final int atIndex = username.indexOf('@'); if (atIndex > 0) username = username.substring(0, atIndex); final String host = getXMPPConnection().getHost(); final Namespace ns = getConnectNamespace(); final ID targetID = IDFactory.getDefault().createID(ns, new Object[] {username, host, null, groupName, username}); return targetID; } protected ISynchAsynchConnection createConnection(ID remoteSpace, Object data) throws ConnectionCreateException { return null; } IChatMessageSender privateSender = new IChatMessageSender() { public void sendChatMessage(ID toID, ID threadID, Type type, String subject, String body, Map properties) throws ECFException { // TODO Auto-generated method stub } public void sendChatMessage(ID toID, String body) throws ECFException { // TODO Auto-generated method stub } }; public IChatMessageSender getPrivateMessageSender() { return privateSender; } public IChatRoomMessageSender getChatRoomMessageSender() { return new IChatRoomMessageSender() { public void sendMessage(String messageBody) throws ECFException { if (multiuserchat != null) { try { multiuserchat.sendMessage(messageBody); } catch (final Exception e) { final ECFException except = new ECFException(Messages.XMPPChatRoomContainer_EXCEPTION_SEND_MESSAGE, e); throw except; } } } }; } public void connect(String groupName) throws ContainerConnectException { ID targetID = null; try { targetID = createChatRoomID(groupName); } catch (final IDCreateException e) { throw new ContainerConnectException(Messages.XMPPChatRoomContainer_EXCEPTION_CREATING_ROOM_ID, e); } this.connect(targetID, null); } public void addChatRoomParticipantListener(IChatRoomParticipantListener participantListener) { if (containerHelper != null) { containerHelper.addChatParticipantListener(participantListener); } } public void removeChatRoomParticipantListener(IChatRoomParticipantListener participantListener) { if (containerHelper != null) { containerHelper.removeChatParticipantListener(participantListener); } } /* * (non-Javadoc) * * @see org.eclipse.ecf.presence.chatroom.IChatRoomContainer#addMessageListener(org.eclipse.ecf.presence.IIMMessageListener) */ public void addMessageListener(IIMMessageListener listener) { containerHelper.addChatRoomMessageListener(listener); } /* * (non-Javadoc) * * @see org.eclipse.ecf.presence.chatroom.IChatRoomContainer#removeMessageListener(org.eclipse.ecf.presence.IIMMessageListener) */ public void removeMessageListener(IIMMessageListener listener) { containerHelper.removeChatRoomMessageListener(listener); } /* * (non-Javadoc) * * @see org.eclipse.ecf.presence.chatroom.IChatRoomContainer#addChatRoomSubjectListener(org.eclipse.ecf.presence.chatroom.IChatRoomAdminListener) */ public void addChatRoomAdminListener(IChatRoomAdminListener subjectListener) { if (subjectListener == null) return; chatRoomAdminListeners.add(subjectListener); } /* * (non-Javadoc) * * @see org.eclipse.ecf.presence.chatroom.IChatRoomContainer#removeChatRoomSubjectListener(org.eclipse.ecf.presence.chatroom.IChatRoomAdminListener) */ public void removeChatRoomAdminListener(IChatRoomAdminListener subjectListener) { if (subjectListener == null) return; chatRoomAdminListeners.remove(subjectListener); } /* (non-Javadoc) * @see org.eclipse.ecf.presence.chatroom.IChatRoomContainer#getChatRoomParticipants() */ public ID[] getChatRoomParticipants() { return containerHelper.getChatRoomParticipants(); } /* (non-Javadoc) * @see org.eclipse.ecf.presence.chatroom.IChatRoomContainer#getChatRoomAdminSender() */ public IChatRoomAdminSender getChatRoomAdminSender() { synchronized (this) { if (chatRoomAdminSender == null) { chatRoomAdminSender = new IChatRoomAdminSender() { public void sendSubjectChange(String newsubject) throws ECFException { if (multiuserchat == null) throw new ECFException(Messages.XMPPChatRoomContainer_EXCEPTION_NOT_CONNECTED); try { multiuserchat.changeSubject(newsubject); } catch (final XMPPException e) { throw new ECFException(e); } } }; } } return chatRoomAdminSender; } }