Commit e8629f0d authored by Armando Jagucki's avatar Armando Jagucki Committed by ajagucki

Initial work on Clearspace MUC transcript-management in place, yet unfinished....

Initial work on Clearspace MUC transcript-management in place, yet unfinished. [CS-4030] Reviewer: David

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/trunk@10168 b35dd754-fafc-0310-a699-88a17e54d16e
parent aa354acb
......@@ -71,23 +71,14 @@ public class ClearspaceMUCEventDelegate extends MUCEventDelegate {
}
/**
* This event will be triggered when an entity joins an existing room.
* <p/>
* Returns true if the user is allowed to join the room.
* Returns true if the user is allowed to join the room. If the userjid is an owner of the room,
* we will return true immediately.
*
* @param room the room the user is attempting to join.
* @param userjid the JID of the user attempting to join the room.
* @return true if the user is allowed to join the room.
*/
public boolean joiningRoom(MUCRoom room, JID userjid) {
// Packet should look like:
// <iq to="clearspace.example.org" from="clearspace-conference.example.org">
// <join-check xmlns="http://jivesoftware.com/clearspace">
// <userjid>username@example.org</userjid>
// <roomjid>14-1234@clearspace-conference.example.org</roomjid>
// </join-check>
// </iq>
// Always allow an owner to join the room (especially since they need to join to configure the
// room on initial creation).
Collection<String> owners = room.getOwners();
......@@ -95,6 +86,13 @@ public class ClearspaceMUCEventDelegate extends MUCEventDelegate {
return true;
}
// Packet should look like:
// <iq to="clearspace.example.org" from="clearspace-conference.example.org">
// <join-check xmlns="http://jivesoftware.com/clearspace">
// <userjid>username@example.org</userjid>
// <roomjid>14-1234@clearspace-conference.example.org</roomjid>
// </join-check>
// </iq>
IQ query = new IQ();
query.setFrom(csMucDomain);
Element cmd = query.setChildElement("join-check", "http://jivesoftware.com/clearspace");
......@@ -131,24 +129,24 @@ public class ClearspaceMUCEventDelegate extends MUCEventDelegate {
IQ result = ClearspaceManager.getInstance().query(iq, 15000);
if (result == null) {
// No answer was received from Clearspace, so return null.
Log.warn(GET_ROOM_CONFIG_WARNING);
Log.warn(GET_ROOM_CONFIG_WARNING + " Room: " + roomJid.toBareJID());
return null;
}
else if (result.getType() != IQ.Type.result) {
// The reply was not a valid result containing the room configuration, so return null.
Log.warn(GET_ROOM_CONFIG_WARNING);
Log.warn(GET_ROOM_CONFIG_WARNING + " Room: " + roomJid.toBareJID());
return null;
}
// Setup room configuration based on the configuration values in the result packet.
Element query = result.getChildElement();
if (query == null) {
Log.warn(GET_ROOM_CONFIG_WARNING);
Log.warn(GET_ROOM_CONFIG_WARNING + " Room: " + roomJid.toBareJID());
return null;
}
Element xElement = query.element("x");
if (xElement == null) {
Log.warn(GET_ROOM_CONFIG_WARNING);
Log.warn(GET_ROOM_CONFIG_WARNING + " Room: " + roomJid.toBareJID());
return null;
}
Iterator fields = xElement.elementIterator("field");
......
package org.jivesoftware.openfire.clearspace;
import org.xmpp.packet.JID;
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.IOException;
import java.io.ObjectOutput;
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.
*
* @author Armando Jagucki
*/
public class ClearspaceMUCTranscriptEvent implements Externalizable, Cacheable {
private Type type;
public Date date;
public String body;
public JID roomJID;
private JID user;
public String nickname;
public static ClearspaceMUCTranscriptEvent roomDestroyed(JID roomJID, Date date) {
ClearspaceMUCTranscriptEvent event = new ClearspaceMUCTranscriptEvent();
event.type = Type.roomDestroyed;
event.roomJID = roomJID;
event.date = date;
return event;
}
public static ClearspaceMUCTranscriptEvent occupantJoined(JID roomJID, JID user, String nickname, Date date) {
ClearspaceMUCTranscriptEvent event = new ClearspaceMUCTranscriptEvent();
event.type = Type.occupantJoined;
event.roomJID = roomJID;
event.user = user;
event.nickname = nickname;
event.date = date;
return event;
}
public static ClearspaceMUCTranscriptEvent occupantLeft(JID roomJID, JID user, Date date) {
ClearspaceMUCTranscriptEvent event = new ClearspaceMUCTranscriptEvent();
event.type = Type.occupantLeft;
event.roomJID = roomJID;
event.user = user;
event.date = date;
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;
}
public static ClearspaceMUCTranscriptEvent roomMessageReceived(JID roomJID, JID user, String nickname, String body,
Date date) {
ClearspaceMUCTranscriptEvent event = new ClearspaceMUCTranscriptEvent();
event.type = Type.roomMessageReceived;
event.roomJID = roomJID;
event.user = user;
event.nickname = nickname;
event.body = body;
event.date = date;
return event;
}
public void writeExternal(ObjectOutput out) throws IOException {
ExternalizableUtil.getInstance().writeInt(out, type.ordinal());
ExternalizableUtil.getInstance().writeLong(out, date.getTime());
ExternalizableUtil.getInstance().writeBoolean(out, body != null);
if (body != null) {
ExternalizableUtil.getInstance().writeSafeUTF(out, body);
}
ExternalizableUtil.getInstance().writeBoolean(out, roomJID != null);
if (roomJID != null) {
ExternalizableUtil.getInstance().writeSerializable(out, roomJID);
}
ExternalizableUtil.getInstance().writeBoolean(out, user != null);
if (user != null) {
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 {
type = Type.values()[ExternalizableUtil.getInstance().readInt(in)];
date = new Date(ExternalizableUtil.getInstance().readLong(in));
if (ExternalizableUtil.getInstance().readBoolean(in)) {
body = ExternalizableUtil.getInstance().readSafeUTF(in);
}
if (ExternalizableUtil.getInstance().readBoolean(in)) {
roomJID = (JID) ExternalizableUtil.getInstance().readSerializable(in);
}
if (ExternalizableUtil.getInstance().readBoolean(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 {
/**
* Event triggered when a room was destroyed.
*/
roomDestroyed,
/**
* Event triggered when a new occupant joins a room.
*/
occupantJoined,
/**
* Event triggered when an occupant left a room.
*/
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.
*/
roomMessageReceived,
/**
* Event triggered when a user sent a message to another user.
*/
chatMessageReceived
}
}
package org.jivesoftware.openfire.clearspace;
import org.jivesoftware.openfire.muc.MUCEventListener;
import org.jivesoftware.openfire.muc.MUCEventDispatcher;
import org.jivesoftware.util.TaskEngine;
import org.jivesoftware.util.JiveConstants;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.cache.CacheSizes;
import org.xmpp.packet.JID;
import org.xmpp.packet.Message;
import java.util.*;
/**
* Stores MUC events that are intended to be recorded as a transcript for a group chat room in Clearspace.
* A task will periodically flush the queue of MUC events, sending them to the Clearspace component via XMPP
* for parsing and storing.
*
* Clearspace is expected to handle the packets containing MUC events by parsing them as they come in, accumulating
* them into a daily group chat transcript for the room it is associated with.
*
* The task will flush each queue of MUC events assoicated with a room based on either the size of the queue, or time.
* If the size of the queue exceeds a limit we have set, or a certain period of time has elapsed,
* the queue will be sent to Clearspace -- whichever happens first. (When we say size of the queue, we really mean
* the effective size as it will appear in a transcript-update packet).
*
* Example of a transcript-update packet:
* TODO: Add example
*
* @author Armando Jagucki
*/
public class ClearspaceMUCTranscriptManager implements MUCEventListener {
/**
* 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 TaskEngine taskEngine;
private TimerTask transcriptUpdateTask;
private final int MAX_QUEUE_SIZE = 32768; // TODO: Fine tune this size value during testing
private final long FLUSH_PERIOD = JiveConstants.MINUTE * 1; // TODO: FIXME: Set to 5 when done testing
public ClearspaceMUCTranscriptManager(TaskEngine taskEngine) {
this.taskEngine = taskEngine;
roomEvents = new ArrayList<ClearspaceMUCTranscriptEvent>();
}
public void start() {
MUCEventDispatcher.addListener(this);
// Schedule a task for this new transcript event queue.
transcriptUpdateTask = new TimerTask() {
public void run() {
if (roomEvents.isEmpty()) {
return;
}
for (ClearspaceMUCTranscriptEvent event : roomEvents) {
// Add event to the packet
// TODO: FIXME: Remove after finished testing
Log.info(event.roomJID.getNode() + " - " + event.nickname + ": " + event.body);
}
// TODO: Send the packet to Clearspace
// We can empty the queue now
roomEvents.clear();
}
};
taskEngine.schedule(transcriptUpdateTask, FLUSH_PERIOD, FLUSH_PERIOD);
}
public void stop() {
MUCEventDispatcher.removeListener(this);
}
public void roomCreated(JID roomJID) {
// TODO: Implement
}
public void roomDestroyed(JID roomJID) {
// TODO: Implement
}
public void occupantJoined(JID roomJID, JID user, String nickname) {
// TODO: Implement
}
public void occupantLeft(JID roomJID, JID user) {
// TODO: Implement
}
public void nicknameChanged(JID roomJID, JID user, String oldNickname, String newNickname) {
// TODO: Implement
}
public void messageReceived(JID roomJID, JID user, String nickname, Message message) {
addGroupChatEvent(ClearspaceMUCTranscriptEvent.roomMessageReceived(roomJID, user, nickname, message.getBody(),
new Date()));
}
/**
* Queues the group chat event to be later sent to Clearspace.
*
* @param event MUC transcript event.
*/
private void addGroupChatEvent(ClearspaceMUCTranscriptEvent event) {
roomEvents.add(event);
// Check if we have exceeded the allowed size before a flush should occur.
if (getEffectiveQueueSize(roomEvents) > MAX_QUEUE_SIZE) {
// Flush the queue immediately and reschedule the task.
transcriptUpdateTask.cancel();
transcriptUpdateTask.run();
taskEngine.schedule(transcriptUpdateTask, FLUSH_PERIOD, FLUSH_PERIOD);
}
}
/**
* Used to estimate the 'effective' size of the event queue as represented in the
* transcript-update packet sent by ClearspaceMUCTranscriptManager. We are not calculating
* 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.
*/
public int getEffectiveQueueSize(List<ClearspaceMUCTranscriptEvent> queue) {
return CacheSizes.sizeOfCollection(queue);
}
}
......@@ -112,6 +112,10 @@ public class ClearspaceManager extends BasicModule implements ExternalComponentM
private String sharedSecret;
private Map<String, Long> userIDCache;
private Map<String, Long> groupIDCache;
/**
* Records transcripts for group chat rooms in Clearspace.
*/
private ClearspaceMUCTranscriptManager mucTranscriptManager = new ClearspaceMUCTranscriptManager(TaskEngine.getInstance());
/**
* Keep the domains of Clearspace components
*/
......@@ -412,12 +416,20 @@ public class ClearspaceManager extends BasicModule implements ExternalComponentM
// Starts the clearspace configuration task
startClearspaceConfig();
// Starts the Clearspace MUC transcript manager
// TODO: Uncomment when the transcript manager is implemented completely
//mucTranscriptManager.start();
}
}
public void stop() {
super.stop();
// Stops the Clearspace MUC transcript manager
// TODO: Uncomment when the transcript manager is implemented completely
//mucTranscriptManager.stop();
// Unregister/shut down custom MUC service
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