Improve MUC message and presence listeners

instead of using a PacketListener, which means that the user has to
downcast the Packet to Message, we now use a Listener which callback
parameter is already Message/Presence.

It is necessary to introduce MessageListener and PresenceListener, which
are interfaces that have a callback for Message/Presence instead of
Packet. The 'old' MessageListener is renamed to ChatMessageListener.

Use Generics in ConnectionDetachedPacketCollector.
This commit is contained in:
Florian Schmaus 2014-10-12 18:01:19 +02:00
parent 38582eed84
commit e835df5641
13 changed files with 178 additions and 65 deletions

View File

@ -40,7 +40,7 @@ public class Chat {
private ChatManager chatManager;
private String threadID;
private String participant;
private final Set<MessageListener> listeners = new CopyOnWriteArraySet<MessageListener>();
private final Set<ChatMessageListener> listeners = new CopyOnWriteArraySet<ChatMessageListener>();
/**
* Creates a new chat with the specified user and thread ID.
@ -118,7 +118,7 @@ public class Chat {
*
* @param listener a packet listener.
*/
public void addMessageListener(MessageListener listener) {
public void addMessageListener(ChatMessageListener listener) {
if(listener == null) {
return;
}
@ -126,7 +126,7 @@ public class Chat {
listeners.add(listener);
}
public void removeMessageListener(MessageListener listener) {
public void removeMessageListener(ChatMessageListener listener) {
listeners.remove(listener);
}
@ -145,7 +145,7 @@ public class Chat {
*
* @return an unmodifiable collection of all of the listeners registered with this chat.
*/
public Collection<MessageListener> getListeners() {
public Collection<ChatMessageListener> getListeners() {
return Collections.unmodifiableCollection(listeners);
}
@ -174,7 +174,7 @@ public class Chat {
// probably never had one.
message.setThread(threadID);
for (MessageListener listener : listeners) {
for (ChatMessageListener listener : listeners) {
listener.processMessage(this, message);
}
}

View File

@ -206,7 +206,7 @@ public class ChatManager extends Manager{
* @param listener the listener which will listen for new messages from this chat.
* @return the created chat.
*/
public Chat createChat(String userJID, MessageListener listener) {
public Chat createChat(String userJID, ChatMessageListener listener) {
return createChat(userJID, null, listener);
}
@ -218,7 +218,7 @@ public class ChatManager extends Manager{
* @param listener the listener to add to the chat
* @return the created chat.
*/
public Chat createChat(String userJID, String thread, MessageListener listener) {
public Chat createChat(String userJID, String thread, ChatMessageListener listener) {
if (thread == null) {
thread = nextID();
}

View File

@ -0,0 +1,27 @@
/**
*
* Copyright 2003-2007 Jive Software.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack;
import org.jivesoftware.smack.packet.Message;
/**
*
*/
public interface ChatMessageListener {
void processMessage(Chat chat, Message message);
}

View File

@ -1,6 +1,6 @@
/**
*
* Copyright 2003-2007 Jive Software.
* Copyright © 2014 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -23,5 +23,5 @@ import org.jivesoftware.smack.packet.Message;
*
*/
public interface MessageListener {
void processMessage(Chat chat, Message message);
void processMessage(Message message);
}

View File

@ -0,0 +1,27 @@
/**
*
* Copyright © 2014 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack;
import org.jivesoftware.smack.packet.Presence;
/**
*
*/
public interface PresenceListener {
void processPresence(Presence presence);
}

View File

@ -0,0 +1,39 @@
/**
*
* Copyright © 2014 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.filter;
import org.jivesoftware.smack.packet.Message;
/**
* Filters message stanzas which have at least one body
*/
public class MessageWithSubjectFilter extends FlexiblePacketTypeFilter<Message> {
public static final PacketFilter INSTANCE = new MessageWithSubjectFilter();
private MessageWithSubjectFilter() {
super(Message.class);
}
@Override
protected boolean acceptSpecific(Message message) {
// Accept only messages which have a subject set
return message.getSubject() != null;
}
}

View File

@ -386,7 +386,7 @@ public class ChatConnectionTest {
class TestChatManagerListener implements ChatManagerListener {
private Chat newChat;
private MessageListener listener;
private ChatMessageListener listener;
public TestChatManagerListener(TestMessageListener msgListener) {
listener = msgListener;
@ -423,7 +423,7 @@ public class ChatConnectionTest {
}
}
private class TestMessageListener implements MessageListener {
private class TestMessageListener implements ChatMessageListener {
private Chat msgChat;
private int counter = 0;

View File

@ -18,14 +18,14 @@
package org.jivesoftware.smackx.chatstates;
import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.MessageListener;
import org.jivesoftware.smack.ChatMessageListener;
/**
* Events for when the state of a user in a chat changes.
*
* @author Alexander Wenckus
*/
public interface ChatStateListener extends MessageListener {
public interface ChatStateListener extends ChatMessageListener {
/**
* Fired when the state of a chat with another user changes.

View File

@ -23,10 +23,10 @@ import java.util.WeakHashMap;
import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.ChatManager;
import org.jivesoftware.smack.ChatManagerListener;
import org.jivesoftware.smack.ChatMessageListener;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.Manager;
import org.jivesoftware.smack.MessageListener;
import org.jivesoftware.smack.PacketInterceptor;
import org.jivesoftware.smack.filter.NotFilter;
import org.jivesoftware.smack.filter.PacketExtensionFilter;
@ -143,7 +143,7 @@ public class ChatStateManager extends Manager {
}
private void fireNewChatState(Chat chat, ChatState state) {
for (MessageListener listener : chat.getListeners()) {
for (ChatMessageListener listener : chat.getListeners()) {
if (listener instanceof ChatStateListener) {
((ChatStateListener) listener).stateChanged(chat, state);
}
@ -164,7 +164,7 @@ public class ChatStateManager extends Manager {
}
}
private class IncomingMessageInterceptor implements ChatManagerListener, MessageListener {
private class IncomingMessageInterceptor implements ChatManagerListener, ChatMessageListener {
public void chatCreated(final Chat chat, boolean createdLocally) {
chat.addMessageListener(this);

View File

@ -31,8 +31,8 @@ import org.jivesoftware.smack.packet.Packet;
*
* @author Larry Kirschner
*/
class ConnectionDetachedPacketCollector {
private ArrayBlockingQueue<Packet> resultQueue;
class ConnectionDetachedPacketCollector<P extends Packet> {
private ArrayBlockingQueue<P> resultQueue;
/**
* Creates a new packet collector. If the packet filter is <tt>null</tt>, then
@ -47,7 +47,7 @@ class ConnectionDetachedPacketCollector {
* all packets will match this collector.
*/
public ConnectionDetachedPacketCollector(int maxSize) {
this.resultQueue = new ArrayBlockingQueue<Packet>(maxSize);
this.resultQueue = new ArrayBlockingQueue<P>(maxSize);
}
/**
@ -58,7 +58,7 @@ class ConnectionDetachedPacketCollector {
* @return the next packet result, or <tt>null</tt> if there are no more
* results.
*/
public Packet pollResult() {
public P pollResult() {
return resultQueue.poll();
}
@ -68,7 +68,7 @@ class ConnectionDetachedPacketCollector {
*
* @return the next available packet.
*/
public Packet nextResult() {
public P nextResult() {
try {
return resultQueue.take();
}
@ -85,7 +85,7 @@ class ConnectionDetachedPacketCollector {
* @param timeout the amount of time to wait for the next packet (in milleseconds).
* @return the next available packet.
*/
public Packet nextResult(long timeout) {
public P nextResult(long timeout) {
try {
return resultQueue.poll(timeout, TimeUnit.MILLISECONDS);
}
@ -100,7 +100,7 @@ class ConnectionDetachedPacketCollector {
*
* @param packet the packet to process.
*/
protected void processPacket(Packet packet) {
protected void processPacket(P packet) {
if (packet == null) {
return;
}

View File

@ -35,12 +35,14 @@ import java.util.logging.Logger;
import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.ChatManager;
import org.jivesoftware.smack.ChatMessageListener;
import org.jivesoftware.smack.ConnectionCreationListener;
import org.jivesoftware.smack.Manager;
import org.jivesoftware.smack.MessageListener;
import org.jivesoftware.smack.PacketCollector;
import org.jivesoftware.smack.PacketInterceptor;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.PresenceListener;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
@ -107,13 +109,16 @@ public class MultiUserChat {
new ArrayList<UserStatusListener>();
private final List<ParticipantStatusListener> participantStatusListeners =
new ArrayList<ParticipantStatusListener>();
private final Set<MessageListener> messageListeners = new CopyOnWriteArraySet<MessageListener>();
private final Set<PresenceListener> presenceListeners = new CopyOnWriteArraySet<PresenceListener>();
private final PacketFilter fromRoomFilter;
private final PacketListener messageListener;
private final PacketListener presenceListener;
private PacketFilter presenceFilter;
private List<PacketInterceptor> presenceInterceptors = new ArrayList<PacketInterceptor>();
private PacketFilter messageFilter;
private RoomListenerMultiplexor roomListenerMultiplexor;
private ConnectionDetachedPacketCollector messageCollector;
private List<PacketListener> connectionListeners = new ArrayList<PacketListener>();
private ConnectionDetachedPacketCollector<Message> messageCollector;
static {
XMPPConnectionRegistry.addConnectionCreationListener(new ConnectionCreationListener() {
@ -174,6 +179,27 @@ public class MultiUserChat {
public MultiUserChat(XMPPConnection connection, String room) {
this.connection = connection;
this.room = room.toLowerCase(Locale.US);
messageListener = new PacketListener() {
@Override
public void processPacket(Packet packet) throws NotConnectedException {
Message message = (Message) packet;
for (MessageListener listener : messageListeners) {
listener.processMessage(message);
}
}
};
presenceListener = new PacketListener() {
@Override
public void processPacket(Packet packet) throws NotConnectedException {
Presence presence = (Presence) packet;
for (PresenceListener listener : presenceListeners) {
listener.processPresence(presence);
}
}
};
fromRoomFilter = FromMatchesFilter.create(room);
init();
}
@ -349,6 +375,11 @@ public class MultiUserChat {
this.nickname = nickname;
joined = true;
// Setup the messageListeners and presenceListeners
connection.addPacketListener(messageListener, new AndFilter(fromRoomFilter,
MessageTypeFilter.GROUPCHAT));
connection.addPacketListener(presenceListener, new AndFilter(fromRoomFilter,
PacketTypeFilter.PRESENCE));
// Update the list of joined rooms through this connection
List<String> rooms = joinedRooms.get(connection);
if (rooms == null) {
@ -1499,22 +1530,22 @@ public class MultiUserChat {
*
* @param listener a packet listener that will be notified of any presence packets
* sent to the group chat.
* @return true if the listener was not already added.
*/
public void addParticipantListener(PacketListener listener) {
connection.addPacketListener(listener, presenceFilter);
connectionListeners.add(listener);
public boolean addParticipantListener(PresenceListener listener) {
return presenceListeners.add(listener);
}
/**
* Remoces a packet listener that was being notified of any new Presence packets
* Removes a packet listener that was being notified of any new Presence packets
* sent to the group chat.
*
* @param listener a packet listener that was being notified of any presence packets
* sent to the group chat.
* @return true if the listener was removed, otherwise the listener was not added previously.
*/
public void removeParticipantListener(PacketListener listener) {
connection.removePacketListener(listener);
connectionListeners.remove(listener);
public boolean removeParticipantListener(PresenceListener listener) {
return presenceListeners.remove(listener);
}
/**
@ -1668,7 +1699,7 @@ public class MultiUserChat {
* created chat.
* @return new Chat for sending private messages to a given room occupant.
*/
public Chat createPrivateChat(String occupant, MessageListener listener) {
public Chat createPrivateChat(String occupant, ChatMessageListener listener) {
return ChatManager.getInstanceFor(connection).createChat(occupant, listener);
}
@ -1704,7 +1735,7 @@ public class MultiUserChat {
* <tt>null</tt> otherwise.
*/
public Message pollMessage() {
return (Message) messageCollector.pollResult();
return messageCollector.pollResult();
}
/**
@ -1714,7 +1745,7 @@ public class MultiUserChat {
* @return the next message.
*/
public Message nextMessage() {
return (Message) messageCollector.nextResult();
return messageCollector.nextResult();
}
/**
@ -1727,7 +1758,7 @@ public class MultiUserChat {
* message becoming available.
*/
public Message nextMessage(long timeout) {
return (Message) messageCollector.nextResult(timeout);
return messageCollector.nextResult(timeout);
}
/**
@ -1739,10 +1770,10 @@ public class MultiUserChat {
* PacketListener.
*
* @param listener a packet listener.
* @return true if the listener was not already added.
*/
public void addMessageListener(PacketListener listener) {
connection.addPacketListener(listener, messageFilter);
connectionListeners.add(listener);
public boolean addMessageListener(MessageListener listener) {
return messageListeners.add(listener);
}
/**
@ -1751,10 +1782,10 @@ public class MultiUserChat {
* being delivered to the listener.
*
* @param listener a packet listener.
* @return true if the listener was removed, otherwise the listener was not added previously.
*/
public void removeMessageListener(PacketListener listener) {
connection.removePacketListener(listener);
connectionListeners.remove(listener);
public boolean removeMessageListener(MessageListener listener) {
return messageListeners.remove(listener);
}
/**
@ -1798,6 +1829,8 @@ public class MultiUserChat {
if (rooms == null) {
return;
}
connection.removePacketListener(messageListener);
connection.removePacketListener(presenceListener);
rooms.remove(room);
cleanup();
}
@ -1906,12 +1939,8 @@ public class MultiUserChat {
}
private void init() {
// Create filters
messageFilter = new AndFilter(FromMatchesFilter.create(room), MessageTypeFilter.GROUPCHAT);
presenceFilter = new AndFilter(FromMatchesFilter.create(room), PacketTypeFilter.PRESENCE);
// Create a collector for incoming messages.
messageCollector = new ConnectionDetachedPacketCollector();
messageCollector = new ConnectionDetachedPacketCollector<Message>();
// Create a listener for subject updates.
PacketListener subjectListener = new PacketListener() {
@ -2324,10 +2353,6 @@ public class MultiUserChat {
try {
if (connection != null) {
roomListenerMultiplexor.removeRoom(room);
// Remove all the PacketListeners added to the connection by this chat
for (PacketListener connectionListener : connectionListeners) {
connection.removePacketListener(connectionListener);
}
}
} catch (Exception e) {
// Do nothing

View File

@ -20,6 +20,7 @@ package org.jivesoftware.smackx.muc;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.filter.MessageTypeFilter;
import org.jivesoftware.smack.filter.MessageWithSubjectFilter;
import org.jivesoftware.smack.filter.PacketExtensionFilter;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.filter.PacketTypeFilter;
@ -37,22 +38,16 @@ import org.jivesoftware.smackx.muc.packet.MUCUser;
class PacketMultiplexListener implements PacketListener {
private static final PacketFilter PRESENCE_FILTER = new PacketTypeFilter(Presence.class);
private static final PacketFilter SUBJECT_FILTER = new PacketFilter() {
public boolean accept(Packet packet) {
Message msg = (Message) packet;
return msg.getSubject() != null;
}
};
private static final PacketFilter DECLINES_FILTER = new PacketExtensionFilter(MUCUser.ELEMENT,
MUCUser.NAMESPACE);
private ConnectionDetachedPacketCollector messageCollector;
private ConnectionDetachedPacketCollector<Message> messageCollector;
private PacketListener presenceListener;
private PacketListener subjectListener;
private PacketListener declinesListener;
public PacketMultiplexListener(
ConnectionDetachedPacketCollector messageCollector,
ConnectionDetachedPacketCollector<Message> messageCollector,
PacketListener presenceListener,
PacketListener subjectListener, PacketListener declinesListener) {
if (messageCollector == null) {
@ -78,9 +73,9 @@ class PacketMultiplexListener implements PacketListener {
presenceListener.processPacket(p);
}
else if (MessageTypeFilter.GROUPCHAT.accept(p)) {
messageCollector.processPacket(p);
messageCollector.processPacket((Message) p);
if (SUBJECT_FILTER.accept(p)) {
if (MessageWithSubjectFilter.INSTANCE.accept(p)) {
subjectListener.processPacket(p);
}
}

View File

@ -28,7 +28,7 @@ public class ConnectionDetachedPacketCollectorTest
@Test
public void verifyRollover()
{
ConnectionDetachedPacketCollector collector = new ConnectionDetachedPacketCollector(5);
ConnectionDetachedPacketCollector<Packet> collector = new ConnectionDetachedPacketCollector<Packet>(5);
for (int i=0; i<6; i++)
{
@ -68,7 +68,7 @@ public class ConnectionDetachedPacketCollectorTest
public void verifyThreadSafety()
{
int insertCount = 500;
final ConnectionDetachedPacketCollector collector = new ConnectionDetachedPacketCollector(insertCount);
final ConnectionDetachedPacketCollector<Packet> collector = new ConnectionDetachedPacketCollector<Packet>(insertCount);
Thread consumer1 = new Thread(new Runnable()
{