Removing Smack ChatManager, Chat and ChatStateManager objects from our code and implements manual sending of messages and handles the responses.

fix-message-formatting 5233
hristoterezov 12 years ago
parent 1c9691a3b4
commit 2a12abaefe

@ -33,6 +33,7 @@
* @author Matthieu Helleringer
* @author Alain Knaebel
* @author Emil Ivov
* @author Hristo Terezov
*/
public class OperationSetBasicInstantMessagingJabberImpl
extends AbstractOperationSetBasicInstantMessaging
@ -56,9 +57,14 @@ public class OperationSetBasicInstantMessagingJabberImpl
* target a specific resource (rather than sending a message to all logged
* instances of a user).
*/
private Map<String, TargetAddress> jids
= new Hashtable<String, TargetAddress>();
private Map<String, StoredThreadID> jids
= new Hashtable<String, StoredThreadID>();
/**
* The most recent full JID used for the contact address.
*/
private Map<String, String> recentJIDForAddress
= new Hashtable<String, String>();
/**
* The smackMessageListener instance listens for incoming messages.
* Keep a reference of it so if anything goes wrong we don't add
@ -70,18 +76,27 @@ public class OperationSetBasicInstantMessagingJabberImpl
* Contains the complete jid of a specific user and the time that it was
* last used so that we could remove it after a certain point.
*/
public static class TargetAddress
public static class StoredThreadID
{
/** The last complete JID (including resource) that we got a msg from*/
String jid;
/** The time that we last sent or received a message from this jid */
long lastUpdatedTime;
/** The last chat used, this way we will reuse the thread-id */
Chat chat;
String threadID;
}
/**
* A prefix helps to make sure that thread ID's are unique across mutliple
* instances.
*/
private static String prefix = StringUtils.randomString(5);
/**
* Keeps track of the current increment, which is appended to the prefix to
* forum a unique thread ID.
*/
private static long id = 0;
/**
* The number of milliseconds that we preserve threads with no traffic
* before considering them dead.
@ -266,11 +281,7 @@ public boolean isContentTypeSupported(String contentType, Contact contact)
return true;
else if(contentType.equals(HTML_MIME_TYPE))
{
String toJID = null;
TargetAddress ta = getJidForAddress(contact.getAddress());
if(ta != null)
toJID = ta.jid;
String toJID = recentJIDForAddress.get(contact.getAddress());
if (toJID == null)
toJID = contact.getAddress();
@ -283,74 +294,25 @@ else if(contentType.equals(HTML_MIME_TYPE))
return false;
}
/**
* Returns a reference to an open chat with the specified
* <tt>jid</tt> if one exists or creates a new one otherwise.
*
* @param toAddress the address without the resource, we used to put
* a reference in jids table.
* @param jid the Jabber ID that we'd like to obtain a chat instance for.
*
* @return a reference to an open chat with the specified
* <tt>jid</tt> if one exists or creates a new one otherwise.
*/
public Chat obtainChatInstance(String toAddress,
String jid)
{
XMPPConnection jabberConnection
= jabberProvider.getConnection();
TargetAddress ta = getJidForAddress(toAddress);
if (ta != null
&& ta.chat != null
&& ta.jid.equals(jid)
&& ta.chat.getParticipant().equals(jid))
{
return ta.chat;
}
org.jivesoftware.smack.MessageListener msgListener
= new org.jivesoftware.smack.MessageListener()
{
public void processMessage(
Chat chat,
org.jivesoftware.smack.packet.Message message)
{
//we are not fully supporting chat based messaging
//right now and only use a hack to make it look that
//way. as a result we don't listen on the chat
//itself and the only thing we do here is an update
//of the active thread timestamp.
}
};
//we don't have a thread for this chat, so let's create one.
Chat chat = jabberConnection.getChatManager()
.createChat(jid, msgListener);
return chat;
}
/**
* Remove from our <tt>jids</tt> map all entries that have not seen any
* activity (i.e. neither outgoing nor incoming messags) for more than
* JID_INACTIVITY_TIMEOUT. Note that this method is not synchronous and that
* it is only meant for use by the {@link #getJidForAddress(String)} and
* it is only meant for use by the {@link #getThreadIDForAddress(String)} and
* {@link #putJidForAddress(String, String, Chat)}
*/
private void purgeOldJids()
{
long currentTime = System.currentTimeMillis();
Iterator<Map.Entry<String, TargetAddress>> entries
Iterator<Map.Entry<String, StoredThreadID>> entries
= jids.entrySet().iterator();
while( entries.hasNext() )
{
Map.Entry<String, TargetAddress> entry = entries.next();
TargetAddress target = entry.getValue();
Map.Entry<String, StoredThreadID> entry = entries.next();
StoredThreadID target = entry.getValue();
if (currentTime - target.lastUpdatedTime
> JID_INACTIVITY_TIMEOUT)
@ -371,19 +333,19 @@ private void purgeOldJids()
* contacted us from or <tt>null</tt> if we don't have a jid for the
* specified <tt>address</tt> yet.
*/
TargetAddress getJidForAddress(String address)
String getThreadIDForAddress(String jid)
{
synchronized(jids)
{
purgeOldJids();
TargetAddress ta = jids.get(address);
StoredThreadID ta = jids.get(jid);
if (ta == null)
return null;
ta.lastUpdatedTime = System.currentTimeMillis();
return ta;
return ta.threadID;
}
}
@ -398,23 +360,24 @@ TargetAddress getJidForAddress(String address)
* @param jid the jid (i.e. address/resource) that the contact with the
* specified <tt>address</tt> last contacted us from.
*/
private void putJidForAddress(String address, String jid, Chat chat)
private void putJidForAddress(String jid, String threadID)
{
synchronized(jids)
{
purgeOldJids();
TargetAddress ta = jids.get(address);
StoredThreadID ta = jids.get(jid);
if (ta == null)
{
ta = new TargetAddress();
jids.put(address, ta);
ta = new StoredThreadID();
jids.put(jid, ta);
}
ta.jid = jid;
recentJIDForAddress.put(StringUtils.parseBareAddress(jid), jid);
ta.lastUpdatedTime = System.currentTimeMillis();
ta.chat = chat;
ta.threadID = threadID;
}
}
@ -441,115 +404,100 @@ private MessageDeliveredEvent sendMessage( Contact to,
"The specified contact is not a Jabber contact."
+ to);
try
{
assertConnected();
org.jivesoftware.smack.packet.Message msg =
new org.jivesoftware.smack.packet.Message();
assertConnected();
String toJID = null;
boolean sendToBaseResource = false;
if (toResource != null)
{
if(toResource.equals(ContactResource.BASE_RESOURCE))
{
toJID = to.getAddress();
sendToBaseResource = true;
}
else
toJID =
((ContactResourceJabberImpl) toResource).getFullJid();
}
org.jivesoftware.smack.packet.Message msg =
new org.jivesoftware.smack.packet.Message();
if (toJID == null)
{
TargetAddress ta = getJidForAddress(to.getAddress());
if(ta != null)
toJID = ta.jid;
}
String toJID = null;
if (toJID == null)
if (toResource != null)
{
if(toResource.equals(ContactResource.BASE_RESOURCE))
{
sendToBaseResource = true;
toJID = to.getAddress();
}
else
toJID =
((ContactResourceJabberImpl) toResource).getFullJid();
}
Chat chat = obtainChatInstance(to.getAddress(), toJID);
if (toJID == null)
{
toJID = to.getAddress();
}
msg.setPacketID(message.getMessageUID());
msg.setTo(toJID);
msg.setPacketID(message.getMessageUID());
msg.setTo(toJID);
for (PacketExtension ext : extensions)
{
msg.addExtension(ext);
}
for (PacketExtension ext : extensions)
{
msg.addExtension(ext);
}
if (logger.isTraceEnabled())
logger.trace("Will send a message to:" + toJID
+ " chat.jid=" + chat.getParticipant()
+ " chat.tid=" + chat.getThreadID());
if (logger.isTraceEnabled())
logger.trace("Will send a message to:" + toJID
+ " chat.jid=" + toJID);
MessageDeliveredEvent msgDeliveryPendingEvt
= new MessageDeliveredEvent(message, to, toResource);
MessageDeliveredEvent msgDeliveryPendingEvt
= new MessageDeliveredEvent(message, to, toResource);
msgDeliveryPendingEvt
= messageDeliveryPendingTransform(msgDeliveryPendingEvt);
msgDeliveryPendingEvt
= messageDeliveryPendingTransform(msgDeliveryPendingEvt);
if (msgDeliveryPendingEvt == null)
return null;
if (msgDeliveryPendingEvt == null)
return null;
String content = msgDeliveryPendingEvt
.getSourceMessage().getContent();
String content = msgDeliveryPendingEvt
.getSourceMessage().getContent();
if(message.getContentType().equals(HTML_MIME_TYPE))
{
msg.setBody(Html2Text.extractText(content));
if(message.getContentType().equals(HTML_MIME_TYPE))
{
msg.setBody(Html2Text.extractText(content));
// Check if the other user supports XHTML messages
// make sure we use our discovery manager as it caches calls
if(jabberProvider.isFeatureListSupported(
chat.getParticipant(),
HTML_NAMESPACE))
{
// Add the XHTML text to the message
XHTMLManager.addBody(msg,
OPEN_BODY_TAG + content + CLOSE_BODY_TAG);
}
}
else
// Check if the other user supports XHTML messages
// make sure we use our discovery manager as it caches calls
if(jabberProvider.isFeatureListSupported(
toJID,
HTML_NAMESPACE))
{
// this is plain text so keep it as it is.
msg.setBody(content);
// Add the XHTML text to the message
XHTMLManager.addBody(msg,
OPEN_BODY_TAG + content + CLOSE_BODY_TAG);
}
}
else
{
// this is plain text so keep it as it is.
msg.setBody(content);
}
//msg.addExtension(new Version());
//msg.addExtension(new Version());
if(msgDeliveryPendingEvt.isMessageEncrypted())
{
msg.addExtension(new CarbonPacketExtension.PrivateExtension());
}
if(msgDeliveryPendingEvt.isMessageEncrypted())
{
msg.addExtension(new CarbonPacketExtension.PrivateExtension());
}
MessageEventManager.
addNotificationsRequests(msg, true, false, false, true);
MessageEventManager.
addNotificationsRequests(msg, true, false, false, true);
chat.sendMessage(msg);
String threadID = getThreadIDForAddress(toJID);
if(threadID == null)
threadID = nextThreadID();
putJidForAddress(to.getAddress(), toJID, chat);
msg.setThread(threadID);
MessageDeliveredEvent msgDeliveredEvt
= new MessageDeliveredEvent(message, to, toResource);
jabberProvider.getConnection().sendPacket(msg);
// msgDeliveredEvt = messageDeliveredTransform(msgDeliveredEvt);
putJidForAddress(toJID, threadID);
return msgDeliveredEvt;
}
catch (XMPPException ex)
{
logger.error("message not sent", ex);
return null;
}
MessageDeliveredEvent msgDeliveredEvt
= new MessageDeliveredEvent(message, to, toResource);
// msgDeliveredEvt = messageDeliveredTransform(msgDeliveredEvt);
return msgDeliveredEvt;
}
/**
@ -1009,27 +957,7 @@ public void processPacket(Packet packet)
fireMessageEvent(ev);
return;
}
//cache the jid (resource included) of the contact that's sending us
//a message so that all following messages would go to the resource
//that they contacted us from.
String address = userBareID;
if(isPrivateMessaging)
{
address = JabberActivator.getResources().getI18NString(
"service.gui.FROM",
new String[]{
StringUtils.parseResource(msg.getFrom()),
userBareID} );
}
Chat chat =
jabberProvider.getConnection().getChatManager()
.getThreadChat(msg.getThread());
putJidForAddress(address, userFullId, chat);
if (logger.isTraceEnabled())
logger.trace("just mapped: " + userBareID
+ " to " + msg.getFrom());
putJidForAddress(userFullId, msg.getThread());
// In the second condition we filter all group chat messages,
// because they are managed by the multi user chat operation set.
@ -1247,6 +1175,11 @@ private String createMailboxDescription(MailboxIQ mailboxIQ)
return message.toString();
}
public String getRecentJIDForAddress(String address)
{
return recentJIDForAddress.get(address);
}
/**
* Receives incoming MailNotification Packets
*/
@ -1358,4 +1291,16 @@ public void addMessageFilters(PacketFilter filter)
{
this.packetFilters.add(filter);
}
/**
* Returns the next unique thread id. Each thread id made up of a short
* alphanumeric prefix along with a unique numeric value.
*
* @return the next thread id.
*/
private static synchronized String nextThreadID() {
return prefix + Long.toString(id++);
}
}

@ -14,8 +14,12 @@
import net.java.sip.communicator.util.*;
import org.jivesoftware.smack.*;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.filter.*;
import org.jivesoftware.smack.packet.*;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.util.*;
import org.jivesoftware.smackx.*;
import org.jivesoftware.smackx.packet.*;
/**
* Maps SIP Communicator typing notifications to those going and coming from
@ -23,6 +27,7 @@
*
* @author Damian Minkov
* @author Emil Ivov
* @author Hristo Terezov
*/
public class OperationSetTypingNotificationsJabberImpl
extends AbstractOperationSetTypingNotifications<ProtocolProviderServiceJabberImpl>
@ -61,12 +66,6 @@ public class OperationSetTypingNotificationsJabberImpl
*/
private SmackChatStateListener smackChatStateListener = null;
/**
* The listener instance that we use in order to track for new chats so that
* we could stick a SmackChatStateListener to them.
*/
private SmackChatManagerListener smackChatManagerListener = null;
/**
* @param provider a ref to the <tt>ProtocolProviderServiceImpl</tt>
* that created us and that we'll use for retrieving the underlying aim
@ -148,15 +147,12 @@ private void sendXep85ChatState(Contact contact, int state)
|| parentProvider.getConnection() == null)
return;
String toJID = null;
String toJID = opSetBasicIM.getRecentJIDForAddress(contact.getAddress());
// find the currently contacted jid to send typing info to him
// or if we do not have a jid and we have already sent message to the
// bare jid we will also send typing info there
OperationSetBasicInstantMessagingJabberImpl.TargetAddress ta
= opSetBasicIM.getJidForAddress(contact.getAddress());
if (ta != null)
toJID = ta.jid;
// if we haven't sent a message yet, do not send typing notifications
if(toJID == null)
@ -166,13 +162,6 @@ private void sendXep85ChatState(Contact contact, int state)
logger.trace("Sending XEP-0085 chat state=" + state
+ " to " + toJID);
Chat chat;
if(ta != null && ta.chat != null)
chat = ta.chat;
else
chat = parentProvider.getConnection()
.getChatManager().createChat(toJID, null);
ChatState chatState = null;
@ -193,18 +182,29 @@ else if(state == STATE_PAUSED)
chatState = ChatState.gone;
}
try
{
ChatStateManager.getInstance(parentProvider.getConnection())
.setCurrentState(chatState, chat);
}
catch(XMPPException exc)
{
//we don't want to bother the user with network exceptions
//so let's simply log it.
logger.warn("Failed to send state [" + state + "] to ["
+ contact.getAddress() + "].", exc);
}
setCurrentState(chatState, toJID);
}
/**
* Creates and sends a packet for the new chat state.
* @param chatState the new chat state.
* @param jid the JID of the receiver.
*/
private void setCurrentState(ChatState chatState, String jid)
{
String threadID = opSetBasicIM.getThreadIDForAddress(jid);
if(threadID == null)
return;
Message message = new Message();
ChatStateExtension extension = new ChatStateExtension(chatState);
message.addExtension(extension);
message.setTo(jid);
message.setType(Message.Type.chat);
message.setThread(threadID);
message.setFrom(parentProvider.getConnection().getUser());
parentProvider.getConnection().sendPacket(message);
}
/**
@ -271,30 +271,27 @@ public void registrationStateChanged(RegistrationStateChangeEvent evt)
messageEventManager.addMessageEventNotificationListener(
new IncomingMessageEventsListener());
//according to the smack api documentation we need to do this
//every time we connect in order to reinitialize the chat state
//manager (@see http://tinyurl.com/6j9uqs )
ChatStateManager.getInstance(parentProvider.getConnection());
if(smackChatStateListener == null)
smackChatStateListener = new SmackChatStateListener();
parentProvider.getConnection().addPacketListener(
smackChatStateListener, new PacketTypeFilter(Message.class));
if(smackChatManagerListener == null)
smackChatManagerListener = new SmackChatManagerListener();
parentProvider.getConnection().getChatManager()
.addChatListener(smackChatManagerListener);
}
else if(evt.getNewState() == RegistrationState.UNREGISTERED
|| evt.getNewState() == RegistrationState.AUTHENTICATION_FAILED
|| evt.getNewState() == RegistrationState.CONNECTION_FAILED)
{
if(parentProvider.getConnection() != null
&& parentProvider.getConnection().getChatManager() != null)
if(parentProvider.getConnection() != null)
{
parentProvider.getConnection().getChatManager()
.removeChatListener(smackChatManagerListener);
parentProvider.getConnection()
.removePacketListener(smackChatStateListener);
}
smackChatManagerListener = null;
smackChatStateListener = null;
if(messageEventManager != null)
{
@ -305,32 +302,6 @@ else if(evt.getNewState() == RegistrationState.UNREGISTERED
}
}
/**
* The class that we use when listening for new chats so that we could start
* tracking them for chat events.
*/
private class SmackChatManagerListener implements ChatManagerListener
{
/**
* Simply adds a chat state listener to every newly created chat
* so that we could track it for chat state events.
*
* @param chat the chat that we need to add a state listener to.
* @param isLocal indicates whether the chat has been initiated by us
*/
public void chatCreated(Chat chat, boolean isLocal)
{
if (logger.isTraceEnabled())
logger.trace("Created a chat with "
+ chat.getParticipant() + " local="+isLocal);
if(smackChatStateListener == null)
smackChatStateListener = new SmackChatStateListener();
chat.addMessageListener(smackChatStateListener);
};
}
/**
* Listens for incoming request for typing info
*/
@ -419,7 +390,7 @@ public void cancelledNotification(String from, String packetID)
* to XEP-0085.
*/
private class SmackChatStateListener
implements ChatStateListener
implements PacketListener
{
/**
* Called by smack when the state of a chat changes.
@ -428,15 +399,16 @@ private class SmackChatStateListener
* @param state the new state of the chat.
* @param message the message containing the new chat state
*/
public void stateChanged(Chat chat,
ChatState state,
public void stateChanged(ChatState state,
org.jivesoftware.smack.packet.Message message)
{
String fromJID = message.getFrom();
if (logger.isTraceEnabled())
logger.trace(chat.getParticipant() + " entered the "
logger.trace(fromJID + " entered the "
+ state.name()+ " state.");
String fromID = StringUtils.parseBareAddress(chat.getParticipant());
String fromID = StringUtils.parseBareAddress(fromJID);
List<ChatRoom> chatRooms = parentProvider.getOperationSet(
OperationSetMultiUserChat.class).getCurrentlyJoinedChatRooms();
@ -465,8 +437,7 @@ public void stateChanged(Chat chat,
{
//create the volatile contact
sourceContact = opSetPersPresence.createVolatileContact(
(isPrivateMessagingAddress? message.getFrom() :
chat.getParticipant()), isPrivateMessagingAddress);
message.getFrom(), isPrivateMessagingAddress);
}
}
@ -496,19 +467,15 @@ else if(evtCode != STATE_UNKNOWN)
logger.warn("Unknown typing state!");
}
/**
* Called when a new message is received. We ignore this one since
* we handle message reception on a lower level.
*
* @param chat the chat that the message belongs to
* @param msg the message that we need to process.
*/
public void processMessage(Chat chat,
org.jivesoftware.smack.packet.Message msg)
@Override
public void processPacket(Packet packet)
{
if (logger.isTraceEnabled())
logger.trace("ignoring a process message");
Message msg = (Message) packet;
ChatStateExtension ext = (ChatStateExtension) msg.getExtension(
"http://jabber.org/protocol/chatstates");
if(ext == null)
return;
stateChanged(ChatState.valueOf(ext.getElementName()), msg);
}
}
}

Loading…
Cancel
Save