• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * $RCSfile$
3  * $Revision: 2407 $
4  * $Date: 2004-11-02 15:37:00 -0800 (Tue, 02 Nov 2004) $
5  *
6  * Copyright 2003-2007 Jive Software.
7  *
8  * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *     http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20 
21 package org.jivesoftware.smackx;
22 
23 import org.jivesoftware.smack.*;
24 import org.jivesoftware.smack.util.collections.ReferenceMap;
25 import org.jivesoftware.smack.filter.PacketFilter;
26 import org.jivesoftware.smack.filter.NotFilter;
27 import org.jivesoftware.smack.filter.PacketExtensionFilter;
28 import org.jivesoftware.smack.packet.Message;
29 import org.jivesoftware.smack.packet.Packet;
30 import org.jivesoftware.smack.packet.PacketExtension;
31 import org.jivesoftware.smackx.packet.ChatStateExtension;
32 
33 import java.util.Map;
34 import java.util.WeakHashMap;
35 
36 /**
37  * Handles chat state for all chats on a particular Connection. This class manages both the
38  * packet extensions and the disco response neccesary for compliance with
39  * <a href="http://www.xmpp.org/extensions/xep-0085.html">XEP-0085</a>.
40  *
41  * NOTE: {@link org.jivesoftware.smackx.ChatStateManager#getInstance(org.jivesoftware.smack.Connection)}
42  * needs to be called in order for the listeners to be registered appropriately with the connection.
43  * If this does not occur you will not receive the update notifications.
44  *
45  * @author Alexander Wenckus
46  * @see org.jivesoftware.smackx.ChatState
47  * @see org.jivesoftware.smackx.packet.ChatStateExtension
48  */
49 public class ChatStateManager {
50 
51     private static final Map<Connection, ChatStateManager> managers =
52             new WeakHashMap<Connection, ChatStateManager>();
53 
54     private static final PacketFilter filter = new NotFilter(
55                 new PacketExtensionFilter("http://jabber.org/protocol/chatstates"));
56 
57     /**
58      * Returns the ChatStateManager related to the Connection and it will create one if it does
59      * not yet exist.
60      *
61      * @param connection the connection to return the ChatStateManager
62      * @return the ChatStateManager related the the connection.
63      */
getInstance(final Connection connection)64     public static ChatStateManager getInstance(final Connection connection) {
65         if(connection == null) {
66             return null;
67         }
68         synchronized (managers) {
69             ChatStateManager manager = managers.get(connection);
70             if (manager == null) {
71                 manager = new ChatStateManager(connection);
72                 manager.init();
73                 managers.put(connection, manager);
74             }
75 
76             return manager;
77         }
78     }
79 
80     private final Connection connection;
81 
82     private final OutgoingMessageInterceptor outgoingInterceptor = new OutgoingMessageInterceptor();
83 
84     private final IncomingMessageInterceptor incomingInterceptor = new IncomingMessageInterceptor();
85 
86     /**
87      * Maps chat to last chat state.
88      */
89     private final Map<Chat, ChatState> chatStates =
90             new ReferenceMap<Chat, ChatState>(ReferenceMap.WEAK, ReferenceMap.HARD);
91 
ChatStateManager(Connection connection)92     private ChatStateManager(Connection connection) {
93         this.connection = connection;
94     }
95 
init()96     private void init() {
97         connection.getChatManager().addOutgoingMessageInterceptor(outgoingInterceptor,
98                 filter);
99         connection.getChatManager().addChatListener(incomingInterceptor);
100 
101         ServiceDiscoveryManager.getInstanceFor(connection)
102                 .addFeature("http://jabber.org/protocol/chatstates");
103     }
104 
105     /**
106      * Sets the current state of the provided chat. This method will send an empty bodied Message
107      * packet with the state attached as a {@link org.jivesoftware.smack.packet.PacketExtension}, if
108      * and only if the new chat state is different than the last state.
109      *
110      * @param newState the new state of the chat
111      * @param chat the chat.
112      * @throws org.jivesoftware.smack.XMPPException
113      *          when there is an error sending the message
114      *          packet.
115      */
setCurrentState(ChatState newState, Chat chat)116     public void setCurrentState(ChatState newState, Chat chat) throws XMPPException {
117         if(chat == null || newState == null) {
118             throw new IllegalArgumentException("Arguments cannot be null.");
119         }
120         if(!updateChatState(chat, newState)) {
121             return;
122         }
123         Message message = new Message();
124         ChatStateExtension extension = new ChatStateExtension(newState);
125         message.addExtension(extension);
126 
127         chat.sendMessage(message);
128     }
129 
130 
equals(Object o)131     public boolean equals(Object o) {
132         if (this == o) return true;
133         if (o == null || getClass() != o.getClass()) return false;
134 
135         ChatStateManager that = (ChatStateManager) o;
136 
137         return connection.equals(that.connection);
138 
139     }
140 
hashCode()141     public int hashCode() {
142         return connection.hashCode();
143     }
144 
updateChatState(Chat chat, ChatState newState)145     private boolean updateChatState(Chat chat, ChatState newState) {
146         ChatState lastChatState = chatStates.get(chat);
147         if (lastChatState != newState) {
148             chatStates.put(chat, newState);
149             return true;
150         }
151         return false;
152     }
153 
fireNewChatState(Chat chat, ChatState state)154     private void fireNewChatState(Chat chat, ChatState state) {
155         for (MessageListener listener : chat.getListeners()) {
156             if (listener instanceof ChatStateListener) {
157                 ((ChatStateListener) listener).stateChanged(chat, state);
158             }
159         }
160     }
161 
162     private class OutgoingMessageInterceptor implements PacketInterceptor {
163 
interceptPacket(Packet packet)164         public void interceptPacket(Packet packet) {
165             Message message = (Message) packet;
166             Chat chat = connection.getChatManager().getThreadChat(message.getThread());
167             if (chat == null) {
168                 return;
169             }
170             if (updateChatState(chat, ChatState.active)) {
171                 message.addExtension(new ChatStateExtension(ChatState.active));
172             }
173         }
174     }
175 
176     private class IncomingMessageInterceptor implements ChatManagerListener, MessageListener {
177 
chatCreated(final Chat chat, boolean createdLocally)178         public void chatCreated(final Chat chat, boolean createdLocally) {
179             chat.addMessageListener(this);
180         }
181 
processMessage(Chat chat, Message message)182         public void processMessage(Chat chat, Message message) {
183             PacketExtension extension
184                     = message.getExtension("http://jabber.org/protocol/chatstates");
185             if (extension == null) {
186                 return;
187             }
188 
189             ChatState state;
190             try {
191                 state = ChatState.valueOf(extension.getElementName());
192             }
193             catch (Exception ex) {
194                 return;
195             }
196 
197             fireNewChatState(chat, state);
198         }
199     }
200 }
201