KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jivesoftware > smack > GroupChat


1 /**
2  * $RCSfile$
3  * $Revision: 2464 $
4  * $Date: 2005-03-07 19:01:18 -0300 (Mon, 07 Mar 2005) $
5  *
6  * Copyright 2003-2004 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.smack;
22
23 import org.jivesoftware.smack.packet.Presence;
24 import org.jivesoftware.smack.packet.Message;
25 import org.jivesoftware.smack.packet.Packet;
26 import org.jivesoftware.smack.filter.*;
27
28 import java.util.*;
29
30 /**
31  * A GroupChat is a conversation that takes place among many users in a virtual
32  * room. When joining a group chat, you specify a nickname, which is the identity
33  * that other chat room users see.
34  *
35  * @see XMPPConnection#createGroupChat(String)
36  * @author Matt Tucker
37  */

38 public class GroupChat {
39
40     private XMPPConnection connection;
41     private String JavaDoc room;
42     private String JavaDoc nickname = null;
43     private boolean joined = false;
44     private List participants = new ArrayList();
45     private List connectionListeners = new ArrayList();
46
47     private PacketFilter presenceFilter;
48     private PacketFilter messageFilter;
49     private PacketCollector messageCollector;
50
51     /**
52      * Creates a new group chat with the specified connection and room name. Note: no
53      * information is sent to or received from the server until you attempt to
54      * {@link #join(String) join} the chat room. On some server implementations,
55      * the room will not be created until the first person joins it.<p>
56      *
57      * Most XMPP servers use a sub-domain for the chat service (eg chat.example.com
58      * for the XMPP server example.com). You must ensure that the room address you're
59      * trying to connect to includes the proper chat sub-domain.
60      *
61      * @param connection the XMPP connection.
62      * @param room the name of the room in the form "roomName@service", where
63      * "service" is the hostname at which the multi-user chat
64      * service is running.
65      */

66     public GroupChat(XMPPConnection connection, String JavaDoc room) {
67         this.connection = connection;
68         this.room = room;
69         // Create a collector for all incoming messages.
70
messageFilter = new AndFilter(new FromContainsFilter(room),
71                 new PacketTypeFilter(Message.class));
72         messageFilter = new AndFilter(messageFilter, new PacketFilter() {
73             public boolean accept(Packet packet) {
74                 Message msg = (Message)packet;
75                 return msg.getType() == Message.Type.GROUP_CHAT;
76             }
77         });
78         messageCollector = connection.createPacketCollector(messageFilter);
79         // Create a listener for all presence updates.
80
presenceFilter = new AndFilter(new FromContainsFilter(room),
81                 new PacketTypeFilter(Presence.class));
82         connection.addPacketListener(new PacketListener() {
83             public void processPacket(Packet packet) {
84                 Presence presence = (Presence)packet;
85                 String JavaDoc from = presence.getFrom();
86                 if (presence.getType() == Presence.Type.AVAILABLE) {
87                     synchronized (participants) {
88                         if (!participants.contains(from)) {
89                             participants.add(from);
90                         }
91                     }
92                 }
93                 else if (presence.getType() == Presence.Type.UNAVAILABLE) {
94                     synchronized (participants) {
95                         participants.remove(from);
96                     }
97                 }
98             }
99         }, presenceFilter);
100     }
101
102     /**
103      * Returns the name of the room this GroupChat object represents.
104      *
105      * @return the groupchat room name.
106      */

107     public String JavaDoc getRoom() {
108         return room;
109     }
110
111     /**
112      * Joins the chat room using the specified nickname. If already joined
113      * using another nickname, this method will first leave the room and then
114      * re-join using the new nickname. The default timeout of 5 seconds for a reply
115      * from the group chat server that the join succeeded will be used.
116      *
117      * @param nickname the nickname to use.
118      * @throws XMPPException if an error occurs joining the room. In particular, a
119      * 409 error can occur if someone is already in the group chat with the same
120      * nickname.
121      */

122     public synchronized void join(String JavaDoc nickname) throws XMPPException {
123         join(nickname, SmackConfiguration.getPacketReplyTimeout());
124     }
125
126     /**
127      * Joins the chat room using the specified nickname. If already joined as
128      * another nickname, will leave as that name first before joining under the new
129      * name.
130      *
131      * @param nickname the nickname to use.
132      * @param timeout the number of milleseconds to wait for a reply from the
133      * group chat that joining the room succeeded.
134      * @throws XMPPException if an error occurs joining the room. In particular, a
135      * 409 error can occur if someone is already in the group chat with the same
136      * nickname.
137      */

138     public synchronized void join(String JavaDoc nickname, long timeout) throws XMPPException {
139         if (nickname == null || nickname.equals("")) {
140             throw new IllegalArgumentException JavaDoc("Nickname must not be null or blank.");
141         }
142         // If we've already joined the room, leave it before joining under a new
143
// nickname.
144
if (joined) {
145             leave();
146         }
147         // We join a room by sending a presence packet where the "to"
148
// field is in the form "roomName@service/nickname"
149
Presence joinPresence = new Presence(Presence.Type.AVAILABLE);
150         joinPresence.setTo(room + "/" + nickname);
151         // Wait for a presence packet back from the server.
152
PacketFilter responseFilter = new AndFilter(
153                 new FromContainsFilter(room + "/" + nickname),
154                 new PacketTypeFilter(Presence.class));
155         PacketCollector response = connection.createPacketCollector(responseFilter);
156         // Send join packet.
157
connection.sendPacket(joinPresence);
158         // Wait up to a certain number of seconds for a reply.
159
Presence presence = (Presence)response.nextResult(timeout);
160         response.cancel();
161         if (presence == null) {
162             throw new XMPPException("No response from server.");
163         }
164         else if (presence.getError() != null) {
165             throw new XMPPException(presence.getError());
166         }
167         this.nickname = nickname;
168         joined = true;
169     }
170
171     /**
172      * Returns true if currently in the group chat (after calling the {@link
173      * #join(String)} method.
174      *
175      * @return true if currently in the group chat room.
176      */

177     public boolean isJoined() {
178         return joined;
179     }
180
181     /**
182      * Leave the chat room.
183      */

184     public synchronized void leave() {
185         // If not joined already, do nothing.
186
if (!joined) {
187             return;
188         }
189         // We leave a room by sending a presence packet where the "to"
190
// field is in the form "roomName@service/nickname"
191
Presence leavePresence = new Presence(Presence.Type.UNAVAILABLE);
192         leavePresence.setTo(room + "/" + nickname);
193         connection.sendPacket(leavePresence);
194         // Reset participant information.
195
participants = new ArrayList();
196         nickname = null;
197         joined = false;
198     }
199
200     /**
201      * Returns the nickname that was used to join the room, or <tt>null</tt> if not
202      * currently joined.
203      *
204      * @return the nickname currently being used.
205      */

206     public String JavaDoc getNickname() {
207         return nickname;
208     }
209
210     /**
211      * Returns the number of participants in the group chat.<p>
212      *
213      * Note: this value will only be accurate after joining the group chat, and
214      * may fluctuate over time. If you query this value directly after joining the
215      * group chat it may not be accurate, as it takes a certain amount of time for
216      * the server to send all presence packets to this client.
217      *
218      * @return the number of participants in the group chat.
219      */

220     public int getParticipantCount() {
221         synchronized (participants) {
222             return participants.size();
223         }
224     }
225
226     /**
227      * Returns an Iterator (of Strings) for the list of fully qualified participants
228      * in the group chat. For example, "conference@chat.jivesoftware.com/SomeUser".
229      * Typically, a client would only display the nickname of the participant. To
230      * get the nickname from the fully qualified name, use the
231      * {@link org.jivesoftware.smack.util.StringUtils#parseResource(String)} method.
232      * Note: this value will only be accurate after joining the group chat, and may
233      * fluctuate over time.
234      *
235      * @return an Iterator for the participants in the group chat.
236      */

237     public Iterator getParticipants() {
238         synchronized (participants) {
239             return Collections.unmodifiableList(new ArrayList(participants)).iterator();
240         }
241     }
242
243     /**
244      * Adds a packet listener that will be notified of any new Presence packets
245      * sent to the group chat. Using a listener is a suitable way to know when the list
246      * of participants should be re-loaded due to any changes.
247      *
248      * @param listener a packet listener that will be notified of any presence packets
249      * sent to the group chat.
250      */

251     public void addParticipantListener(PacketListener listener) {
252         connection.addPacketListener(listener, presenceFilter);
253         connectionListeners.add(listener);
254     }
255
256     /**
257      * Sends a message to the chat room.
258      *
259      * @param text the text of the message to send.
260      * @throws XMPPException if sending the message fails.
261      */

262     public void sendMessage(String JavaDoc text) throws XMPPException {
263         Message message = new Message(room, Message.Type.GROUP_CHAT);
264         message.setBody(text);
265         connection.sendPacket(message);
266     }
267
268     /**
269      * Creates a new Message to send to the chat room.
270      *
271      * @return a new Message addressed to the chat room.
272      */

273     public Message createMessage() {
274         return new Message(room, Message.Type.GROUP_CHAT);
275     }
276
277     /**
278      * Sends a Message to the chat room.
279      *
280      * @param message the message.
281      * @throws XMPPException if sending the message fails.
282      */

283     public void sendMessage(Message message) throws XMPPException {
284         connection.sendPacket(message);
285     }
286
287      /**
288      * Polls for and returns the next message, or <tt>null</tt> if there isn't
289      * a message immediately available. This method provides significantly different
290      * functionalty than the {@link #nextMessage()} method since it's non-blocking.
291      * In other words, the method call will always return immediately, whereas the
292      * nextMessage method will return only when a message is available (or after
293      * a specific timeout).
294      *
295      * @return the next message if one is immediately available and
296      * <tt>null</tt> otherwise.
297      */

298     public Message pollMessage() {
299         return (Message)messageCollector.pollResult();
300     }
301
302     /**
303      * Returns the next available message in the chat. The method call will block
304      * (not return) until a message is available.
305      *
306      * @return the next message.
307      */

308     public Message nextMessage() {
309         return (Message)messageCollector.nextResult();
310     }
311
312     /**
313      * Returns the next available message in the chat. The method call will block
314      * (not return) until a packet is available or the <tt>timeout</tt> has elapased.
315      * If the timeout elapses without a result, <tt>null</tt> will be returned.
316      *
317      * @param timeout the maximum amount of time to wait for the next message.
318      * @return the next message, or <tt>null</tt> if the timeout elapses without a
319      * message becoming available.
320      */

321     public Message nextMessage(long timeout) {
322         return (Message)messageCollector.nextResult(timeout);
323     }
324
325     /**
326      * Adds a packet listener that will be notified of any new messages in the
327      * group chat. Only "group chat" messages addressed to this group chat will
328      * be delivered to the listener. If you wish to listen for other packets
329      * that may be associated with this group chat, you should register a
330      * PacketListener directly with the XMPPConnection with the appropriate
331      * PacketListener.
332      *
333      * @param listener a packet listener.
334      */

335     public void addMessageListener(PacketListener listener) {
336         connection.addPacketListener(listener, messageFilter);
337         connectionListeners.add(listener);
338     }
339
340     public void finalize() throws Throwable JavaDoc {
341         super.finalize();
342         try {
343             if (messageCollector != null) {
344                 messageCollector.cancel();
345             }
346             // Remove all the PacketListeners added to the connection by this GroupChat
347
for (Iterator it=connectionListeners.iterator(); it.hasNext();) {
348                 connection.removePacketListener((PacketListener) it.next());
349             }
350         }
351         catch (Exception JavaDoc e) {}
352     }
353 }
Popular Tags