Commit 554c9eaa authored by Armando Jagucki's avatar Armando Jagucki Committed by ajagucki

Updated the Openfire side of the MUC Transcript feature in Clearspace....

Updated the Openfire side of the MUC Transcript feature in Clearspace. [CS-4030] Reviewer: David Smith

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/trunk@10203 b35dd754-fafc-0310-a699-88a17e54d16e
parent b94b5b7e
...@@ -2,82 +2,65 @@ package org.jivesoftware.openfire.clearspace; ...@@ -2,82 +2,65 @@ package org.jivesoftware.openfire.clearspace;
import org.xmpp.packet.JID; import org.xmpp.packet.JID;
import org.jivesoftware.util.cache.ExternalizableUtil; import org.jivesoftware.util.cache.ExternalizableUtil;
import org.jivesoftware.util.cache.Cacheable;
import org.jivesoftware.util.cache.CacheSizes;
import java.io.Externalizable; import java.io.Externalizable;
import java.io.IOException; import java.io.IOException;
import java.io.ObjectOutput; import java.io.ObjectOutput;
import java.io.ObjectInput; import java.io.ObjectInput;
import java.util.Date;
/** /**
* A MUC event that is intended to be recorded in a transcript for a group chat room in Clearspace. * A MUC event that is intended to be recorded in a transcript for a group chat room in Clearspace.
* *
* @author Armando Jagucki * @author Armando Jagucki
*/ */
public class ClearspaceMUCTranscriptEvent implements Externalizable, Cacheable { public class ClearspaceMUCTranscriptEvent implements Externalizable {
private Type type; public Type type;
public Date date; public long timestamp;
public String body; public String body;
public JID roomJID; public JID roomJID;
private JID user; public JID user;
public String nickname;
public static ClearspaceMUCTranscriptEvent roomDestroyed(JID roomJID, long timestamp) {
public static ClearspaceMUCTranscriptEvent roomDestroyed(JID roomJID, Date date) {
ClearspaceMUCTranscriptEvent event = new ClearspaceMUCTranscriptEvent(); ClearspaceMUCTranscriptEvent event = new ClearspaceMUCTranscriptEvent();
event.type = Type.roomDestroyed; event.type = Type.roomDestroyed;
event.roomJID = roomJID; event.roomJID = roomJID;
event.date = date; event.timestamp = timestamp;
return event; return event;
} }
public static ClearspaceMUCTranscriptEvent occupantJoined(JID roomJID, JID user, String nickname, Date date) { public static ClearspaceMUCTranscriptEvent occupantJoined(JID roomJID, JID user, long timestamp) {
ClearspaceMUCTranscriptEvent event = new ClearspaceMUCTranscriptEvent(); ClearspaceMUCTranscriptEvent event = new ClearspaceMUCTranscriptEvent();
event.type = Type.occupantJoined; event.type = Type.occupantJoined;
event.roomJID = roomJID; event.roomJID = roomJID;
event.user = user; event.user = user;
event.nickname = nickname; event.timestamp = timestamp;
event.date = date;
return event; return event;
} }
public static ClearspaceMUCTranscriptEvent occupantLeft(JID roomJID, JID user, Date date) { public static ClearspaceMUCTranscriptEvent occupantLeft(JID roomJID, JID user, long timestamp) {
ClearspaceMUCTranscriptEvent event = new ClearspaceMUCTranscriptEvent(); ClearspaceMUCTranscriptEvent event = new ClearspaceMUCTranscriptEvent();
event.type = Type.occupantLeft; event.type = Type.occupantLeft;
event.roomJID = roomJID; event.roomJID = roomJID;
event.user = user; event.user = user;
event.date = date; event.timestamp = timestamp;
return event;
}
public static ClearspaceMUCTranscriptEvent nicknameChanged(JID roomJID, JID user, String newNickname, Date date) {
ClearspaceMUCTranscriptEvent event = new ClearspaceMUCTranscriptEvent();
event.type = Type.nicknameChanged;
event.roomJID = roomJID;
event.user = user;
event.nickname = newNickname;
event.date = date;
return event; return event;
} }
public static ClearspaceMUCTranscriptEvent roomMessageReceived(JID roomJID, JID user, String nickname, String body, public static ClearspaceMUCTranscriptEvent roomMessageReceived(JID roomJID, JID user, String body,
Date date) { long timestamp) {
ClearspaceMUCTranscriptEvent event = new ClearspaceMUCTranscriptEvent(); ClearspaceMUCTranscriptEvent event = new ClearspaceMUCTranscriptEvent();
event.type = Type.roomMessageReceived; event.type = Type.roomMessageReceived;
event.roomJID = roomJID; event.roomJID = roomJID;
event.user = user; event.user = user;
event.nickname = nickname;
event.body = body; event.body = body;
event.date = date; event.timestamp = timestamp;
return event; return event;
} }
public void writeExternal(ObjectOutput out) throws IOException { public void writeExternal(ObjectOutput out) throws IOException {
ExternalizableUtil.getInstance().writeInt(out, type.ordinal()); ExternalizableUtil.getInstance().writeInt(out, type.ordinal());
ExternalizableUtil.getInstance().writeLong(out, date.getTime()); ExternalizableUtil.getInstance().writeLong(out, timestamp);
ExternalizableUtil.getInstance().writeBoolean(out, body != null); ExternalizableUtil.getInstance().writeBoolean(out, body != null);
if (body != null) { if (body != null) {
...@@ -92,15 +75,11 @@ public class ClearspaceMUCTranscriptEvent implements Externalizable, Cacheable { ...@@ -92,15 +75,11 @@ public class ClearspaceMUCTranscriptEvent implements Externalizable, Cacheable {
if (user != null) { if (user != null) {
ExternalizableUtil.getInstance().writeSerializable(out, user); ExternalizableUtil.getInstance().writeSerializable(out, user);
} }
ExternalizableUtil.getInstance().writeBoolean(out, nickname != null);
if (nickname != null) {
ExternalizableUtil.getInstance().writeSafeUTF(out, nickname);
}
} }
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
type = Type.values()[ExternalizableUtil.getInstance().readInt(in)]; type = Type.values()[ExternalizableUtil.getInstance().readInt(in)];
date = new Date(ExternalizableUtil.getInstance().readLong(in)); timestamp = ExternalizableUtil.getInstance().readLong(in);
if (ExternalizableUtil.getInstance().readBoolean(in)) { if (ExternalizableUtil.getInstance().readBoolean(in)) {
body = ExternalizableUtil.getInstance().readSafeUTF(in); body = ExternalizableUtil.getInstance().readSafeUTF(in);
...@@ -112,29 +91,9 @@ public class ClearspaceMUCTranscriptEvent implements Externalizable, Cacheable { ...@@ -112,29 +91,9 @@ public class ClearspaceMUCTranscriptEvent implements Externalizable, Cacheable {
if (ExternalizableUtil.getInstance().readBoolean(in)) { if (ExternalizableUtil.getInstance().readBoolean(in)) {
user = (JID) ExternalizableUtil.getInstance().readSerializable(in); user = (JID) ExternalizableUtil.getInstance().readSerializable(in);
} }
if (ExternalizableUtil.getInstance().readBoolean(in)) {
nickname = ExternalizableUtil.getInstance().readSafeUTF(in);
}
}
/**
* Used to estimate the size of this event as represented in a fragment of a
* transcript-update packet sent by ClearspaceMUCTranscriptManager.
*
* We do not intend to use this object in a cache.
*
* @return the estimated size of this event as represented in a transcript-update packet.
*/
public int getCachedSize() {
int size = CacheSizes.sizeOfString(date.toString());
size += CacheSizes.sizeOfString(body);
size += CacheSizes.sizeOfString(roomJID.toString());
size += CacheSizes.sizeOfString(user.toString());
size += CacheSizes.sizeOfString(nickname);
return size;
} }
private static enum Type { public static enum Type {
/** /**
* Event triggered when a room was destroyed. * Event triggered when a room was destroyed.
*/ */
...@@ -147,17 +106,9 @@ public class ClearspaceMUCTranscriptEvent implements Externalizable, Cacheable { ...@@ -147,17 +106,9 @@ public class ClearspaceMUCTranscriptEvent implements Externalizable, Cacheable {
* Event triggered when an occupant left a room. * Event triggered when an occupant left a room.
*/ */
occupantLeft, occupantLeft,
/**
* Event triggered when an occupant changed his nickname in a room.
*/
nicknameChanged,
/** /**
* Event triggered when a room occupant sent a message to a room. * Event triggered when a room occupant sent a message to a room.
*/ */
roomMessageReceived, roomMessageReceived
/**
* Event triggered when a user sent a message to another user.
*/
chatMessageReceived
} }
} }
...@@ -2,12 +2,14 @@ package org.jivesoftware.openfire.clearspace; ...@@ -2,12 +2,14 @@ package org.jivesoftware.openfire.clearspace;
import org.jivesoftware.openfire.muc.MUCEventListener; import org.jivesoftware.openfire.muc.MUCEventListener;
import org.jivesoftware.openfire.muc.MUCEventDispatcher; import org.jivesoftware.openfire.muc.MUCEventDispatcher;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.util.TaskEngine; import org.jivesoftware.util.TaskEngine;
import org.jivesoftware.util.JiveConstants; import org.jivesoftware.util.JiveConstants;
import org.jivesoftware.util.Log; import org.jivesoftware.util.Log;
import org.jivesoftware.util.cache.CacheSizes;
import org.xmpp.packet.JID; import org.xmpp.packet.JID;
import org.xmpp.packet.Message; import org.xmpp.packet.Message;
import org.xmpp.packet.IQ;
import org.dom4j.Element;
import java.util.*; import java.util.*;
...@@ -25,7 +27,40 @@ import java.util.*; ...@@ -25,7 +27,40 @@ import java.util.*;
* the effective size as it will appear in a transcript-update packet). * the effective size as it will appear in a transcript-update packet).
* *
* Example of a transcript-update packet: * Example of a transcript-update packet:
* TODO: Add example * <iq type='set' to='clearspace.example.org' from='clearspace-conference.example.org'>
* <transcript-update xmlns='http://jivesoftware.com/clearspace'>
* <presence from='user1@example.org'>
* <roomjid>14-1234@clearspace-conference.example.org</roomjid>
* <timestamp>1207933781000</timestamp>
* </presence>
* <message from='user1@example.org'>
* <roomjid>14-1234@clearspace-conference.example.org</roomjid>
* <timestamp>1207933783000</timestamp>
* <body>user2, I won the lottery!</body>
* </message>
* <message from='user2@example.org'>
* <roomjid>14-1234@clearspace-conference.example.org</roomjid>
* <timestamp>1207933785000</timestamp>
* <body>WHAT?!</body>
* </message>
* <message from='user1@example.org'>
* <roomjid>14-1234@clearspace-conference.example.org</roomjid>
* <timestamp>1207933787000</timestamp>
* <body>April Fools!</body>
* </message>
* <presence from='user3@example.org' type='unavailable'>
* <roomjid>14-1234@clearspace-conference.example.org</roomjid>
* <timestamp>1207933789000</timestamp>
* </presence>
* <message from="user2@example.org">
* <roomjid>14-1234@clearspace-conference.example.org</roomjid>
* <timestamp>120793379100</timestamp>
* <body>Wow, that was lame.</body>
* </message>
*
* ...
* </transcript-update>
* </iq>
* *
* @author Armando Jagucki * @author Armando Jagucki
*/ */
...@@ -33,19 +68,25 @@ public class ClearspaceMUCTranscriptManager implements MUCEventListener { ...@@ -33,19 +68,25 @@ public class ClearspaceMUCTranscriptManager implements MUCEventListener {
/** /**
* Group chat events that are pending to be sent to Clearspace. * Group chat events that are pending to be sent to Clearspace.
* Key: MUC Room JID; Value: List of group chat events.
*/ */
private final List<ClearspaceMUCTranscriptEvent> roomEvents; private final List<ClearspaceMUCTranscriptEvent> roomEvents;
private final TaskEngine taskEngine; private final TaskEngine taskEngine;
private TimerTask transcriptUpdateTask; private TimerTask transcriptUpdateTask;
private final int MAX_QUEUE_SIZE = 32768; // TODO: Fine tune this size value during testing private final int MAX_QUEUE_SIZE = 64;
private final long FLUSH_PERIOD = JiveConstants.MINUTE * 1; // TODO: FIXME: Set to 5 when done testing private final long FLUSH_PERIOD = JiveConstants.MINUTE * 2;
private String csMucDomain;
private String csComponentAddress;
public ClearspaceMUCTranscriptManager(TaskEngine taskEngine) { public ClearspaceMUCTranscriptManager(TaskEngine taskEngine) {
this.taskEngine = taskEngine; this.taskEngine = taskEngine;
roomEvents = new ArrayList<ClearspaceMUCTranscriptEvent>(); roomEvents = new ArrayList<ClearspaceMUCTranscriptEvent>();
String xmppDomain = XMPPServer.getInstance().getServerInfo().getXMPPDomain();
csMucDomain = ClearspaceManager.MUC_SUBDOMAIN + "." + xmppDomain;
csComponentAddress = ClearspaceManager.CLEARSPACE_COMPONENT + "." + xmppDomain;
} }
public void start() { public void start() {
...@@ -58,16 +99,50 @@ public class ClearspaceMUCTranscriptManager implements MUCEventListener { ...@@ -58,16 +99,50 @@ public class ClearspaceMUCTranscriptManager implements MUCEventListener {
return; return;
} }
// Create the transcript-update packet
IQ packet = new IQ();
packet.setTo(csComponentAddress);
packet.setFrom(csMucDomain);
packet.setType(IQ.Type.set);
Element transcriptElement = packet.setChildElement("transcript-update", "http://jivesoftware.com/clearspace");
for (ClearspaceMUCTranscriptEvent event : roomEvents) { for (ClearspaceMUCTranscriptEvent event : roomEvents) {
// Add event to the packet // Add event to the packet
Element mucEventElement = null;
// TODO: FIXME: Remove after finished testing
Log.info(event.roomJID.getNode() + " - " + event.nickname + ": " + event.body); switch (event.type) {
case roomMessageReceived:
mucEventElement = transcriptElement.addElement("message");
mucEventElement.addElement("body").setText(event.body);
break;
case occupantJoined:
mucEventElement = transcriptElement.addElement("presence");
break;
case occupantLeft:
mucEventElement = transcriptElement.addElement("presence");
mucEventElement.addAttribute("type", "unavailable");
break;
}
// Now add those event fields that are common to all elements in the transcript-update packet.
if (mucEventElement != null) {
mucEventElement.addAttribute("from", event.user.toBareJID());
mucEventElement.addElement("roomjid").setText(event.roomJID.toBareJID());
mucEventElement.addElement("timestamp").setText(Long.toString(event.timestamp));
}
} }
// TODO: Send the packet to Clearspace // Send the transcript-update packet to Clearspace.
IQ result = ClearspaceManager.getInstance().query(packet, 15000);
if (result == null) {
// No answer was received from Clearspace.
Log.warn("Did not get a reply from sending a transcript-update packet to Clearspace.");
// We can empty the queue now // Return early so that the room-events queue is not cleared.
return;
}
// We can clear the queue now.
roomEvents.clear(); roomEvents.clear();
} }
}; };
...@@ -80,28 +155,29 @@ public class ClearspaceMUCTranscriptManager implements MUCEventListener { ...@@ -80,28 +155,29 @@ public class ClearspaceMUCTranscriptManager implements MUCEventListener {
} }
public void roomCreated(JID roomJID) { public void roomCreated(JID roomJID) {
// TODO: Implement // Do nothing
} }
public void roomDestroyed(JID roomJID) { public void roomDestroyed(JID roomJID) {
// TODO: Implement // We want to flush the queue immediately when a room is destroyed.
forceQueueFlush();
} }
public void occupantJoined(JID roomJID, JID user, String nickname) { public void occupantJoined(JID roomJID, JID user, String nickname) {
// TODO: Implement addGroupChatEvent(ClearspaceMUCTranscriptEvent.occupantJoined(roomJID, user, new Date().getTime()));
} }
public void occupantLeft(JID roomJID, JID user) { public void occupantLeft(JID roomJID, JID user) {
// TODO: Implement addGroupChatEvent(ClearspaceMUCTranscriptEvent.occupantLeft(roomJID, user, new Date().getTime()));
} }
public void nicknameChanged(JID roomJID, JID user, String oldNickname, String newNickname) { public void nicknameChanged(JID roomJID, JID user, String oldNickname, String newNickname) {
// TODO: Implement // Do nothing
} }
public void messageReceived(JID roomJID, JID user, String nickname, Message message) { public void messageReceived(JID roomJID, JID user, String nickname, Message message) {
addGroupChatEvent(ClearspaceMUCTranscriptEvent.roomMessageReceived(roomJID, user, nickname, message.getBody(), addGroupChatEvent(ClearspaceMUCTranscriptEvent.roomMessageReceived(roomJID, user, message.getBody(),
new Date())); new Date().getTime()));
} }
/** /**
...@@ -113,23 +189,21 @@ public class ClearspaceMUCTranscriptManager implements MUCEventListener { ...@@ -113,23 +189,21 @@ public class ClearspaceMUCTranscriptManager implements MUCEventListener {
roomEvents.add(event); roomEvents.add(event);
// Check if we have exceeded the allowed size before a flush should occur. // Check if we have exceeded the allowed size before a flush should occur.
if (getEffectiveQueueSize(roomEvents) > MAX_QUEUE_SIZE) { if (roomEvents.size() > MAX_QUEUE_SIZE) {
// Flush the queue immediately and reschedule the task. // Flush the queue immediately and reschedule the task.
transcriptUpdateTask.cancel(); forceQueueFlush();
transcriptUpdateTask.run();
taskEngine.schedule(transcriptUpdateTask, FLUSH_PERIOD, FLUSH_PERIOD);
} }
} }
/** /**
* Used to estimate the 'effective' size of the event queue as represented in the * Forces the transcript-event queue to be sent to Clearspace by running the transcript-update
* transcript-update packet sent by ClearspaceMUCTranscriptManager. We are not calculating * task immediately.
* the size of the object in memory, but rather the approximate size of the resulting packet
* to be sent, in bytes.
* *
* @return the estimated size of the event queue represented as a transcript-update packet. * The transcript-update task is then rescheduled.
*/ */
public int getEffectiveQueueSize(List<ClearspaceMUCTranscriptEvent> queue) { private void forceQueueFlush() {
return CacheSizes.sizeOfCollection(queue); transcriptUpdateTask.cancel();
transcriptUpdateTask.run();
taskEngine.schedule(transcriptUpdateTask, FLUSH_PERIOD, FLUSH_PERIOD);
} }
} }
...@@ -466,8 +466,7 @@ public class ClearspaceManager extends BasicModule implements ExternalComponentM ...@@ -466,8 +466,7 @@ public class ClearspaceManager extends BasicModule implements ExternalComponentM
startClearspaceConfig(); startClearspaceConfig();
// Starts the Clearspace MUC transcript manager // Starts the Clearspace MUC transcript manager
// TODO: Uncomment when the transcript manager is implemented completely mucTranscriptManager.start();
//mucTranscriptManager.start();
} }
} }
...@@ -475,8 +474,7 @@ public class ClearspaceManager extends BasicModule implements ExternalComponentM ...@@ -475,8 +474,7 @@ public class ClearspaceManager extends BasicModule implements ExternalComponentM
super.stop(); super.stop();
// Stops the Clearspace MUC transcript manager // Stops the Clearspace MUC transcript manager
// TODO: Uncomment when the transcript manager is implemented completely mucTranscriptManager.stop();
//mucTranscriptManager.stop();
// Unregister/shut down custom MUC service // Unregister/shut down custom MUC service
XMPPServer.getInstance().getMultiUserChatManager().unregisterMultiUserChatService(MUC_SUBDOMAIN); XMPPServer.getInstance().getMultiUserChatManager().unregisterMultiUserChatService(MUC_SUBDOMAIN);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment